Grafoscopio/src/Grafoscopio/GrafoscopioNotebook.class.st

821 lines
24 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'
],
#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 : #'editing nodes' }
GrafoscopioNotebook >> addNode [
| addedNode |
addedNode := tree highlightedItem content 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 entity value content ]";
onChangeOfPort: #activePresentation
act: [ aNode output: aNode processOutput ]"
]
{ #category : #accessing }
GrafoscopioNotebook >> body [
^ body
]
{ #category : #accessing }
GrafoscopioNotebook >> body: anObject [
body := anObject
]
{ #category : #'editing nodes' }
GrafoscopioNotebook >> copyNodeToClipboard [
tree highlightedItem content copyToClipboard.
self notebookContent: notebook.
]
{ #category : #utilities }
GrafoscopioNotebook >> cryptoSignature [
"I return the cryptographic signature of the workingFile where this notebook is being stored.
I'm useful for data provenance and traceability of derivated files related with this source
notebook."
self workingFile ifNil: [ ^ self ].
self workingFile contents = '' ifTrue: [ ^ self ].
^ (SHA1 new hashMessage: (self workingFile contents)) hex.
]
{ #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 ].
OSProcess 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.
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."
| pdfFile pandocCommand |
self markdownFile exists ifFalse: [ self exportAsMarkdown ].
pdfFile := self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.pdf'.
pdfFile asFileReference exists ifTrue: [ pdfFile asFileReference delete ].
pandocCommand := 'pandoc ',
self notebook pandocOptions, ' ',
self markdownFile fullName, ' -o ', pdfFile.
Transcript show: pandocCommand.
ExternalUnixOSProcess command: pandocCommand.
self inform: ('File exported as: ', String cr, pdfFile)
]
{ #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 exists ifTrue: [ aFile delete ].
aFile
ensureCreateFile;
writeStreamDo: [:stream | stream nextPutAll: aGrafoscopioNode asMarkdown].
self inform: 'Exported as: ', String cr, aFile fullName
]
{ #category : #api }
GrafoscopioNotebook >> extent [
^900@500
]
{ #category : #accessing }
GrafoscopioNotebook >> header [
^ header
]
{ #category : #accessing }
GrafoscopioNotebook >> header: anObject [
header := anObject
]
{ #category : #initialization }
GrafoscopioNotebook >> initialize [
super initialize.
self
notebook: (GrafoscopioNode new becomeDefaultTree);
title: ' New | 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 links) includes: arg)
ifFalse: [
(tree highlightedItem) content links add: arg.
self updateForSpecialLinks]]
]
{ #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 : #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 : #'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: '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: (Smalltalk ui icons iconNamed: #smallInspectIt);
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: '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 workingFile: aFileReference.
self notebook: ((STON fromString: self workingFile contents) at: 1) parent.
self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'.
self notebookContent: self notebook.
^ 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: anUrl [
"Opens a tree from a file named aFileName"
| fileName |
fileName := (anUrl splitOn: '/') last.
GrafoscopioUtils
downloadingFrom: anUrl
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 : #'editing nodes' }
GrafoscopioNotebook >> pasteNodeFromClipboard [
tree highlightedItem content pasteFromClipboard.
self notebookContent: notebook.
]
{ #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 exists ifTrue: [self workingFile delete].
self workingFile ensureCreateFile.
[ self exportAsSton: self notebook on: (self workingFile writeStream)]
ensure: [ (self workingFile writeStream) ifNotNil: #close ].
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 tags = 'código')
ifTrue: [ currentNode content tagAs: '' ]
ifFalse: [ currentNode content tagAs: 'código' ].
self updateBodyFor: currentNode.
]
{ #category : #initialization }
GrafoscopioNotebook >> topBar [
^MenuModel new
addGroup: [ :group |
group addItem: [ :item |
item
name: 'Notebook';
icon: Smalltalk ui icons smallObjects;
subMenu: self notebookSubMenu ].
group addItem: [ :item |
item
name: 'Project';
icon: Smalltalk ui icons catalog;
subMenu: self projectSubMenu ] ];
addGroup: [ :group |
group addItem: [ :item |
item
name: nil;
description: 'Save notebook';
icon: Smalltalk ui icons glamorousSave;
action: [ self saveWorkingNotebook ] ].
group addItem: [ :item |
item
name: nil;
description: 'Export all markdown subtrees';
icon: Smalltalk ui icons glamorousMore;
action: [ self exportAllSubtreesAsMarkdow ] ].
group addItem: [ :item |
item
name: nil;
description: 'Cut';
icon: Smalltalk ui icons smallCut;
action: [ self cutNodeToClipboard ] ].
group addItem: [ :item |
item
name: nil;
description: 'Copy';
icon: Smalltalk ui icons smallCopy;
action: [ self copyNodeToClipboard ] ].
group addItem: [ :item |
item
name: nil;
description: 'Paste';
icon: Smalltalk ui icons smallPaste;
action: [ self pasteNodeFromClipboard ] ]];
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: 'Togle: code <--> text';
icon: MendaIcons new smalltalkCodeIcon;
action: [ self toggleCodeNode ] ].
group addItem: [ :item |
item
name: nil;
description: 'Visit link';
icon: Smalltalk ui icons glamorousRight;
action: [ self visitNodeLink ] ].
group addItem: [ :item |
item
name: nil;
description: 'Reload link';
icon: Smalltalk ui icons glamorousRefresh;
action: [ self updateForSpecialLinks ] ].
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: Smalltalk ui icons glamorousBug;
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 >> updateForSpecialLinks [
"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 importLinkContent.
self updateBodyFor: currentNode
]
{ #category : #'editing nodes' }
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: aFile [
workingFile := aFile.
]