diff --git a/src/Grafoscopio/GrafoscopioAbstractNode.class.st b/src/Grafoscopio/GrafoscopioAbstractNode.class.st new file mode 100644 index 0000000..18d291c --- /dev/null +++ b/src/Grafoscopio/GrafoscopioAbstractNode.class.st @@ -0,0 +1,21 @@ +Class { + #name : #GrafoscopioAbstractNode, + #superclass : #Object, + #instVars : [ + 'header', + 'created', + 'edited', + 'selected', + 'key', + 'icon', + 'body', + 'tags', + 'children', + 'parent', + 'node', + 'nodesInPreorder', + 'links', + 'output' + ], + #category : #'Grafoscopio-Model' +} diff --git a/src/Grafoscopio/GrafoscopioNode.class.st b/src/Grafoscopio/GrafoscopioNode.class.st index 21a9a5f..2d4bf3c 100644 --- a/src/Grafoscopio/GrafoscopioNode.class.st +++ b/src/Grafoscopio/GrafoscopioNode.class.st @@ -12,11 +12,10 @@ Class { #name : #GrafoscopioNode, #superclass : #Object, #instVars : [ + 'id', 'header', - 'headers', 'created', 'edited', - 'selected', 'key', 'icon', 'body', @@ -24,9 +23,7 @@ Class { 'children', 'parent', 'node', - 'nodesInPreorder', - 'links', - 'output' + 'links' ], #classInstVars : [ 'clipboard' @@ -102,15 +99,19 @@ GrafoscopioNode >> addNode: aNode [ { #category : #'add/remove nodes' } GrafoscopioNode >> addNodeAfterMe [ "Adds a generic node after the given node so they become slibings of the same parent" + | genericNode | - genericNode := self class new + genericNode := self class new created: DateAndTime now printString; - header: 'newNode'; + header: 'newNode'; body: ''. - self parent children add: genericNode after: self. - genericNode parent: self parent. + 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 } @@ -203,6 +204,16 @@ GrafoscopioNode >> asStonFromRoot [ ] +{ #category : #exporting } +GrafoscopioNode >> asTreeNodePresenter [ + node := TreeNodePresenter new. + node + hasChildren: [ self children isNotEmpty ]; + children: [ self children collect: [ :subNode | subNode asTreeNodePresenter ] ]; + content: self. + ^ node +] + { #category : #accessing } GrafoscopioNode >> body [ "Returns the receivers body" @@ -235,9 +246,15 @@ GrafoscopioNode >> bodyAsMarkdownInto: aStream [ self embeddedNodes ifNotNil: [ aStream nextPutAll: (self embedNodes contents asString withInternetLineEndings); crlf; crlf]. ] -{ #category : #exporting } -GrafoscopioNode >> calculateLevel [ - ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent calculateLevel ] +{ #category : #'add/remove nodes' } +GrafoscopioNode >> 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 } @@ -279,19 +296,9 @@ GrafoscopioNode >> children: aCollection [ children := aCollection. ] -{ #category : #accessing } +{ #category : #'as yet unclassified' } GrafoscopioNode >> content [ - "Returns the receivers body" - - ^ self body - -] - -{ #category : #accessing } -GrafoscopioNode >> content: anObject [ - "Sets the receivers body to the given object" - - body := anObject + ^ body ] { #category : #'add/remove nodes' } @@ -406,13 +413,6 @@ GrafoscopioNode >> embeddedNodes [ ^ self children select: [:each | each headerStartsWith: '%embed'] ] -{ #category : #accessing } -GrafoscopioNode >> expanded: aBoolean [ - "I tell if the node is expanded from the UI, showing my children. - Several nodes can be expanded in a single document." - selected := aBoolean -] - { #category : #exporting } GrafoscopioNode >> exportCodeBlockTo: aStream [ "I convert the content of a node taged as 'código' (code) as pandoc markdown and put it @@ -553,7 +553,7 @@ GrafoscopioNode >> headerStartsWith: aString [ { #category : #accessing } GrafoscopioNode >> headers [ "I returns the headers of the receiver children" - ^ headers := self children collect: [:currentNode | currentNode header ] + ^ self children collect: [:currentNode | currentNode header ] ] { #category : #operation } @@ -639,7 +639,10 @@ GrafoscopioNode >> initialize [ self header: 'newHeader'; tagAs: 'text'; - body: '' + body: ''. + id := UUID new. + created := DateAndTime now. + edited := DateAndTime now ] { #category : #accessing } @@ -713,14 +716,14 @@ GrafoscopioNode >> lastLink [ { #category : #accessing } GrafoscopioNode >> lastNetLink [ - ^ self links detect: [ :l | l asZnUrl isURL ] + ^ self links detect: [ :l | l asZnUrl ] ] { #category : #accessing } -GrafoscopioNode >> level [ - "Returns the level of the node. See the setter message for details" - - ^ self calculateLevel +GrafoscopioNode >> level [ + "Returns the level of the node. See the setter message for details" + + ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] ] { #category : #accessing } @@ -838,29 +841,33 @@ GrafoscopioNode >> metadataAsYamlIn: markdownStream [ ] { #category : #movement } -GrafoscopioNode >> moveAfter [ +GrafoscopioNode >> moveDown [ "Moves the current node a place before in the children collection where is located" - | collection index successor | - collection := self parent children. - index := collection indexOf: self. - (index between: 1 and: collection size - 1) - ifTrue: [ - successor := collection after: self. - collection at: index + 1 put: self. - collection at: index put: successor] + + self parent moveDown: self ] { #category : #movement } -GrafoscopioNode >> moveBefore [ +GrafoscopioNode >> moveDown: aNode [ + | index | "Moves the current node a place before in the children collection where is located" - | collection index predecessor | - collection := self parent children. - index := collection indexOf: self. - (index between: 2 and: collection size) - ifTrue: [ - predecessor := collection before: self. - collection at: index -1 put: self. - collection at: index put: predecessor] + index := children indexOf: aNode. + children swap: index with: (index + 1 min: children size) +] + +{ #category : #movement } +GrafoscopioNode >> moveUp [ + "Moves the current node a place before in the children collection where is located" + + self parent moveUp: self +] + +{ #category : #movement } +GrafoscopioNode >> 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) ] { #category : #'instance creation' } @@ -883,11 +890,6 @@ GrafoscopioNode >> output [ evaluate ] -{ #category : #accessing } -GrafoscopioNode >> output: anObject [ - output := anObject -] - { #category : #utility } GrafoscopioNode >> pandocOptions [ self metadata ifNil: [ ^ nil ]. @@ -946,24 +948,10 @@ GrafoscopioNode >> pasteFromClipboard [ { #category : #operation } GrafoscopioNode >> preorderTraversal [ + | nodesInPreorder | nodesInPreorder := OrderedCollection new. - self visitedGoTo: nodesInPreorder. - ^ nodesInPreorder. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNode >> preorderTraversalIndex [ - "I tell which place I occupy in the tree children (without counting the root)." - - | root | - root := self root. - root preorderTraversalRootChildren doWithIndex: [ :currentNode :index | - currentNode = self ifTrue: [^ index] ]. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNode >> preorderTraversalRootChildren [ - ^ self root preorderTraversal allButFirst + self buildPreorderCollection: nodesInPreorder. + ^ nodesInPreorder ] { #category : #movement } @@ -1061,18 +1049,6 @@ GrafoscopioNode >> selectMarkupSubtreesToExport [ ^ (self root preorderTraversal) select: [ :each | each linksToMarkupFile ]. ] -{ #category : #accessing } -GrafoscopioNode >> selected [ - ^ selected -] - -{ #category : #accessing } -GrafoscopioNode >> selected: aBoolean [ - "I tell if the node is selected from the UI. - Once other node is selected my value becomes false." - selected := aBoolean -] - { #category : #accessing } GrafoscopioNode >> specModelClass [ @@ -1163,19 +1139,6 @@ GrafoscopioNode >> toggleCodeText [ ifTrue: [ ^ self tags replaceAll: 'código' with: 'text' ]. ] -{ #category : #accessing } -GrafoscopioNode >> toggleSelected [ - "I made the receiver the current selected node and deselect all other nodes." - - | root previousSelection | - self isSelected ifTrue: [ ^ self ]. - root := self root. - previousSelection := self preorderTraversalRootChildren at: (self detectSelectionIndex). - previousSelection selected: false. - self selected: true. - ^ self. -] - { #category : #operation } GrafoscopioNode >> unsavedNodes [ "I collect all nodes that have changed after the last saving" @@ -1195,8 +1158,7 @@ GrafoscopioNode >> updateEditionTimestamp [ { #category : #importing } GrafoscopioNode >> uploadBodyFrom: fileLocator filteredFor: selectedLink [ - (self linksFilters contains: selectedLink) - ifFalse: [ self body: fileLocator contents ] + self body: fileLocator contents ] { #category : #operation } @@ -1206,17 +1168,6 @@ GrafoscopioNode >> visitLastLink [ [WebBrowser openOn: self lastLink] fork ] -{ #category : #'add/remove nodes' } -GrafoscopioNode >> visitedGoTo: 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 visitedGoTo: aCollection]]. - - -] - { #category : #'as yet unclassified' } GrafoscopioNode >> wrapBodyLines [ "I convert the node body from HTML format to Pandoc's Markdown." diff --git a/src/Grafoscopio/GrafoscopioNodeTest.class.st b/src/Grafoscopio/GrafoscopioNodeTest.class.st index 63dca0b..d285964 100644 --- a/src/Grafoscopio/GrafoscopioNodeTest.class.st +++ b/src/Grafoscopio/GrafoscopioNodeTest.class.st @@ -103,17 +103,6 @@ GrafoscopioNodeTest >> testInitializeIsOk [ self shouldnt: [ GrafoscopioNode new ] raise: Error ] -{ #category : #tests } -GrafoscopioNodeTest >> testNodeSelection [ - | tree child1 | - tree := self newTestTree.. - child1 := tree preorderTraversalRootChildren at: 1. - child1 selected: true. - self assert: tree detectSelectionIndex equals: 1 - - -] - { #category : #tests } GrafoscopioNodeTest >> testPromoteNode [ | tree child1 child2 | @@ -180,16 +169,3 @@ GrafoscopioNodeTest >> testSanitizedLink [ self assert: (node sanitizeDefaultLink = 'https://docutopia.tupale.co/hackbo:hackbot') equals: true ] - -{ #category : #tests } -GrafoscopioNodeTest >> testToggleNodeSelection [ - "I verify that a selected node can be unchosen once a new selection has been done." - - | tree testNode1 testNode2 | - tree := self newTestTree.. - testNode1 := (tree preorderTraversalRootChildren at: 1) selected: true. - self assert: tree detectSelectionIndex equals: testNode1 preorderTraversalIndex. - testNode2 := (tree preorderTraversalRootChildren at: 2). - testNode2 toggleSelected. - self assert: tree detectSelectionIndex equals: testNode2 preorderTraversalIndex -] diff --git a/src/Grafoscopio/GrafoscopioNotebook.class.st b/src/Grafoscopio/GrafoscopioNotebook.class.st index 9a597a8..b77fbaa 100644 --- a/src/Grafoscopio/GrafoscopioNotebook.class.st +++ b/src/Grafoscopio/GrafoscopioNotebook.class.st @@ -87,7 +87,7 @@ GrafoscopioNotebook >> addCommandFrom: dictionary into: stream [ { #category : #'editing nodes' } GrafoscopioNotebook >> addNode [ - self currentNodeContent addNodeAfterMe. + self currentNode addNodeAfterMe. self notebookContent: notebook. ] @@ -148,6 +148,11 @@ GrafoscopioNotebook >> checksumForRootSubtree [ ^ GrafoscopioUtils checksumFor: self workingFile ] +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> content [ + self shouldBeImplemented. +] + { #category : #'editing nodes' } GrafoscopioNotebook >> copyNodeToClipboard [ tree highlightedItem content copyToClipboard. @@ -160,22 +165,25 @@ GrafoscopioNotebook >> createNewExample [ node0 := GrafoscopioNode new created: DateAndTime now printString; header: 'Arbol principal'; - tagAs: 'código'. + tagAs: 'código'; + yourself. node1 := GrafoscopioNode new created: DateAndTime now printString; header: 'Node 1'; body: ''; - tagAs: 'text'. + tagAs: 'text'; + yourself. node0 addNode: node1. ^ node0 ] { #category : #operation } GrafoscopioNotebook >> currentNode [ - | currentNode | - currentNode := tree highlightedItem. - currentNode ifNil: [ ^ self ]. - ^ currentNode + ^ tree highlightedItem + ifNil: [ notebook children + ifEmpty: [ notebook root ] + ifNotEmpty: [ notebook children first ] ] + ifNotNil: [ :v | v content ] ] { #category : #operation } @@ -457,7 +465,7 @@ GrafoscopioNotebook >> imagesList [ { #category : #accessing } GrafoscopioNotebook >> imagesList: anObject [ -self halt. + self halt. imagesList := anObject ] @@ -568,7 +576,7 @@ GrafoscopioNotebook >> linksList [ { #category : #utilities } GrafoscopioNotebook >> listImagesUI [ - ListModel new + ListPresenter new title: 'Images files list'; items: self imagesList ; openWithSpec @@ -613,18 +621,18 @@ GrafoscopioNotebook >> metadata [ ] { #category : #'editing nodes' } -GrafoscopioNotebook >> moveNodeAfter [ +GrafoscopioNotebook >> moveSelectedNodeDown [ | editedNode | editedNode := tree selectedItem content. - editedNode moveAfter. + editedNode moveDown. self notebookContent: notebook ] { #category : #'editing nodes' } -GrafoscopioNotebook >> moveNodeBefore [ +GrafoscopioNotebook >> moveSelectedNodeUp [ | editedNode | editedNode := tree highlightedItem content. - editedNode moveBefore. + editedNode moveUp. self notebookContent: notebook ] @@ -664,16 +672,9 @@ GrafoscopioNotebook >> notebook: anObject [ { #category : #api } GrafoscopioNotebook >> notebookContent: aTree [ - - | nodeBlock | - nodeBlock:= [:gfcNode | |node| - node := TreeNodeModel new. - node - hasChildren: [ gfcNode children isNotEmpty ]; - children: [ gfcNode children collect: [:subNode | nodeBlock value: subNode ]]; - content: gfcNode]. - tree roots: (aTree children collect:[ :gfcNode | nodeBlock value: gfcNode]) - + tree + roots: (aTree children collect: [ :gfcNode | gfcNode asTreeNodePresenter ]). + tree selectedIndex: (tree selectedIndex min: aTree children size) ] { #category : #initialization } @@ -916,14 +917,14 @@ GrafoscopioNotebook >> promoteNode [ { #category : #'editing nodes' } GrafoscopioNotebook >> removeNode [ | contentToDelete parentContent newSelectedContent children | + tree selectedItem + ifNil: [ ^ self inform: 'No node available or properly selected ' ]. contentToDelete := tree selectedItem content. parentContent := contentToDelete parent. children := parentContent children. - children size > 1 - ifTrue: [ - children last = contentToDelete - ifTrue: [ newSelectedContent := children at: (children size - 1) ] - ] + children size > 1 + ifTrue: [ children last = contentToDelete + ifTrue: [ newSelectedContent := children at: children size - 1 ] ] ifFalse: [ newSelectedContent := parentContent ]. contentToDelete parent removeNode: contentToDelete. self notebookContent: notebook @@ -1008,7 +1009,7 @@ GrafoscopioNotebook >> toggleCodeNode [ { #category : #initialization } GrafoscopioNotebook >> topBar [ - ^ MenuModel new + ^ MenuPresenter new addGroup: [ :group | group addItem: [ :item | @@ -1086,14 +1087,14 @@ GrafoscopioNotebook >> topBar [ name: nil; description: 'Move node up'; icon: MendaIcons new arrowUpIcon; - action: [ self moveNodeBefore ] ]. + action: [ self moveSelectedNodeUp ] ]. group addItem: [ :item | item name: nil; description: 'Move node down'; icon: MendaIcons new arrowDownIcon; - action: [ self moveNodeAfter ] ]. + action: [ self moveSelectedNodeDown ] ]. group addItem: [ :item | item @@ -1207,7 +1208,6 @@ GrafoscopioNotebook >> updateBodyFor: aNodeContainer [ tree needRebuild: false. body needRebuild: true. aNode := aNodeContainer content. - aNode toggleSelected. header text: aNode header. body := self instantiate: aNode specModelClass new. body content: aNode body. diff --git a/src/Grafoscopio/TreePresenter.extension.st b/src/Grafoscopio/TreePresenter.extension.st new file mode 100644 index 0000000..d4b37a8 --- /dev/null +++ b/src/Grafoscopio/TreePresenter.extension.st @@ -0,0 +1,17 @@ +Extension { #name : #TreePresenter } + +{ #category : #'*Grafoscopio' } +TreePresenter >> selectedIndex [ + ^ selectedItem value + ifNil: [ 1 min: rootsHolder value size ] + ifNotNil: [ rootsHolder value indexOf: selectedItem value ] +] + +{ #category : #'*Grafoscopio' } +TreePresenter >> selectedIndex: anInteger [ + anInteger = 0 + ifTrue: [ self resetSelection. + self highlightedItem: nil ] + ifFalse: [ self selectedItem: (rootsHolder value at: anInteger). + self highlightedItem: (rootsHolder value at: anInteger) ] +]