diff --git a/repository/Grafoscopio/GfUIHelpers.class.st b/repository/Grafoscopio/GfUIHelpers.class.st index d87d1ed..0be3b69 100644 --- a/repository/Grafoscopio/GfUIHelpers.class.st +++ b/repository/Grafoscopio/GfUIHelpers.class.st @@ -44,7 +44,7 @@ GfUIHelpers class >> addToHelpMenu: aGrafoscopioNotebook [ ifNone: [ self helpMenu add: (metadata at: 'shortTitle') - target: [ GrafoscopioNewNotebook open: nbFile ] + target: [ GrafoscopioNotebook open: nbFile ] selector: #value ] ]. self updateUI ] @@ -170,7 +170,7 @@ GfUIHelpers class >> openFromRecentlyUsed [ selection := UIManager default chooseFrom: recentNotebooksReversed title: 'Choose a notebook...'. selection > 0 - ifTrue: [ GrafoscopioNewNotebook new openFromFile: (recentNotebooksReversed at: selection)] + ifTrue: [ GrafoscopioNotebook new openFromFile: (recentNotebooksReversed at: selection)] ifFalse: [ self inform: 'No notebook selected!' ] ] ifEmpty: [self messageNoRecentDocuments] diff --git a/repository/Grafoscopio/GrafoscopioAbstractNode.class.st b/repository/Grafoscopio/GrafoscopioAbstractNode.class.st index 5422c02..5b37bb3 100644 --- a/repository/Grafoscopio/GrafoscopioAbstractNode.class.st +++ b/repository/Grafoscopio/GrafoscopioAbstractNode.class.st @@ -1,13 +1,15 @@ +" +Just an abstract node. +" Class { #name : #GrafoscopioAbstractNode, #superclass : #Object, #instVars : [ - 'id', - 'header', 'created', 'edited', - 'parent', - 'tags' + 'tags', + 'order', + 'name' ], #classInstVars : [ 'clipboard' @@ -15,67 +17,24 @@ Class { #category : 'Grafoscopio-Model' } -{ #category : #utility } -GrafoscopioAbstractNode class >> cleanTreeRootReferences [ - - | ref | - clipboard ifNil: [ ^ self ]. - clipboard children ifNil: [ ^ self ]. - clipboard preorderTraversal allButFirstDo: [ :n | - ref := n. - n level - 1 timesRepeat: [ ref := ref parent ]. - ref parent = clipboard parent ifTrue: [ ref parent: nil ]]. - clipboard parent: nil. -] - -{ #category : #accessing } -GrafoscopioAbstractNode class >> clipboard [ - ^ clipboard -] - -{ #category : #accessing } -GrafoscopioAbstractNode class >> clipboard: anObject [ - clipboard := anObject -] - -{ #category : #utility } -GrafoscopioAbstractNode class >> contentProviders [ - "I list the domains of certain providers that are treated specially, because they - store and offer content like Smalltalk playgrounds or source code, that can be used - in particular ways while importing or exporting content in a node." - - ^ Dictionary new - at: 'playgrounds' put: #('ws.stfx.eu'); - at: 'fossil' put: #('mutabit.com/repos.fossil'); - at: 'etherpads' put: #('pad.tupale.co' ); - yourself. -] - { #category : #testing } GrafoscopioAbstractNode class >> isAbstract [ ^ self = GrafoscopioAbstractNode ] -{ #category : #utility } -GrafoscopioAbstractNode class >> specialWords [ - "I return a list of word that were used in the first versions of Grafoscopio to mark node - headers to indicate special ways to handle them and their node contents. - Previous versions of first notebooks stored in Grafoscopio using this convention should be - migrated to newer versions where tags are used for the same function with simpler code" - - ^ #('%config' '%abstract' '%invisible' '%idea' '%footnote' '%metadata' '%output' '%embed' '%item'). +{ #category : #testing } +GrafoscopioAbstractNode class >> isLeaf [ + ^ false ] -{ #category : #'add/remove nodes' } -GrafoscopioAbstractNode >> addNodeAfterMe: genericNode [ - "Adds a generic node after the given node so they become slibings of the same parent" +{ #category : #testing } +GrafoscopioAbstractNode class >> showInMenu [ + ^ false +] - self parent - ifNil: [ self children add: genericNode. - genericNode parent: self ] - ifNotNil: [ self parent children add: genericNode after: self. - genericNode parent: self parent ]. - ^ genericNode +{ #category : #accessing } +GrafoscopioAbstractNode >> addChild: aBlock ofClass: aClass [ + self subclassResponsibility ] { #category : #accessing } @@ -88,11 +47,6 @@ GrafoscopioAbstractNode >> addTag: aTag [ ] -{ #category : #'as yet unclassified' } -GrafoscopioAbstractNode >> content [ - self subclassResponsibility -] - { #category : #accessing } GrafoscopioAbstractNode >> created [ @@ -106,21 +60,6 @@ GrafoscopioAbstractNode >> created: aTimestamp [ created := aTimestamp ] -{ #category : #movement } -GrafoscopioAbstractNode >> demote [ - "I move the current node down in the hierachy, making it a children of its current previous - slibing" - - | collection index predecessor | - collection := self parent children. - index := collection indexOf: self. - (index between: 2 and: collection size) - ifTrue: [ predecessor := collection before: self. - collection remove: self. - predecessor addNode: self] - -] - { #category : #accessing } GrafoscopioAbstractNode >> edited [ ^ edited @@ -137,129 +76,27 @@ GrafoscopioAbstractNode >> edited: aTimestamp [ edited := aTimestamp ] -{ #category : #accessing } -GrafoscopioAbstractNode >> header [ - "Returns the receiver header" - - ^ header -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> header: anObject [ - "Sets the receivers header" - - header := anObject -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> id [ - ^id -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> id: aChecksum [ - "I'm a unique identifier that changes when node content changes (i.e. header, body, links)." - - id := aChecksum -] - { #category : #accessing } GrafoscopioAbstractNode >> initialize [ "I create a empty new node" - super initialize. - self header: 'newHeader'. - id := UUID new. created := DateAndTime now. edited := DateAndTime now ] -{ #category : #accessing } -GrafoscopioAbstractNode >> instantiateBody [ - | body | - body := self specModelClass new. - body content: self content. - ^ body -] - { #category : #accessing } GrafoscopioAbstractNode >> isLeaf [ - ^ true + ^ self class isLeaf ] { #category : #accessing } -GrafoscopioAbstractNode >> level [ - "Returns the level of the node. See the setter message for details" - - ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] -] - -{ #category : #movement } -GrafoscopioAbstractNode >> moveDown [ - "Moves the current node a place before in the children collection where is located" - - self parent moveDown: self -] - -{ #category : #movement } -GrafoscopioAbstractNode >> moveUp [ - "Moves the current node a place before in the children collection where is located" - - self parent moveUp: self +GrafoscopioAbstractNode >> name [ + ^ name ] { #category : #accessing } -GrafoscopioAbstractNode >> openIn: aNotebook [ - aNotebook header text: self header. - aNotebook - body: - (self instantiateBody - owner: aNotebook; - yourself) -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> parent [ - "Returns the parent of the current node" - ^ parent -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> parent: aNode [ - "A parent is a node that has the current node in its children" - aNode ifNil: [ - parent := aNode. - ^self ]. - aNode parent = self ifTrue: [ ^ self ]. - parent := aNode. - (aNode children includes: self) - ifFalse: [ aNode addNode: self ] - -] - -{ #category : #movement } -GrafoscopioAbstractNode >> promote [ - "Moves the current node up in the hierachy, making it a slibing of its current parent" - | collection grandparent | - collection := self parent children. - grandparent := self parent parent. - collection isNotNil & grandparent isNotNil - ifTrue: [ - (grandparent children) add: self after: (self parent). - self parent: grandparent. - collection remove: self.] - - -] - -{ #category : #'as yet unclassified' } -GrafoscopioAbstractNode >> shouldAskBeforeRemove [ - self subclassResponsibility -] - -{ #category : #'as yet unclassified' } -GrafoscopioAbstractNode >> specModelClass [ - ^ GrafoscopioNewTextModel +GrafoscopioAbstractNode >> name: aName [ + name := aName ] { #category : #accessing } @@ -281,14 +118,7 @@ GrafoscopioAbstractNode >> tags: aCollection [ tags := aCollection ] -{ #category : #accessing } -GrafoscopioAbstractNode >> title [ - "Returns the receiver header" - - ^ header size > 30 ifTrue: [ (header readStream next: 28) , '...' ] ifFalse: [ header ] -] - { #category : #'as yet unclassified' } -GrafoscopioAbstractNode >> updateEditionTimestamp [ +GrafoscopioAbstractNode >> updateStamp [ self edited: DateAndTime now ] diff --git a/repository/Grafoscopio/GrafoscopioBranchNode.class.st b/repository/Grafoscopio/GrafoscopioBranchNode.class.st new file mode 100644 index 0000000..a4884c3 --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioBranchNode.class.st @@ -0,0 +1,54 @@ +" +Branch node. this kind of node has no content +" +Class { + #name : #GrafoscopioBranchNode, + #superclass : #GrafoscopioLeafNode, + #instVars : [ + 'children' + ], + #category : 'Grafoscopio-Model' +} + +{ #category : #'instance creation' } +GrafoscopioBranchNode class >> isLeaf [ + ^ false +] + +{ #category : #accessing } +GrafoscopioBranchNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitBranchNode: self. + +] + +{ #category : #accessing } +GrafoscopioBranchNode >> acceptsChildsOfClass: aClass [ + ^ {GrafoscopioBranchNode. + GrafoscopioUnitNode} includes: aClass +] + +{ #category : #accessing } +GrafoscopioBranchNode >> addChild: aBlock ofClass: aClass [ + (self acceptsChildsOfClass: aClass) + ifTrue: [ self children add: aBlock value ] +] + +{ #category : #accessing } +GrafoscopioBranchNode >> children [ + "Returns the receivers list of children" + + ^ children ifNil: [ children := OrderedCollection new ] +] + +{ #category : #accessing } +GrafoscopioBranchNode >> children: aCollection [ + "Sets the receivers children" + + aCollection do: [:currentNode | currentNode parent: self ]. + children := aCollection. +] + +{ #category : #accessing } +GrafoscopioBranchNode >> isLeaf [ + ^ false +] diff --git a/repository/Grafoscopio/GrafoscopioCodeNode.class.st b/repository/Grafoscopio/GrafoscopioCodeNode.class.st index 456f655..f9aed5d 100644 --- a/repository/Grafoscopio/GrafoscopioCodeNode.class.st +++ b/repository/Grafoscopio/GrafoscopioCodeNode.class.st @@ -1,62 +1,24 @@ +" +This kind of a leafNodes holds code text. +" Class { #name : #GrafoscopioCodeNode, - #superclass : #GrafoscopioTrunkNode, - #instVars : [ - 'icon', - 'body' - ], + #superclass : #GrafoscopioTextNode, #category : 'Grafoscopio-Model' } +{ #category : #'as yet unclassified' } +GrafoscopioCodeNode class >> icon [ + ^ self iconNamed:#objects +] + { #category : #'as yet unclassified' } GrafoscopioCodeNode class >> nameForSelection [ ^ 'New Code Node' ] -{ #category : #adding } -GrafoscopioCodeNode >> addNode: aNode [ - "Adds the given node to the receivers collection of children, and sets this object as the parent - of the node" - "aNode parent = self ifTrue: [ ^ self ]." - self children add: aNode. - aNode parent: self. - ^aNode -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> body [ - ^ body -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> body: aBody [ - body := aBody -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> content [ - ^ body ifNil:[ '' ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> header [ - ^ super header, ' (Code)' -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> instantiateBody [ - | widget | - widget := super instantiateBody. - widget body whenTextChangedDo: [ :arg | self body: arg ]. - ^ widget -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> shouldAskBeforeRemove [ - ^ self content isNotEmpty -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode >> specModelClass [ - ^ GrafoscopioNewCodeModel +{ #category : #accessing } +GrafoscopioCodeNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitCodeNode: self. + ] diff --git a/repository/Grafoscopio/GrafoscopioDocumentEditionPerspective.class.st b/repository/Grafoscopio/GrafoscopioDocumentEditionPerspective.class.st new file mode 100644 index 0000000..5e972dd --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioDocumentEditionPerspective.class.st @@ -0,0 +1,76 @@ +Class { + #name : #GrafoscopioDocumentEditionPerspective, + #superclass : #GrafoscopioPerspective, + #instVars : [ + 'tree', + 'document' + ], + #category : 'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioDocumentEditionPerspective class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: #toolbar height: self toolbarHeight; + add: + (SpBoxLayout newHorizontal + add: #tree width: 100; + add: #viewport; + yourself) yourself +] + +{ #category : #accessing } +GrafoscopioDocumentEditionPerspective class >> icon [ + ^ self iconNamed: #merge +] + +{ #category : #'as yet unclassified' } +GrafoscopioDocumentEditionPerspective >> addNewNodeOfClass: aClass [ + (tree selectedItem ifNil: [ document ]) + addChild: [ self instantiateNode: aClass ] + ofClass: aClass. + self needRebuild: false. + self buildWithSpec +] + +{ #category : #initialization } +GrafoscopioDocumentEditionPerspective >> createDefaultViewportVisitor [ + ^ GrafoscopioViewportVisitor new +] + +{ #category : #initialization } +GrafoscopioDocumentEditionPerspective >> createViewport [ + ^ self createDefaultViewportVisitor createViewportFor: document into: self +] + +{ #category : #initialization } +GrafoscopioDocumentEditionPerspective >> initializeWidgets [ + super initializeWidgets. + tree := self newTreeTable. + tree + addColumn: (SpStringTableColumn evaluated: #name); + children: [ :node | + node isLeaf + ifTrue: [ {} ] + ifFalse: [ node children ] ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioDocumentEditionPerspective >> instantiateNode: aClass [ + aClass class = GrafoscopioUnitNode species + ifTrue: [ | name | + name := UIManager default + request: 'Unit name' + initialAnswer: 'New unit'. + ^ aClass new + name: name; + yourself ]. + ^ aClass new +" self error: 'Unexpected class'" +] + +{ #category : #initialization } +GrafoscopioDocumentEditionPerspective >> updateModel: aModel [ + document := aModel document. + tree roots: aModel document children. +] diff --git a/repository/Grafoscopio/GrafoscopioExportPerspective.class.st b/repository/Grafoscopio/GrafoscopioExportPerspective.class.st new file mode 100644 index 0000000..b224e78 --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioExportPerspective.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GrafoscopioExportPerspective, + #superclass : #GrafoscopioPerspective, + #category : 'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioExportPerspective class >> icon [ + ^ self iconNamed: #export +] + +{ #category : #initialization } +GrafoscopioExportPerspective >> createViewport [ + ^ self newLabel +] diff --git a/repository/Grafoscopio/GrafoscopioLeafNode.class.st b/repository/Grafoscopio/GrafoscopioLeafNode.class.st new file mode 100644 index 0000000..8579eaa --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioLeafNode.class.st @@ -0,0 +1,53 @@ +" +Leaf node. Any content node is leaf +" +Class { + #name : #GrafoscopioLeafNode, + #superclass : #GrafoscopioAbstractNode, + #instVars : [ + 'parent' + ], + #category : 'Grafoscopio-Model' +} + +{ #category : #testing } +GrafoscopioLeafNode class >> isLeaf [ + ^ true +] + +{ #category : #accessing } +GrafoscopioLeafNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitLeafNode: self. + +] + +{ #category : #accessing } +GrafoscopioLeafNode >> addChild: aBlock ofClass: aClass [ + self error: 'Leaf nodes are abstract. ' +] + +{ #category : #accessing } +GrafoscopioLeafNode >> level [ + "Returns the level of the node. See the setter message for details" + + ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] +] + +{ #category : #accessing } +GrafoscopioLeafNode >> parent [ + "Returns the parent of the current node" + ^ parent +] + +{ #category : #accessing } +GrafoscopioLeafNode >> parent: aNode [ + "A parent is a node that has the current node in its children" + aNode ifNil: [ + parent := aNode. + ^self ]. + aNode parent = self ifTrue: [ ^ self ]. + parent := aNode. + (aNode children includes: self) + ifFalse: [ aNode addNode: self ] + +] diff --git a/repository/Grafoscopio/GrafoscopioNewCodeModel.class.st b/repository/Grafoscopio/GrafoscopioNewCodeModel.class.st index 5d45516..cf753a0 100644 --- a/repository/Grafoscopio/GrafoscopioNewCodeModel.class.st +++ b/repository/Grafoscopio/GrafoscopioNewCodeModel.class.st @@ -29,32 +29,6 @@ GrafoscopioNewCodeModel >> content: aGrafoscopioNodeContent [ body text: aGrafoscopioNodeContent ] -{ #category : #'as yet unclassified' } -GrafoscopioNewCodeModel >> extractHtmlImages [ - "comment stating purpose of message" - |imgSoup imgHost imgList src| - - imgList := Set new. - imgSoup := Soup fromString: self body. - (imgSoup findAllTags: 'img') do: [ :each| - src := (each attributeAt: 'src') asUrl. - (src host) ifNil: [src host: self links last asUrl removeLastPathSegment]. - imgList add: src. - "imgList add: (each attributeAt: 'src') asUrl." - - "OSProcess waitForCommand: 'wget ', (each attributeAt: 'src')." - "imgHost := self links last removeLastPathSegment." - - "imgPath:= ((each attributeAt: 'src') asUrl). " - "ZnEasy getJpeg: (imgHost , imgPath) asUrl." - - "OSProcess waitForCommand: ('mkdir ', imgPath)." - - "Transcript show: ' wget ', imgPath , '/',(each attributeAt: 'src'). " - ]. - ^imgList . -] - { #category : #initialization } GrafoscopioNewCodeModel >> initializeWidgets [ diff --git a/repository/Grafoscopio/GrafoscopioNewNotebook.class.st b/repository/Grafoscopio/GrafoscopioNewNotebook.class.st deleted file mode 100644 index 7316f96..0000000 --- a/repository/Grafoscopio/GrafoscopioNewNotebook.class.st +++ /dev/null @@ -1,1316 +0,0 @@ -" -I am a Grafoscopio Notebook. - -Example: -| testTree nb | -testTree := GrafoscopioNode new becomeDefaultTestTree. -nb := GrafoscopioNotebook new. -nb notebookContent: testTree. -nb openWithSpec -" -Class { - #name : #GrafoscopioNewNotebook, - #superclass : #SpPresenter, - #instVars : [ - 'tree', - 'header', - 'body', - 'links', - 'windowMainMenu', - 'workingFile', - 'notebook', - 'debugMessage', - 'imagesList', - 'exporting' - ], - #classInstVars : [ - 'recents' - ], - #category : 'Grafoscopio-New-UI' -} - -{ #category : #utility } -GrafoscopioNewNotebook 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 } -GrafoscopioNewNotebook class >> defaultSpec [ - ^ SpBoxLayout newVertical - add: #windowMainMenu - withConstraints: [ :constraints | constraints height: self toolbarHeight ]; - add: - (SpPanedLayout newHorizontal - position: 250; - add: #tree; - add: - (SpBoxLayout newVertical - add: #header height: self toolbarHeight; - add: #body; - add: #links height: self toolbarHeight; - yourself); - yourself); - yourself -] - -{ #category : #'instance creation' } -GrafoscopioNewNotebook class >> kindsOfNode [ - ^ GrafoscopioAbstractNode allSubclasses select: [ : c | c isAbstract not ] -] - -{ #category : #'instance creation' } -GrafoscopioNewNotebook class >> newDefault [ - ^ self new. -] - -{ #category : #'instance creation' } -GrafoscopioNewNotebook class >> open: aFileReference [ - self newDefault openFromFile: aFileReference -] - -{ #category : #'instance creation' } -GrafoscopioNewNotebook class >> recents [ - ^ recents ifNil: [ recents := Set new. ] -] - -{ #category : #'instance creation' } -GrafoscopioNewNotebook class >> registerRecent: aFileReference [ - recents add: aFileReference -] - -{ #category : #utilities } -GrafoscopioNewNotebook >> addCommandFrom: dictionary into: stream [ - dictionary keysAndValuesDo: [ :k :v | - k = 'thisNotebook' - ifTrue: [ - stream nextPutAll: (GrafoscopioUtils perform: v on: self) ]] -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> addNode [ - | newNode kind previousPath | - kind := self chooseKindOfNode. - kind isClass - ifTrue: [ newNode := kind new. - - self currentNode addNodeAfterMe: newNode. - previousPath := tree selection selectedPath. - self notebookContent: notebook. - - self selectedPathNextTo: previousPath ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> askToSaveBeforeClosing [ - - | saveChanges | - - saveChanges := UIManager default - question: 'Do you want to save changes in the notebook before closing?' - title: 'Save changes before closing?'. - saveChanges ifNil: [ ^ self notebook unsavedNodes inspect ]. - ^ saveChanges -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> body [ - ^ body -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> body: anObject [ - body := anObject -] - -{ #category : #utilities } -GrafoscopioNewNotebook >> checksumForRootSubtree [ - "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' } -GrafoscopioNewNotebook >> chooseKindOfNode [ - | idx values | - values := self kindsOfNode. - idx := UIManager default - chooseFrom: (values collect: [ :v | v nameForSelection ]) - lines: {} - title: 'What kind of node? '. - ^ idx = 0 - ifTrue: [ nil ] - ifFalse: [ values at: idx ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewNotebook >> content [ - self shouldBeImplemented. -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> copyNodeToClipboard [ - tree selectedItem copyToClipboard. - self notebookContent: notebook -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> createNewExample [ - | node0 node1 | - node0 := GrafoscopioTextNode new - created: DateAndTime now ; - header: 'Arbol principal'; - yourself. - node1 := GrafoscopioTextNode new - created: DateAndTime now; - header: 'Node 1'; - body: ''; - yourself. - node0 addNode: node1. - ^ node0 -] - -{ #category : #operation } -GrafoscopioNewNotebook >> currentNode [ - ^ tree selectedItem - ifNil: [ notebook children - ifEmpty: [ notebook root ] - ifNotEmpty: [ notebook children first ] ] -] - -{ #category : #operation } -GrafoscopioNewNotebook >> currentNodeContent [ - ^ self currentNode content -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> cutNodeToClipboard [ - self copyNodeToClipboard; removeNode. -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> debugMessage [ - ^ debugMessage ifNil: [ self defineDebugMessageUI ] -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> debugMessage: aGrafoscopioNodeSelector [ - "I define a message that can be used for debugging purposes in the current notebook." - debugMessage := aGrafoscopioNodeSelector -] - -{ #category : #operation } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> 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' } -GrafoscopioNewNotebook >> demoteNode [ - | editedNode | - editedNode := tree selectedItem. - editedNode demote. - self notebookContent: notebook. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewNotebook >> downloadImages [ - "I download all images in a notebook into a local folder that respects relative paths. - So if a image refers to http://mysite.com/uploads/chap1/myimage.png, it will be stored - into: 'uploads/chap1/myimage.png' in the same folder where the notebook is stored. - This is helpful for notebooks conversions that expect to have local images in particular - locations." - - | parentFolder | - parentFolder := UIManager default - chooseDirectory: 'Please, choose a destination for the images' - from: - (self workingFile - ifNil: [ FileLocator home ] - ifNotNil: [ self workingFile parent ]). - parentFolder - ifNotNil: [ ^ self imagesList - do: [ :each | - | relativePathString link | - link := each contents asUrl. - relativePathString := link directory. - relativePathString - ifNotEmpty: [ GrafoscopioUtils - ensureCreateDirectory: relativePathString - into: parentFolder ] ] ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> ensureNotExporting [ - self isAlreadyExporting - ifTrue: [ ^ self error: ' Already exporting! Please wait ' ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> exportAllSubtreesAsMarkup [ - | toBeExported | - toBeExported := self notebook selectMarkupSubtreesToExport. - toBeExported ifEmpty: [ ^ self ]. - toBeExported do: [ :each | self subtreeAsMarkdownFileFor: each ]. - self inform: toBeExported size asString , ' exported markdown subtrees.' -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 htmlFile. - htmlFile exists - ifTrue: [ htmlFile delete ]. - self - exportUsing: - {'--standalone'. - self markdownFile fullName. - '--output'. - htmlFile fullName} - output: htmlFile fullName - " - - - Smalltalk platformName = 'Win32' - ifTrue: [WinProcess createProcess: 'pandoc --standalone ', self markdownFile fullName, ' -o ', htmlFile]." -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 halt. - - "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 } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> 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 ensureNotExporting. - self exportAsMarkdown. - self pdfFile ensureDelete. - pandocCommonCommand := self pandocOptionsComputed , ' ' - , self markdownFile fullName , ' --output ' , self pdfFile fullName. - ^ self - exportUsing: ((' ' split: pandocCommonCommand) reject: #isEmpty) - output: self pdfFile fullName -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> exportAsSton: aNotebook on: aFileStream [ - aNotebook flatten. - self notebook root updateEditionTimestamp. - (STON writer on: aFileStream) - newLine: String crlf; - prettyPrint: true; - keepNewLines: true; - nextPut: aNotebook children. - -] - -{ #category : #utility } -GrafoscopioNewNotebook >> exportLinkContent [ - Transcript show: (self currentNodeContent asMarkdown) -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 checksumForRootSubtree - , String cr) withInternetLineEndings. - aGrafoscopioNode metadataAsYamlIn: stream. - stream - nextPutAll: - ('---' , String cr , String cr) withInternetLineEndings - , aGrafoscopioNode asMarkdown. - stream flush ]. - self inform: 'Exported as: ' , String cr , aFile fullName -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> exportUsing: arguments [ - self ensureNotExporting. - exporting := (#pandoc command arguments: arguments) future. - exporting - onSuccessDo: [ :val | - exporting := nil. - self - inform: 'File exported as: ' , String cr , self pdfFile fullName ]. - exporting - onFailureDo: [ :e | - exporting := nil. - self - inform: 'Error exporting, ' , self pdfFile fullName , ': ' , e messageText ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> exportUsing: arguments output: aName [ - self ensureNotExporting. - exporting := (#pandoc command arguments: arguments) future. - exporting - onSuccessDo: [ :val | - exporting := nil. - self inform: 'File exported as: ' , String cr , aName ]. - exporting - onFailureDo: [ :e | - exporting := nil. - self inform: 'Error exporting, ' , aName , ': ' , e messageText ]. - ^ exporting -] - -{ #category : #api } -GrafoscopioNewNotebook >> extent [ - ^900@500 -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewNotebook >> findAndReplace [ - | currentNode replaceGUI findString replaceString | - currentNode := tree selectedItem. - 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 : #testing } -GrafoscopioNewNotebook >> hasAWorkingFileDefined [ - self workingFile ifNil: [ ^ false ] ifNotNil: [ ^ true ] -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> header [ - ^ header -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> header: anObject [ - header := anObject -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> htmlFile [ - ^ (self markdownFile parent fullName , '/' - , self markdownFile basenameWithoutExtension , '.html') - asFileReference -] - -{ #category : #operation } -GrafoscopioNewNotebook >> htmlToMarkdown [ - self currentNodeContent htmlToMarkdown. - self updateBodyFor: self currentNode -] - -{ #category : #operation } -GrafoscopioNewNotebook >> htmlToMarkdownSubtree [ - self currentNodeContent htmlToMarkdownSubtree. - self updateBodyFor: self currentNode -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> imagesList [ - imagesList ifNil: [ ^ #('No images list for this notebook') ]. - ^ imagesList -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> imagesList: anObject [ - self halt. - imagesList := anObject -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> importLinkContent [ - "I see if a node link 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 | - currentNode := tree selectedItem. - currentNode ifNil: [ ^ self ]. - currentNode importPlaygroundLink. - currentNode importHtmlLink. - self updateBodyFor: currentNode -] - -{ #category : #initialization } -GrafoscopioNewNotebook >> initialize [ - super initialize. - self notebook: self createNewExample. - self notebookContent: self notebook -] - -{ #category : #initialization } -GrafoscopioNewNotebook >> initializePresenter [ - tree - whenSelectionChangedDo: - [ :selection | selection selectedItem ifNotNil: [ self updateBodyFor: selection selectedItem ] ]. - header - whenTextChangedDo: [ :arg | - tree selectedItem header = arg - ifFalse: [ | item | - item := tree selectedItem. - tree selectedItem header: arg. - tree selectedItem updateEditionTimestamp. -" tree roots: tree roots." -" tree selectItem: item "] ]. - links - whenTextChangedDo: [ :arg | - (tree respondsTo: #link:) - ifTrue: [ tree selectedItem link: arg. - tree selectedItem updateEditionTimestamp ] ] -] - -{ #category : #initialization } -GrafoscopioNewNotebook >> initializeWidgets [ - windowMainMenu := self topBar. - header := self newTextInput. - header autoAccept: true. - body := self newText. - body class traceCr. - body disable. - body text: '<- Select a node'. - body autoAccept: true. - links := self newTextInput. - tree := self newTreeTable. - tree - addColumn: (SpStringTableColumn evaluated: #title); - children: [ :node | - node isLeaf - ifTrue: [ {} ] - ifFalse: [ node children ] ]. - self focusOrder - add: tree; - add: header; - add: body; - add: links -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> initializeWindow: aWindowPresenter [ - super initializeWindow: aWindowPresenter. - aWindowPresenter - title: ' New | Grafoscopio 2.0 notebook'; - windowIcon: self taskbarIcon; - askOkToClose: true -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> isAlreadyExporting [ - ^ exporting isNotNil -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> isSaved [ - "I tell if a notebook has been saved in a persistence storage, including last editions." - ^ self hasAWorkingFileDefined and: [self isSavedAfterLastEdition ]. -] - -{ #category : #testing } -GrafoscopioNewNotebook >> isSavedAfterLastEdition [ - ^ self notebook isSavedAfterLastEdition -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> kindsOfNode [ - ^ self class kindsOfNode -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> links [ - ^ links -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> links: anObject [ - links := anObject -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewNotebook >> linksList [ - | currentNode | - currentNode := tree selectedItem. - GrafoscopioLinksList new - content: currentNode; - openWithSpec - -] - -{ #category : #utilities } -GrafoscopioNewNotebook >> listImagesUI [ - ListPresenter new - title: 'Images files list'; - items: self imagesList ; - openWithSpec -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> markdownFileChecksum [ - self workingFile ifNil: [ ^ self ]. - self workingFile contents = '' ifTrue: [ ^ self ]. - ^ GrafoscopioUtils checksumFor: self markdownFile -] - -{ #category : #utilities } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> metadata [ - ^ self notebook metadata -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> moveSelectedNodeDown [ - | editedNode | - editedNode := tree selectedItem. - editedNode moveDown. - self notebookContent: notebook. - tree needRebuild: true. - tree selectItem: editedNode. -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> moveSelectedNodeUp [ - | editedNode | - editedNode := tree selectedItem. - editedNode moveUp. - self notebookContent: notebook -] - -{ #category : #utilities } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> notebook [ - ^ notebook -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> notebook: anObject [ - notebook := anObject -] - -{ #category : #api } -GrafoscopioNewNotebook >> notebookContent: aTree [ - tree roots: aTree children -] - -{ #category : #initialization } -GrafoscopioNewNotebook >> notebookSubMenu [ - ^ SpMenuBarPresenter 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: 'See images list'; - icon: (Smalltalk ui icons iconNamed: #processBrowser); - action: [ self listImagesUI ] ]. - 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: (self iconNamed: #smallInspectIt); - action: [ self seeHtml ] ]. - group - addItem: [ :item | - item - name: 'See pdf'; - icon: (Smalltalk ui icons iconNamed: #smallInspectIt); - action: [ self seePdf ] ]. - - group - addItem: [ :item | - item - name: 'Define debug message...'; - icon: Smalltalk ui icons glamorousBug; - action: [ self defineDebugMessageUI ] ] ] -] - -{ #category : #'event handling' } -GrafoscopioNewNotebook >> okToChange [ - - self isSaved ifTrue: [ ^ true ] ifFalse: [ ^ self askToSaveBeforeClosing ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> openDefault [ -"I open a new default notebook" - - ^ self class new openWithSpec. -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> openFromFile: aFileReference [ - self class registerRecent: aFileReference. - self loadFromFile: aFileReference. - ^ self openWithSpec. -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> openFromFileSelector [ - - | file nb | - file := UIManager default - chooseExistingFileReference:'Choose a file' - extensions: #('ston') - path: FileLocator documents. - file ifNil: [ - self inform: 'No file selected'. - ^ self ]. - self workingFile: file. - nb := self class new. - nb openFromFile: self workingFile. - GfUIHelpers updateRecentNotebooksWith: workingFile -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> pandocOptions [ - ^ self notebook pandocOptions -] - -{ #category : #utilities } -GrafoscopioNewNotebook >> 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' } -GrafoscopioNewNotebook >> pasteNodeFromClipboard [ - tree selectedItem pasteFromClipboard. - self notebookContent: notebook. -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 } -GrafoscopioNewNotebook >> 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: (self iconNamed: #userIcon); - action: [ self inform: 'To be implemented ...' ] ] ] -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> promoteNode [ - | editedNote | - editedNote := tree selectedItem. - editedNote promote. - self notebookContent: notebook -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> removeNode [ - | contentToDelete parentContent newSelectedContent children path | - tree selectedItem - ifNil: [ ^ self inform: 'No node available or properly selected ' ]. - - path := tree selection selectedPath. - contentToDelete := tree selectedItem. - contentToDelete shouldAskBeforeRemove - ifTrue: [ (UIManager default - questionWithoutCancel: - 'The selected node has children and / or content. This change so far cannot be undone. Are you sure you want to proceed? ' - title: 'Remove node') - ifFalse: [ ^ self ] ]. - parentContent := tree selectedItem 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. - self resetSelectedItemTo: path. -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> resetSelectedItem [ - tree selectedIndex: (tree selectedIndex min: notebook children size). - tree highlightedItem: tree selectedItem. - tree updatePresenter. -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> resetSelectedItemTo: aPath [ - self selectedPathPreviousTo: aPath -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> saveToFile: aFileReference [ - "I save the current tree/document to a file and update storage timestamp." - - 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). - GfUIHelpers updateRecentNotebooksWith: aFileReference. -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> saveToFileUI [ - - | file | - - file := UIManager default - chooseForSaveFileReference: 'Save notebook to file as...' - extensions: #('ston') - path: (workingFile ifNotNil: [ workingFile parent ] ifNil: [ FileLocator documents ] ). - file - ifNil: [ self inform: 'Saving to file cancelled'. ^ self ] - ifNotNil:[self saveToFile: file]. -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> 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 ]. - self notebook root updateEditionTimestamp. - GfUIHelpers updateRecentNotebooksWith: workingFile - - -] - -{ #category : #inspecting } -GrafoscopioNewNotebook >> seeHtml [ - self pdfFile exists - ifTrue: [ (#open command argument: self htmlFile fullName) schedule ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> seePdf [ - self exportAsPDF - onSuccessDo: [ :v | (#open command argument: self pdfFile fullName) schedule ] -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> selectedItem: anItem [ - tree selectItem: (tree roots detect: [ : p | p = anItem ]). -] - -{ #category : #'asdas yet unclassified' } -GrafoscopioNewNotebook >> selectedItem: aGrafoscopioTextNode path: aPath [ - aPath - at: aPath size - put: (aPath at: aPath size) + 1. - tree selectPath: aPath -] - -{ #category : #'asdas yet unclassified' } -GrafoscopioNewNotebook >> selectedPathNextTo: aPath [ - aPath isEmpty ifTrue: [ ^ self ]. - aPath - at: aPath size - put: (aPath at: aPath size) + 1. - tree selectPath: aPath -] - -{ #category : #'asdas yet unclassified' } -GrafoscopioNewNotebook >> selectedPathPreviousTo: aPath [ - (aPath at: aPath size) - 1 > 0 - ifTrue: [ aPath at: aPath size put: (aPath at: aPath size) - 1. - tree selectPath: aPath ] - ifFalse: [ self selectedPathPreviousTo: aPath allButLast ] -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> subtreeAsMarkdown [ - | currentNode | - currentNode := tree selectedItem. - self inform: ('Exported as: ', String cr, (self subtreeAsMarkdownFileFor: currentNode) fullName ) -] - -{ #category : #persistence } -GrafoscopioNewNotebook >> subtreeAsMarkdownFileFor: aNode [ - | exportedFile | - aNode links ifEmpty: [ ^ self ]. - exportedFile:= self navigateRelativePathFor: aNode links last. - exportedFile class = self class ifTrue: [ ^ self ]. - self exportNode: aNode asMarkdownIn: exportedFile. - ^ exportedFile -] - -{ #category : #'editing nodes' } -GrafoscopioNewNotebook >> toggleCodeNode [ - | currentNode | - currentNode := tree selectedItem. - currentNode toggleCodeText. - self updateBodyFor: currentNode -] - -{ #category : #initialization } -GrafoscopioNewNotebook >> topBar [ - ^ SpMenuBarPresenter new - addGroup: [ :group | - group - addItem: [ :item | - item - name: 'Notebook'; - icon: (self iconNamed: #smallObjects); - subMenu: self notebookSubMenu ]. - group - addItem: [ :item | - item - name: 'Project'; - icon: (self iconNamed: #catalog); - subMenu: self projectSubMenu ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: ''; - description: 'Save notebook'; - icon: (self iconNamed: #glamorousSave); - action: [ self saveWorkingNotebook ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Export all Markdown subtrees'; - icon: (self iconNamed: #glamorousMore); - action: [ self exportAllSubtreesAsMarkup ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Cut'; - icon: (self iconNamed: #smallCut); - action: [ self cutNodeToClipboard ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Copy'; - icon: (self iconNamed: #smallCopy); - action: [ self copyNodeToClipboard ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Paste'; - icon: (self iconNamed: #smallPaste); - action: [ self pasteNodeFromClipboard ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Find & Replace'; - icon: (self iconNamed: #smallFind); - action: [ self findAndReplace ] ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: ''; - description: 'Add node'; - icon: MendaIcons new plusIcon; - action: [ self addNode ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Delete node'; - icon: MendaIcons new minusIcon; - action: [ self removeNode ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Move node up'; - icon: MendaIcons new arrowUpIcon; - action: [ self moveSelectedNodeUp ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Move node down'; - icon: MendaIcons new arrowDownIcon; - action: [ self moveSelectedNodeDown ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Move node left'; - icon: MendaIcons new arrowLeftIcon; - action: [ self promoteNode ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Move node right'; - icon: MendaIcons new arrowRightIcon; - action: [ self demoteNode ] ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: ''; - description: 'Toggle: code <--> text'; - icon: MendaIcons new smalltalkCodeIcon; - action: [ self toggleCodeNode ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'List node links'; - icon: (self iconNamed: #tinyMenu); - action: [ self linksList ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Visit link'; - icon: (self iconNamed: #glamorousRight); - action: [ self visitNodeLink ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Import link content'; - icon: (self iconNamed: #glamorousOpenFromUrl); - action: [ self importLinkContent ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Export link content'; - icon: (self iconNamed: #glamorousSaveToUrl); - action: [ self exportLinkContent ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'HTML to Markdown'; - icon: (self iconNamed: #smallProfile); - action: [ self htmlToMarkdown ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'HTML to Markdown subtree'; - icon: (self iconNamed: #hierarchy); - action: [ self htmlToMarkdownSubtree ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Tag as...'; - icon: MendaIcons new tagAddIcon; - action: [ self inform: 'To be implemented...' ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Untag ....'; - icon: MendaIcons new tagMinusIcon; - action: [ self inform: 'To be implemented...' ] ]. - group - addItem: [ :item | - item - name: ''; - description: 'Edit tags...'; - icon: FontAwesomeIcons new tagsIcon; - action: [ self inform: 'To be implemented...' ] ] ]; - addGroup: [ :debug | - debug - addItem: [ :item | - item - name: ''; - description: 'Debug'; - icon: - (self iconNamed: #glamorousBug); - action: [ self debugWithSelector: self debugMessage ] ] ] -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> tree [ - ^ tree -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> tree: anObject [ - tree := anObject -] - -{ #category : #operation } -GrafoscopioNewNotebook >> updateBodyFor: aNode [ - | item | - self needRebuild: false. - tree needRebuild: false. - item := tree selectedItem. - body needRebuild: true. - aNode openIn: self. - self buildWithSpec. - tree selectItem: item. -] - -{ #category : #operation } -GrafoscopioNewNotebook >> visitNodeLink [ - tree selectedItem visitLastLink -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> windowMainMenu [ - ^ windowMainMenu -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> windowMainMenu: anObject [ - windowMainMenu := anObject -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> workingFile [ - ^ workingFile -] - -{ #category : #accessing } -GrafoscopioNewNotebook >> workingFile: aFileReference [ - workingFile := aFileReference. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewNotebook >> wrapBodyLines [ - self currentNodeContent wrapBodyLines. - self updateBodyFor: self currentNode -] diff --git a/repository/Grafoscopio/GrafoscopioNewTextModel.class.st b/repository/Grafoscopio/GrafoscopioNewTextModel.class.st index fb50530..9ea11a6 100644 --- a/repository/Grafoscopio/GrafoscopioNewTextModel.class.st +++ b/repository/Grafoscopio/GrafoscopioNewTextModel.class.st @@ -10,7 +10,7 @@ Class { { #category : #specs } GrafoscopioNewTextModel class >> defaultSpec [ ^ SpBoxLayout newVertical - add: #body; + add: #body height: 300; yourself ] diff --git a/repository/Grafoscopio/GrafoscopioPerspective.class.st b/repository/Grafoscopio/GrafoscopioPerspective.class.st new file mode 100644 index 0000000..a347738 --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioPerspective.class.st @@ -0,0 +1,179 @@ +Class { + #name : #GrafoscopioPerspective, + #superclass : #SpPresenter, + #instVars : [ + 'toolbar', + 'viewport' + ], + #category : 'Grafoscopio-New-UI' +} + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> defaultPerspective [ + ^ GrafoscopioDocumentEditionPerspective +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: #toolbar height: self toolbarHeight; + add: #viewport; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> isAbstract [ + ^ self = GrafoscopioPerspective +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> perspectives [ + ^ self allSubclasses select: [ : c | c isAbstract not ] +] + +{ #category : #initialization } +GrafoscopioPerspective >> aboutToBeUninstalledFrom: aTreeNotebook [ +] + +{ #category : #initialization } +GrafoscopioPerspective >> addItemTo: aGroup [ + aGroup + addItem: [ :item | + item + name: 'Dynamic'; + icon: (self iconNamed: #delete); + action: [ aGroup menuItems remove: item. + self needRebuild: false. + self buildWithSpec ] ]. + self needRebuild: false. + self buildWithSpec +] + +{ #category : #initialization } +GrafoscopioPerspective >> addingMenu [ + | menu | + menu := self newMenu. + GrafoscopioAbstractNode allSubclasses + select: [ :c | c showInMenu ] + thenDo: [ :n | + menu + addItem: [ :item | + item + name: n nameForSelection; + icon: n icon; + action: [ self addNewNodeOfClass: n ] ] ]. + ^ menu +] + +{ #category : #initialization } +GrafoscopioPerspective >> createToolbar [ + | aMenu | + aMenu := self newMenuBar + addGroup: [ :group | + group + addItem: [ :item | + item + name: 'File'; + icon: (self iconNamed: #openIcon); + subMenu: self subMenu ]. + group + addItem: [ :item | + item + name: nil; + description: 'Open file'; + icon: (self iconNamed: #openIcon); + action: [ self inform: 'Open File' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Save File'; + icon: (self iconNamed: #smallSaveIcon); + action: [ self inform: 'Save File' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Print file'; + icon: (self iconNamed: #smallPrintIcon); + action: [ self inform: 'Print file' ] ] ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Undo'; + icon: (self iconNamed: #smallUndoIcon); + action: [ self inform: 'Undo' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Redo'; + icon: (self iconNamed: #smallRedoIcon); + action: [ self inform: 'Redo' ] ]. + group + addItem: [ :item | + item + name: ''; + icon: (self iconNamed: #add); + subMenu: self addingMenu ]. + + ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Add menu item'; + icon: (self iconNamed: #add); + action: [ self addItemTo: group ] ] ]. + aMenu color: Color transparent. + ^ aMenu +] + +{ #category : #initialization } +GrafoscopioPerspective >> createViewport [ + self subclassResponsibility +] + +{ #category : #initialization } +GrafoscopioPerspective >> initializeWidgets [ + super initializeWidgets. + toolbar := self createToolbar. + viewport := self createViewport +] + +{ #category : #initialization } +GrafoscopioPerspective >> subMenu [ + ^ self newMenu + addItem: [ :item | + item + name: 'Open'; + icon: (self iconNamed: #openIcon); + shortcut: $o meta; + action: [ self inform: 'Open' ] ]; + addItem: [ :item | + item + name: 'Save'; + icon: (self iconNamed: #smallSaveIcon); + shortcut: $s meta; + action: [ self inform: 'Save' ] ]; + addItem: [ :item | + item + name: 'Print'; + shortcut: $p meta; + icon: (self iconNamed: #smallPrintIcon); + action: [ self inform: 'Print' ] ]; + addItem: [ :item | + item + name: 'Close'; + shortcut: $c meta; + icon: (self iconNamed: #smallCancelIcon); + action: [ self inform: 'Kill' ] ]; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective >> updateModel: aModel [ +] diff --git a/repository/Grafoscopio/GrafoscopioProject.class.st b/repository/Grafoscopio/GrafoscopioProject.class.st new file mode 100644 index 0000000..02e8593 --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioProject.class.st @@ -0,0 +1,21 @@ +Class { + #name : #GrafoscopioProject, + #superclass : #Object, + #instVars : [ + 'document', + 'dictionary' + ], + #category : 'Grafoscopio-Model' +} + +{ #category : #'as yet unclassified' } +GrafoscopioProject >> document [ + ^ document +] + +{ #category : #initialization } +GrafoscopioProject >> initialize [ + super initialize. + document := GrafoscopioRootNode new. + dictionary := GrafoscopioRootNode new +] diff --git a/repository/Grafoscopio/GrafoscopioRootNode.class.st b/repository/Grafoscopio/GrafoscopioRootNode.class.st new file mode 100644 index 0000000..9497d8b --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioRootNode.class.st @@ -0,0 +1,46 @@ +" +This is a root node. It represents a document. +" +Class { + #name : #GrafoscopioRootNode, + #superclass : #GrafoscopioAbstractNode, + #instVars : [ + 'children' + ], + #category : 'Grafoscopio-Model' +} + +{ #category : #accessing } +GrafoscopioRootNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitRootNode: self. + +] + +{ #category : #accessing } +GrafoscopioRootNode >> acceptsChildsOfClass: aClass [ + ^ {GrafoscopioUnitNode} includes: aClass +] + +{ #category : #accessing } +GrafoscopioRootNode >> addChild: aBlock ofClass: aClass [ + (self acceptsChildsOfClass: aClass) + ifTrue: [ self children add: aBlock value ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioRootNode >> children [ + ^ children +] + +{ #category : #accessing } +GrafoscopioRootNode >> initialize [ + super initialize. + children := SortedCollection new + sortBlock: [ :a :b | a order < b order ]; + yourself +] + +{ #category : #accessing } +GrafoscopioRootNode >> level [ + ^ 1 +] diff --git a/repository/Grafoscopio/GrafoscopioTextNode.class.st b/repository/Grafoscopio/GrafoscopioTextNode.class.st index 17b28f6..3866a27 100644 --- a/repository/Grafoscopio/GrafoscopioTextNode.class.st +++ b/repository/Grafoscopio/GrafoscopioTextNode.class.st @@ -1,923 +1,37 @@ " -An UbakyeNode is and administrator of all node operations in a tree. - -Instance Variables - node: - -node - - xxxxx - +This kind of a leafNodes holds plain text. " Class { #name : #GrafoscopioTextNode, - #superclass : #GrafoscopioTrunkNode, + #superclass : #GrafoscopioLeafNode, #instVars : [ - 'key', - 'icon', - 'body', - 'links' + 'text' ], #category : 'Grafoscopio-Model' } -{ #category : #'as yet unclassified' } -GrafoscopioTextNode class >> nameForSelection [ - ^ 'New Text Node' -] - -{ #category : #operation } -GrafoscopioTextNode >> addLink: anUrl [ - "anUrl is a string" - - (self links includes: anUrl) - ifFalse: [ self links add: anUrl ] - -] - -{ #category : #adding } -GrafoscopioTextNode >> addNode: aNode [ - "Adds the given node to the receivers collection of children, and sets this object as the parent - of the node" - "aNode parent = self ifTrue: [ ^ self ]." - self children add: aNode. - aNode parent: self. - ^aNode -] - { #category : #accessing } -GrafoscopioTextNode >> ancestors [ - "I return a collection of all the nodes wich are ancestors of the receiver node" - | currentNode ancestors | - - currentNode := self. - ancestors := OrderedCollection new. - [ currentNode parent notNil and: [ currentNode level > 0 ] ] - whileTrue: [ - ancestors add: currentNode parent. - currentNode := currentNode parent]. - ancestors := ancestors reversed. - ^ ancestors -] - -{ #category : #accessing } -GrafoscopioTextNode >> ancestorsAll [ - "I return a collection of all the nodes wich are ancestors of the receiver node" - | currentNode ancestors | - - currentNode := self. - ancestors := OrderedCollection new. - [ currentNode parent notNil and: [ currentNode level > 0 ] ] - whileTrue: [ - ancestors add: currentNode parent. - currentNode := currentNode parent]. - ancestors := ancestors reversed. - ^ ancestors -] - -{ #category : #accessing } -GrafoscopioTextNode >> ancestorsHeaders [ - "Returns the headers of all the ancestors of the node. - Maybe this and 'headers' should be integrated, so both act on a collection of children instead of - having two separate methods" - | currentNode ancestors | - - currentNode := self. - ancestors := OrderedCollection new. - (self level - 1) - timesRepeat: [ - ancestors add: currentNode parent. - currentNode := currentNode parent.]. - ancestors := ancestors reversed. - ^ ancestors collect: [:ancestor | ancestor header ] - - -] - -{ #category : #exporting } -GrafoscopioTextNode >> asMarkdown [ - "I export children of the current node as pandoc markdown, using special nodes - accoding to tags. - Early version... tags processing should be vastly improved" - | markdownOutput | - - markdownOutput := '' writeStream. - "self metadataAsYamlIn: markdownOutput." - (self preorderTraversal) do: [ :eachNode | - (eachNode level > 0) - ifTrue: [(eachNode hasAncestorTaggedAs: 'invisible') - | (eachNode tags includes: 'invisible') - ifFalse: [ - markdownOutput nextPutAll: (eachNode markdownContent) ]]]. - ^ markdownOutput contents -] - -{ #category : #exporting } -GrafoscopioTextNode >> asSton [ - "Exports current tree as STON format" - | stonOutput | - - stonOutput := '' writeStream. - stonOutput nextPutAll: (STON toStringPretty: self "flatten"). - ^stonOutput contents - -] - -{ #category : #exporting } -GrafoscopioTextNode >> asStonFromRoot [ - "Exports current tree as STON format" - | stonOutput | - - stonOutput := '' writeStream. - self flatten. - stonOutput nextPutAll: (STON toStringPretty: self children). - ^stonOutput contents - -] - -{ #category : #accessing } -GrafoscopioTextNode >> asTreeNodePresenter [ - | node | - node := SpTreeNodePresenter new. - node - hasChildren: [ self children isNotEmpty ]; - children: [ self children collect: [ :subNode | subNode asTreeNodePresenter ] ]; - content: self. - ^ node -] - -{ #category : #accessing } -GrafoscopioTextNode >> body [ - "Returns the receivers body" - - ^ body - -] - -{ #category : #accessing } -GrafoscopioTextNode >> body: anObject [ - body := anObject -] - -{ #category : #exporting } -GrafoscopioTextNode >> bodyAsCode [ - "I return the node body with proper decorators added to show them as raw code" - | codeBody | - codeBody := '' writeStream. - codeBody - nextPutAll: '~~~{.numberLines}'; lf; - nextPutAll: (self body contents asString withInternetLineEndings); lf; - nextPutAll: '~~~'; lf; lf. - ^ codeBody contents -] - -{ #category : #exporting } -GrafoscopioTextNode >> bodyAsMarkdownInto: aStream [ - "I export the header as markdown using the level inside the tree to determine hierarchy - and replacing all line endings to make them Internet friendly". - self embeddedNodes ifNotNil: [ aStream nextPutAll: (self embedNodes contents asString withInternetLineEndings); crlf; crlf]. -] - -{ #category : #'add/remove nodes' } -GrafoscopioTextNode >> buildPreorderCollection: aCollection [ - "Stores the current node in a collection and recursively stores its children" - - aCollection add: self. - (self children isNotEmpty) & ((self header findString: '#invisible')=1) not - ifTrue: [ (self children) do: [ :eachNode | eachNode buildPreorderCollection: aCollection]]. - - -] - -{ #category : #operation } -GrafoscopioTextNode >> checksum [ - "I return the SHA1SUM of the current node. - I'm used to test changes on the node contents, without including changes in the children." - | nodeCopy | - nodeCopy := self surfaceCopy. - ^ self checksumFor: nodeCopy asSton. -] - -{ #category : #utility } -GrafoscopioTextNode >> checksumFor: aText [ - "I return the SHA1SUM of the current tree. I'm used to test changes on the contents - and for traceability of how the document tree is converted to other formats, as markdown." - ^ (SHA1 new hashMessage: aText) hex -] - -{ #category : #operation } -GrafoscopioTextNode >> checksumForRootSubtree [ - "I return the SHA1SUM of the current tree. I'm used to test changes on the contents - and for traceability of how the document tree is converted to other formats, as markdown." - ^ self checksumFor: self root flatten asStonFromRoot. - "^ (SHA1 new hashMessage: self root flatten asStonFromRoot) hex" +GrafoscopioTextNode class >> icon [ + ^ self iconNamed: #workspace ] { #category : #'as yet unclassified' } -GrafoscopioTextNode >> content [ - ^ body +GrafoscopioTextNode class >> nameForSelection [ + ^ 'New text node' ] { #category : #'as yet unclassified' } -GrafoscopioTextNode >> content: aContent [ - self body: aContent -] - -{ #category : #'add/remove nodes' } -GrafoscopioTextNode >> copyToClipboard [ - self class clipboard: self subtreeCopy. - - -] - -{ #category : #operation } -GrafoscopioTextNode >> currentLink [ - "TODO: This method should not only select sanitized links, but also provide ways to detect wich link - is selected from the list. For the moment, is only the last one, but probably links needs to be heavily - refactored to support this kind of operations and a better UI." - ^ self sanitizeDefaultLink -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode >> detectSelectionIndex [ - "I tell which is the index of the current selected node or return the first childre - (indexed at 1) if is not found." - - | root | - root := self root. - root preorderTraversal allButFirst doWithIndex: [ :currentNode :index | - currentNode isSelected ifTrue: [ ^ index ] ]. - ^ 1. -] - -{ #category : #'custom markup' } -GrafoscopioTextNode >> embedAll [ - "This is just a previous part of the messy markDownContent. The %embed-all keyword should be revaluated. - By default a node embeds all its children. Any non-embedable content should be under a %invisible node" - "(temporalBody includesSubstring: '%embed-all') - ifFalse: [ ] - ifTrue: [ - self embeddedNodes do: [ :each | - temporalBody := temporalBody copyReplaceAll: '%embed-all' with: (each body, (String with: Character cr), - '%embed-all')]. - temporalBody := temporalBody copyReplaceAll: '%embed-all' with: '']" - -] - -{ #category : #'custom markup' } -GrafoscopioTextNode >> embedNodes [ - "I find any ocurrence of '%embed a node header' in the body of a node and replace it - by the children which have such header. - Using embedded nodes is useful to change the order in which children appear into parents body, - while exporting" - | temporalBody | - temporalBody := self body. - self embeddedNodes ifNotNil: [ self embeddedNodes do: [ :each | - (each isTaggedAs: 'código') - ifFalse: [temporalBody := temporalBody copyReplaceAll: (each header) with: each body] - ifTrue: [temporalBody := temporalBody copyReplaceAll: (each header) with: each bodyAsCode]]]. - ^ temporalBody -] - -{ #category : #'custom markup' } -GrafoscopioTextNode >> embeddedNodes [ - ^ self children select: [:each | each headerStartsWith: '%embed'] -] - -{ #category : #exporting } -GrafoscopioTextNode >> exportCodeBlockTo: aStream [ - "I convert the content of a node taged as 'código' (code) as pandoc markdown and put it - into aStream." - aStream nextPutAll: ('~~~{.numberLines}'); lf. - aStream nextPutAll: (self body contents asString withInternetLineEndings); lf. - aStream nextPutAll: ('~~~'); lf;lf. - ^aStream contents -] - -{ #category : #exporting } -GrafoscopioTextNode >> exportCodeNodeTo: aStream [ - "I convert the content of a node taged as 'código' (code) as pandoc markdown - and put it into aStream." - ((self headerStartsWith: '%output') or: [ self headerStartsWith: '%metadata' ]) - ifTrue: [ self exportCodeOutputTo: aStream ] - ifFalse: [ self exportCodeBlockTo: aStream ] -] - -{ #category : #exporting } -GrafoscopioTextNode >> exportCodeOutputTo: aStream [ - "I convert the output of a node taged as 'código' (code) as pandoc markdown and - put it into aStream." - (self headerStartsWith: '%metadata') ifTrue: [ ^ self ]. - aStream nextPutAll: ('~~~{.numberLines}'); lf. - aStream nextPutAll: (self output asString withInternetLineEndings); lf. - aStream nextPutAll: ('~~~'); lf;lf. - ^aStream contents -] - -{ #category : #exporting } -GrafoscopioTextNode >> exportLaTeXCodeBlockTo: aStream [ - "I convert the content of a node taged as 'código' (code) as pandoc markdown and put it - into aStream. - The code block is decorated with LaTeX commands for proper syntax highlighting using pygments. - Pdf exportation requires the installation of pygments and minted package for latex" - aStream nextPutAll: ('\begin{minted}{smalltalk}'); lf. - aStream nextPutAll: (self body contents asString withInternetLineEndings); lf. - aStream nextPutAll: '\end{minted}';lf;lf. - ^aStream contents -] - -{ #category : #exporting } -GrafoscopioTextNode >> exportPreambleTo: aStream [ - "comment stating purpose of message" - | configDict | - (self header = '%config') - ifTrue: [ - configDict := STON fromString: (self body). - aStream nextPutAll: 'title: ', (configDict at: 'title'); lf. - aStream nextPutAll: 'author: ', ((configDict at: 'author') at: 'given'), ' ', ((configDict at: 'author') at: 'family'); lf. - aStream nextPutAll: 'bibliography: ', (configDict at: 'bibliography'); lf. - aStream nextPutAll: 'abstract: ', '|'; lf; nextPutAll: (configDict at: 'abstract'); lf] -] - -{ #category : #utility } -GrafoscopioTextNode >> find: aString andReplaceWith: anotherString [ - anotherString ifNil: [ ^ self ]. - self body: ((self body) copyReplaceAll: aString with: anotherString) -] - -{ #category : #exporting } -GrafoscopioTextNode >> flatten [ - "I traverse the tree looking for node bodies containing 'Text' objects and transform them to - their string content, so space is saved and storage format is DVCS friendly while serializing - them to STON" - - (self preorderTraversal) do: [ :eachNode | - (eachNode body class = Text) - ifTrue: [eachNode body: (eachNode body asString)]] -] - -{ #category : #exporting } -GrafoscopioTextNode >> footnoteAsMarkdownInto: aStream [ - "I export a node with %footnode in its header for valid Pandoc's markdown - and replace all line endings to make them Internet friendly. - Maybe I should include the condition about my own header, instead of leaving it to markdownCotent..." - aStream nextPutAll: ('[^',(self header copyReplaceAll: '%footnote ' with: ''),']: ' ); lf. - self body contents asString withInternetLineEndings - linesDo: [ :line | aStream nextPutAll: ' ', line; lf ]. - aStream nextPutAll: String lf. - -] - -{ #category : #exporting } -GrafoscopioTextNode >> hasAncestorHeaderWith: aSpecialWord [ - "Looks if the receptor node has an ancestor with a header with 'aSpecialWord' as the only or the first word" - - ^ (self ancestorsHeaders includes: aSpecialWord) | ((self ancestorsHeaders collect: [:eachHeader | (eachHeader findTokens: $ ) at: 1 ]) includes: aSpecialWord) - - -] - -{ #category : #exporting } -GrafoscopioTextNode >> hasAncestorTaggedAs: aSpecialWord [ - "Looks if the receptor node has an ancestor with a header with 'aSpecialWord' in its tags" - - self ancestors detect: [:eachAncestor | eachAncestor tags includes: aSpecialWord ] - ifFound: [^true ] - ifNone: [^false ]. +GrafoscopioTextNode class >> showInMenu [ + ^ true ] { #category : #accessing } -GrafoscopioTextNode >> hasChildren [ - (self children size > 0) - ifTrue: [ ^true ] - ifFalse: [ ^false ] -] - -{ #category : #exporting } -GrafoscopioTextNode >> headerAsMarkdownInto: aStream [ - "I export the header as markdown using the level inside the tree to determine hierarchy - and replacing all line endings to make them Internet friendly" - self level timesRepeat: [ aStream nextPutAll: '#' ]. - aStream nextPutAll: ' '. - aStream nextPutAll: (self header copyReplaceTokens: #cr with: #lf); crlf; crlf. -] - -{ #category : #'custom markup' } -GrafoscopioTextNode >> headerStartsWith: aString [ - ^ (self header findString: aString) = 1 -] - -{ #category : #accessing } -GrafoscopioTextNode >> headers [ - "I returns the headers of the receiver children" - ^ self children collect: [:currentNode | currentNode header ] -] - -{ #category : #operation } -GrafoscopioTextNode >> htmlToMarkdown [ - "I convert the node body from HTML format to Pandoc's Markdown." - | htmlFile | - (self isTaggedAs: 'código' ) ifTrue: [ ^self ]. - ((self headerStartsWith: '%invisible') "or:[self hasAncestorHeaderWith: '%invisible']") - ifTrue: [ ^self ]. - htmlFile := FileLocator temp asFileReference / 'body.html'. - htmlFile ensureCreateFile. - htmlFile writeStreamDo: [:out | out nextPutAll: self body ]. - Smalltalk platformName = 'unix' - ifTrue: [ self body: (Pandoc htmlToMarkdown: htmlFile) ]. - Smalltalk platformName = 'Win32' - ifTrue: [ self shouldBeImplemented ]. - htmlFile ensureDelete. -] - -{ #category : #operation } -GrafoscopioTextNode >> htmlToMarkdownSubtree [ - "I convert self and childern nodes body from HTML format to Pandoc's Markdown." - self preorderTraversal do: [ :each | each htmlToMarkdown ] -] - -{ #category : #accessing } -GrafoscopioTextNode >> icon [ - "Returns the receivers icon" - - ^icon -] - -{ #category : #accessing } -GrafoscopioTextNode >> icon: aSymbol [ - "Sets the receivers icon" - - icon := aSymbol -] - -{ #category : #importing } -GrafoscopioTextNode >> importHtmlLink [ - "I take the last link and import its contents in node body. " - | selectedLink downloadedContent | - selectedLink := self currentLink. - selectedLink asUrl host = 'ws.stfx.eu' ifTrue: [ ^ self ]. - selectedLink asUrl host = 'docutopia.tupale.co' - ifTrue: [ self inform: 'Docutopia importing still not supported.'. - ^ self ]. - downloadedContent := (GrafoscopioUtils - downloadingFrom: selectedLink - withMessage: 'Downloading node contents...' - into: FileLocator temp). - self uploadBodyFrom: downloadedContent filteredFor: selectedLink. -] - -{ #category : #importing } -GrafoscopioTextNode >> importPlaygroundLink [ - "I take the last link and import its contents in node body. - Last links should be hosted in http://zn.stfx.eu/" - self currentLink asUrl host = 'ws.stfx.eu' ifFalse: [ ^ self ]. - self - body: (ZnClient new get: self currentLink); - tagAs: 'código'. -] - -{ #category : #initialization } -GrafoscopioTextNode >> initialize [ - "I create a empty new node" - - super initialize. - self body: '' -] - -{ #category : #accessing } -GrafoscopioTextNode >> instantiateBody [ - | widget | - widget := super instantiateBody. - widget body whenTextChangedDo: [ :arg | self body: arg ]. - ^ widget -] - -{ #category : #accessing } -GrafoscopioTextNode >> isEmpty [ - body ifNil: [ ^ true ] ifNotNil: [ ^ false ] -] - -{ #category : #operation } -GrafoscopioTextNode >> isSavedAfterLastEdition [ - | root | - root := self root. - root edited ifNil: [ ^ false ]. - ^ self unsavedNodes isEmpty. - "self unsavedNodes isEmpty ifFalse: [ ^ self unsavedNodes inspect ]" -] - -{ #category : #testing } -GrafoscopioTextNode >> isSelected [ - self selected ifNil: [ ^ false ]. - ^ self selected. -] - -{ #category : #operation } -GrafoscopioTextNode >> isTaggedAs: aString [ - ^ self tags includes: aString -] - -{ #category : #exporting } -GrafoscopioTextNode >> itemAsMarkdownInto: aStream [ - "I export a node with %item in its header as valid Pandoc's markdown - and replace all line endings to make them Internet friendly. - Maybe I should include the condition about my own header, instead of leaving it to markdownContent..." - - | lines | - lines := self body contents asString withInternetLineEndings lines. - lines ifEmpty: [ ^ self ]. - aStream - nextPutAll: ' - '; - nextPutAll: lines first; - lf. - lines - allButFirstDo: [ :line | - aStream - nextPutAll: ' '; - nextPutAll: line; - lf ]. - aStream nextPutAll: String lf -] - -{ #category : #accessing } -GrafoscopioTextNode >> key [ - "Returns a unique key identifying the receiver in the help system" - - ^key -] - -{ #category : #accessing } -GrafoscopioTextNode >> key: aUniqueKey [ - "Sets a unique key identifying the receiver in the help system" - - key := aUniqueKey -] - -{ #category : #accessing } -GrafoscopioTextNode >> lastLink [ - self links ifNil: [ ^ '' ]. - self links ifEmpty: [ ^ '' ]. - ^ self links last -] - -{ #category : #accessing } -GrafoscopioTextNode >> lastNetLink [ - ^ self links detect: [ :l | l asZnUrl ] -] - -{ #category : #accessing } -GrafoscopioTextNode >> links [ - "I model local or remote links that are associated to a particular node." - ^ links ifNil: [ ^ links := OrderedCollection new ] -] - -{ #category : #accessing } -GrafoscopioTextNode >> links: anObject [ - self links add: anObject -] - -{ #category : #operation } -GrafoscopioTextNode >> linksToMarkupFile [ - "I detect if the links contains any reference to a file ending in '.md' or '.markdown'" - self links - ifNotNil: [ - self links - detect: [:l | (l endsWithAnyOf: #('.md' '.markdown' '.md.html'))] - ifFound: [ ^ true ] - ifNone: [^ false]]. - ^ false +GrafoscopioTextNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitTextNode: self. ] -{ #category : #operation } -GrafoscopioTextNode >> localFilesLinks [ - "I collect all the links that point to the local file system. Because is supposed that - links contains only references to remote URL or local files, anything that is not a URL is - treated as a loca file link." - ^ self links collect: [ :l | l asZnUrl host isNil ] -] - -{ #category : #exporting } -GrafoscopioTextNode >> margin [ - "I define the same margin of the page used for PDF exportations" - - ^'2 cm' -] - -{ #category : #exporting } -GrafoscopioTextNode >> margins [ - "I define each individual margin of the page used for PDF exportations" - - | margins | - margins := Dictionary new - add: 'top' -> '3 cm'; - add: 'bottom' -> '3 cm'; - add: 'left' -> '2 cm'; - add: 'right' -> '2 cm'; - yourself. - ^ margins -] - -{ #category : #exporting } -GrafoscopioTextNode >> markdownContent [ - "I extract the markdown of a node using body as content, header as title and level as - hierarchical level of the title. - If special nodes types are present, that use %keywords in its header or body I convert them - into proper markup" - | markdownStream | - markdownStream := '' writeStream. - (self class specialWords includes: self header) not - & (self class specialWords includes: ((self header findTokens: $ ) at: 1)) not - & (self isTaggedAs: 'código') not - & (self hasAncestorHeaderWith: '%invisible') not - ifTrue: [ - self headerAsMarkdownInto: markdownStream. - self bodyAsMarkdownInto: markdownStream ]. - (self headerStartsWith: '%idea') - ifTrue: [ self bodyAsMarkdownInto: markdownStream ]. - (self headerStartsWith: '%item') - ifTrue: [ self itemAsMarkdownInto: markdownStream ]. - (self headerStartsWith: '%footnote') - ifTrue: [ self footnoteAsMarkdownInto: markdownStream ]. - ((self isTaggedAs: 'código') - and: [(self hasAncestorHeaderWith: '%invisible') not - & (self headerStartsWith: '%embed') not ]) - ifTrue: [ self exportCodeNodeTo: markdownStream ]. - ^ markdownStream contents -] - -{ #category : #operation } -GrafoscopioTextNode >> metadata [ - | mnode | - mnode := self root preorderTraversal - detect: [ :n | n header beginsWith: '%metadata' ] - ifNone: [ ^ nil ]. - ^ mnode output. - -] - -{ #category : #exporting } -GrafoscopioTextNode >> metadataAsYamlIn: markdownStream [ - "I convert the first '%metadata' node into a YAML preamble contents to be used by Pandoc - exportation." - self metadata - ifNil: [ markdownStream nextPutAll: String crlf. ] - ifNotNil: [ - self metadata - keysAndValuesDo: [ :k :v | - k = 'pandocOptions' - ifTrue: [ - markdownStream - nextPutAll: - (k, ': ', self pandocOptionsPrettyYaml) ] - ifFalse: [ - markdownStream - nextPutAll: - (k , ': ' , v asString) withInternetLineEndings; - lf] ]]. - markdownStream - nextPutAll: String cr, String cr. -] - { #category : #accessing } -GrafoscopioTextNode >> openIn: aNotebook [ - super openIn: aNotebook. - aNotebook links text: self lastLink -] - -{ #category : #accessing } -GrafoscopioTextNode >> output [ - (self isTaggedAs: 'código') - ifFalse: [ ^ self ]. - self body ifNil: [ ^ nil ]. - ^ OpalCompiler new - source: self body; - evaluate -] - -{ #category : #utility } -GrafoscopioTextNode >> pandocOptions [ - self metadata ifNil: [ ^ nil ]. - self metadata at: 'pandocOptions' ifAbsent: [ ^ '' ]. - ^ self metadata at: 'pandocOptions' -] - -{ #category : #utility } -GrafoscopioTextNode >> pandocOptionsPrettyYaml [ - "I convert pandoc options, if present into an indented Yaml block." - | yamlOutput pretyOutput | - pretyOutput := STON toStringPretty: self pandocOptions. - yamlOutput := '' writeStream. - yamlOutput - nextPutAll: - '|'; - lf. - pretyOutput linesDo: [ :line | - yamlOutput - nextPutAll: - ' ', line; - lf ]. - ^ yamlOutput contents -] - -{ #category : #'add/remove nodes' } -GrafoscopioTextNode >> pasteFromClipboard [ - | clipchild | - self class clipboard - ifNotNil: [ - clipchild := self class clipboard. - self addNode: clipchild. - clipchild ] - ifNil: [ self inform: 'Cache is emtpy. Pleas cut/copy a node before pasting' ] - -] - -{ #category : #operation } -GrafoscopioTextNode >> preorderTraversal [ - | nodesInPreorder | - nodesInPreorder := OrderedCollection new. - self buildPreorderCollection: nodesInPreorder. - ^ nodesInPreorder -] - -{ #category : #exporting } -GrafoscopioTextNode >> publish [ - | publishedUrl | - (self confirm: 'Publish playground content to the cloud?') - ifFalse: [ ^ self ]. - self content ifEmpty: [ - self inform: 'Nothing was published because the playground is empty'. - ^ self ]. - Clipboard clipboardText: (publishedUrl := (GTUrlProvider new post: self content) asString). - self inform: publishedUrl , ' was published and the url was copied to clipboard' -] - -{ #category : #'add/remove nodes' } -GrafoscopioTextNode >> removeLastNode [ - "Adds the given node to the receivers collection of children, and sets this object as the parent - of the node" - - self children removeLast. - -] - -{ #category : #utility } -GrafoscopioTextNode >> removeLeadingLineNumbersSized: anInteger [ - | cleanBody | - cleanBody := ''. - self body lines do: [ :line | | cleanLine | - line size >= anInteger - ifTrue: [ cleanLine := line copyFrom: anInteger to: line size. ] - ifFalse: [ cleanLine := '' ]. - cleanBody := cleanBody, cleanLine, String cr ]. - self body: cleanBody asString. -] - -{ #category : #'add/remove nodes' } -GrafoscopioTextNode >> removeNode: aNode [ - (self children includes: aNode) - ifTrue: [ self children remove: aNode ] - ifFalse: [ self inform: 'The node doesn''t belong to this node children' ] - - -] - -{ #category : #utility } -GrafoscopioTextNode >> replaceAccentedHTMLChars [ - self body: (self body copyReplaceAll: 'í' with: 'í' ) -] - -{ #category : #accessing } -GrafoscopioTextNode >> root [ - "I return the root node of the Grafoscopio tree, i.e the common ancestor of all other nodes" - self level = 0 - ifFalse: [ ^ self ancestors first ]. - ^ self -] - -{ #category : #operation } -GrafoscopioTextNode >> sanitizeDefaultLink [ - | defaultLink sanitized protocol | - defaultLink := self lastLink. - protocol := 'docutopia://'. - sanitized := (defaultLink beginsWith: protocol) - ifTrue: [ defaultLink - copyReplaceAll: protocol - with: 'https://docutopia.tupale.co/' ] - ifFalse: [ defaultLink ]. - ^ sanitized -] - -{ #category : #accessing } -GrafoscopioTextNode >> saveContent: anObject [ - "Sets the receivers body to the given object" - - body := anObject -] - -{ #category : #operation } -GrafoscopioTextNode >> selectMarkupSubtreesToExport [ - ^ (self root preorderTraversal) select: [ :each | each linksToMarkupFile ]. -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode >> shouldAskBeforeRemove [ - ^ self body isNotEmpty or: [ self hasChildren ] -] - -{ #category : #accessing } -GrafoscopioTextNode >> specModelClass [ - "por defecto" - - ^ GrafoscopioNewTextModel -] - -{ #category : #operation } -GrafoscopioTextNode >> subtreeCopy [ - "I return the same node if its subtree only contains the receiver, or a copy of the receivers - subtree, in other cases." - | linearSubtree linearSubtreeCopy | - linearSubtree := self preorderTraversal. - linearSubtreeCopy := OrderedCollection new. - linearSubtree do: [ :cn | linearSubtreeCopy add: cn surfaceCopy ]. - linearSubtreeCopy allButFirst doWithIndex: [ :n :i | | parentPos | - parentPos := linearSubtree indexOf: (linearSubtree at: i+1) parent. - n parent: (linearSubtreeCopy at: parentPos) ]. - ^ linearSubtreeCopy at: 1. -] - -{ #category : #operation } -GrafoscopioTextNode >> surfaceCopy [ - "I copy the most relevant values of the receiver. I'm useful to avoid copying references - to the rest of the container tree, which could end in copying the whole tree." - | newNode | - newNode := self class new. - newNode - header: self header; - body: self body; - tags: self tags. - self links ifNotEmpty: [ newNode links addAll: self links ]. - ^ newNode. - - -] - -{ #category : #operation } -GrafoscopioTextNode >> toggleCodeText [ - "Some tags are exclusionary. - For example a node can not be tagged as text and as 'code' (código) simultaneosly. - In that case, I replace the ocurrence of one tag by the other to warranty that both are not - in the same node." - (self isTaggedAs: 'text') - ifTrue: [ ^ self tags replaceAll: 'text' with: 'código']. - (self isTaggedAs: 'código') - ifTrue: [ ^ self tags replaceAll: 'código' with: 'text' ]. -] - -{ #category : #operation } -GrafoscopioTextNode >> unsavedNodes [ - "I collect all nodes that have changed after the last saving" - | lastSavedOn root unsavedNodes | - root := self root. - lastSavedOn := root edited asDateAndTime. - unsavedNodes := root preorderTraversal select: [ :currentNode | - currentNode edited isNotNil and: [currentNode edited asDateAndTime > lastSavedOn] ]. - ^ unsavedNodes. - -] - -{ #category : #importing } -GrafoscopioTextNode >> uploadBodyFrom: fileLocator filteredFor: selectedLink [ - self body: fileLocator contents -] - -{ #category : #operation } -GrafoscopioTextNode >> visitLastLink [ - self lastLink = '' - ifTrue: [ self inform: 'This node has no associated links to visit'. ^ self ]. - [WebBrowser openOn: self lastLink] fork -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode >> wrapBodyLines [ - "I convert the node body from HTML format to Pandoc's Markdown." - - self wrapBodyLines: 80 -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode >> wrapBodyLines: aWidth [ - "I convert the node body from HTML format to Pandoc's Markdown." - - | bodyReader chunck | - (self isTaggedAs: 'código') - ifTrue: [ ^ self ]. - bodyReader := body readStream. - body := String - streamContents: [ :bodyWriter | - [ bodyReader atEnd ] - whileFalse: [ - chunck := bodyReader next: aWidth - 1. - bodyWriter nextPutAll: chunck. - bodyReader atEnd - ifFalse: [ bodyWriter nextPut: Character lf ] ] ] +GrafoscopioTextNode >> text: aString [ + text := aString ] diff --git a/repository/Grafoscopio/GrafoscopioTreeNotebook.class.st b/repository/Grafoscopio/GrafoscopioTreeNotebook.class.st new file mode 100644 index 0000000..9a8c20b --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioTreeNotebook.class.st @@ -0,0 +1,85 @@ +Class { + #name : #GrafoscopioTreeNotebook, + #superclass : #SpPresenter, + #instVars : [ + '#sidebar', + '#viewport', + '#model', + '=>', + 'SpObservableSlot', + '#empty', + '#perspectives' + ], + #category : 'Grafoscopio-New-UI' +} + +{ #category : #specs } +GrafoscopioTreeNotebook class >> defaultSpec [ + ^ SpBoxLayout newHorizontal + add: + (SpBoxLayout newVertical + add: #empty height: self toolbarHeight; + add: #sidebar; + yourself) + width: 51; + add: #viewport; + yourself +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> createDefaultComponent [ + ^ GrafoscopioPerspective defaultPerspective new +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> initialize [ + super initialize. + perspectives := Dictionary new. +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> initializePrivateAnnouncements [ + super initializePrivateAnnouncements. + self property: #model whenChangedDo: [ self updateModel ] +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> initializeWidgets [ + super initializeWidgets. + sidebar := self sidebar. + viewport := self createDefaultComponent. + empty := self newLabel. +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> installPerspective: aPerspective [ + viewport ifNotNil: [ viewport aboutToBeUninstalledFrom: self ]. + viewport := (perspectives at: aPerspective ifAbsentPut: [ self instantiate: aPerspective ]) . + self updateModel. +] + +{ #category : #'as yet unclassified' } +GrafoscopioTreeNotebook >> open: aGrafoscopioProject [ + model := aGrafoscopioProject. + self openWithSpec. + self updateModel +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> sidebar [ + | bar | + bar := self instantiate: SpSidebar. + GrafoscopioPerspective perspectives + do: + [ :p | bar addAction: [ self installPerspective: p ] icon: p icon ]. + ^ bar +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> updateModel [ + viewport updateModel: model. + + self needRebuild: false. + self buildWithSpec + +] diff --git a/repository/Grafoscopio/GrafoscopioTrunkNode.class.st b/repository/Grafoscopio/GrafoscopioTrunkNode.class.st deleted file mode 100644 index 7533099..0000000 --- a/repository/Grafoscopio/GrafoscopioTrunkNode.class.st +++ /dev/null @@ -1,50 +0,0 @@ -Class { - #name : #GrafoscopioTrunkNode, - #superclass : #GrafoscopioAbstractNode, - #instVars : [ - 'children' - ], - #category : 'Grafoscopio-Model' -} - -{ #category : #testing } -GrafoscopioTrunkNode class >> isAbstract [ - ^ self = GrafoscopioTrunkNode -] - -{ #category : #accessing } -GrafoscopioTrunkNode >> children [ - "Returns the receivers list of children" - - ^ children ifNil: [ children := OrderedCollection new ] -] - -{ #category : #accessing } -GrafoscopioTrunkNode >> children: aCollection [ - "Sets the receivers children" - - aCollection do: [:currentNode | currentNode parent: self ]. - children := aCollection. -] - -{ #category : #accessing } -GrafoscopioTrunkNode >> isLeaf [ - ^ false -] - -{ #category : #movement } -GrafoscopioTrunkNode >> moveDown: aNode [ - | index | - "Moves the current node a place before in the children collection where is located" - index := (children indexOf: aNode) max: 1 . - - children swap: index with: (index + 1 min: children size) -] - -{ #category : #movement } -GrafoscopioTrunkNode >> moveUp: aNode [ - | index | - "Moves the current node a place before in the children collection where is located" - index := children indexOf: aNode. - children swap: index with: (index - 1 max: 1) -] diff --git a/repository/Grafoscopio/GrafoscopioUnitNode.class.st b/repository/Grafoscopio/GrafoscopioUnitNode.class.st new file mode 100644 index 0000000..f743668 --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioUnitNode.class.st @@ -0,0 +1,31 @@ +Class { + #name : #GrafoscopioUnitNode, + #superclass : #GrafoscopioBranchNode, + #category : 'Grafoscopio-Model' +} + +{ #category : #'instance creation' } +GrafoscopioUnitNode class >> icon [ + ^ self iconNamed: #smallHierarchyBrowser +] + +{ #category : #'instance creation' } +GrafoscopioUnitNode class >> nameForSelection [ + ^ 'New unit' +] + +{ #category : #'instance creation' } +GrafoscopioUnitNode class >> showInMenu [ + ^ true +] + +{ #category : #accessing } +GrafoscopioUnitNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitUnitNode: self. + +] + +{ #category : #accessing } +GrafoscopioUnitNode >> acceptsChildsOfClass: aClass [ + ^ aClass isLeaf or: [ aClass = self class ] +] diff --git a/repository/Grafoscopio/GrafoscopioUrlCachedNode.class.st b/repository/Grafoscopio/GrafoscopioUrlCachedNode.class.st deleted file mode 100644 index c9ad11e..0000000 --- a/repository/Grafoscopio/GrafoscopioUrlCachedNode.class.st +++ /dev/null @@ -1,27 +0,0 @@ -Class { - #name : #GrafoscopioUrlCachedNode, - #superclass : #GrafoscopioUrlNode, - #instVars : [ - 'content' - ], - #category : 'Grafoscopio-Model' -} - -{ #category : #'instance creation' } -GrafoscopioUrlCachedNode class >> nameForSelection [ - ^ 'New URL-Cached Node' -] - -{ #category : #accessing } -GrafoscopioUrlCachedNode >> content [ - ^ content isEmptyOrNil - ifTrue: [ content := super content ] - ifFalse: [ content ] -] - -{ #category : #accessing } -GrafoscopioUrlCachedNode >> content: aContent [ - self - error: - 'You are not supposed to change the content of url node from outside. The content comes only from the fetch.' -] diff --git a/repository/Grafoscopio/GrafoscopioUrlNode.class.st b/repository/Grafoscopio/GrafoscopioUrlNode.class.st index 74baee6..ae8023c 100644 --- a/repository/Grafoscopio/GrafoscopioUrlNode.class.st +++ b/repository/Grafoscopio/GrafoscopioUrlNode.class.st @@ -1,12 +1,20 @@ +" +URL Node downloads content for rendering. +" Class { #name : #GrafoscopioUrlNode, - #superclass : #GrafoscopioAbstractNode, + #superclass : #GrafoscopioLeafNode, #instVars : [ 'link' ], #category : 'Grafoscopio-Model' } +{ #category : #'instance creation' } +GrafoscopioUrlNode class >> icon [ + ^ self iconNamed: #remote +] + { #category : #'instance creation' } GrafoscopioUrlNode class >> nameForSelection [ ^ 'New URL Node' @@ -19,6 +27,17 @@ GrafoscopioUrlNode class >> new [ yourself ] +{ #category : #'instance creation' } +GrafoscopioUrlNode class >> showInMenu [ + ^ true +] + +{ #category : #'as yet unclassified' } +GrafoscopioUrlNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitUrlNode: self. + +] + { #category : #'as yet unclassified' } GrafoscopioUrlNode >> content [ ^ (self url diff --git a/repository/Grafoscopio/GrafoscopioViewportVisitor.class.st b/repository/Grafoscopio/GrafoscopioViewportVisitor.class.st new file mode 100644 index 0000000..59d42e1 --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioViewportVisitor.class.st @@ -0,0 +1,45 @@ +Class { + #name : #GrafoscopioViewportVisitor, + #superclass : #GrafoscopioVisitor, + #instVars : [ + 'viewport', + 'stack', + 'items', + 'presenter' + ], + #category : 'Grafoscopio-New-UI' +} + +{ #category : #visiting } +GrafoscopioViewportVisitor >> createViewportFor: aDocumentNode into: aPresenter [ + presenter := aPresenter. + items := OrderedCollection new. + aDocumentNode acceptVisitor: self. + viewport := aPresenter instantiate: SpComponentListPresenter. + viewport items: items. + ^ viewport +] + +{ #category : #visiting } +GrafoscopioViewportVisitor >> visitCodeNode: aNode [ + | code | + code := presenter newCode. + code text: aNode text. + items add: code. +] + +{ #category : #visiting } +GrafoscopioViewportVisitor >> visitTextNode: aNode [ + | text | + text := presenter newText . + text text: aNode text. + items add: text. +] + +{ #category : #visiting } +GrafoscopioViewportVisitor >> visitUrlNode: aNode [ + | text | + text := presenter newText . + text text: aNode content. + items add: text. +] diff --git a/repository/Grafoscopio/GrafoscopioVisitor.class.st b/repository/Grafoscopio/GrafoscopioVisitor.class.st new file mode 100644 index 0000000..380874d --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioVisitor.class.st @@ -0,0 +1,48 @@ +Class { + #name : #GrafoscopioVisitor, + #superclass : #Object, + #category : 'Grafoscopio-Model' +} + +{ #category : #visiting } +GrafoscopioVisitor >> visitBranchNode: aNode [ + self visitNode: aNode. + aNode children do: [ : c | c acceptVisitor: self ]. +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitCodeNode: aNode [ + self visitNode: aNode +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitLeafNode: aNode [ + self visitNode: aNode +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitNode: aNode [ + " nothing to do here" +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitRootNode: aNode [ + self visitNode: aNode. + aNode children do: [ : c | c acceptVisitor: self ]. +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitTextNode: aNode [ + self visitNode: aNode +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitUnitNode: aNode [ + self visitNode: aNode. + aNode children do: [ : c | c acceptVisitor: self ]. +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitUrlNode: aNode [ + self visitNode: aNode +] diff --git a/repository/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st b/repository/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st new file mode 100644 index 0000000..1bf88be --- /dev/null +++ b/repository/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GrafoscopioZoaPediaPerspective, + #superclass : #GrafoscopioPerspective, + #category : 'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioZoaPediaPerspective class >> icon [ + ^ self iconNamed: #edit +] + +{ #category : #initialization } +GrafoscopioZoaPediaPerspective >> createViewport [ + ^ self newLabel +] diff --git a/repository/Grafoscopio/SpSidebar.class.st b/repository/Grafoscopio/SpSidebar.class.st new file mode 100644 index 0000000..4cab327 --- /dev/null +++ b/repository/Grafoscopio/SpSidebar.class.st @@ -0,0 +1,44 @@ +Class { + #name : #SpSidebar, + #superclass : #SpPresenter, + #instVars : [ + 'container' + ], + #category : 'Grafoscopio-New-UI' +} + +{ #category : #specs } +SpSidebar class >> defaultSpec [ + ^ SpBoxLayout newHorizontal + add: #container ; + yourself +] + +{ #category : #initialization } +SpSidebar >> addAction: aBlock icon: anIcon [ + container + addPresenter: + (self createDefaultPresenter + parent: self; + action: [ :state | + container unselectAll. + aBlock cull: state ]; + icon: anIcon; + yourself) +] + +{ #category : #'as yet unclassified' } +SpSidebar >> buttons [ + ^ container items +] + +{ #category : #initialization } +SpSidebar >> createDefaultPresenter [ + ^ self instantiate: SpSquareButton. +] + +{ #category : #initialization } +SpSidebar >> initializeWidgets [ + super initializeWidgets. + container := self instantiate: SpComponentListPresenter. +] diff --git a/repository/Grafoscopio/SpSquareButton.class.st b/repository/Grafoscopio/SpSquareButton.class.st new file mode 100644 index 0000000..7457a56 --- /dev/null +++ b/repository/Grafoscopio/SpSquareButton.class.st @@ -0,0 +1,52 @@ +Class { + #name : #SpSquareButton, + #superclass : #SpPresenter, + #instVars : [ + 'button', + 'parent' + ], + #category : 'Grafoscopio-New-UI' +} + +{ #category : #specs } +SpSquareButton class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: (SpBoxLayout newHorizontal add: #button width: 50; yourself) height: 50; + yourself +] + +{ #category : #initialization } +SpSquareButton >> action: anAction [ + button + action: [ :state | + state + ifTrue: [ parent buttons + reject: [ :b | b = self ] + thenDo: [ :b | b toggleOff ]. + anAction cull: state ] + ifFalse: [ ] ] +] + +{ #category : #initialization } +SpSquareButton >> icon: icon [ + button icon: icon +] + +{ #category : #initialization } +SpSquareButton >> initializeWidgets [ + super initializeWidgets. + button := self newToggleButton + extent: 90 @ 50; + color: Color transparent; + yourself +] + +{ #category : #'as yet unclassified' } +SpSquareButton >> parent: aSpSidebar [ + parent := aSpSidebar +] + +{ #category : #initialization } +SpSquareButton >> toggleOff [ + button state: false. +]