Grafoscopio/src/Grafoscopio/GrafoscopioBrowser.class.st

656 lines
22 KiB
Smalltalk

"
I am a graphical interface for showing trees, outlines and grafoscopio notebooks.
Instance Variables
browser: <Object>
browser
- xxxxx
"
Class {
#name : #GrafoscopioBrowser,
#superclass : #Object,
#instVars : [
'browser',
'mainTree',
'tagsAvailable',
'cacheNode',
'workingFile'
],
#classVars : [
'dockingBar',
'draftsLocation',
'recentTrees'
],
#classInstVars : [
'workingFile'
],
#category : #'Grafoscopio-UI'
}
{ #category : #tags }
GrafoscopioBrowser >> addTagTo: aNode [
"Tags the node passed as argument with a value from the collection of tags available"
| selection |
tagsAvailable isNil
ifFalse: [
selection := UIManager default chooseFrom: tagsAvailable title: 'Elija una etiqueta'.
aNode tagAs: (tagsAvailable at: selection)
]
ifTrue: [self messageNoTagsAvailable].
browser update.
]
{ #category : #tags }
GrafoscopioBrowser >> addToTagsAvailable [
"Adds a tag to the collection of tags available"
| tag |
tagsAvailable isNil
ifTrue: [tagsAvailable := OrderedCollection new].
tag := UIManager default
textEntry: 'Nombre de la etiqueta'
title: 'Agregue una etiqueta'.
tagsAvailable add: tag.
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> bodyForCodeIn: constructor for: aNode [
"Shows the body of a node as an interactive playground. If node is not tagged it will return itself,
so it can be rendered properly for other messages"
| innerBrowser |
aNode tags = 'código'
ifFalse: [^self].
innerBrowser := GTPlayground new.
constructor custom: innerBrowser.
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> bodyIn: constructor for: aNode [
"Shows the body in a constructor for selected node, if node is not tagged.
If it is tagged it will return itself, so it can be rendered properly for other messages"
| specialTags |
specialTags := #('código').
(specialTags includes: aNode tags)
ifTrue: [^self].
(constructor text)
title: 'Texto ';
format:[
aNode body isNil
ifTrue: [ '' ]
ifFalse: [ aNode body]].
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> buildBrowserNamed: aName [
"Main method for building the interface for trees and its nodes.
The name of the browser corresponds to the name of the file
where tree is stored (or is named 'draft.ston' by default)"
self configureInitialTags.
browser := GLMTabulator new
title: aName, ' | Grafoscopio'.
browser
column: [:c |
c row: #tree span: 6;
row: #nodeHeader span: 1] span: 2;
column: [ :c |
c row: #nodeBody span: 2] span: 5.
browser
updateOn: GLMItemAdded from: #yourself;
updateOn: GLMItemRemoved from: #yourself.
(browser transmit)
to: #tree;
andShow: [:a | self treeOn: a].
"Creating a self updatable body pane"
(browser transmit)
from: #tree port: #selection;
to: #nodeBody;
when: [:selection | selection notNil];
andShow: [ :a :node |
self bodyIn: a for: node.
self bodyForCodeIn: a for: node ].
(browser transmit )
from: #tree port: #selection;
from: #nodeBody port: #text;
when: [:node :text | text notNil & node notNil];
to: #nodeBody port: #neverland;
transformed: [:node :content |
node body: content asString.
"node tags = 'código'
ifFalse: [node body: content asString]
ifTrue: [node body: content asString. Transcript show: content asString]"].
(browser transmit)
from: #tree;
to: #nodeHeader;
andShow: [ :h | self headerOn: h ].
(browser transmit )
from: #tree port: #selection;
from: #nodeHeader port: #text;
to: #nodeHeader port: #neverland1;
when: [:node :text | text notNil & node notNil];
transformed: [:node :text | node header: text asString]
]
{ #category : #'system-support' }
GrafoscopioBrowser >> configureInitialTags [
"Configures a list of predefined tags available to use. More tags should be added from a repository.
The idea is that nodes are processed and show differently according to their tags. So users should be
able to define their own tags with custom UI and behaviour"
tagsAvailable := OrderedCollection new.
tagsAvailable
add: 'código';
add: 'ejercicio';
add: 'invisible';
add: 'obra';
add: 'original';
add: 'transmediado'.
]
{ #category : #persistence }
GrafoscopioBrowser >> copyNodeIntoCache: aNode [
"Copy the contents of the current node into cacheNode. It doesn't work recursively yet, so it doen't copy the children of the nodes"
cacheNode := GrafoscopioNode new.
cacheNode header: (aNode header).
cacheNode body: (aNode body).
self inform: 'Nodo copiado: ', aNode header
]
{ #category : #bibliography }
GrafoscopioBrowser >> customKeys [
"Replaces the default key in a BibTeX file for a custom key taken from the 'shorttitle' field.
Useful when autogenerated BibTeX has cryptic or non easy to memorize keys (for example in some
Zotero exports)"
| bibFile bibliography bibStream bibOutputer |
bibFile := ((workingFile parent) children
detect: [:each | each basename endsWith: 'bib' ] ifNone: [ ]).
bibFile ifNotNil: [
bibliography := CZBibParser parse: bibFile contents.
bibStream := '' writeStream.
(bibliography entries) do: [:bibItem |
bibItem fields do: [:some | some key = 'shorttitle'
ifTrue: [ bibItem key: some value ]].
bibOutputer := CZBibtexOutputer new.
bibStream nextPutAll:
(bibOutputer entryToBibtexString: bibItem); cr].
bibFile writeStreamDo: [:stream |
stream nextPutAll: bibStream contents withUnixLineEndings ]].
]
{ #category : #persistence }
GrafoscopioBrowser >> exportAsHtml [
"I exports the current tree to HTML, using the same name but different extension (.html).
IMPORTANT: Pandoc must be installed in your system. In the future Grafoscopio will provide"
| markdownFileLocation htmlFileLocation |
markdownFileLocation := ((workingFile parent) / workingFile basenameWithoutExtension) fullName, '.markdown'.
htmlFileLocation := ((workingFile parent) / workingFile basenameWithoutExtension) fullName, '.html'.
ExternalApp pandoc notNil
ifTrue:[
(Smalltalk platform name = 'unix') | (Smalltalk platform name = 'Mac OS')
ifTrue: [
OSProcess command: 'pandoc ', ' ', markdownFileLocation , ' --standalone -o ' , htmlFileLocation.
OSProcess command: 'exec echo "exportando como html"'.
self inform: 'Archivo exportado como html en: ', htmlFileLocation].
Smalltalk platform name = 'Win32'
ifTrue: [ OSProcess command: 'pandoc ', markdownFileLocation , ' --standalone -o ' , htmlFileLocation ]]
ifFalse: [ExternalApp pandoc].
]
{ #category : #persistence }
GrafoscopioBrowser >> exportAsLatex [
"I Export the current tree to LaTeX, using the same name but different extension (.tex).
IMPORTANT: The user needs to have TeX installed with minted support and pygments for syntax highlighting.
More details at: http://ctan.dcc.uchile.cl/macros/latex/contrib/minted/minted.pdf"
| markdownFileLocation latexFileLocation fileName |
fileName := workingFile basenameWithoutExtension.
markdownFileLocation := ((workingFile parent) / fileName) fullName, '.markdown'.
latexFileLocation := ((workingFile parent) / fileName) fullName, '.tex'.
ExternalApp pandoc binaryLocation exists
ifFalse: [ExternalApp configurePandoc]
ifTrue:[
OSProcess command:
'pandoc ', markdownFileLocation,
' -V documentclass:article -V geometry:margin=2cm --standalone -o ' ,
fileName, '.tex'.
"Moving the pdf output to the expected location"
latexFileLocation asFileReference exists ifTrue: [latexFileLocation asFileReference ensureDelete ].
(FileLocator imageDirectory parent / (fileName, '.tex'))
moveTo: latexFileLocation asFileReference.
"Cleaning left overs"
(FileLocator imageDirectory parent / (fileName, '.tex')) ensureDelete.
self inform: 'Archivo exportado como latex en: ', latexFileLocation
]
]
{ #category : #persistence }
GrafoscopioBrowser >> exportAsPdf [
"Exports the current tree to HTML, using the same name but different extension (.pdf).
IMPORTANT: The user needs to have installed TeX to create the pdf."
| latexFileLocation pdfFileLocation fileName |
fileName := workingFile basenameWithoutExtension.
self exportAsLatex.
latexFileLocation := ((workingFile parent) / fileName) fullName, '.tex'.
pdfFileLocation := ((workingFile parent) / fileName) fullName, '.pdf'.
'/usr/bin/xelatex' asFileReference exists
ifTrue: [
"Generating the pdf"
OSProcess command: 'xelatex --shell-escape ', latexFileLocation.
"Moving the pdf output to the expected location"
pdfFileLocation asFileReference exists ifTrue: [pdfFileLocation asFileReference ensureDelete ].
(FileLocator imageDirectory parent / (fileName, '.pdf'))
moveTo: pdfFileLocation asFileReference.
"Cleaning left overs"
#('.aux' '.out' '.log') do: [ :fileExension |
(FileLocator imageDirectory parent asFileReference / (fileName, fileExension)) ensureDelete].
(FileLocator imageDirectory parent asFileReference / ('_minted-', fileName)) deleteAll.
self inform: 'Archivo exportado como pdf en: ', pdfFileLocation]
ifFalse: [
self inform:
'Necesita instalar XeLaTeX/TeX más información en:', String cr,'
http://xetex.sourceforge.net/' ].
]
{ #category : #persistence }
GrafoscopioBrowser >> exportAsSton: aTree on: locator [
| stonPrettyString |
aTree flatten.
stonPrettyString := String streamContents: [ :stream |
(STON writer on: stream)
newLine: String crlf;
prettyPrint: true;
keepNewLines: true;
nextPut: aTree children].
locator nextPutAll: stonPrettyString
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> headerOn: constructor [
"Shows the body of a selected node"
(constructor text)
title: 'Cabecera ';
format:[:eachNode |
(eachNode header) isNil
ifTrue: [ '' ]
ifFalse: [ eachNode header]].
]
{ #category : #'system-support' }
GrafoscopioBrowser >> installPandoc [
"Installs pandoc binary for each major platform: Linux, Windows, Mac"
| linuxSupplies |
linuxSupplies := (FileSystem disk workingDirectory
/ 'Platform' / 'Linux' / 'Programs' / 'Pandoc')
ensureCreateDirectory.
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> messageNoTagsAvailable [
"Shows that there is no tags available and the procedure to add some tags"
UIManager default
abort:
'Use primero el menu:',
(String with: Character cr),
'"Etiquetas > Agregar a disponibles"',
(String with: Character cr),
'para agregar etiquetas y luego sí agregue etiquetas al nodo'
title: 'Sin etiquetas disponibles!'
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> messageNotImplementedYet [
"Shows that a feature is not implemeted and point to further documentation on the web"
UIManager default abort:
'Esta funcionalidad aún no está implementada!',
(String with: Character cr),
'Para información sobre futuras versiones, por favor visite: ',
(String with: Character cr),
(String with: Character cr),
'http://mutabit.com/grafoscopio',
(String with: Character cr),
(String with: Character cr)
title: 'No implementado aún'.
]
{ #category : #persistence }
GrafoscopioBrowser >> open [
"Opens a new browser with a default tree and assigns a default draft file for storing it.
This is changed when the file is saved with the 'Save as' menu option"
"Opens a new browser with a default tree"
"GrafoscopioBrowser configureSettings."
self openDefault.
]
{ #category : #persistence }
GrafoscopioBrowser >> openDefault [
"Opens a new browser with a default tree"
"GrafoscopioBrowser configureSettings."
self buildBrowserNamed: 'draft.ston'.
mainTree := GrafoscopioNode new.
mainTree becomeDefaultTree.
workingFile := draftsLocation / 'draft.ston'.
self saveToFile: workingFile.
self openFromFile: workingFile.
" browser openOn: mainTree children."
]
{ #category : #persistence }
GrafoscopioBrowser >> openFromFile: aFileName [
"Opens a tree from a file named aFileName"
| currentChildren |
"GrafoscopioBrowser configureSettings."
workingFile := aFileName.
currentChildren := (STON fromString: aFileName contents).
self buildBrowserNamed: aFileName basenameWithIndicator.
mainTree := GrafoscopioNode new
header: (currentChildren at: 1) parent header;
level: (currentChildren at: 1) parent level;
children: currentChildren.
browser openOn: mainTree children.
]
{ #category : #persistence }
GrafoscopioBrowser >> openFromFileSelector [
"Opens a tree from a file by using the file selector GUI."
"REFACTORING NOTES: This sould call openFromFile, instead of having the code repeated here.
This creates errors on debugging and worse on proper behavior"
| fileStream currentChildren |
"GrafoscopioBrowser configureSettings."
fileStream := UIManager default
fileOpen: 'Choose a notebook file'
extensions: #('ston').
fileStream isNil ifTrue: [ ^nil ].
workingFile := fileStream name asFileReference.
currentChildren := (STON fromStream: fileStream).
self buildBrowserNamed: workingFile basenameWithIndicator.
recentTrees isNil
ifTrue:[recentTrees := OrderedCollection new].
"recentTrees detect: workingFile ifFound: [recentTrees add: workingFile]."
recentTrees add: workingFile.
mainTree := GrafoscopioNode new
header: (currentChildren at: 1) parent header;
level: (currentChildren at: 1) parent level;
children: currentChildren.
browser openOn: mainTree children.
]
{ #category : #persistence }
GrafoscopioBrowser >> openHelpInGrafoscopio [
"Opens the help tree from a file"
| currentChildren |
"GrafoscopioBrowser configureSettings."
workingFile := (FileSystem disk workingDirectory / 'Docs' / 'Es' / 'Manual' / 'manual-grafoscopio.ston') asFileReference.
currentChildren := (STON fromString: workingFile contents).
self buildBrowserNamed: workingFile basenameWithIndicator.
mainTree := GrafoscopioNode new
header: 'Arbol principal';
level: 0.
mainTree children: currentChildren.
browser openOn: mainTree children.
]
{ #category : #persistence }
GrafoscopioBrowser >> openTutorialInGrafoscopio [
"Opens the help tree from a file"
| currentChildren |
"GrafoscopioBrowser configureSettings."
workingFile := (FileSystem disk workingDirectory / 'Docs' / 'Es' / 'Tutoriales' / 'tutorial.ston') asFileReference.
currentChildren := (STON fromString: workingFile contents).
self buildBrowserNamed: workingFile basenameWithIndicator.
mainTree := GrafoscopioNode new
header: 'Arbol principal';
level: 0.
mainTree children: currentChildren.
browser openOn: mainTree children.
]
{ #category : #persistence }
GrafoscopioBrowser >> replaceContentsFromCache: aNode [
"Replaces the contents of aNode from the ones in cacheNode. It doesn't work recursively yet, so it doen't copy the children of the nodes"
cacheNode notNil
ifTrue: [
aNode header: cacheNode header.
aNode body: cacheNode body.
self inform: 'Contenidos reemplazados']
ifFalse: [self inform: 'El nodo cache está vacio']
]
{ #category : #persistence }
GrafoscopioBrowser >> repositoryCommit [
"Commits current changes to the open repository"
fossil notNil ifTrue: [
OSProcess command:
'cd ', localRepository parent fullName, '; ',
fossil, ' commit -m "" --no-warnings; ',
'echo "Commited changes to the repository"'. ]
]
{ #category : #persistence }
GrafoscopioBrowser >> repositoryCredentials [
"Asks for the user credentials of the current project repository"
repositoryUser := UIManager default request: 'Ingrese su usuario en el repositorio del proyecto'.
repositoryPassword := UIManager default request: 'Ingrese su contraseña en el repositorio del proyecto'.
]
{ #category : #persistence }
GrafoscopioBrowser >> saveToFile: aFileReference [
"Saves the current tree/document to a file"
| file writeStream markdownFile |
file := aFileReference.
file ifNil: [ self inform: 'Exportación cancelada'. ^ self ].
writeStream := file writeStream.
workingFile := writeStream name asFileReference.
markdownFile := (workingFile parent) / (workingFile basenameWithoutExtension, '.markdown').
markdownFile ensureCreateFile.
[ self exportAsSton: mainTree on: writeStream.
self exportAsMarkdown: mainTree on: markdownFile ]
ensure: [ writeStream ifNotNil: #close ].
recentTrees isNil
ifTrue:[recentTrees := OrderedCollection new].
recentTrees add: workingFile.
browser update.
]
{ #category : #persistence }
GrafoscopioBrowser >> saveToFileUI [
"Saves the current tree to a file"
| file |
file := UIManager default
fileSave: 'Save file as...'
extensions: #('ston')
path: nil.
file
ifNil: [ self inform: 'File export canceled'. ^ self ]
ifNotNil:[self saveToFile: file].
browser update.
]
{ #category : #persistence }
GrafoscopioBrowser >> saveWorkingTree [
"Saves the current tree to the user predefined file location used when he/she opened it."
| markdownFile |
markdownFile := (workingFile parent) / (workingFile basenameWithoutExtension, '.markdown').
markdownFile exists ifTrue: [ markdownFile delete ].
markdownFile ensureCreateFile.
workingFile exists ifTrue: [ workingFile delete ].
workingFile ensureCreateFile.
[ self exportAsSton: mainTree on: (workingFile writeStream).
self exportAsMarkdown: mainTree on: markdownFile
] ensure: [ (workingFile writeStream) ifNotNil: #close.].
self inform: 'Archivo guardado como: ', workingFile asString.
]
{ #category : #tags }
GrafoscopioBrowser >> showTagsAvailable [
"Adds a tag to the collection of tags available"
tagsAvailable isNil
ifTrue: [self messageNoTagsAvailable]
ifFalse: [self messageNotImplementedYet]
]
{ #category : #'graphical interface' }
GrafoscopioBrowser >> treeOn: constructor [
"Shows the correspondent tree of a node"
(constructor tree) "Layout"
title: mainTree header;
children: [ :eachNode |
(eachNode children) isNil
ifTrue: [ self inform: 'Seleccione un nodo para ver su contenido' ]
ifFalse:[ eachNode children ] ];
format:[:eachNode |
(eachNode header) isNil
ifTrue: [ '' ]
ifFalse: [ eachNode header ]];
tags: [:eachNode |
(eachNode tags) isNil
ifFalse: [ (eachNode tags) asSymbol ]
ifTrue: [ #() ] ];
"Nodes Toolbar
=============="
"Adding nodes"
act: [:treePresentation |
(treePresentation selection) isNotNil
ifTrue: [treePresentation selection addNodeAfterMe].
treePresentation update]
icon: GLMUIThemeExtraIcons glamorousZoomIn
entitled: 'Adicionar nodo';
"Removing nodes"
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [(treePresentation selection parent)
removeNode: treePresentation selection]
ifFalse: [treePresentation entity removeLast].
treePresentation update]
icon: GLMUIThemeExtraIcons glamorousZoomOut
entitled: 'Remover nodo';
"Move nodes in the same hierarchy"
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [treePresentation selection moveBefore].
treePresentation update]
icon: GLMUIThemeExtraIcons glamorousUp
entitled: 'Mover nodo hacia arriba';
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [treePresentation selection moveAfter].
treePresentation update]
icon: GLMUIThemeExtraIcons glamorousDown
entitled: 'Mover nodo hacia abajo';
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [treePresentation selection promote].
treePresentation update]
icon: GLMUIThemeExtraIcons glamorousLeft
entitled: 'Mover nodo hacia la izquierda';
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [treePresentation selection demote].
treePresentation update]
icon: GLMUIThemeExtraIcons glamorousRight
entitled: 'Mover nodo hacia la derecha';
act: [ :treePresentation | treePresentation update ]
icon: GLMUIThemeExtraIcons glamorousRefresh
entitled: 'Actualizar vista del árbol';
act: [self saveWorkingTree]
icon: GLMUIThemeExtraIcons glamorousSave
entitled: 'Guardar árbol';
"Options Menu
============="
"For trees"
act: [self saveToFileUI] entitled: 'Guardar como ...' categorized: 'Arbol';
act: [self saveWorkingTree; exportAsHtml] entitled: 'Exportar como HTML' categorized: 'Arbol';
act: [self saveWorkingTree; exportAsLatex] entitled: 'Exportar como LaTeX' categorized: 'Arbol';
act: [self saveWorkingTree; exportAsPdf] entitled: 'Exportar como PDF' categorized: 'Arbol';
act: [self viewExportedHtml] entitled: 'Ver HTML' categorized: 'Arbol';
act: [self messageNotImplementedYet] entitled: 'Ver PDF' categorized: 'Arbol';
"For nodes"
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [self copyNodeIntoCache: treePresentation selection].
treePresentation update] entitled: 'Copiar nodo' categorized: 'Nodo';
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [self replaceContentsFromCache: treePresentation selection].
treePresentation update] entitled: 'Pegar nodo' categorized: 'Nodo';
"For tags"
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [self addTagTo: treePresentation selection]]
entitled: 'Etiquetar nodo como...' categorized: 'Etiquetas';
act: [:treePresentation |
(treePresentation selection isNotNil)
ifTrue: [
treePresentation selection tagAs: nil.
browser update.
]]
entitled: 'Desetiquetar nodo como...' categorized: 'Etiquetas';
act: [self showTagsAvailable] entitled: 'Ver disponibles' categorized: 'Etiquetas';
act: [self addToTagsAvailable] entitled: 'Agregar a disponibles' categorized: 'Etiquetas'.
]
{ #category : #persistence }
GrafoscopioBrowser >> viewExportedHtml [
"Exports the current tree to HTML, using the same name but different extension (.html)"
| htmlFileLocation |
htmlFileLocation := ((workingFile parent) / workingFile basenameWithoutExtension) fullName, '.html'.
Smalltalk platform name = 'unix'
ifTrue: [OSProcess command: 'xdg-open ', htmlFileLocation].
Smalltalk platform name = 'Win32'
ifTrue: [OSProcess command: 'explorer ', htmlFileLocation].
Smalltalk platform name = 'Mac OS'
ifTrue: [OSProcess command: 'open ', htmlFileLocation].
]