Neww notebook.
We have then now launch / launch compatibility. The new development is scoped in newnotebook and GrafoscopioAbstractNode. t
This commit is contained in:
parent
d57e560554
commit
87dcc43e4d
@ -44,7 +44,7 @@ GfUIHelpers class >> addToHelpMenu: aGrafoscopioNotebook [
|
|||||||
ifNone: [
|
ifNone: [
|
||||||
self helpMenu
|
self helpMenu
|
||||||
add: (metadata at: 'shortTitle')
|
add: (metadata at: 'shortTitle')
|
||||||
target: [ GrafoscopioNotebook open: nbFile ]
|
target: [ GrafoscopioNewNotebook open: nbFile ]
|
||||||
selector: #value ] ].
|
selector: #value ] ].
|
||||||
self updateUI
|
self updateUI
|
||||||
]
|
]
|
||||||
@ -170,7 +170,7 @@ GfUIHelpers class >> openFromRecentlyUsed [
|
|||||||
selection := UIManager default
|
selection := UIManager default
|
||||||
chooseFrom: recentNotebooksReversed title: 'Choose a notebook...'.
|
chooseFrom: recentNotebooksReversed title: 'Choose a notebook...'.
|
||||||
selection > 0
|
selection > 0
|
||||||
ifTrue: [ GrafoscopioNotebook new openFromFile: (recentNotebooksReversed at: selection)]
|
ifTrue: [ GrafoscopioNewNotebook new openFromFile: (recentNotebooksReversed at: selection)]
|
||||||
ifFalse: [ self inform: 'No notebook selected!' ]
|
ifFalse: [ self inform: 'No notebook selected!' ]
|
||||||
]
|
]
|
||||||
ifEmpty: [self messageNoRecentDocuments]
|
ifEmpty: [self messageNoRecentDocuments]
|
||||||
|
@ -18,7 +18,7 @@ GfWorldMenu class >> helpMenuOn: aBuilder [
|
|||||||
label: 'Manual';
|
label: 'Manual';
|
||||||
order: 1;
|
order: 1;
|
||||||
parent: #GfHelpAndDocs;
|
parent: #GfHelpAndDocs;
|
||||||
action: [ GrafoscopioNotebook open: GrafoscopioDocs manual ].
|
action: [ GrafoscopioNewNotebook open: GrafoscopioDocs manual ].
|
||||||
(aBuilder item: #GfManualPDF)
|
(aBuilder item: #GfManualPDF)
|
||||||
label: 'Manual (PDF)';
|
label: 'Manual (PDF)';
|
||||||
order: 2;
|
order: 2;
|
||||||
@ -28,12 +28,12 @@ GfWorldMenu class >> helpMenuOn: aBuilder [
|
|||||||
label: 'Dataviz';
|
label: 'Dataviz';
|
||||||
order: 3;
|
order: 3;
|
||||||
parent: #GfHelpAndDocs;
|
parent: #GfHelpAndDocs;
|
||||||
action: [ GrafoscopioNotebook open: DatavizDocs introNotebook ].
|
action: [ GrafoscopioNewNotebook open: DatavizDocs introNotebook ].
|
||||||
(aBuilder item: #GfHelpDevNotes)
|
(aBuilder item: #GfHelpDevNotes)
|
||||||
label: 'Devs''s notes';
|
label: 'Devs''s notes';
|
||||||
order: 4;
|
order: 4;
|
||||||
parent: #GfHelpAndDocs;
|
parent: #GfHelpAndDocs;
|
||||||
action: [ GrafoscopioNotebook open: GrafoscopioDocs devNotes ].
|
action: [ GrafoscopioNewNotebook open: GrafoscopioDocs devNotes ].
|
||||||
(aBuilder item: #GfHelpAbout)
|
(aBuilder item: #GfHelpAbout)
|
||||||
label: 'About Grafoscopio';
|
label: 'About Grafoscopio';
|
||||||
order: 5;
|
order: 5;
|
||||||
@ -41,6 +41,31 @@ GfWorldMenu class >> helpMenuOn: aBuilder [
|
|||||||
action: [ GfUIHelpers messageAbout ].
|
action: [ GfUIHelpers messageAbout ].
|
||||||
]
|
]
|
||||||
|
|
||||||
|
{ #category : #'world menu' }
|
||||||
|
GfWorldMenu class >> launchCompatibilityMenuOn: aBuilder [
|
||||||
|
<worldMenu>
|
||||||
|
(aBuilder item: #'New notebook')
|
||||||
|
label: 'New notebook';
|
||||||
|
order: 1;
|
||||||
|
parent: #GfLaunchCompatibility;
|
||||||
|
action: [ GrafoscopioNotebook new openDefault ].
|
||||||
|
(aBuilder item: #'Notebook from file...')
|
||||||
|
label: 'Notebook from file...';
|
||||||
|
order: 2;
|
||||||
|
parent: #GfLaunchCompatibility;
|
||||||
|
action: [ GrafoscopioNotebook new openFromFileSelector ].
|
||||||
|
(aBuilder item: #'Notebook from the Internet...')
|
||||||
|
label: 'Notebook from the Internet...';
|
||||||
|
order: 3;
|
||||||
|
parent: #GfLaunchCompatibility;
|
||||||
|
action: [ GrafoscopioNotebook new openFromUrlUI ].
|
||||||
|
(aBuilder item: #recentNotebooks)
|
||||||
|
label: 'Recent notebooks...';
|
||||||
|
order: 4;
|
||||||
|
parent: #GfLaunchCompatibility;
|
||||||
|
action: [ GfUIHelpers openFromRecentlyUsed ]
|
||||||
|
]
|
||||||
|
|
||||||
{ #category : #'world menu' }
|
{ #category : #'world menu' }
|
||||||
GfWorldMenu class >> launchMenuOn: aBuilder [
|
GfWorldMenu class >> launchMenuOn: aBuilder [
|
||||||
<worldMenu>
|
<worldMenu>
|
||||||
@ -48,12 +73,12 @@ GfWorldMenu class >> launchMenuOn: aBuilder [
|
|||||||
label: 'New notebook';
|
label: 'New notebook';
|
||||||
order: 1;
|
order: 1;
|
||||||
parent: #GfLaunch;
|
parent: #GfLaunch;
|
||||||
action: [ GrafoscopioNotebook new openDefault ].
|
action: [ GrafoscopioNewNotebook new openDefault ].
|
||||||
(aBuilder item: #'Notebook from file...')
|
(aBuilder item: #'Notebook from file...')
|
||||||
label: 'Notebook from file...';
|
label: 'Notebook from file...';
|
||||||
order: 2;
|
order: 2;
|
||||||
parent: #GfLaunch;
|
parent: #GfLaunch;
|
||||||
action: [ GrafoscopioNotebook new openFromFileSelector ].
|
action: [ GrafoscopioNewNotebook new openFromFileSelector ].
|
||||||
(aBuilder item: #GfLaunchOpenRecent)
|
(aBuilder item: #GfLaunchOpenRecent)
|
||||||
label: 'Open recent...';
|
label: 'Open recent...';
|
||||||
order: 2;
|
order: 2;
|
||||||
@ -62,7 +87,7 @@ GfWorldMenu class >> launchMenuOn: aBuilder [
|
|||||||
label: 'Notebook from the Internet...';
|
label: 'Notebook from the Internet...';
|
||||||
order: 3;
|
order: 3;
|
||||||
parent: #GfLaunch;
|
parent: #GfLaunch;
|
||||||
action: [ GrafoscopioNotebook new openFromUrlUI ].
|
action: [ GrafoscopioNewNotebook new openFromUrlUI ].
|
||||||
(aBuilder item: #recentNotebooks)
|
(aBuilder item: #recentNotebooks)
|
||||||
label: 'Recent notebooks...';
|
label: 'Recent notebooks...';
|
||||||
order: 4;
|
order: 4;
|
||||||
@ -73,28 +98,36 @@ GfWorldMenu class >> launchMenuOn: aBuilder [
|
|||||||
{ #category : #'world menu' }
|
{ #category : #'world menu' }
|
||||||
GfWorldMenu class >> mainMenuItemsOn: aBuilder [
|
GfWorldMenu class >> mainMenuItemsOn: aBuilder [
|
||||||
"I add the main Grafoscopio menu to the Pharo World."
|
"I add the main Grafoscopio menu to the Pharo World."
|
||||||
<worldMenu>
|
|
||||||
|
|
||||||
|
<worldMenu>
|
||||||
(aBuilder item: #Grafoscopio)
|
(aBuilder item: #Grafoscopio)
|
||||||
label: 'Grafoscopio';
|
label: 'Grafoscopio';
|
||||||
order: 1;
|
order: 1;
|
||||||
with: [
|
with: [ (aBuilder
|
||||||
(aBuilder item: #GfLaunch; label: 'Launch') target: self.
|
item: #GfLaunch;
|
||||||
(aBuilder item: #GfUpdate; label: 'Update') target: self.
|
label: 'Launch') target: self.
|
||||||
(aBuilder item: #GfHelpAndDocs; label: 'Help & Docs') target: self. ]
|
(aBuilder
|
||||||
|
item: #GfLaunchCompatibility;
|
||||||
|
label: 'Launch Compatibility') target: self.
|
||||||
|
(aBuilder
|
||||||
|
item: #GfUpdate;
|
||||||
|
label: 'Update') target: self.
|
||||||
|
(aBuilder
|
||||||
|
item: #GfHelpAndDocs;
|
||||||
|
label: 'Help & Docs') target: self ]
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'world menu' }
|
{ #category : #'world menu' }
|
||||||
GfWorldMenu class >> openRecentMenu: aBuilder [
|
GfWorldMenu class >> openRecentMenu: aBuilder [
|
||||||
<worldMenu>
|
<worldMenu>
|
||||||
|
|
||||||
GrafoscopioNotebook recents
|
GrafoscopioNewNotebook recents
|
||||||
do: [ :f |
|
do: [ :f |
|
||||||
(aBuilder item: #'Open', f basename )
|
(aBuilder item: #'Open', f basename )
|
||||||
label: 'Open ', f basename;
|
label: 'Open ', f basename;
|
||||||
order: 1;
|
order: 1;
|
||||||
parent: #GfLaunchOpenRecent;
|
parent: #GfLaunchOpenRecent;
|
||||||
action: [ GrafoscopioNotebook open: f ] ]
|
action: [ GrafoscopioNewNotebook open: f ] ]
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'world menu' }
|
{ #category : #'world menu' }
|
||||||
|
@ -2,20 +2,234 @@ Class {
|
|||||||
#name : #GrafoscopioAbstractNode,
|
#name : #GrafoscopioAbstractNode,
|
||||||
#superclass : #Object,
|
#superclass : #Object,
|
||||||
#instVars : [
|
#instVars : [
|
||||||
|
'id',
|
||||||
'header',
|
'header',
|
||||||
'created',
|
'created',
|
||||||
'edited',
|
'edited',
|
||||||
'selected',
|
|
||||||
'key',
|
|
||||||
'icon',
|
|
||||||
'body',
|
|
||||||
'tags',
|
|
||||||
'children',
|
|
||||||
'parent',
|
'parent',
|
||||||
'node',
|
'tags'
|
||||||
'nodesInPreorder',
|
|
||||||
'links',
|
|
||||||
'output'
|
|
||||||
],
|
],
|
||||||
#category : #'Grafoscopio-Model'
|
#category : #'Grafoscopio-Model'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ #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 : #exporting }
|
||||||
|
GrafoscopioAbstractNode >> asTreeNodePresenter [
|
||||||
|
| node |
|
||||||
|
node := TreeNodePresenter new.
|
||||||
|
node
|
||||||
|
hasChildren: [ false ];
|
||||||
|
children: [ {} ];
|
||||||
|
content: self.
|
||||||
|
^ node
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 >> 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: (aNotebook instantiate: self specModelClass new).
|
||||||
|
aNotebook body content: self content
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 [
|
||||||
|
^ GrafoscopioTextModel
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 > 10 ifTrue: [ (header readStream next: 8) , '...' ]
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioAbstractNode >> updateEditionTimestamp [
|
||||||
|
self edited: DateAndTime now
|
||||||
|
]
|
||||||
|
34
repository/Grafoscopio/GrafoscopioCodeNode.class.st
Normal file
34
repository/Grafoscopio/GrafoscopioCodeNode.class.st
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
Class {
|
||||||
|
#name : #GrafoscopioCodeNode,
|
||||||
|
#superclass : #GrafoscopioAbstractNode,
|
||||||
|
#instVars : [
|
||||||
|
'icon',
|
||||||
|
'body'
|
||||||
|
],
|
||||||
|
#category : #'Grafoscopio-Model'
|
||||||
|
}
|
||||||
|
|
||||||
|
{ #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 >> content [
|
||||||
|
^ body ifNil:[ '' ]
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioCodeNode >> header [
|
||||||
|
^ super header, ' (Code)'
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioCodeNode >> shouldAskBeforeRemove [
|
||||||
|
^ self content isNotEmpty
|
||||||
|
]
|
1292
repository/Grafoscopio/GrafoscopioNewNotebook.class.st
Normal file
1292
repository/Grafoscopio/GrafoscopioNewNotebook.class.st
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,10 +12,11 @@ Class {
|
|||||||
#name : #GrafoscopioNode,
|
#name : #GrafoscopioNode,
|
||||||
#superclass : #Object,
|
#superclass : #Object,
|
||||||
#instVars : [
|
#instVars : [
|
||||||
'id',
|
|
||||||
'header',
|
'header',
|
||||||
|
'headers',
|
||||||
'created',
|
'created',
|
||||||
'edited',
|
'edited',
|
||||||
|
'selected',
|
||||||
'key',
|
'key',
|
||||||
'icon',
|
'icon',
|
||||||
'body',
|
'body',
|
||||||
@ -23,7 +24,10 @@ Class {
|
|||||||
'children',
|
'children',
|
||||||
'parent',
|
'parent',
|
||||||
'node',
|
'node',
|
||||||
'links'
|
'level',
|
||||||
|
'nodesInPreorder',
|
||||||
|
'links',
|
||||||
|
'output'
|
||||||
],
|
],
|
||||||
#classInstVars : [
|
#classInstVars : [
|
||||||
'clipboard'
|
'clipboard'
|
||||||
@ -851,8 +855,7 @@ GrafoscopioNode >> moveDown [
|
|||||||
GrafoscopioNode >> moveDown: aNode [
|
GrafoscopioNode >> moveDown: aNode [
|
||||||
| index |
|
| index |
|
||||||
"Moves the current node a place before in the children collection where is located"
|
"Moves the current node a place before in the children collection where is located"
|
||||||
index := (children indexOf: aNode) max: 1 .
|
index := children indexOf: aNode.
|
||||||
|
|
||||||
children swap: index with: (index + 1 min: children size)
|
children swap: index with: (index + 1 min: children size)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -19,28 +19,28 @@ GrafoscopioNodeTest >> dummyHtml [
|
|||||||
{ #category : #tests }
|
{ #category : #tests }
|
||||||
GrafoscopioNodeTest >> newTestTree [
|
GrafoscopioNodeTest >> newTestTree [
|
||||||
| node0 node1 node2 node3 node4 |
|
| node0 node1 node2 node3 node4 |
|
||||||
node0 := GrafoscopioNode new
|
node0 := GrafoscopioTextNode new
|
||||||
created: DateAndTime now printString;
|
created: DateAndTime now;
|
||||||
header: 'Arbol principal'.
|
header: 'Arbol principal'.
|
||||||
node1 := GrafoscopioNode new
|
node1 := GrafoscopioTextNode new
|
||||||
created: DateAndTime now printString;
|
created: DateAndTime now;
|
||||||
header: 'Markup';
|
header: 'Markup';
|
||||||
body: 'I am <b>just a node with markup</b>';
|
body: 'I am <b>just a node with markup</b>';
|
||||||
tagAs: 'text';
|
tagAs: 'text';
|
||||||
links: 'temp.md'.
|
links: 'temp.md'.
|
||||||
node2 := GrafoscopioNode new
|
node2 := GrafoscopioTextNode new
|
||||||
created: DateAndTime now printString;
|
created: DateAndTime now ;
|
||||||
header: '%output Code';
|
header: '%output Code';
|
||||||
tagAs: 'código';
|
tagAs: 'código';
|
||||||
body: '(ConfigurationOfGrafoscopio>>#version14:) sourceCode'.
|
body: '(ConfigurationOfGrafoscopio>>#version14:) sourceCode'.
|
||||||
node3 := GrafoscopioNode new
|
node3 := GrafoscopioTextNode new
|
||||||
created: DateAndTime now printString;
|
created: DateAndTime now ;
|
||||||
header: '%invisible';
|
header: '%invisible';
|
||||||
tagAs: 'text';
|
tagAs: 'text';
|
||||||
body: '<i>Just testing</i>'.
|
body: '<i>Just testing</i>'.
|
||||||
node1 addNode: node3.
|
node1 addNode: node3.
|
||||||
node4 := GrafoscopioNode new
|
node4 := GrafoscopioTextNode new
|
||||||
created: DateAndTime now printString;
|
created: DateAndTime now ;
|
||||||
header: 'Something';
|
header: 'Something';
|
||||||
tagAs: 'text';
|
tagAs: 'text';
|
||||||
body: '<h1>else</h1>'.
|
body: '<h1>else</h1>'.
|
||||||
@ -54,7 +54,7 @@ GrafoscopioNodeTest >> newTestTree [
|
|||||||
GrafoscopioNodeTest >> testAddingChildren [
|
GrafoscopioNodeTest >> testAddingChildren [
|
||||||
| tree nnode orig |
|
| tree nnode orig |
|
||||||
tree := self newTestTree.
|
tree := self newTestTree.
|
||||||
nnode := GrafoscopioNode new.
|
nnode := GrafoscopioTextNode new.
|
||||||
orig := tree children size.
|
orig := tree children size.
|
||||||
tree addNode: nnode.
|
tree addNode: nnode.
|
||||||
self assert: tree children size equals: orig + 1.
|
self assert: tree children size equals: orig + 1.
|
||||||
@ -64,9 +64,9 @@ GrafoscopioNodeTest >> testAddingChildren [
|
|||||||
{ #category : #tests }
|
{ #category : #tests }
|
||||||
GrafoscopioNodeTest >> testDemoteNode [
|
GrafoscopioNodeTest >> testDemoteNode [
|
||||||
| tree child1 child2 |
|
| tree child1 child2 |
|
||||||
tree := GrafoscopioNode new.
|
tree := GrafoscopioTextNode new.
|
||||||
child1 := GrafoscopioNode new.
|
child1 := GrafoscopioTextNode new.
|
||||||
child2 := GrafoscopioNode new.
|
child2 := GrafoscopioTextNode new.
|
||||||
tree
|
tree
|
||||||
addNode: child1;
|
addNode: child1;
|
||||||
addNode: child2.
|
addNode: child2.
|
||||||
@ -79,7 +79,7 @@ GrafoscopioNodeTest >> testDemoteNode [
|
|||||||
{ #category : #tests }
|
{ #category : #tests }
|
||||||
GrafoscopioNodeTest >> testFindAndReplace [
|
GrafoscopioNodeTest >> testFindAndReplace [
|
||||||
| tree |
|
| tree |
|
||||||
tree := GrafoscopioNode new.
|
tree := GrafoscopioTextNode new.
|
||||||
tree body: 'I''m only a test node.'.
|
tree body: 'I''m only a test node.'.
|
||||||
tree find: 'only' andReplaceWith: 'JUST'.
|
tree find: 'only' andReplaceWith: 'JUST'.
|
||||||
self assert: (tree body findString: 'JUST') > 0.
|
self assert: (tree body findString: 'JUST') > 0.
|
||||||
@ -100,15 +100,15 @@ GrafoscopioNodeTest >> testHasMarkdownSubtreesToExport [
|
|||||||
|
|
||||||
{ #category : #tests }
|
{ #category : #tests }
|
||||||
GrafoscopioNodeTest >> testInitializeIsOk [
|
GrafoscopioNodeTest >> testInitializeIsOk [
|
||||||
self shouldnt: [ GrafoscopioNode new ] raise: Error
|
self shouldnt: [ GrafoscopioTextNode new ] raise: Error
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #tests }
|
{ #category : #tests }
|
||||||
GrafoscopioNodeTest >> testPromoteNode [
|
GrafoscopioNodeTest >> testPromoteNode [
|
||||||
| tree child1 child2 |
|
| tree child1 child2 |
|
||||||
tree := GrafoscopioNode new.
|
tree := GrafoscopioTextNode new.
|
||||||
child1 := GrafoscopioNode new.
|
child1 := GrafoscopioTextNode new.
|
||||||
child2 := GrafoscopioNode new.
|
child2 := GrafoscopioTextNode new.
|
||||||
tree addNode: child1.
|
tree addNode: child1.
|
||||||
child1 addNode: child2.
|
child1 addNode: child2.
|
||||||
child2 promote.
|
child2 promote.
|
||||||
@ -132,7 +132,7 @@ GrafoscopioNodeTest >> testRemoveLeadingLineNumbersSized [
|
|||||||
9var nodes = tree.nodes(treeData);
|
9var nodes = tree.nodes(treeData);
|
||||||
10var links = tree.links(nodes);
|
10var links = tree.links(nodes);
|
||||||
11 '.
|
11 '.
|
||||||
testNode := GrafoscopioNode new
|
testNode := GrafoscopioTextNode new
|
||||||
body: copiedCode.
|
body: copiedCode.
|
||||||
testNode removeLeadingLineNumbersSized: 3.
|
testNode removeLeadingLineNumbersSized: 3.
|
||||||
self assert: testNode body equals: '
|
self assert: testNode body equals: '
|
||||||
@ -165,7 +165,7 @@ GrafoscopioNodeTest >> testSanitizedLink [
|
|||||||
| node link |
|
| node link |
|
||||||
|
|
||||||
link := 'docutopia://hackbo:hackbot'.
|
link := 'docutopia://hackbo:hackbot'.
|
||||||
node := GrafoscopioNode new links: link.
|
node := GrafoscopioTextNode new links: link.
|
||||||
self assert: (node sanitizeDefaultLink = 'https://docutopia.tupale.co/hackbo:hackbot') equals: true
|
self assert: (node sanitizeDefaultLink = 'https://docutopia.tupale.co/hackbo:hackbot') equals: true
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -87,10 +87,8 @@ GrafoscopioNotebook >> addCommandFrom: dictionary into: stream [
|
|||||||
|
|
||||||
{ #category : #'editing nodes' }
|
{ #category : #'editing nodes' }
|
||||||
GrafoscopioNotebook >> addNode [
|
GrafoscopioNotebook >> addNode [
|
||||||
| newNode |
|
self currentNode addNodeAfterMe.
|
||||||
newNode := self currentNode addNodeAfterMe.
|
|
||||||
self notebookContent: notebook.
|
self notebookContent: notebook.
|
||||||
self selectedItem: newNode.
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #persistence }
|
{ #category : #persistence }
|
||||||
@ -627,8 +625,7 @@ GrafoscopioNotebook >> moveSelectedNodeDown [
|
|||||||
| editedNode |
|
| editedNode |
|
||||||
editedNode := tree selectedItem content.
|
editedNode := tree selectedItem content.
|
||||||
editedNode moveDown.
|
editedNode moveDown.
|
||||||
self notebookContent: notebook.
|
self notebookContent: notebook
|
||||||
tree needRebuild: true.
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'editing nodes' }
|
{ #category : #'editing nodes' }
|
||||||
@ -676,7 +673,8 @@ GrafoscopioNotebook >> notebook: anObject [
|
|||||||
{ #category : #api }
|
{ #category : #api }
|
||||||
GrafoscopioNotebook >> notebookContent: aTree [
|
GrafoscopioNotebook >> notebookContent: aTree [
|
||||||
tree
|
tree
|
||||||
roots: (aTree children collect: [ :gfcNode | gfcNode asTreeNodePresenter ])
|
roots: (aTree children collect: [ :gfcNode | gfcNode asTreeNodePresenter ]).
|
||||||
|
tree selectedIndex: (tree selectedIndex min: aTree children size)
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #initialization }
|
{ #category : #initialization }
|
||||||
@ -922,12 +920,6 @@ GrafoscopioNotebook >> removeNode [
|
|||||||
tree selectedItem
|
tree selectedItem
|
||||||
ifNil: [ ^ self inform: 'No node available or properly selected ' ].
|
ifNil: [ ^ self inform: 'No node available or properly selected ' ].
|
||||||
contentToDelete := tree selectedItem content.
|
contentToDelete := tree selectedItem content.
|
||||||
(contentToDelete body isNotEmpty or: [ contentToDelete hasChildren ])
|
|
||||||
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 := contentToDelete parent.
|
parentContent := contentToDelete parent.
|
||||||
children := parentContent children.
|
children := parentContent children.
|
||||||
children size > 1
|
children size > 1
|
||||||
@ -935,15 +927,7 @@ GrafoscopioNotebook >> removeNode [
|
|||||||
ifTrue: [ newSelectedContent := children at: children size - 1 ] ]
|
ifTrue: [ newSelectedContent := children at: children size - 1 ] ]
|
||||||
ifFalse: [ newSelectedContent := parentContent ].
|
ifFalse: [ newSelectedContent := parentContent ].
|
||||||
contentToDelete parent removeNode: contentToDelete.
|
contentToDelete parent removeNode: contentToDelete.
|
||||||
self notebookContent: notebook.
|
self notebookContent: notebook
|
||||||
self resetSelectedItem.
|
|
||||||
]
|
|
||||||
|
|
||||||
{ #category : #'editing nodes' }
|
|
||||||
GrafoscopioNotebook >> resetSelectedItem [
|
|
||||||
tree selectedIndex: (tree selectedIndex min: notebook children size).
|
|
||||||
tree highlightedItem: tree selectedItem.
|
|
||||||
tree updatePresenter.
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #persistence }
|
{ #category : #persistence }
|
||||||
@ -998,12 +982,6 @@ GrafoscopioNotebook >> seePdf [
|
|||||||
onSuccessDo: [ :v | (#open command argument: self pdfFile fullName) schedule ]
|
onSuccessDo: [ :v | (#open command argument: self pdfFile fullName) schedule ]
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'editing nodes' }
|
|
||||||
GrafoscopioNotebook >> selectedItem: anItem [
|
|
||||||
tree selectedItem: (tree roots detect: [ : p | p content = anItem ]).
|
|
||||||
tree highlightedItem: tree selectedItem.
|
|
||||||
]
|
|
||||||
|
|
||||||
{ #category : #persistence }
|
{ #category : #persistence }
|
||||||
GrafoscopioNotebook >> subtreeAsMarkdown [
|
GrafoscopioNotebook >> subtreeAsMarkdown [
|
||||||
| currentNode |
|
| currentNode |
|
||||||
|
@ -36,6 +36,6 @@ GrafoscopioTextModel >> content: aGrafoscopioNodeContent [
|
|||||||
GrafoscopioTextModel >> initializeWidgets [
|
GrafoscopioTextModel >> initializeWidgets [
|
||||||
|
|
||||||
body := self newText.
|
body := self newText.
|
||||||
body beForGrafoscopio.
|
body beForText.
|
||||||
body autoAccept: true.
|
body autoAccept: true.
|
||||||
]
|
]
|
||||||
|
995
repository/Grafoscopio/GrafoscopioTextNode.class.st
Normal file
995
repository/Grafoscopio/GrafoscopioTextNode.class.st
Normal file
@ -0,0 +1,995 @@
|
|||||||
|
"
|
||||||
|
An UbakyeNode is and administrator of all node operations in a tree.
|
||||||
|
|
||||||
|
Instance Variables
|
||||||
|
node: <Object>
|
||||||
|
|
||||||
|
node
|
||||||
|
- xxxxx
|
||||||
|
|
||||||
|
"
|
||||||
|
Class {
|
||||||
|
#name : #GrafoscopioTextNode,
|
||||||
|
#superclass : #GrafoscopioAbstractNode,
|
||||||
|
#instVars : [
|
||||||
|
'key',
|
||||||
|
'icon',
|
||||||
|
'body',
|
||||||
|
'children',
|
||||||
|
'links'
|
||||||
|
],
|
||||||
|
#classInstVars : [
|
||||||
|
'clipboard'
|
||||||
|
],
|
||||||
|
#category : #'Grafoscopio-Model'
|
||||||
|
}
|
||||||
|
|
||||||
|
{ #category : #utility }
|
||||||
|
GrafoscopioTextNode 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 }
|
||||||
|
GrafoscopioTextNode class >> clipboard [
|
||||||
|
^ clipboard
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #accessing }
|
||||||
|
GrafoscopioTextNode class >> clipboard: anObject [
|
||||||
|
clipboard := anObject
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #utility }
|
||||||
|
GrafoscopioTextNode 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 : #utility }
|
||||||
|
GrafoscopioTextNode 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 : #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 := TreeNodePresenter 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"
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #accessing }
|
||||||
|
GrafoscopioTextNode >> children [
|
||||||
|
"Returns the receivers list of children"
|
||||||
|
|
||||||
|
^ children ifNil: [ children := OrderedCollection new ]
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #accessing }
|
||||||
|
GrafoscopioTextNode >> children: aCollection [
|
||||||
|
"Sets the receivers children"
|
||||||
|
|
||||||
|
aCollection do: [:currentNode | currentNode parent: self ].
|
||||||
|
children := aCollection.
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioTextNode >> content [
|
||||||
|
^ body
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 ].
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 >> 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
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 headerStartsWith: '%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 : #movement }
|
||||||
|
GrafoscopioTextNode >> 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 }
|
||||||
|
GrafoscopioTextNode >> 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 : #accessing }
|
||||||
|
GrafoscopioTextNode >> openIn: aNotebook [
|
||||||
|
super openIn: aNotebook.
|
||||||
|
aNotebook autoSaveBodyOf: self.
|
||||||
|
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 [
|
||||||
|
|
||||||
|
(self isTaggedAs: 'código') ifTrue: [^GrafoscopioCodeModel].
|
||||||
|
(self isTaggedAs: 'johan') ifTrue:[^GrafoscopioButtonModel].
|
||||||
|
"por defecto"
|
||||||
|
^ GrafoscopioTextModel
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #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 ] ] ]
|
||||||
|
]
|
22
repository/Grafoscopio/GrafoscopioUrlCachedNode.class.st
Normal file
22
repository/Grafoscopio/GrafoscopioUrlCachedNode.class.st
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Class {
|
||||||
|
#name : #GrafoscopioUrlCachedNode,
|
||||||
|
#superclass : #GrafoscopioUrlNode,
|
||||||
|
#instVars : [
|
||||||
|
'content'
|
||||||
|
],
|
||||||
|
#category : #'Grafoscopio-Model'
|
||||||
|
}
|
||||||
|
|
||||||
|
{ #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.'
|
||||||
|
]
|
56
repository/Grafoscopio/GrafoscopioUrlNode.class.st
Normal file
56
repository/Grafoscopio/GrafoscopioUrlNode.class.st
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
Class {
|
||||||
|
#name : #GrafoscopioUrlNode,
|
||||||
|
#superclass : #GrafoscopioAbstractNode,
|
||||||
|
#instVars : [
|
||||||
|
'link'
|
||||||
|
],
|
||||||
|
#category : #'Grafoscopio-Model'
|
||||||
|
}
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioUrlNode >> content [
|
||||||
|
^ (self url
|
||||||
|
ifNil: [ ' Invalid url ' ]
|
||||||
|
ifNotNil: [ :url | self fetchContent: url ])
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioUrlNode >> fetchContent: anUrl [
|
||||||
|
^ (ZnEasy get: anUrl) entity contents
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioUrlNode >> getUrl [
|
||||||
|
| url |
|
||||||
|
url := UIManager default
|
||||||
|
request: 'Please insert a url '
|
||||||
|
initialAnswer: 'http://'
|
||||||
|
title: 'URL Node'.
|
||||||
|
url ifNil: [ ^ nil ].
|
||||||
|
url := url asZnUrl.
|
||||||
|
(url host isEmptyOrNil
|
||||||
|
or: [ url scheme isEmptyOrNil or: [ url authority isEmptyOrNil ] ])
|
||||||
|
ifTrue: [ ^ nil ].
|
||||||
|
^ url
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #accessing }
|
||||||
|
GrafoscopioUrlNode >> link: aZnUrl [
|
||||||
|
link := aZnUrl
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioUrlNode >> openIn: aNotebook [
|
||||||
|
super openIn: aNotebook.
|
||||||
|
aNotebook links text: (link ifNil: 'Invalid url')
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioUrlNode >> shouldAskBeforeRemove [
|
||||||
|
^ false
|
||||||
|
]
|
||||||
|
|
||||||
|
{ #category : #'as yet unclassified' }
|
||||||
|
GrafoscopioUrlNode >> url [
|
||||||
|
^ link ifNil: [ link := self getUrl ]
|
||||||
|
]
|
@ -9,7 +9,7 @@ Class {
|
|||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
ManifestGrafoscopio class >> ruleRBAssignmentInIfTrueRuleV1FalsePositive [
|
ManifestGrafoscopio class >> ruleRBAssignmentInIfTrueRuleV1FalsePositive [
|
||||||
^ #(#(#(#RGMethodDefinition #(#GrafoscopioNode #embedNodes #false)) #'2017-02-16T20:07:02.600781-05:00') #(#(#RGMethodDefinition #(#GrafoscopioNotebook #navigateRelativePathFor: #false)) #'2017-03-28T22:30:53.541042-05:00') )
|
^ #(#(#(#RGMethodDefinition #(#GrafoscopioTextNode #embedNodes #false)) #'2017-02-16T20:07:02.600781-05:00') #(#(#RGMethodDefinition #(#GrafoscopioNewNotebook #navigateRelativePathFor: #false)) #'2017-03-28T22:30:53.541042-05:00') )
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
@ -24,7 +24,7 @@ ManifestGrafoscopio class >> ruleRBBooleanPrecedenceRuleV1FalsePositive [
|
|||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
ManifestGrafoscopio class >> ruleRBCascadedNextPutAllsRuleV1FalsePositive [
|
ManifestGrafoscopio class >> ruleRBCascadedNextPutAllsRuleV1FalsePositive [
|
||||||
^ #(#(#(#RGMethodDefinition #(#GrafoscopioNode #metadataAsYamlIn: #false)) #'2017-04-20T19:01:59.286212-05:00') )
|
^ #(#(#(#RGMethodDefinition #(#GrafoscopioTextNode #metadataAsYamlIn: #false)) #'2017-04-20T19:01:59.286212-05:00') )
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
@ -39,12 +39,12 @@ ManifestGrafoscopio class >> ruleRBEqualsTrueRuleV1FalsePositive [
|
|||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
ManifestGrafoscopio class >> ruleRBLongMethodsRuleV1FalsePositive [
|
ManifestGrafoscopio class >> ruleRBLongMethodsRuleV1FalsePositive [
|
||||||
^ #(#(#(#RGMethodDefinition #(#GrafoscopioNotebook #topBar #false)) #'2016-12-17T18:51:33.99062-05:00') #(#(#RGMethodDefinition #(#GrafoscopioNotebook #notebookSubMenu #false)) #'2017-02-02T11:43:53.106456-05:00') )
|
^ #(#(#(#RGMethodDefinition #(#GrafoscopioNewNotebook #topBar #false)) #'2016-12-17T18:51:33.99062-05:00') #(#(#RGMethodDefinition #(#GrafoscopioNewNotebook #notebookSubMenu #false)) #'2017-02-02T11:43:53.106456-05:00') )
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
ManifestGrafoscopio class >> ruleRBSentNotImplementedRuleV1FalsePositive [
|
ManifestGrafoscopio class >> ruleRBSentNotImplementedRuleV1FalsePositive [
|
||||||
^ #(#(#(#RGMetaclassDefinition #(#'GrafoscopioGUI class' #GfUIHelpers)) #'2015-12-23T10:38:16.706667-05:00') #(#(#RGClassDefinition #(#GfUIHelpers)) #'2016-01-06T18:53:45.844051-05:00') #(#(#RGMethodDefinition #(#GrafoscopioNotebook #topBar #false)) #'2016-12-17T18:51:40.617924-05:00') )
|
^ #(#(#(#RGMetaclassDefinition #(#'GrafoscopioGUI class' #GfUIHelpers)) #'2015-12-23T10:38:16.706667-05:00') #(#(#RGClassDefinition #(#GfUIHelpers)) #'2016-01-06T18:53:45.844051-05:00') #(#(#RGMethodDefinition #(#GrafoscopioNewNotebook #topBar #false)) #'2016-12-17T18:51:40.617924-05:00') )
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
@ -59,5 +59,5 @@ ManifestGrafoscopio class >> ruleRBStringConcatenationRuleV1FalsePositive [
|
|||||||
|
|
||||||
{ #category : #'code-critics' }
|
{ #category : #'code-critics' }
|
||||||
ManifestGrafoscopio class >> ruleSmTMethodTestedRuleV1FalsePositive [
|
ManifestGrafoscopio class >> ruleSmTMethodTestedRuleV1FalsePositive [
|
||||||
^ #(#(#(#RGClassDefinition #(#GrafoscopioNode)) #'2017-10-31T19:59:03.294735-05:00') )
|
^ #(#(#(#RGClassDefinition #(#GrafoscopioTextNode)) #'2017-10-31T19:59:03.294735-05:00') )
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user