Class { #name : #GrafoscopioAbstractNode, #superclass : #Object, #instVars : [ 'id', 'header', 'created', 'edited', 'parent', 'tags' ], #classInstVars : [ 'clipboard' ], #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 : #'add/remove nodes' } GrafoscopioAbstractNode >> addNodeAfterMe: genericNode [ "Adds a generic node after the given node so they become slibings of the same 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 } GrafoscopioAbstractNode >> addTag: aTag [ "Tags the recipient node with aTag (string). For the moment we will have only one tag. In the future we will have several and there will be rules to know how tags interact with each other" self tags add: aTag ] { #category : #'as yet unclassified' } GrafoscopioAbstractNode >> content [ self subclassResponsibility ] { #category : #accessing } GrafoscopioAbstractNode >> created [ ^ created ] { #category : #accessing } GrafoscopioAbstractNode >> created: aTimestamp [ "I tell when this object was created" 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 ] { #category : #accessing } GrafoscopioAbstractNode >> edited: aTimestamp [ "I store the last time when a node was edited. Because nodes in the notebook have a autosave feature, I'm updated automatically when nodes are edited from the GUI. If I'm in the notebook root (i.e. node's level equals 0) I should store the last time the notebook was saved on the hard drive." 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 ] { #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 ] { #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 ] { #category : #accessing } GrafoscopioAbstractNode >> tagAs: aTag [ self error: 'tags are not used as markers anymore. Use addTag: instead and ensure you are not relying on text/codigo etc.' ] { #category : #accessing } GrafoscopioAbstractNode >> tags [ "I returns the receiver tags." ^ tags ifNil: [ tags := Set new ] ] { #category : #accessing } 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 [ self edited: DateAndTime now ]