En estos días se ha visto en la lista internacional mucha discusión acerca de los BookMorphs.
Lex Spoon ha puntualizado que esta clase está obsoleta y que la última vez que anduvo como fue diseñada fue en la versión 2.8 y debemos sacar esta clase de la imagen.
Otros opinan que es util todavía.
Hoy comparto mi humilde contribución, con una subclase denominada Anotador , que espero en un futuro evolucione a un sistema de Help, tan necesario en Squeak, sobre todo para los newbies.
Lo primero, ya que tengo Mac , es solucionar los problemas al tratar de modificar lo menos posible lo que en su momento diseño Scott Wallace.
Tomando el ejemplo de los Squeaklets , o directorio dedicado a los proyectos en el path de la imagen, podemos hacer
ServerDirectory class
localSqueakBooksDirectory
| fd |
"Aqui nos aseguramos la existencia del directorio o lo
creamos en caso contrario"
LocalSqueakBooksDirectory ifNil: [fd := FileDirectory default.
(fd directoryExists: 'SqueakBooks') ifFalse:[fd createDirectory: 'SqueakBooks'].
LocalSqueakBooksDirectory := FileDirectory default pathName , FileDirectory
slash , 'SqueakBooks' ].
^LocalSqueakBooksDirectory
defaultStemUrl
"For writing on an FTP directory. Users should insert their own server
url here."
"ftp://jumbo.rd.wdi.disney.com/raid1/people/dani/Books/Grp/Grp"
" ServerDirectory defaultStemUrl "
"Hace bastante que se fueron de Disney, no ? "
| rand dir |
rand := String new: 4.
1 to: rand size do: [:ii |
rand at: ii put: ('bdfghklmnpqrstvwz' at: 17 atRandom)].
dir := self serverNamed: 'DaniOnJumbo' ifAbsent: [^ 'file://', ServerDirectory
localSqueakBooksDirectory,FileDirectory slash].
^ 'ftp://', dir server, dir slashDirectory, '/BK', rand
fileNamed: fullName
"Create a RemoteFileStream for writing. If the file exists, do not complain.
fullName is directory path, and does include name of the server. Or it can
just be a fileName. Only write the data upon close."
| file remoteStrm path |
file := self asServerFileNamed: fullName.
file readWrite.
file isTypeFile ifTrue: [(Smalltalk platformName = 'Mac OS') ifFalse: [
^ FileStream fileNamed: (file fileNameRelativeTo: self)] ifTrue:[ path :=
fullName copyFrom: 8to: fullName size.
^ FileStream fileNamed: path ]
].
remoteStrm := RemoteFileStream on: (String new: 2000).
remoteStrm remoteFile: file.
^ remoteStrm "no actual writing till close"
ServerDirectory
fileNamed: fullName
"Create a RemoteFileStream for writing. If the file exists, do not complain.
fullName is directory path, and does include name of the server. Or it can
just be a fileName. Only write the data upon close."
| file remoteStrm path |
file := self asServerFileNamed: fullName.
file readWrite.
file isTypeFile ifTrue: [(Smalltalk platformName = 'Mac OS') ifFalse: [
^ FileStream fileNamed: (file fileNameRelativeTo: self)] ifTrue:[ path :=
fullName copyFrom: 8to: fullName size.
^ FileStream fileNamed: path ]
].
remoteStrm := RemoteFileStream on: (String new: 2000).
remoteStrm remoteFile: file.
^ remoteStrm "no actual writing till close"
printOn: aStrm
aStrm nextPutAll: self class name; nextPut: $<.
aStrm nextPutAll: self moniker.
(Smalltalk platformName = 'Mac OS') ifFalse: [aStrm nextPut: $>].
Estos dos y lo siguiente para que funcione bien en Mac
FileUrl
privateInitializeFromText: aString
"Calculate host and path from a file URL in String format.
Some malformed formats are allowed and interpreted by guessing."
| schemeName pathString bare hasDriveLetter stream char i |
bare := aString withBlanksTrimmed.
schemeName := Url schemeNameForString: bare.
(schemeName isNil or: [schemeName ~= self schemeName])
ifTrue: [
host := ''.
pathString := bare]
ifFalse: [
"First remove schemeName and colon"
bare := bare copyFrom: (schemeName size + 2) to: bare size.
"A proper file URL then has two slashes before host,
A malformed URL is interpreted as using syntax file:<path>."
(bare beginsWith: '//')
ifTrue: [i := bare indexOf: $/ startingAt: 3.
i=0 ifTrue: [
host := bare copyFrom: 3 to: bare size.
pathString := '']
ifFalse: [
host := bare copyFrom: 3 to: i-1.
pathString := bare copyFrom: host size + 3 to: bare size]]
ifFalse: [host := ''.
pathString := bare]].
( Smalltalk platformName = 'Mac OS') ifFalse: [
self initializeFromPathString: pathString]
toText
"Return the FileUrl according to RFC1738 plus supporting fragments:
'file://<host>/<path>#<fragment>'
Note that <host> being '' is equivalent to 'localhost'.
Note: The pathString can not start with a leading $/
to indicate an 'absolute' file path.
This is not according to RFC1738 where the path should have
no leading or trailing slashes, and always
be considered absolute relative to the filesystem."
^String streamContents: [:s |
s nextPutAll: self schemeName, '://'.
host ifNotNil: [s nextPutAll: host].
(Smalltalk platformName = 'Mac OS') ifFalse: [s nextPut: $/]. s nextPutAll:
self pathString.
fragment ifNotNil: [ s nextPut: $#; nextPutAll: fragment encodeForHTTP ]]
Aqui pueden ver el Anotador con el menu desplegado al presionar el boton "How to"
Para los que tienen dificultades con los Morphs , lo mejor es ver que existe en el sistema, que se pueda manipular manualmente y luego hacer código para obtener la misma funcionalidad.
En mi caso, saque un Book del Flap Supplies y le di tamaño y color con el halo.
Decidi que los únicos elementos de cada "página" iban a ser un título y un contenedor de texto y que con un botón se desplegaría el "indice"
initialize
| text r boton lev lev2 lev3 |
super initialize.
self borderWidth: 2.
self
color: (Color
r: 0.939
g: 0.939
b: 0.258).
self resizePagesTo: 350 @ 500.
self pages first color: self color.
self showMoreControls.
self openInWorld.
boton := SimpleButtonMorph new label: 'How to ...';
target: self;
color: Color lightBlue;
actionSelector: #nombres.
lev := (submorphs at: 1).
lev2 := lev submorphs at: 1.
lev3 := lev2 submorphs at: 1.
lev3 submorphs last delete.
lev3 addMorph: boton .
lev3 color: self color.
self title: (StringMorph contents: 'My Help System' font: self font).
text := ScrollableField new.
text
color: (Color
r: 0.972
g: 0.972
b: 0.662).
self currentPage addMorph: text.
r := Rectangle
left: text owner left + 5
right: text owner right - 5
top: text owner top + 25
bottom: text owner bottom - 5.
text bounds: rRevisando el código en la imagen, aquí lo necesario para cada vez que se presione el boton "+" para agregar una nueva página
insertPage
| texto r |
pages isEmpty
ifTrue: [^ self insertPageColored: self color].
self insertPageColored: self color.
self getPageNamefromUser.
texto := self textContainer.
r := Rectangle
left: currentPage left + 5
right: currentPage right - 5
top: currentPage top + 20
bottom: currentPage bottom - 5.
texto bounds: r.
self currentPage addMorph: texto
Finalmente, los métodos de la categoría fileIn/out
localDiskSave
| nombre |
self forgetURLs.
self pages
do: [ :aPage |
nombre := ServerDirectory defaultStemUrl , self pageTitle: aPage
,'.sp'.
aPage saveOnURL: nombre].
self saveIndexOnDisk
saveIndexOnDisk
"Make up an index to the pages of this book, with thumbnails, and store it on the server. (aDictionary, aMorphObjectOut, aMorphObjectOut, aMorphObjectOut). The last part corresponds exactly to what pages looks like when they are all out. Each holds onto a SqueakPage, which holds a url and a thumbnail."
| index sf remoteFile urlList |
index := ServerDirectory defaultStemUrl , self getBookNamefromUser
, '.ind'.
urlList := pages collect: [:ppg | self pageTitle:ppg ].
sf := ServerDirectory new fullPath: index.
Cursor wait showWhile:
[remoteFile := sf fileNamed: index.
remoteFile dataIsValid.
remoteFile fileOutClass: nil andObject: urlList
"remoteFile close"]Aquí decidí reutilizar el código ya existente que graba un archivo .bo, que contiene un diccionario con varios items no necesarios a los fines del anotador, así que solo grabo una lista de los nombres de las páginas con nombres compatibilizados con lo ya existente en el sistema.
Por la misma razeon, para cargar nuevamente el Anotador
reloadMeFromDisk
| index urlList fileStream dir newpage |
dir := ServerDirectory defaultStemUrl.
dir := dir copyFrom: 8 to: dir size.
index := dir , self getBookNamefromUser
, '.ind'.
fileStream := FileStream oldFileNamed: index.
urlList := fileStream fileInObjectAndCode.
fileStream close.
urlList do: [:pgName | fileStream := FileStream oldFileNamed: dir,pgName. newpage := fileStream fileInObjectAndCode.
(newpage isKindOf: SqueakPage) ifTrue:[newpage := newpage asMorph].
fileStream close.pages add: newpage ].Como parte del Sblog team, decidí compatibilizar (y ver si hay sugerencias para completar esta idea a un sistema de help mas completo
exporting
| nombre texto blog story aPage|
blog := SLBlog name: 'help'.
2 to: pages size
do: [:index|
aPage := pages at: index.
nombre := self pageTitle: aPage .
texto := ( aPage findA: ScrollableField ) text .
texto ifNotNil: [texto := texto string]
ifNil: [self halt].
story := SLStory parent: blog.
story title: nombre.
story text: texto.
story post.
self halt].
blog save.
SLRssStore restore
Todo esto está funcionando en lo que alguno podría llamar un "fork" de Squeak, denominado Squeak3.7Heavy-5969.image, que es el Squeak 3.7 con un montón de paquetes cargados para desarrollo profesional.
Los que deseen experimentar con el Sblog, tal como está en Squeak Map, les destruirá la imagen y nunca mas la podrán usar.
Al día de hoy 14-jul-04, la solucion es
Sin embargo, si hacen backup antes de esto, les quedará en el directorio package-cache creado por el SM un archivo SmallBlog.1.sar, expandalo convenientemente y reemplazen el NamedProcess-mas.3.mcz por el NamedProcess-1.2.sar .
O esperen la actualización.
El código completo del anotador está en http://ar.groups.yahoo.com/group/squeakRos/
Como convertir el Anotador en un boton.
Seleccionar el anotador con el halo, presionar el boton marcado en el dibujo.
Aparecerá este dibujo
Es algo poderoso denominado "tile" .
Seleccionar con el halo y desplegar el menu.
Los items individuales pueden ser seleccionados y separados con sucesivos comando click (o alt click en la PC)
Así obtenemos
Seleccionando este botón y luego de cambiar color y agregarle un dibujo
queda