1037 lines
29 KiB
Smalltalk
1037 lines
29 KiB
Smalltalk
"
|
|
I am a Grafoscopio Notebook.
|
|
|
|
Example:
|
|
| testTree nb |
|
|
testTree := GrafoscopioNode new becomeDefaultTestTree.
|
|
nb := GrafoscopioNotebook new.
|
|
nb notebookContent: testTree.
|
|
nb openWithSpec
|
|
"
|
|
Class {
|
|
#name : #GrafoscopioNotebook,
|
|
#superclass : #ComposableModel,
|
|
#instVars : [
|
|
'tree',
|
|
'header',
|
|
'body',
|
|
'links',
|
|
'windowMainMenu',
|
|
'workingFile',
|
|
'notebook',
|
|
'debugMessage',
|
|
'imagesList'
|
|
],
|
|
#category : #'Grafoscopio-UI'
|
|
}
|
|
|
|
{ #category : #utility }
|
|
GrafoscopioNotebook class >> SHA1For: aFile is: aSHA1String [
|
|
"I verify that a file has the same signature that the one in a given string,
|
|
returning true in that case or false otherwise"
|
|
^ (SHA1 new hashMessage: aFile asFileReference binaryReadStream contents) hex = aSHA1String
|
|
|
|
]
|
|
|
|
{ #category : #specs }
|
|
GrafoscopioNotebook class >> defaultSpec [
|
|
|
|
^ SpecLayout composed
|
|
newColumn: [:tcol|
|
|
tcol newRow: [ :wrow | wrow add: #windowMainMenu ] height: (self toolbarHeight);
|
|
newRow: [:row |
|
|
row newColumn: [ :tc |
|
|
tc add: #tree
|
|
] width: 300.
|
|
row newColumn: [ :bc |
|
|
bc newRow: [ :bcr | bcr add: #header ] height: self toolbarHeight.
|
|
bc add: #body; add: #links height: self toolbarHeight ]]]
|
|
]
|
|
|
|
{ #category : #'instance creation' }
|
|
GrafoscopioNotebook class >> newDefault [
|
|
^ self new.
|
|
]
|
|
|
|
{ #category : #'instance creation' }
|
|
GrafoscopioNotebook class >> open: aFileReference [
|
|
self newDefault openFromFile: aFileReference
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> addCommandFrom: dictionary into: stream [
|
|
dictionary keysAndValuesDo: [ :k :v |
|
|
k = 'thisNotebook'
|
|
ifTrue: [
|
|
stream nextPutAll: (GrafoscopioUtils perform: v on: self) ]]
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> addNode [
|
|
self currentNodeContent addNodeAfterMe.
|
|
self notebookContent: notebook.
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> autoSaveBodyOf: aNode [
|
|
| playground |
|
|
body body class = TextModel
|
|
ifTrue: [ body body whenTextChanged: [ :arg | aNode body: arg ] ].
|
|
body body class = GlamourPresentationModel
|
|
ifFalse: [ ^ self ].
|
|
playground := body body glmPres.
|
|
playground
|
|
onChangeOfPort: #text
|
|
act: [ :x | aNode body: (x pane port: #text) value ]";
|
|
onChangeOfPort: #activePresentation
|
|
act: [ aNode output: aNode processOutput ]"
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> body [
|
|
^ body
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> body: anObject [
|
|
body := anObject
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> checksum [
|
|
"I return the checksum (crypto hash) of the workingFile where this notebook is being stored.
|
|
I'm useful for data provenance and traceability of derivated files coming from this source
|
|
notebook."
|
|
self workingFile ifNil: [ ^ self ].
|
|
self workingFile contents = '' ifTrue: [ ^ self ].
|
|
^ GrafoscopioUtils checksumFor: self workingFile
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> copyNodeToClipboard [
|
|
tree highlightedItem content copyToClipboard.
|
|
self notebookContent: notebook.
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> currentNode [
|
|
| currentNode |
|
|
currentNode := tree highlightedItem.
|
|
currentNode ifNil: [ ^ self ].
|
|
^ currentNode
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> currentNodeContent [
|
|
^ self currentNode content
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> cutNodeToClipboard [
|
|
self copyNodeToClipboard; removeNode.
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> debugMessage [
|
|
^ debugMessage ifNil: [ self defineDebugMessageUI ]
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> debugMessage: aGrafoscopioNodeSelector [
|
|
"I define a message that can be used for debugging purposes in the current notebook."
|
|
debugMessage := aGrafoscopioNodeSelector
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> debugWithSelector: aSymbol [
|
|
"I invoke a message to debug in the current node. In the future the debugging scope can be changed to
|
|
include different elements instead of the current node."
|
|
| currentNode nodeContent |
|
|
currentNode := tree highlightedItem.
|
|
currentNode ifNil: [ ^ self ].
|
|
nodeContent := currentNode content.
|
|
^ (nodeContent perform: aSymbol asSymbol) inspect
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> defineDebugMessageUI [
|
|
| answer |
|
|
answer := UIManager default
|
|
request: 'Define debug message to be send to a selected node in this notebook.'
|
|
initialAnswer: 'messageNoDebugSelector'.
|
|
self debugMessage: answer
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> demoteNode [
|
|
| editedNode |
|
|
editedNode := tree highlightedItem content.
|
|
editedNode demote.
|
|
self notebookContent: notebook.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportAllSubtreesAsMarkdow [
|
|
| toBeExported |
|
|
toBeExported := self notebook selectMarkdownSubtreesToExport.
|
|
toBeExported ifEmpty: [ ^ self ].
|
|
toBeExported do: [ :each | self subtreeAsMarkdownFor: each ].
|
|
self inform: toBeExported size asString , ' exported markdown subtrees.'
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportAsHTML [
|
|
"I export the current tree/document to a HTML file, using pandoc external app.
|
|
I suppose pandoc is already installed and available in the system."
|
|
| htmlFile |
|
|
self markdownFile exists ifTrue: [ self markdownFile delete ].
|
|
self exportAsMarkdown.
|
|
htmlFile := self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.html'.
|
|
htmlFile asFileReference exists ifTrue: [ htmlFile asFileReference delete ].
|
|
Smalltalk platformName = 'unix'
|
|
ifTrue: [OSProcess command: 'pandoc --standalone ', self markdownFile fullName, ' -o ', htmlFile].
|
|
Smalltalk platformName = 'Win32'
|
|
ifTrue: [WindowsProcess command: 'pandoc --standalone ', self markdownFile fullName, ' -o ', htmlFile].
|
|
self inform: ('File exported as: ', String cr, htmlFile).
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportAsLaTeX [
|
|
"I export the current tree/document to a LaTeX file, using pandoc external app.
|
|
I suppose pandoc is already installed and available in the system."
|
|
| texFile |
|
|
self markdownFile exists ifTrue: [ self markdownFile delete ].
|
|
"self exportAsMarkdown.""<- This violates the separation of concenrs. Markdown exportation should
|
|
be explicit. There is still the issue of how to deal with desynchronization between a notebook
|
|
which has unsaved changes as markdown.... TO BE REVIWED!"
|
|
texFile := self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.tex'.
|
|
texFile asFileReference exists ifTrue: [ texFile asFileReference delete ].
|
|
OSProcess command: 'pandoc --standalone ', self markdownFile fullName, ' -o ', texFile.
|
|
self inform: ('File exported as: ', String cr, texFile).
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportAsMarkdown [
|
|
"I export the current working tree/document to a markdown file."
|
|
workingFile
|
|
ifNil: [ self inform: 'File NOT exported. Please save the notebook on hard drive first' ]
|
|
ifNotNil: [ self exportNode: (self notebook) asMarkdownIn: (self markdownFile) ]
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportAsPDF [
|
|
"I export the current tree/document to a PDF file, using pandoc and LaTeX external apps.
|
|
The latex engine used is xelatex, to minimize errors and warnings related with UTF8 support.
|
|
I suppose all them are already installed and defined in the system."
|
|
| pandocCommonCommand |
|
|
self markdownFile exists ifFalse: [ self exportAsMarkdown ].
|
|
self pdfFile ensureDelete.
|
|
pandocCommonCommand := 'pandoc ', self pandocOptionsComputed, ' ', self markdownFile fullName,
|
|
' --output ', self pdfFile fullName.
|
|
Smalltalk platformName = 'unix'
|
|
ifTrue: [ ExternalOSProcess command: 'cd ', self markdownFile parent fullName,'; ', pandocCommonCommand ].
|
|
Smalltalk platformName = 'Win32'
|
|
ifTrue: [ WinProcess createProcess: pandocCommonCommand ].
|
|
self inform: ('File exported as: ', String cr, self pdfFile fullName)
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportAsSton: aNotebook on: aFileStream [
|
|
aNotebook flatten.
|
|
(STON writer on: aFileStream)
|
|
newLine: String crlf;
|
|
prettyPrint: true;
|
|
keepNewLines: true;
|
|
nextPut: aNotebook children
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> exportNode: aGrafoscopioNode asMarkdownIn: aFile [
|
|
"I export the current tree/document to a markdown file"
|
|
aFile ensureDelete.
|
|
aFile
|
|
ensureCreateFile;
|
|
writeStreamDo: [:stream |
|
|
stream
|
|
nextPutAll:
|
|
('---', String cr,
|
|
'exportedFrom: ', self checksum, String cr) withInternetLineEndings.
|
|
aGrafoscopioNode metadataAsYamlIn: stream.
|
|
stream
|
|
nextPutAll:
|
|
('---', String cr, String cr) withInternetLineEndings,
|
|
aGrafoscopioNode asMarkdown ].
|
|
self inform: 'Exported as: ', String cr, aFile fullName
|
|
]
|
|
|
|
{ #category : #api }
|
|
GrafoscopioNotebook >> extent [
|
|
^900@500
|
|
]
|
|
|
|
{ #category : #'as yet unclassified' }
|
|
GrafoscopioNotebook >> findAndReplace [
|
|
| currentNode replaceGUI findString replaceString |
|
|
currentNode := tree highlightedItem content.
|
|
replaceGUI := GrafoscopioReplace new.
|
|
replaceGUI openWithSpec.
|
|
replaceGUI ok
|
|
on: [ ]
|
|
do: [
|
|
findString := replaceGUI returnValues at: 'find'.
|
|
replaceString := replaceGUI returnValues at: 'replace'.
|
|
currentNode find: findString andReplaceWith: replaceString. ]
|
|
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> header [
|
|
^ header
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> header: anObject [
|
|
header := anObject
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> htmlToMarkdown [
|
|
self currentNodeContent htmlToMarkdown.
|
|
self updateBodyFor: self currentNode
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> htmlToMarkdownSubtree [
|
|
self currentNodeContent htmlToMarkdownSubtree.
|
|
self updateBodyFor: self currentNode
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> imagesList [
|
|
imagesList ifNil: [ ^ #('No images list for this notebook') ].
|
|
^ imagesList
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> imagesList: anObject [
|
|
imagesList := anObject
|
|
]
|
|
|
|
{ #category : #'as yet unclassified' }
|
|
GrafoscopioNotebook >> importImages [
|
|
self imagesList: (Pandoc listImagesFrom: self markdownFile).
|
|
self inform: 'All notebook images has been imported.', String cr, 'Now you can list and download them.'
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> importLinkContent [
|
|
"I see if a node header is an url located at 'http://ws.stfx.eu', wich means that is a shared
|
|
workspace, and convert the node body to an interactive playground"
|
|
| currentNode nodeContent |
|
|
currentNode := tree highlightedItem.
|
|
currentNode ifNil: [ ^ self ].
|
|
nodeContent := currentNode content.
|
|
nodeContent importPlaygroundLink.
|
|
nodeContent importHtmlLink.
|
|
self updateBodyFor: currentNode
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> initialize [
|
|
super initialize.
|
|
self
|
|
notebook: (GrafoscopioNode new becomeDefaultTree);
|
|
title: ' New | Grafoscopio notebook'.
|
|
self notebookContent: self notebook.
|
|
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> initializeDefaultTest [
|
|
super initialize.
|
|
self
|
|
notebook: (GrafoscopioNode new becomeDefaultTestTree);
|
|
title: ' New test | Grafoscopio notebook'.
|
|
self notebookContent: self notebook.
|
|
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> initializePresenter [
|
|
tree whenHighlightedItemChanged: [ :item |
|
|
tree highlightedItem ifNotNil: [self updateBodyFor: item]].
|
|
tree whenTreeUpdated: [ :item | item ifNotNil: [self updateBodyFor: item]].
|
|
header whenTextChanged: [ :arg |
|
|
(tree highlightedItem content header) = arg
|
|
ifFalse: [
|
|
tree highlightedItem content header: arg.
|
|
tree roots: tree roots]].
|
|
links whenTextChanged: [ :arg | tree highlightedItem content addLink: arg ]
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> initializeWidgets [
|
|
windowMainMenu := self topBar.
|
|
header := self newTextInput.
|
|
header autoAccept: true.
|
|
body := self newText.
|
|
body disable.
|
|
body text: '<- Select a node'.
|
|
links := self newTextInput.
|
|
tree := TreeModel new.
|
|
tree
|
|
childrenBlock: [:node | node children];
|
|
displayBlock: [:node | node title ].
|
|
self focusOrder
|
|
add: tree;
|
|
add: header;
|
|
add: body;
|
|
add: links.
|
|
self askOkToClose: true.
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> links [
|
|
^ links
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> links: anObject [
|
|
links := anObject
|
|
]
|
|
|
|
{ #category : #'as yet unclassified' }
|
|
GrafoscopioNotebook >> listImagesUI [
|
|
ListModel new
|
|
title: 'Images files list';
|
|
items: self imagesList ;
|
|
openWithSpec
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> loadFromFile: aFileReference [
|
|
"I load the contents of aFileReference into a GrafoscopioNotebook, without opening it."
|
|
(aFileReference basename endsWith: 'ston') ifFalse: [ ^ self ].
|
|
self workingFile: aFileReference.
|
|
self notebook: ((STON fromString: self workingFile contents) at: 1) parent.
|
|
self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'.
|
|
self notebookContent: self notebook.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> markdownFile [
|
|
"I define the location of the markdown file where the notebook will be exported"
|
|
| markdownFile |
|
|
markdownFile := (((workingFile parent) / workingFile basenameWithoutExtension) fullName, '.markdown') asFileReference.
|
|
^ markdownFile
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> markdownFileChecksum [
|
|
self workingFile ifNil: [ ^ self ].
|
|
self workingFile contents = '' ifTrue: [ ^ self ].
|
|
^ GrafoscopioUtils checksumFor: self markdownFile
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> markdownFileChecksumUpto: anInteger [
|
|
"I cut the markdownFileCheckup upto a given integer.
|
|
Type coersion is needed, because this message argument can be read from a string in %metadata nodes.
|
|
Maybe the way used by playgrounds to import text as commands can be useful here."
|
|
^ self markdownFileChecksum copyFrom: 1 to: anInteger asInteger.
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> metadata [
|
|
^ self notebook metadata
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> moveNodeAfter [
|
|
| editedNode |
|
|
editedNode := tree selectedItem content.
|
|
editedNode moveAfter.
|
|
self notebookContent: notebook
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> moveNodeBefore [
|
|
| editedNode |
|
|
editedNode := tree highlightedItem content.
|
|
editedNode moveBefore.
|
|
self notebookContent: notebook
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> navigateRelativePathFor: aFileString [
|
|
"Given a relative path according to location of the notebook's workingFile,
|
|
I navigate to that file if exist and create it, including subdirectories if it does not exist.
|
|
If the relative path is located in a subdirectory that shares the route with the notebooks working
|
|
file, it must start with the folders name,
|
|
without using './' to point the same shared root "
|
|
|
|
| finalLocation pathSegments |
|
|
aFileString ifEmpty: [ ^ self ].
|
|
aFileString asUrl host ifNotNil: [ ^self ].
|
|
finalLocation := workingFile parent.
|
|
pathSegments := aFileString splitOn: '/'.
|
|
pathSegments allButLastDo: [ :segment |
|
|
(segment = '..')
|
|
ifTrue: [ finalLocation := finalLocation parent ]
|
|
ifFalse: [
|
|
finalLocation := finalLocation / segment.
|
|
finalLocation exists ifFalse: [ finalLocation ensureCreateDirectory ]]].
|
|
finalLocation := finalLocation / (pathSegments last).
|
|
finalLocation exists ifFalse: [ finalLocation ensureCreateFile ].
|
|
^ finalLocation
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> notebook [
|
|
^ notebook
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> notebook: anObject [
|
|
notebook := anObject
|
|
]
|
|
|
|
{ #category : #api }
|
|
GrafoscopioNotebook >> notebookContent: aTree [
|
|
|
|
| nodeBlock |
|
|
nodeBlock:= [:gfcNode | |node|
|
|
node := TreeNodeModel new.
|
|
node
|
|
hasChildren: [ gfcNode children isNotEmpty ];
|
|
children: [ gfcNode children collect: [:subNode | nodeBlock value: subNode ]];
|
|
content: gfcNode].
|
|
tree roots: (aTree children collect:[ :gfcNode | nodeBlock value: gfcNode])
|
|
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> notebookSubMenu [
|
|
^ MenuModel new
|
|
addGroup: [ :group |
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Save';
|
|
icon: (Smalltalk ui icons iconNamed: #smallSave);
|
|
shortcut: $s command;
|
|
action: [ self saveWorkingNotebook ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Save as...';
|
|
icon: (Smalltalk ui icons iconNamed: #smallSaveAs);
|
|
action: [ self saveToFileUI ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Import images';
|
|
icon: (Smalltalk ui icons iconNamed: #processBrowser);
|
|
action: [ self importImages ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Download images';
|
|
icon: (Smalltalk ui icons iconNamed: #processBrowser);
|
|
action: [ self downloadImages ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Export as markdown';
|
|
icon: (Smalltalk ui icons iconNamed: #smallSaveAs);
|
|
action: [ self exportAsMarkdown ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Export as html';
|
|
icon: (Smalltalk ui icons iconNamed: #smallWindow);
|
|
action: [ self exportAsHTML ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Export as LaTeX';
|
|
icon: (Smalltalk ui icons iconNamed: #smallPrint);
|
|
action: [ self exportAsLaTeX ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Export as pdf';
|
|
icon: (Smalltalk ui icons iconNamed: #smallPrint);
|
|
action: [ self exportAsPDF ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'See html';
|
|
icon: #smallInspectIt asIcon;
|
|
action: [ self inform: 'To be implemented...' ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'See pdf';
|
|
icon: (Smalltalk ui icons iconNamed: #smallInspectIt);
|
|
action: [ self inform: 'To be implemented...' ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'See images list';
|
|
icon: (Smalltalk ui icons iconNamed: #processBrowser);
|
|
action: [ self listImagesUI ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Define debug message...';
|
|
icon: Smalltalk ui icons glamorousBug;
|
|
action: [ self defineDebugMessageUI ] ] ]
|
|
]
|
|
|
|
{ #category : #private }
|
|
GrafoscopioNotebook >> okToChange [
|
|
^ true
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> openDefault [
|
|
"I open a new default notebook"
|
|
|
|
^ self class new openWithSpec.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> openFromFile: aFileReference [
|
|
|
|
self loadFromFile: aFileReference.
|
|
^ self openWithSpec.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> openFromFileSelector [
|
|
|
|
| fileStream nb |
|
|
fileStream := UIManager default
|
|
fileOpen: 'Choose a file'
|
|
extensions: #('ston').
|
|
fileStream ifNil: [
|
|
self inform: 'No file selected'.
|
|
^ self ].
|
|
self workingFile: fileStream name asFileReference.
|
|
nb := self class new.
|
|
nb openFromFile: self workingFile.
|
|
GrafoscopioDockingBar updateRecentNotebooksWith: workingFile
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> openFromUrl: url [
|
|
"Opens a tree from a file named aFileName"
|
|
|
|
| fileName sanitized |
|
|
sanitized := GrafoscopioUtils sanitize: url.
|
|
fileName := sanitized segments last.
|
|
GrafoscopioUtils
|
|
downloadingFrom: sanitized
|
|
withMessage: 'Downloading document...'
|
|
into: FileLocator temp.
|
|
self class new openFromFile: (FileLocator temp / fileName)
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> openFromUrlUI [
|
|
"This method generates the UI for the openFromUrl: method, it asks for a URL from the user"
|
|
|
|
| fileUrl |
|
|
"GrafoscopioBrowser configureSettings."
|
|
fileUrl := UIManager default
|
|
textEntry: 'Enter the URL'
|
|
title: 'Open notebook from URL'.
|
|
fileUrl isNil ifTrue: [ ^nil ].
|
|
self class new openFromUrl: fileUrl
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> pandocOptions [
|
|
^ self notebook pandocOptions
|
|
]
|
|
|
|
{ #category : #utilities }
|
|
GrafoscopioNotebook >> pandocOptionsComputed [
|
|
"I convert the pandoc options array into a single line that can be used with the pandoc command."
|
|
|
|
| result |
|
|
result := '' writeStream.
|
|
self pandocOptions ifNil: [ ^ '' ].
|
|
self pandocOptions
|
|
do: [ :option |
|
|
option isDictionary
|
|
ifTrue: [
|
|
self addCommandFrom: option into: result ]
|
|
ifFalse: [
|
|
result
|
|
nextPutAll: option] ].
|
|
^ result contents
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> pasteNodeFromClipboard [
|
|
tree highlightedItem content pasteFromClipboard.
|
|
self notebookContent: notebook.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> pdfFile [
|
|
"I define the location of the markdown file where the notebook will be exported"
|
|
| pdfFile |
|
|
pdfFile := (self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.pdf') asFileReference.
|
|
^ pdfFile.
|
|
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> projectSubMenu [
|
|
|
|
^ MenuModel new
|
|
addGroup: [ :group |
|
|
group addItem: [ :item |
|
|
item
|
|
name: 'Activate remote repository...';
|
|
icon: Smalltalk ui icons smallPushpinIcon;
|
|
action: [ self inform: 'To be implemented ...' ] ].
|
|
group addItem: [ :item |
|
|
item
|
|
name: 'Activate local repository...';
|
|
icon: Smalltalk ui icons homeIcon;
|
|
action: [ self inform: 'To be implemented ...' ] ].
|
|
group addItem: [ :item |
|
|
item
|
|
name: 'Add file...';
|
|
icon: Smalltalk ui icons newerPackagesAvailableIcon;
|
|
action: [ self inform: 'To be implemented ...' ] ].
|
|
group addItem: [ :item |
|
|
item
|
|
name: 'Delete file...';
|
|
icon: Smalltalk ui icons packageDeleteIcon;
|
|
action: [ self inform: 'To be implemented ...' ] ].
|
|
group addItem: [ :item |
|
|
item
|
|
name: 'Commit to repository';
|
|
icon: Smalltalk ui icons smallScreenshotIcon;
|
|
action: [ self inform: 'To be implemented ...' ] ].
|
|
group addItem: [ :item |
|
|
item
|
|
name: 'Credentials';
|
|
icon: Smalltalk ui icons userIcon;
|
|
action: [ self inform: 'To be implemented ...' ] ] ]
|
|
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> promoteNode [
|
|
| editedNote |
|
|
editedNote := tree selectedItem content.
|
|
editedNote promote.
|
|
self notebookContent: notebook
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> removeNode [
|
|
| contentToDelete parentContent newSelectedContent children |
|
|
contentToDelete := tree selectedItem content.
|
|
parentContent := contentToDelete parent.
|
|
children := parentContent children.
|
|
children size > 1
|
|
ifTrue: [
|
|
children last = contentToDelete
|
|
ifTrue: [ newSelectedContent := children at: (children size - 1) ]
|
|
]
|
|
ifFalse: [ newSelectedContent := parentContent ].
|
|
contentToDelete parent removeNode: contentToDelete.
|
|
self notebookContent: notebook
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> saveToFile: aFileReference [
|
|
"I save the current tree/document to a file."
|
|
|
|
aFileReference ifNil: [ self inform: 'No file selected for saving. Save NOT done.'. ^ self ].
|
|
workingFile := aFileReference.
|
|
self workingFile ensureDelete.
|
|
self workingFile writeStreamDo: [:stream |
|
|
self exportAsSton: self notebook on: stream ].
|
|
self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'.
|
|
self inform: ('File saved at: ', String cr, self workingFile fullName).
|
|
GrafoscopioDockingBar updateRecentNotebooksWith: aFileReference.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> saveToFileUI [
|
|
|
|
| file |
|
|
|
|
file := UIManager default
|
|
fileSave: 'Export notebook to file as...'
|
|
extensions: #('ston')
|
|
path: (workingFile ifNotNil: [ workingFile parent ] ifNil: [ FileLocator documents ] ).
|
|
file
|
|
ifNil: [ self inform: 'Export cancelled'. ^ self ]
|
|
ifNotNil:[self saveToFile: file].
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> saveWorkingNotebook [
|
|
"Saves the current tree to the user predefined file location used when he/she opened it."
|
|
self workingFile
|
|
ifNil: [ self saveToFileUI ]
|
|
ifNotNil: [ self saveToFile: workingFile ].
|
|
GrafoscopioDockingBar updateRecentNotebooksWith: workingFile
|
|
|
|
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> subtreeAsMarkdown [
|
|
| currentNode |
|
|
currentNode := tree highlightedItem content.
|
|
self inform: ('Exported as: ', String cr, (self subtreeAsMarkdownFor: currentNode) fullName )
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
GrafoscopioNotebook >> subtreeAsMarkdownFor: aNode [
|
|
| exportedFile |
|
|
aNode links ifEmpty: [ ^ self ].
|
|
exportedFile:= self navigateRelativePathFor: aNode links last.
|
|
exportedFile class = GrafoscopioNotebook ifTrue: [ ^ self ].
|
|
self exportNode: aNode asMarkdownIn: exportedFile.
|
|
^ exportedFile
|
|
]
|
|
|
|
{ #category : #'editing nodes' }
|
|
GrafoscopioNotebook >> toggleCodeNode [
|
|
| currentNode |
|
|
currentNode := tree highlightedItem.
|
|
currentNode content toggleCodeText.
|
|
self updateBodyFor: currentNode.
|
|
]
|
|
|
|
{ #category : #initialization }
|
|
GrafoscopioNotebook >> topBar [
|
|
^ MenuModel new
|
|
addGroup: [ :group |
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Notebook';
|
|
icon: #smallObjects asIcon;
|
|
subMenu: self notebookSubMenu ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: 'Project';
|
|
icon: #catalog asIcon;
|
|
subMenu: self projectSubMenu ] ];
|
|
addGroup: [ :group |
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Save notebook';
|
|
icon: #glamorousSave asIcon;
|
|
action: [ self saveWorkingNotebook ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Export all markdown subtrees';
|
|
icon: #glamorousMore asIcon;
|
|
action: [ self exportAllSubtreesAsMarkdow ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Cut';
|
|
icon: #smallCut asIcon;
|
|
action: [ self cutNodeToClipboard ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Copy';
|
|
icon: #smallCopy asIcon;
|
|
action: [ self copyNodeToClipboard ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Paste';
|
|
icon: #smallPaste asIcon;
|
|
action: [ self pasteNodeFromClipboard ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Find & Replace';
|
|
icon: #smallFind asIcon;
|
|
action: [ self findAndReplace ] ] ];
|
|
addGroup: [ :group |
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Add node';
|
|
icon: MendaIcons new plusIcon;
|
|
action: [ self addNode ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Delete node';
|
|
icon: MendaIcons new minusIcon;
|
|
action: [ self removeNode ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Move node up';
|
|
icon: MendaIcons new arrowUpIcon;
|
|
action: [ self moveNodeBefore ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Move node down';
|
|
icon: MendaIcons new arrowDownIcon;
|
|
action: [ self moveNodeAfter ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Move node left';
|
|
icon: MendaIcons new arrowLeftIcon;
|
|
action: [ self promoteNode ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Move node right';
|
|
icon: MendaIcons new arrowRightIcon;
|
|
action: [ self demoteNode ] ] ];
|
|
addGroup: [ :group |
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Toggle: code <--> text';
|
|
icon: MendaIcons new smalltalkCodeIcon;
|
|
action: [ self toggleCodeNode ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Visit link';
|
|
icon: #glamorousRight asIcon;
|
|
action: [ self visitNodeLink ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Import link content';
|
|
icon: #glamorousRefresh asIcon;
|
|
action: [ self importLinkContent ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'HTML to Markdown';
|
|
icon: #smallProfile asIcon;
|
|
action: [ self htmlToMarkdown ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'HTML to Markdown subtree';
|
|
icon: #hierarchy asIcon;
|
|
action: [ self htmlToMarkdownSubtree ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Tag as...';
|
|
icon: MendaIcons new tagAddIcon;
|
|
action: [ self inform: 'To be implemented...' ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Untag ....';
|
|
icon: MendaIcons new tagMinusIcon;
|
|
action: [ self inform: 'To be implemented...' ] ].
|
|
group
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Edit tags...';
|
|
icon: FontAwesomeIcons new tagsIcon;
|
|
action: [ self inform: 'To be implemented...' ] ] ];
|
|
addGroup: [ :debug |
|
|
debug
|
|
addItem: [ :item |
|
|
item
|
|
name: nil;
|
|
description: 'Debug';
|
|
icon: #glamorousBug asIcon;
|
|
action: [ self debugWithSelector: self debugMessage ] ] ]
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> tree [
|
|
^ tree
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> tree: anObject [
|
|
tree := anObject
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> updateBodyFor: aNodeContainer [
|
|
| aNode |
|
|
self needRebuild: false.
|
|
tree needRebuild: false.
|
|
body needRebuild: true.
|
|
aNode := aNodeContainer content.
|
|
header text: aNode header.
|
|
body := self instantiate: aNode specModelClass new.
|
|
body content: aNode body.
|
|
links text: aNode lastLink.
|
|
self autoSaveBodyOf: aNode.
|
|
self buildWithSpecLayout: self class defaultSpec
|
|
]
|
|
|
|
{ #category : #operation }
|
|
GrafoscopioNotebook >> visitNodeLink [
|
|
|
|
tree highlightedItem content visitLastLink.
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> windowMainMenu [
|
|
^ windowMainMenu
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> windowMainMenu: anObject [
|
|
windowMainMenu := anObject
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> workingFile [
|
|
^ workingFile
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
GrafoscopioNotebook >> workingFile: aFileReference [
|
|
workingFile := aFileReference.
|
|
]
|