diff --git a/src/Grafoscopio/FileUtils.class.st b/src/Grafoscopio/FileUtils.class.st index 411aa0d..1c1a722 100644 --- a/src/Grafoscopio/FileUtils.class.st +++ b/src/Grafoscopio/FileUtils.class.st @@ -1,28 +1,28 @@ -" -I provide some convenience functionality to work with files. -" -Class { - #name : #FileUtils, - #superclass : #Object, - #category : #'Grafoscopio-Model' -} - -{ #category : #utilities } -FileUtils class >> createFilesNamed: fileNames endingWith: fileExtension intoFolder: aFolder [ - - | createdFiles fullFileName existingFiles typedFileName | - createdFiles := OrderedCollection new. - existingFiles := OrderedCollection new. - fileNames do: [ :eachFile | - typedFileName := eachFile, fileExtension. - fullFileName := aFolder / typedFileName. - fullFileName exists - ifFalse: [ - fullFileName ensureCreateFile. - createdFiles add: fullFileName basename ] - ifTrue: [existingFiles add: fullFileName basename ]]. - ^ (Dictionary new - at: 'created files' put: createdFiles; - at: 'existing files' put: existingFiles; - yourself) -] +" +I provide some convenience functionality to work with files. +" +Class { + #name : #FileUtils, + #superclass : #Object, + #category : #'Grafoscopio-Model' +} + +{ #category : #utilities } +FileUtils class >> createFilesNamed: fileNames endingWith: fileExtension intoFolder: aFolder [ + + | createdFiles fullFileName existingFiles typedFileName | + createdFiles := OrderedCollection new. + existingFiles := OrderedCollection new. + fileNames do: [ :eachFile | + typedFileName := eachFile, fileExtension. + fullFileName := aFolder / typedFileName. + fullFileName exists + ifFalse: [ + fullFileName ensureCreateFile. + createdFiles add: fullFileName basename ] + ifTrue: [existingFiles add: fullFileName basename ]]. + ^ (Dictionary new + at: 'created files' put: createdFiles; + at: 'existing files' put: existingFiles; + yourself) +] diff --git a/src/Grafoscopio/FontAwesomeIcons.class.st b/src/Grafoscopio/FontAwesomeIcons.class.st index b8d6107..e18abbe 100644 --- a/src/Grafoscopio/FontAwesomeIcons.class.st +++ b/src/Grafoscopio/FontAwesomeIcons.class.st @@ -1,139 +1,139 @@ -" -Please comment me using the following template inspired by Class Responsibility Collaborator (CRC) design: - -I'm FontAwesomeIcons, a custom selection of some icons in this font family used for the GUI of grafoscopio. - -For the Responsibility part: Three sentences about my main responsibility, what I'm doing, what services do I offer. - -For the Collaborators Part: State my main collaborators and one line about how I interact with them. - -Public API and Key Messages - -- message one -- message two -- what is the way to create instances is a plus. - - One simple example is simply gorgeous. - -Internal Representation and Key Implementation Points. - - Instance Variables - icons: - - - Implementation Points -" -Class { - #name : #FontAwesomeIcons, - #superclass : #Object, - #instVars : [ - 'icons' - ], - #classVars : [ - 'Current' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #'accessing - icons' } -FontAwesomeIcons >> copyIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self copyIconContents readStream) -] - -{ #category : #'private - contents' } -FontAwesomeIcons >> copyIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/copy.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA2UlEQVR4nJXQIU6DQRAF4K+F -4DAYVFHlBEgctQgScL0DCbp3wKNJOAIXQDcEC5iqQoNsA4KkiO6GJX92/p+XvGxm983bN8Mv -+rjFOnFYMNchbvCNSWooUTXopfMalxhjipfiLRucYg9bxf0zXCXBJP1yUkmwxhyviTN8wVsh -yHwsms/wiSUuivu8m3BBR1hglH5tGGxXGmGAO5zjIdBVE9xrjnb8nwQjHBb1ymaJfxAZ7NjM -HSIbHLQJIzxpzpoZYdim6WTQ7xayjmiJ7x1SLHrB4y72Www+fgBc9T/67LlhnQAAAABJRU5E -rkJggg==' -] - -{ #category : #'accessing - icons' } -FontAwesomeIcons >> cutIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self cutIconContents readStream) -] - -{ #category : #'private - contents' } -FontAwesomeIcons >> cutIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/cut.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABOUlEQVR4nNXSzyvlYRTH8Rcu -N5dmIt2FX4VpJktRUpKssB//wGSDUsrCahYzuykaGyk/YiFlb2VhYWVhYaUkKxsbTSYSYfE9 -V3e+EQub+dRTz4/zvM85n+fhHXSNhxhX+IHSV+50IVNYbKAKn7EeoO8vXOyNmHFsolFMirWD -89TeFyxhEuXoxjI+ZPAVFZjHX+SRQz3uMINbTOMCoxEzinsYwlGRDw/4iX2s4VNUkcUvDD/X -Wwna0Y8WLGI7snSgGatx9o8KTn4MQB6VqA1ITuL4IP5E76dpyBQuUy0shGmHElPLI3YSs+HZ -k+4kb98QlewGMIsyfMMKOiO+B1toKgDmimC5yHqSqjIb2efRhrqAtGbQhxHJs41JXB9JAW7w -G9WYkHygY5zBQfR9jz0MpE16i2ok7v+HegQfqT9MAMiDKwAAAABJRU5ErkJggg==' -] - -{ #category : #'accessing - icons' } -FontAwesomeIcons >> html5Icon [ - "Private - Generated method" - ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self html5IconContents readStream) -] - -{ #category : #'private - contents' } -FontAwesomeIcons >> html5IconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/html5.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA+UlEQVR4nK3Tv0oDQRDH8c9J -BMFKJGIXBBtFwc7SUlDfwpewEewsbQQL8QHsA2lSiK2NCFr4Av4hjYigVSxuV/bWjRhxYJhl -9uY7v9m9rdR2ikm/tx7OoRUSO5gfA3AfFxMhPo9RDE85ICZ2Uf3gNzkgjhAVzIV4hJlC58Vc -cQ5oh7iJpb+MEBUsF+Snh/wNkI9QsqjuFe+jABu4xgDDxFcS+Ff30ggDrOEs6/6YKGgA8kOc -DfEQV+jgMoDb2bcNm0rkHqv/zOmwt4o93IX9kxIAXjTn/sBDlhviYBRgH7eFguhv6GI9LaoK -oA62sYUF9NWv70Jyff9mnwBsQJo0owsMAAAAAElFTkSuQmCC' -] - -{ #category : #'accessing - icons' } -FontAwesomeIcons >> pasteIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self pasteIconContents readStream) -] - -{ #category : #'private - contents' } -FontAwesomeIcons >> pasteIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/paste.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAqUlEQVR4nK3TTQrBQRzG8c/I -yk6SsrAilk5hYWfjWA6hXICycwtLByDJ2upvgZrG6+BbU7+3nnnmVxNQuDDC2j09LK5xB5u4 -WY7iUpLH9ZcUGaedrZ4wznWQzh0wQfjWwRB9TF33lStwwABHzFG5WfvkCfsH/Vl4MBgToriC -ZpS3sMxxkNJGkbuDO/4isPtFoIwu6km9hdUvwg2cvP8bu/BMAVXU3ly0PQOGrUCXHk1emQAA -AABJRU5ErkJggg==' -] - -{ #category : #'accessing - icons' } -FontAwesomeIcons >> tagIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self tagIconContents readStream) -] - -{ #category : #'private - contents' } -FontAwesomeIcons >> tagIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/tag.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAmUlEQVR4nKXPMQ4BQRSA4U9D -oeIgKsdwAUfARSSiUTmExgUcYU8gGoVGpZFIGM0WG5mxM0zyMpni/zKPP0+nvleYNN4BZyxx -yIGedfQ5L8xygIA7RhjjUYoEXNFDH7fIT+ZtQMARpy/rJJFLIorNIgasC4Ao0sW+EJn+i2xi -q+QiFQYxIAepMEzFbUhWnEKK4iayxe6XOOu8ARtNbaMQ63x5AAAAAElFTkSuQmCC' -] - -{ #category : #'accessing - icons' } -FontAwesomeIcons >> tagsIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self tagsIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -FontAwesomeIcons >> tagsIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/tags.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAYAAACgu+4kAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAXRJREFUKJGN0j1Mk1EYxfH/edRFSKijH4kTJBgTJ0cGTdxE -TKyUGAfFSQcKUl3taIjIh3EUTJzaAoORwUGdmUlMXB1ktSaQGNp7HOyrgG8jZ733+d1zc69G -y9NvsC6RRbZhIxQLtkdtX5DjSW1x5hM5CdBtxGnjLUQf6IzQDSfXWu30EvTF4fdjE49GugCA -Wa8vPLuIufO3CSePRnxspzQr+3UKGnlIdGoPlMqV84ihfasdpJX8XLCch0RnZz/SJlD+p2OG -tNNcHhLA57y7dUOwl/YiKk48vCLFuuDYfyGz1Urp8pHQJNK4SUXZZqz8+GqS1w6DGL/j29fr -OnX2O6b1+9XhsIixbyX0M8Sa8Ys/AEBpqjJsa7ULYtt3JTexGoZX5070PtgHANycnL4mYuUA -YsO4TTNwHWlpsNBzv1qtpjh4TGN+9q1JRcPuntr3Av8IXAeWs2HIfn5OSlOVYayntmeON3tr -O4XtD4KNwUJPJRsG+AVxqr+5c/rf8wAAAABJRU5ErkJggg==' -] +" +Please comment me using the following template inspired by Class Responsibility Collaborator (CRC) design: + +I'm FontAwesomeIcons, a custom selection of some icons in this font family used for the GUI of grafoscopio. + +For the Responsibility part: Three sentences about my main responsibility, what I'm doing, what services do I offer. + +For the Collaborators Part: State my main collaborators and one line about how I interact with them. + +Public API and Key Messages + +- message one +- message two +- what is the way to create instances is a plus. + + One simple example is simply gorgeous. + +Internal Representation and Key Implementation Points. + + Instance Variables + icons: + + + Implementation Points +" +Class { + #name : #FontAwesomeIcons, + #superclass : #Object, + #instVars : [ + 'icons' + ], + #classVars : [ + 'Current' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #'accessing - icons' } +FontAwesomeIcons >> copyIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self copyIconContents readStream) +] + +{ #category : #'private - contents' } +FontAwesomeIcons >> copyIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/copy.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA2UlEQVR4nJXQIU6DQRAF4K+F +4DAYVFHlBEgctQgScL0DCbp3wKNJOAIXQDcEC5iqQoNsA4KkiO6GJX92/p+XvGxm983bN8Mv ++rjFOnFYMNchbvCNSWooUTXopfMalxhjipfiLRucYg9bxf0zXCXBJP1yUkmwxhyviTN8wVsh +yHwsms/wiSUuivu8m3BBR1hglH5tGGxXGmGAO5zjIdBVE9xrjnb8nwQjHBb1ymaJfxAZ7NjM +HSIbHLQJIzxpzpoZYdim6WTQ7xayjmiJ7x1SLHrB4y72Www+fgBc9T/67LlhnQAAAABJRU5E +rkJggg==' +] + +{ #category : #'accessing - icons' } +FontAwesomeIcons >> cutIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self cutIconContents readStream) +] + +{ #category : #'private - contents' } +FontAwesomeIcons >> cutIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/cut.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABOUlEQVR4nNXSzyvlYRTH8Rcu +N5dmIt2FX4VpJktRUpKssB//wGSDUsrCahYzuykaGyk/YiFlb2VhYWVhYaUkKxsbTSYSYfE9 +V3e+EQub+dRTz4/zvM85n+fhHXSNhxhX+IHSV+50IVNYbKAKn7EeoO8vXOyNmHFsolFMirWD +89TeFyxhEuXoxjI+ZPAVFZjHX+SRQz3uMINbTOMCoxEzinsYwlGRDw/4iX2s4VNUkcUvDD/X +Wwna0Y8WLGI7snSgGatx9o8KTn4MQB6VqA1ITuL4IP5E76dpyBQuUy0shGmHElPLI3YSs+HZ +k+4kb98QlewGMIsyfMMKOiO+B1toKgDmimC5yHqSqjIb2efRhrqAtGbQhxHJs41JXB9JAW7w +G9WYkHygY5zBQfR9jz0MpE16i2ok7v+HegQfqT9MAMiDKwAAAABJRU5ErkJggg==' +] + +{ #category : #'accessing - icons' } +FontAwesomeIcons >> html5Icon [ + "Private - Generated method" + ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self html5IconContents readStream) +] + +{ #category : #'private - contents' } +FontAwesomeIcons >> html5IconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/html5.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA+UlEQVR4nK3Tv0oDQRDH8c9J +BMFKJGIXBBtFwc7SUlDfwpewEewsbQQL8QHsA2lSiK2NCFr4Av4hjYigVSxuV/bWjRhxYJhl +9uY7v9m9rdR2ikm/tx7OoRUSO5gfA3AfFxMhPo9RDE85ICZ2Uf3gNzkgjhAVzIV4hJlC58Vc +cQ5oh7iJpb+MEBUsF+Snh/wNkI9QsqjuFe+jABu4xgDDxFcS+Ff30ggDrOEs6/6YKGgA8kOc +DfEQV+jgMoDb2bcNm0rkHqv/zOmwt4o93IX9kxIAXjTn/sBDlhviYBRgH7eFguhv6GI9LaoK +oA62sYUF9NWv70Jyff9mnwBsQJo0owsMAAAAAElFTkSuQmCC' +] + +{ #category : #'accessing - icons' } +FontAwesomeIcons >> pasteIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self pasteIconContents readStream) +] + +{ #category : #'private - contents' } +FontAwesomeIcons >> pasteIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/paste.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAqUlEQVR4nK3TTQrBQRzG8c/I +yk6SsrAilk5hYWfjWA6hXICycwtLByDJ2upvgZrG6+BbU7+3nnnmVxNQuDDC2j09LK5xB5u4 +WY7iUpLH9ZcUGaedrZ4wznWQzh0wQfjWwRB9TF33lStwwABHzFG5WfvkCfsH/Vl4MBgToriC +ZpS3sMxxkNJGkbuDO/4isPtFoIwu6km9hdUvwg2cvP8bu/BMAVXU3ly0PQOGrUCXHk1emQAA +AABJRU5ErkJggg==' +] + +{ #category : #'accessing - icons' } +FontAwesomeIcons >> tagIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: self tagIconContents readStream) +] + +{ #category : #'private - contents' } +FontAwesomeIcons >> tagIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/tag.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAmUlEQVR4nKXPMQ4BQRSA4U9D +oeIgKsdwAUfARSSiUTmExgUcYU8gGoVGpZFIGM0WG5mxM0zyMpni/zKPP0+nvleYNN4BZyxx +yIGedfQ5L8xygIA7RhjjUYoEXNFDH7fIT+ZtQMARpy/rJJFLIorNIgasC4Ao0sW+EJn+i2xi +q+QiFQYxIAepMEzFbUhWnEKK4iayxe6XOOu8ARtNbaMQ63x5AAAAAElFTkSuQmCC' +] + +{ #category : #'accessing - icons' } +FontAwesomeIcons >> tagsIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self tagsIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +FontAwesomeIcons >> tagsIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/FontAwesome/Png/tags.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAYAAACgu+4kAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAXRJREFUKJGN0j1Mk1EYxfH/edRFSKijH4kTJBgTJ0cGTdxE +TKyUGAfFSQcKUl3taIjIh3EUTJzaAoORwUGdmUlMXB1ktSaQGNp7HOyrgG8jZ733+d1zc69G +y9NvsC6RRbZhIxQLtkdtX5DjSW1x5hM5CdBtxGnjLUQf6IzQDSfXWu30EvTF4fdjE49GugCA +Wa8vPLuIufO3CSePRnxspzQr+3UKGnlIdGoPlMqV84ihfasdpJX8XLCch0RnZz/SJlD+p2OG +tNNcHhLA57y7dUOwl/YiKk48vCLFuuDYfyGz1Urp8pHQJNK4SUXZZqz8+GqS1w6DGL/j29fr +OnX2O6b1+9XhsIixbyX0M8Sa8Ys/AEBpqjJsa7ULYtt3JTexGoZX5070PtgHANycnL4mYuUA +YsO4TTNwHWlpsNBzv1qtpjh4TGN+9q1JRcPuntr3Av8IXAeWs2HIfn5OSlOVYayntmeON3tr +O4XtD4KNwUJPJRsG+AVxqr+5c/rf8wAAAABJRU5ErkJggg==' +] diff --git a/src/Grafoscopio/FormCanvas.extension.st b/src/Grafoscopio/FormCanvas.extension.st index 03e9f19..9408c41 100644 --- a/src/Grafoscopio/FormCanvas.extension.st +++ b/src/Grafoscopio/FormCanvas.extension.st @@ -1,13 +1,13 @@ -Extension { #name : #FormCanvas } - -{ #category : #'*Grafoscopio' } -FormCanvas >> gfcParagraph: para bounds: bounds color: c [ - - | scanner | - self setPaintColor: c. - scanner := (port clippedBy: (bounds translateBy: origin)) gfcDisplayScannerFor: para - foreground: c background: Color transparent - ignoreColorChanges: false. - para drawOn: (self copyClipRect: bounds) using: scanner at: origin + bounds topLeft. - -] +Extension { #name : #FormCanvas } + +{ #category : #'*Grafoscopio' } +FormCanvas >> gfcParagraph: para bounds: bounds color: c [ + + | scanner | + self setPaintColor: c. + scanner := (port clippedBy: (bounds translateBy: origin)) gfcDisplayScannerFor: para + foreground: c background: Color transparent + ignoreColorChanges: false. + para drawOn: (self copyClipRect: bounds) using: scanner at: origin + bounds topLeft. + +] diff --git a/src/Grafoscopio/GfUIHelpers.class.st b/src/Grafoscopio/GfUIHelpers.class.st index fdabe45..6a35b68 100644 --- a/src/Grafoscopio/GfUIHelpers.class.st +++ b/src/Grafoscopio/GfUIHelpers.class.st @@ -1,351 +1,351 @@ -" -I'm the Graphical User Interface for Grafoscopio environment. - -My main responsability is to provide grafoscopio with a docking bar -with menu options for open, browse notebooks, update the system -and its components and the help menu. - -" -Class { - #name : #GfUIHelpers, - #superclass : #DynamicComposablePresenter, - #instVars : [ - 'windowMainMenu', - 'tree', - 'nodeHeader', - 'nodeBody' - ], - #classVars : [ - 'dockingBar', - 'recentNotebooks' - ], - #classInstVars : [ - 'dockingBar', - 'recentNotebooks', - 'helpMenu' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #adding } -GfUIHelpers class >> addToHelpMenu: aGrafoscopioNotebook [ - | metadata nbFile | - metadata := aGrafoscopioNotebook metadata. - nbFile := aGrafoscopioNotebook workingFile. - (metadata at: 'showOnHelp' ifAbsent: [ ^ self ]) - ifTrue: [ - self helpMenu submorphs - detect: [ :s | s contents = (metadata at: 'shortTitle') ] - ifFound: [ - self inform: - 'The notebook: ', String cr, - nbFile fullName, String cr, - ' needs another shortTitle to be added to the docking bar Help Menu.' ] - ifNone: [ - self helpMenu - add: (metadata at: 'shortTitle') - target: [ GrafoscopioNotebook open: nbFile ] - selector: #value ] ]. - self updateUI -] - -{ #category : #updating } -GfUIHelpers class >> installDataviz [ - "I install the Dataviz package. - DataViz contains Data visualization helpers" - "self isDatavizInstalled ifTrue: [ ^ self ]." - Metacello new - smalltalkhubUser: 'Offray' project: 'Dataviz'; - configuration: 'Dataviz'; - load. - self inform: 'Dataviz package install finished.' -] - -{ #category : #updating } -GfUIHelpers class >> installDatavizUI [ - "I'm the User Interface for installing the Dataviz package for the first time." - | install | - install := (UIManager default - confirm: - 'Dataviz is data visualization package, with several', String cr, - 'Domain Specific Examples, like Panama Papers, Twitter Data Selfies.', - String cr, 'Do you want to install it?' - label: 'Install Dataviz package'). - install ifFalse: [ ^ self ]. - self installDataviz -] - -{ #category : #updating } -GfUIHelpers class >> isDatavizInstalled [ - ^ (Metacello image - configuration: #('Dataviz'); - list) isNotEmpty -] - -{ #category : #'graphical interface' } -GfUIHelpers class >> messageAbout [ - "I show the author(s), license, sponsors and main contributors to the project - and point to further documentation on the web" - LongMessageDialogWindow new - entryText: - '_.:| Grafoscopio |:._', - (String with: Character cr), - '(c) Copyright 2014-2017 by Offray Vladimir Luna Cárdenas', - (String with: Character cr), - 'Covered under MIT license.', - (String with: Character cr), - (String with: Character cr), - '[ Sponsors ]', - (String with: Character cr), - 'mutabiT | www.mutabit.com ', - (String with: Character cr), - 'HiTec Lab, Fundación Universitaria Los Libertadores | www.ulibertadores.edu.co ', - (String with: Character cr), - (String with: Character cr), - '[ Thanks to ]', - (String with: Character cr), - 'HackBo, Hackerspace Bogota | http://hackbo.co', - (String with: Character cr), - '// Regular workshops attendees \\ - Rafael Medida, Iván Pulido, Camilo Hurtado', - (String with: Character cr), - '// Coffe talk (mostly about grafoscopio) \\ - Yanneth Gil, Andrés Calderón, Luis Alejandro Bernal', - (String with: Character cr), - '// Pharo, Moose and Agile Visualization communities \\ - Tudor Girba, Alexandre Bergel, Nicolai Hess, Peter Uhnák, Milton Mamani ', - (String with: Character cr), - '// Family support while writing, coding & travelling (among others!) \\ - Divian Luna, Hilda Cárdenas', - (String with: Character cr), - (String with: Character cr), - 'For further details and versions go to:', - (String with: Character cr), - (String with: Character cr), - 'http://mutabit.com/grafoscopio'; - title: 'About Grafoscopio'; - open. -] - -{ #category : #'graphical interface' } -GfUIHelpers class >> messageNoRecentDocuments [ - "Shows that a feature is not implemeted and point to further documentation on the web" - - UIManager default abort: - 'Recent documents list is emtpy.', - (String with: Character cr), - 'To fill it, open a document using the Grafoscopio menu option at the top bar:', - (String with: Character cr), - (String with: Character cr), - '"Launch > Notebook from file..."', - (String with: Character cr), - (String with: Character cr) - title: 'No recent documents'. -] - -{ #category : #'graphical interface' } -GfUIHelpers class >> messageNotImplementedYet [ - "Shows that a feature is not implemeted and point to further documentation on the web" - - UIManager default abort: - 'This functionality is not implemented yet!', - (String with: Character cr), - 'For more information about future releases, please visit: ', - (String with: Character cr), - (String with: Character cr), - 'http://mutabit.com/grafoscopio', - (String with: Character cr), - (String with: Character cr) - title: 'To be implemented'. -] - -{ #category : #'graphical interface' } -GfUIHelpers class >> openFromRecentlyUsed [ - "Opens a recent notebooks list" - | selection recentNotebooksReversed | - - self recentNotebooks - ifNotEmpty: [ - recentNotebooksReversed := recentNotebooks reversed. - selection := UIManager default - chooseFrom: recentNotebooksReversed title: 'Choose a notebook...'. - selection > 0 - ifTrue: [ GrafoscopioNotebook new openFromFile: (recentNotebooksReversed at: selection)] - ifFalse: [ self inform: 'No notebook selected!' ] - ] - ifEmpty: [self messageNoRecentDocuments] - - - -] - -{ #category : #accessing } -GfUIHelpers class >> recentNotebooks [ - ^ recentNotebooks ifNil: [recentNotebooks := OrderedCollection new ] -] - -{ #category : #updating } -GfUIHelpers class >> updateDataviz [ - "I update the Dataviz package with new versions of itself take from the source code - repository. - DataViz contains Data visualization helpers" - self isDatavizInstalled - ifFalse: [ self installDatavizUI ] - ifTrue: [ - Gofer it - smalltalkhubUser: 'Offray' project: 'Dataviz'; - configurationOf: 'Dataviz'; - load. - self inform: 'Dataviz package update finished.']. - -] - -{ #category : #updating } -GfUIHelpers class >> updateDatavizUI [ - "I'm the User Interface for updating the Dataviz package with new versions of itself - take from the source code repository" - | update | - update := (UIManager default - confirm: - 'Dataviz is data visualization package, with several', String cr, - 'Domain Specific Examples, like Panama Papers, Twitter Data Selfies.', - String cr, 'Do you want to update it?' - label: 'Update Dataviz package'). - update ifFalse: [ ^ self ]. - self updateDataviz -] - -{ #category : #updating } -GfUIHelpers class >> updateGrafoscopio [ - "Updates Grafoscopio with new versions of itself take from the source code repository and - reloads the User Interface" - Gofer new - smalltalkhubUser: 'Offray' project: 'Grafoscopio'; - package: 'Grafoscopio'; - load. - GrafoscopioUtils update. -] - -{ #category : #updating } -GfUIHelpers class >> updateGrafoscopioUI [ - "Updates Grafoscopio with new versions of itself take from the source code repository and - the User Interface" - | update | - - update := (UIManager default - confirm: 'Do you wish to update Grafoscopio?' - label: 'Grafoscopio update'). - update ifFalse: [ ^ self ]. - self updateGrafoscopio. - GrafoscopioUtils update. - self updateUI. - self inform: 'Grafoscopio update finished' -] - -{ #category : #updating } -GfUIHelpers class >> updatePrerrequisitesScript [ - "Updates the system prerequisites with new versions of itself take from the source code repository" - - "Visualization library (which also makes main menu loadable)" - Gofer it - smalltalkhubUser: 'ObjectProfile' project: 'Roassal2'; - configurationOf: 'Roassal2'; - loadStable. - - "Support for the STON format used in file persistance for grafoscopio notebooks" - Gofer new - smalltalkhubUser: 'SvenVanCaekenberghe' project: 'STON'; - configurationOf: 'Ston'; - loadBleedingEdge. - - "Moose and Roassal integration" - Gofer new - smalltalkhubUser: 'Moose' project: 'Glamour'; - package: 'Glamour-Tools'; - package: 'Glamour-Roassal2-Presentations'; - load. - - Gofer new - smalltalkhubUser: 'Moose' project: 'GToolkit'; - package: 'GT-InspectorExtensions-CoreRoassal'; - load. - - "Fast Table support" - Gofer it - smalltalkhubUser: 'estebanlm' project: 'FastTable'; - package: 'FastTable'; - load. - - "Integration with external tools" - Gofer new - smalltalkhubUser: 'Offray' project: 'Grafoscopio'; - package: 'Grafoscopio-ExternalTools'; - load. - - "HTML scrapping" - Gofer new - smalltalkhubUser: 'PharoExtras' project: 'Soup'; - configurationOf: 'Soup'; - loadStable. - - "SQLite support" - Gofer new - url: 'http://smalltalkhub.com/mc/PharoExtras/NBSQLite3/main'; - package: 'NBSQLite3'; - load. - - Gofer new - url: 'http://smalltalkhub.com/mc/PharoExtras/NBSQLite3/main'; - package: 'NBSQLite3-Examples'; - load. - - "Support for Operative System integration" - Gofer new - squeaksource: 'OSProcess'; - package: 'OSProcess'; - load. - - Gofer new - squeaksource: 'CommandShell'; - package: 'CommandShell-Piping'; - load. - - "Bibliographic support" - Gofer new - squeaksource: 'Citezen'; - package: 'ConfigurationOfCitezen'; - load. - -] - -{ #category : #updating } -GfUIHelpers class >> updateRecentNotebooksWith: aFileReference [ - (self recentNotebooks includes: aFileReference) - ifFalse: [self recentNotebooks add: aFileReference]. -] - -{ #category : #updating } -GfUIHelpers class >> updateSystem [ - | tmp1 | - tmp1 := UIManager default - question: 'Do you wish to update all Grafoscopio and its co-dependencies ?' - title: 'Grafoscopio update'. - tmp1 - ifNotNil: [ tmp1 - ifTrue: [ - self - updateGrafoscopio; - updateDataviz. - GrafoscopioDocumentation updateAll. - self inform: 'System update finished.' ]] -] - -{ #category : #updating } -GfUIHelpers class >> updateUI [ - "I update the User Interface (UI) with new versions of the docking bar or logos where available. - I'm helpful while testing new functionality that should be expossed to the user via the UI" - self start. - (World submorphs select: [ :each | each class = DockingBarMorph ]) - allButFirstDo: [ :bar | bar delete ]. - -] +" +I'm the Graphical User Interface for Grafoscopio environment. + +My main responsability is to provide grafoscopio with a docking bar +with menu options for open, browse notebooks, update the system +and its components and the help menu. + +" +Class { + #name : #GfUIHelpers, + #superclass : #DynamicComposablePresenter, + #instVars : [ + 'windowMainMenu', + 'tree', + 'nodeHeader', + 'nodeBody' + ], + #classVars : [ + 'dockingBar', + 'recentNotebooks' + ], + #classInstVars : [ + 'dockingBar', + 'recentNotebooks', + 'helpMenu' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #adding } +GfUIHelpers class >> addToHelpMenu: aGrafoscopioNotebook [ + | metadata nbFile | + metadata := aGrafoscopioNotebook metadata. + nbFile := aGrafoscopioNotebook workingFile. + (metadata at: 'showOnHelp' ifAbsent: [ ^ self ]) + ifTrue: [ + self helpMenu submorphs + detect: [ :s | s contents = (metadata at: 'shortTitle') ] + ifFound: [ + self inform: + 'The notebook: ', String cr, + nbFile fullName, String cr, + ' needs another shortTitle to be added to the docking bar Help Menu.' ] + ifNone: [ + self helpMenu + add: (metadata at: 'shortTitle') + target: [ GrafoscopioNotebook open: nbFile ] + selector: #value ] ]. + self updateUI +] + +{ #category : #updating } +GfUIHelpers class >> installDataviz [ + "I install the Dataviz package. + DataViz contains Data visualization helpers" + "self isDatavizInstalled ifTrue: [ ^ self ]." + Metacello new + smalltalkhubUser: 'Offray' project: 'Dataviz'; + configuration: 'Dataviz'; + load. + self inform: 'Dataviz package install finished.' +] + +{ #category : #updating } +GfUIHelpers class >> installDatavizUI [ + "I'm the User Interface for installing the Dataviz package for the first time." + | install | + install := (UIManager default + confirm: + 'Dataviz is data visualization package, with several', String cr, + 'Domain Specific Examples, like Panama Papers, Twitter Data Selfies.', + String cr, 'Do you want to install it?' + label: 'Install Dataviz package'). + install ifFalse: [ ^ self ]. + self installDataviz +] + +{ #category : #updating } +GfUIHelpers class >> isDatavizInstalled [ + ^ (Metacello image + configuration: #('Dataviz'); + list) isNotEmpty +] + +{ #category : #'graphical interface' } +GfUIHelpers class >> messageAbout [ + "I show the author(s), license, sponsors and main contributors to the project + and point to further documentation on the web" + LongMessageDialogWindow new + entryText: + '_.:| Grafoscopio |:._', + (String with: Character cr), + '(c) Copyright 2014-2017 by Offray Vladimir Luna Cárdenas', + (String with: Character cr), + 'Covered under MIT license.', + (String with: Character cr), + (String with: Character cr), + '[ Sponsors ]', + (String with: Character cr), + 'mutabiT | www.mutabit.com ', + (String with: Character cr), + 'HiTec Lab, Fundación Universitaria Los Libertadores | www.ulibertadores.edu.co ', + (String with: Character cr), + (String with: Character cr), + '[ Thanks to ]', + (String with: Character cr), + 'HackBo, Hackerspace Bogota | http://hackbo.co', + (String with: Character cr), + '// Regular workshops attendees \\ + Rafael Medida, Iván Pulido, Camilo Hurtado', + (String with: Character cr), + '// Coffe talk (mostly about grafoscopio) \\ + Yanneth Gil, Andrés Calderón, Luis Alejandro Bernal', + (String with: Character cr), + '// Pharo, Moose and Agile Visualization communities \\ + Tudor Girba, Alexandre Bergel, Nicolai Hess, Peter Uhnák, Milton Mamani ', + (String with: Character cr), + '// Family support while writing, coding & travelling (among others!) \\ + Divian Luna, Hilda Cárdenas', + (String with: Character cr), + (String with: Character cr), + 'For further details and versions go to:', + (String with: Character cr), + (String with: Character cr), + 'http://mutabit.com/grafoscopio'; + title: 'About Grafoscopio'; + open. +] + +{ #category : #'graphical interface' } +GfUIHelpers class >> messageNoRecentDocuments [ + "Shows that a feature is not implemeted and point to further documentation on the web" + + UIManager default abort: + 'Recent documents list is emtpy.', + (String with: Character cr), + 'To fill it, open a document using the Grafoscopio menu option at the top bar:', + (String with: Character cr), + (String with: Character cr), + '"Launch > Notebook from file..."', + (String with: Character cr), + (String with: Character cr) + title: 'No recent documents'. +] + +{ #category : #'graphical interface' } +GfUIHelpers class >> messageNotImplementedYet [ + "Shows that a feature is not implemeted and point to further documentation on the web" + + UIManager default abort: + 'This functionality is not implemented yet!', + (String with: Character cr), + 'For more information about future releases, please visit: ', + (String with: Character cr), + (String with: Character cr), + 'http://mutabit.com/grafoscopio', + (String with: Character cr), + (String with: Character cr) + title: 'To be implemented'. +] + +{ #category : #'graphical interface' } +GfUIHelpers class >> openFromRecentlyUsed [ + "Opens a recent notebooks list" + | selection recentNotebooksReversed | + + self recentNotebooks + ifNotEmpty: [ + recentNotebooksReversed := recentNotebooks reversed. + selection := UIManager default + chooseFrom: recentNotebooksReversed title: 'Choose a notebook...'. + selection > 0 + ifTrue: [ GrafoscopioNotebook new openFromFile: (recentNotebooksReversed at: selection)] + ifFalse: [ self inform: 'No notebook selected!' ] + ] + ifEmpty: [self messageNoRecentDocuments] + + + +] + +{ #category : #accessing } +GfUIHelpers class >> recentNotebooks [ + ^ recentNotebooks ifNil: [recentNotebooks := OrderedCollection new ] +] + +{ #category : #updating } +GfUIHelpers class >> updateDataviz [ + "I update the Dataviz package with new versions of itself take from the source code + repository. + DataViz contains Data visualization helpers" + self isDatavizInstalled + ifFalse: [ self installDatavizUI ] + ifTrue: [ + Gofer it + smalltalkhubUser: 'Offray' project: 'Dataviz'; + configurationOf: 'Dataviz'; + load. + self inform: 'Dataviz package update finished.']. + +] + +{ #category : #updating } +GfUIHelpers class >> updateDatavizUI [ + "I'm the User Interface for updating the Dataviz package with new versions of itself + take from the source code repository" + | update | + update := (UIManager default + confirm: + 'Dataviz is data visualization package, with several', String cr, + 'Domain Specific Examples, like Panama Papers, Twitter Data Selfies.', + String cr, 'Do you want to update it?' + label: 'Update Dataviz package'). + update ifFalse: [ ^ self ]. + self updateDataviz +] + +{ #category : #updating } +GfUIHelpers class >> updateGrafoscopio [ + "Updates Grafoscopio with new versions of itself take from the source code repository and + reloads the User Interface" + Gofer new + smalltalkhubUser: 'Offray' project: 'Grafoscopio'; + package: 'Grafoscopio'; + load. + GrafoscopioUtils update. +] + +{ #category : #updating } +GfUIHelpers class >> updateGrafoscopioUI [ + "Updates Grafoscopio with new versions of itself take from the source code repository and + the User Interface" + | update | + + update := (UIManager default + confirm: 'Do you wish to update Grafoscopio?' + label: 'Grafoscopio update'). + update ifFalse: [ ^ self ]. + self updateGrafoscopio. + GrafoscopioUtils update. + self updateUI. + self inform: 'Grafoscopio update finished' +] + +{ #category : #updating } +GfUIHelpers class >> updatePrerrequisitesScript [ + "Updates the system prerequisites with new versions of itself take from the source code repository" + + "Visualization library (which also makes main menu loadable)" + Gofer it + smalltalkhubUser: 'ObjectProfile' project: 'Roassal2'; + configurationOf: 'Roassal2'; + loadStable. + + "Support for the STON format used in file persistance for grafoscopio notebooks" + Gofer new + smalltalkhubUser: 'SvenVanCaekenberghe' project: 'STON'; + configurationOf: 'Ston'; + loadBleedingEdge. + + "Moose and Roassal integration" + Gofer new + smalltalkhubUser: 'Moose' project: 'Glamour'; + package: 'Glamour-Tools'; + package: 'Glamour-Roassal2-Presentations'; + load. + + Gofer new + smalltalkhubUser: 'Moose' project: 'GToolkit'; + package: 'GT-InspectorExtensions-CoreRoassal'; + load. + + "Fast Table support" + Gofer it + smalltalkhubUser: 'estebanlm' project: 'FastTable'; + package: 'FastTable'; + load. + + "Integration with external tools" + Gofer new + smalltalkhubUser: 'Offray' project: 'Grafoscopio'; + package: 'Grafoscopio-ExternalTools'; + load. + + "HTML scrapping" + Gofer new + smalltalkhubUser: 'PharoExtras' project: 'Soup'; + configurationOf: 'Soup'; + loadStable. + + "SQLite support" + Gofer new + url: 'http://smalltalkhub.com/mc/PharoExtras/NBSQLite3/main'; + package: 'NBSQLite3'; + load. + + Gofer new + url: 'http://smalltalkhub.com/mc/PharoExtras/NBSQLite3/main'; + package: 'NBSQLite3-Examples'; + load. + + "Support for Operative System integration" + Gofer new + squeaksource: 'OSProcess'; + package: 'OSProcess'; + load. + + Gofer new + squeaksource: 'CommandShell'; + package: 'CommandShell-Piping'; + load. + + "Bibliographic support" + Gofer new + squeaksource: 'Citezen'; + package: 'ConfigurationOfCitezen'; + load. + +] + +{ #category : #updating } +GfUIHelpers class >> updateRecentNotebooksWith: aFileReference [ + (self recentNotebooks includes: aFileReference) + ifFalse: [self recentNotebooks add: aFileReference]. +] + +{ #category : #updating } +GfUIHelpers class >> updateSystem [ + | tmp1 | + tmp1 := UIManager default + question: 'Do you wish to update all Grafoscopio and its co-dependencies ?' + title: 'Grafoscopio update'. + tmp1 + ifNotNil: [ tmp1 + ifTrue: [ + self + updateGrafoscopio; + updateDataviz. + GrafoscopioDocumentation updateAll. + self inform: 'System update finished.' ]] +] + +{ #category : #updating } +GfUIHelpers class >> updateUI [ + "I update the User Interface (UI) with new versions of the docking bar or logos where available. + I'm helpful while testing new functionality that should be expossed to the user via the UI" + self start. + (World submorphs select: [ :each | each class = DockingBarMorph ]) + allButFirstDo: [ :bar | bar delete ]. + +] diff --git a/src/Grafoscopio/GfWorldMenu.class.st b/src/Grafoscopio/GfWorldMenu.class.st index 28ee670..b0e4ded 100644 --- a/src/Grafoscopio/GfWorldMenu.class.st +++ b/src/Grafoscopio/GfWorldMenu.class.st @@ -1,165 +1,182 @@ -" -I define the Grafoscopio main menu presented in the Pharo World. -" -Class { - #name : #GfWorldMenu, - #superclass : #Object, - #classInstVars : [ - 'recentNotebooks' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #'world menu' } -GfWorldMenu class >> helpMenuOn: aBuilder [ - - - (aBuilder item: #GfManual) - label: 'Manual'; - order: 1; - parent: #GfHelpAndDocs; - action: [ GrafoscopioNotebook open: GrafoscopioDocs manual ]. - (aBuilder item: #GfManualPDF) - label: 'Manual (PDF)'; - order: 2; - parent: #GfHelpAndDocs; - action: [ GrafoscopioDocs openPDFManual ]. - (aBuilder item: #GfHelpDataviz) - label: 'Dataviz'; - order: 3; - parent: #GfHelpAndDocs; - action: [ GrafoscopioNotebook open: DatavizDocs introNotebook ]. - (aBuilder item: #GfHelpDevNotes) - label: 'Devs''s notes'; - order: 4; - parent: #GfHelpAndDocs; - action: [ GrafoscopioNotebook open: GrafoscopioDocs devNotes ]. - (aBuilder item: #GfHelpAbout) - label: 'About Grafoscopio'; - order: 5; - parent: #GfHelpAndDocs; - action: [ GfUIHelpers messageAbout ]. -] - -{ #category : #'world menu' } -GfWorldMenu class >> launchCompatibilityMenuOn: aBuilder [ - - - (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' } -GfWorldMenu class >> launchMenuOn: aBuilder [ - - - (aBuilder item: #'New notebook') - label: 'New notebook'; - order: 1; - parent: #GfLaunch; - action: [ GrafoscopioNewNotebook new openDefault ]. - (aBuilder item: #'Notebook from file...') - label: 'Notebook from file...'; - order: 2; - parent: #GfLaunch; - action: [ GrafoscopioNewNotebook new openFromFileSelector ]. - (aBuilder item: #GfLaunchOpenRecent) - label: 'Open recent...'; - order: 2; - parent: #GfLaunch. - (aBuilder item: #'Notebook from the Internet...') - label: 'Notebook from the Internet...'; - order: 3; - parent: #GfLaunch; - action: [ GrafoscopioNewNotebook new openFromUrlUI ]. - (aBuilder item: #recentNotebooks) - label: 'Recent notebooks...'; - order: 4; - parent: #GfLaunch; - action: [ GfUIHelpers openFromRecentlyUsed ] -] - -{ #category : #'world menu' } -GfWorldMenu class >> mainMenuItemsOn: aBuilder [ - "I add the main Grafoscopio menu to the Pharo World." - - - (aBuilder item: #Grafoscopio) - label: 'Grafoscopio'; - order: 1; - with: [ (aBuilder - item: #GfLaunch; - label: 'Launch') 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' } -GfWorldMenu class >> openRecentMenu: aBuilder [ - - - GrafoscopioNotebook recents - do: [ :f | - (aBuilder item: #'Open', f basename ) - label: 'Open ', f basename; - order: 1; - parent: #GfLaunchOpenRecent; - action: [ GrafoscopioNotebook open: f ] ] -] - -{ #category : #'world menu' } -GfWorldMenu class >> updateMenuOn: aBuilder [ - - - (aBuilder item: #GfUpdateGrafoscopio) - label: 'Grafoscopio'; - order: 1; - parent: #GfUpdate; - action: [ GfUIHelpers updateGrafoscopioUI ]. - (aBuilder item: #GfUpdateDocs) - label: 'Documentation'; - order: 2; - parent: #GfUpdate; - action: [ GrafoscopioDocumentation updateAllUI ]. - (aBuilder item: #GfUpdateDataviz) - label: 'DataViz package'; - order: 3; - parent: #GfUpdate; - action: [ GfUIHelpers updateDatavizUI ]. - (aBuilder item: #GfUpdateAll) - label: 'All the system'; - order: 4; - parent: #GfUpdate; - action: [ GfUIHelpers updateSystem ]. -] - -{ #category : #updating } -GfWorldMenu class >> updateRecentNotebooksWith: aFileReference [ - (self recentNotebooks includes: aFileReference) - ifFalse: [self recentNotebooks add: aFileReference]. -] +" +I define the Grafoscopio main menu presented in the Pharo World. +" +Class { + #name : #GfWorldMenu, + #superclass : #Object, + #classInstVars : [ + 'recentNotebooks' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #'world menu' } +GfWorldMenu class >> getProjectExample [ + | project unit | + project := GrafoscopioProject new. + unit := GrafoscopioUnitNode new. + unit name: 'Pillar-Example'. + unit + addChild: [ GrafoscopioPillarNode new + text: GrafoscopioPillarASText openExample; + yourself ] + ofClass: GrafoscopioPillarNode. + project document addChild: unit ofClass: GrafoscopioUnitNode. + ^ project +] + +{ #category : #'world menu' } +GfWorldMenu class >> getProjectFromFile [ + | file | + file := UIManager default + chooseExistingFileReference: 'Choose a file' + extensions: #('ston') + path: FileLocator documents. + ^ (STON fromStream: file readStream) +] + +{ #category : #'world menu' } +GfWorldMenu class >> helpMenuOn: aBuilder [ + + + (aBuilder item: #GfManual) + label: 'Manual'; + order: 1; + parent: #GfHelpAndDocs; + action: [ GrafoscopioNotebook open: GrafoscopioDocs manual ]. + (aBuilder item: #GfManualPDF) + label: 'Manual (PDF)'; + order: 2; + parent: #GfHelpAndDocs; + action: [ GrafoscopioDocs openPDFManual ]. + (aBuilder item: #GfHelpDataviz) + label: 'Dataviz'; + order: 3; + parent: #GfHelpAndDocs; + action: [ GrafoscopioNotebook open: DatavizDocs introNotebook ]. + (aBuilder item: #GfHelpDevNotes) + label: 'Devs''s notes'; + order: 4; + parent: #GfHelpAndDocs; + action: [ GrafoscopioNotebook open: GrafoscopioDocs devNotes ]. + (aBuilder item: #GfHelpAbout) + label: 'About Grafoscopio'; + order: 5; + parent: #GfHelpAndDocs; + action: [ GfUIHelpers messageAbout ]. +] + +{ #category : #'world menu' } +GfWorldMenu class >> launchCompatibilityMenuOn: aBuilder [ + + + (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' } +GfWorldMenu class >> launchMenuOn: aBuilder [ + + + (aBuilder item: #'New notebook') + label: 'New notebook'; + order: 1; + parent: #GfLaunch; + action: [ (GrafoscopioTreeNotebook on: GrafoscopioProject new ) openWithSpec ]. + (aBuilder item: #'New Notebook from example ') + label: 'Notebook from from example '; + order: 2; + parent: #GfLaunch; + action: [ (GrafoscopioTreeNotebook on: (self getProjectExample)) openWithSpec ]. + (aBuilder item: #'Notebook from file...') + label: 'Notebook from file...'; + order: 2; + parent: #GfLaunch; + action: [ (GrafoscopioTreeNotebook on: (self getProjectFromFile)) openWithSpec ]. + (aBuilder item: #GfLaunchOpenRecent) + label: 'Open recent...'; + order: 2; + parent: #GfLaunch. + (aBuilder item: #'Notebook from the Internet...') + label: 'Notebook from the Internet...'; + order: 3; + parent: #GfLaunch; + action: [ GrafoscopioTreeNotebook new openFromUrlUI ]. + (aBuilder item: #recentNotebooks) + label: 'Recent notebooks...'; + order: 4; + parent: #GfLaunch; + action: [ GfUIHelpers openFromRecentlyUsed ] +] + +{ #category : #'world menu' } +GfWorldMenu class >> mainMenuItemsOn: aBuilder [ + "I add the main Grafoscopio menu to the Pharo World." + + + (aBuilder item: #Grafoscopio) + label: 'Grafoscopio'; + order: 1; + with: [ (aBuilder + item: #GfLaunch; + label: 'Launch') 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' } +GfWorldMenu class >> updateMenuOn: aBuilder [ + + + (aBuilder item: #GfUpdateGrafoscopio) + label: 'Grafoscopio'; + order: 1; + parent: #GfUpdate; + action: [ GfUIHelpers updateGrafoscopioUI ]. + (aBuilder item: #GfUpdateDocs) + label: 'Documentation'; + order: 2; + parent: #GfUpdate; + action: [ GrafoscopioDocumentation updateAllUI ]. + (aBuilder item: #GfUpdateDataviz) + label: 'DataViz package'; + order: 3; + parent: #GfUpdate; + action: [ GfUIHelpers updateDatavizUI ]. + (aBuilder item: #GfUpdateAll) + label: 'All the system'; + order: 4; + parent: #GfUpdate; + action: [ GfUIHelpers updateSystem ]. +] + +{ #category : #updating } +GfWorldMenu class >> updateRecentNotebooksWith: aFileReference [ + (self recentNotebooks includes: aFileReference) + ifFalse: [self recentNotebooks add: aFileReference]. +] diff --git a/src/Grafoscopio/GlamourPresentationModel.class.st b/src/Grafoscopio/GlamourPresentationModel.class.st index 1ed0d3f..bf53243 100644 --- a/src/Grafoscopio/GlamourPresentationModel.class.st +++ b/src/Grafoscopio/GlamourPresentationModel.class.st @@ -1,79 +1,79 @@ -" -I am a Spec ComposableModel for all Glamour presentations (GLMPresentation subclasses.) By default I open a Playground because its creation contract is special. - -Example uses : - -""open a playground"" -GlamourPresentationModel new openWithSpec. - -""open a playground on 42"" -|ui| -ui := GlamourPresentationModel new. -ui presentationClass: GTPlayground startOn: (GTPlayPage new saveContent: '42'). -ui openWithSpec - -""open an inspector on 42"" -|ui| -ui := GlamourPresentationModel new. -ui presentationClass: GTInspector startOn: 42. -ui openWithSpec -" -Class { - #name : #GlamourPresentationModel, - #superclass : #SpPresenter, - #instVars : [ - 'presentation', - 'glmPres' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #specs } -GlamourPresentationModel class >> defaultSpec [ - - ^ SpBoxLayout newHorizontal - add: #presentation; - yourself -] - -{ #category : #deprecation } -GlamourPresentationModel class >> isDeprecated [ - ^ true -] - -{ #category : #accessing } -GlamourPresentationModel >> glmPres [ - glmPres isNil ifTrue:[glmPres := GTPlayground new startOn: GTPlayPage new]. - ^glmPres -] - -{ #category : #initialization } -GlamourPresentationModel >> initializeWidgets [ - "Should actually do nothing at all. - This is because we cannot create the morph and later set the presentation. - So we will do all of this in the presentation: accessor. - " - self presentation. -] - -{ #category : #accessing } -GlamourPresentationModel >> presentation [ - presentation isNil ifTrue:[ - | morph | - morph := GLMMorphicRenderer new render: self glmPres. - morph hResizing: #spaceFill. - morph vResizing: #spaceFill. - presentation := MorphicGenericAdapter morph: morph]. - - ^ presentation -] - -{ #category : #accessing } -GlamourPresentationModel >> presentation: anObject [ - presentation := anObject -] - -{ #category : #api } -GlamourPresentationModel >> presentationClass: aGLMCompositePresentation startOn: anObject [ - glmPres := aGLMCompositePresentation new startOn: anObject -] +" +I am a Spec ComposableModel for all Glamour presentations (GLMPresentation subclasses.) By default I open a Playground because its creation contract is special. + +Example uses : + +""open a playground"" +GlamourPresentationModel new openWithSpec. + +""open a playground on 42"" +|ui| +ui := GlamourPresentationModel new. +ui presentationClass: GTPlayground startOn: (GTPlayPage new saveContent: '42'). +ui openWithSpec + +""open an inspector on 42"" +|ui| +ui := GlamourPresentationModel new. +ui presentationClass: GTInspector startOn: 42. +ui openWithSpec +" +Class { + #name : #GlamourPresentationModel, + #superclass : #SpPresenter, + #instVars : [ + 'presentation', + 'glmPres' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #specs } +GlamourPresentationModel class >> defaultSpec [ + + ^ SpBoxLayout newHorizontal + add: #presentation; + yourself +] + +{ #category : #deprecation } +GlamourPresentationModel class >> isDeprecated [ + ^ true +] + +{ #category : #accessing } +GlamourPresentationModel >> glmPres [ + glmPres isNil ifTrue:[glmPres := GTPlayground new startOn: GTPlayPage new]. + ^glmPres +] + +{ #category : #initialization } +GlamourPresentationModel >> initializeWidgets [ + "Should actually do nothing at all. + This is because we cannot create the morph and later set the presentation. + So we will do all of this in the presentation: accessor. + " + self presentation. +] + +{ #category : #accessing } +GlamourPresentationModel >> presentation [ + presentation isNil ifTrue:[ + | morph | + morph := GLMMorphicRenderer new render: self glmPres. + morph hResizing: #spaceFill. + morph vResizing: #spaceFill. + presentation := MorphicGenericAdapter morph: morph]. + + ^ presentation +] + +{ #category : #accessing } +GlamourPresentationModel >> presentation: anObject [ + presentation := anObject +] + +{ #category : #api } +GlamourPresentationModel >> presentationClass: aGLMCompositePresentation startOn: anObject [ + glmPres := aGLMCompositePresentation new startOn: anObject +] diff --git a/src/Grafoscopio/GrafPort.extension.st b/src/Grafoscopio/GrafPort.extension.st index 188740f..d8cbd2f 100644 --- a/src/Grafoscopio/GrafPort.extension.st +++ b/src/Grafoscopio/GrafPort.extension.st @@ -1,9 +1,9 @@ -Extension { #name : #GrafPort } - -{ #category : #'*Grafoscopio' } -GrafPort >> gfcDisplayScannerFor: para foreground: foreColor background: backColor ignoreColorChanges: shadowMode [ - ^ (GrafoscopioDisplayScanner new text: para text textStyle: para textStyle - foreground: foreColor background: backColor fillBlt: self - ignoreColorChanges: shadowMode) - setPort: self shallowCopy -] +Extension { #name : #GrafPort } + +{ #category : #'*Grafoscopio' } +GrafPort >> gfcDisplayScannerFor: para foreground: foreColor background: backColor ignoreColorChanges: shadowMode [ + ^ (GrafoscopioDisplayScanner new text: para text textStyle: para textStyle + foreground: foreColor background: backColor fillBlt: self + ignoreColorChanges: shadowMode) + setPort: self shallowCopy +] diff --git a/src/Grafoscopio/GrafoscopioAbstractNode.class.st b/src/Grafoscopio/GrafoscopioAbstractNode.class.st index f29fe8f..8df38c6 100644 --- a/src/Grafoscopio/GrafoscopioAbstractNode.class.st +++ b/src/Grafoscopio/GrafoscopioAbstractNode.class.st @@ -1,134 +1,134 @@ -" -Just an abstract node. -" -Class { - #name : #GrafoscopioAbstractNode, - #superclass : #Object, - #instVars : [ - 'created', - 'edited', - 'tags', - 'order', - 'name' - ], - #classInstVars : [ - 'clipboard' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #testing } -GrafoscopioAbstractNode class >> isAbstract [ - ^ self = GrafoscopioAbstractNode -] - -{ #category : #testing } -GrafoscopioAbstractNode class >> isLeaf [ - ^ false -] - -{ #category : #testing } -GrafoscopioAbstractNode class >> showInMenu [ - ^ false -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> addChild: aBlock ofClass: aClass [ - self subclassResponsibility -] - -{ #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 : #accessing } -GrafoscopioAbstractNode >> created [ - - ^ created -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> created: aTimestamp [ - "I tell when this object was created" - - created := aTimestamp -] - -{ #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 >> initialize [ - "I create a empty new node" - super initialize. - created := DateAndTime now. - edited := DateAndTime now -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> isLeaf [ - ^ self class isLeaf -] - -{ #category : #'as yet unclassified' } -GrafoscopioAbstractNode >> moveDown [ - self subclassResponsibility -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> name [ - ^ name -] - -{ #category : #accessing } -GrafoscopioAbstractNode >> name: aName [ - name := aName -] - -{ #category : #'as yet unclassified' } -GrafoscopioAbstractNode >> order [ - ^ order ifNil: [ 0 ] -] - -{ #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 : #'as yet unclassified' } -GrafoscopioAbstractNode >> updateStamp [ - self edited: DateAndTime now -] +" +Just an abstract node. +" +Class { + #name : #GrafoscopioAbstractNode, + #superclass : #Object, + #instVars : [ + 'created', + 'edited', + 'tags', + 'order', + 'name' + ], + #classInstVars : [ + 'clipboard' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #testing } +GrafoscopioAbstractNode class >> isAbstract [ + ^ self = GrafoscopioAbstractNode +] + +{ #category : #testing } +GrafoscopioAbstractNode class >> isLeaf [ + ^ false +] + +{ #category : #testing } +GrafoscopioAbstractNode class >> showInMenu [ + ^ false +] + +{ #category : #accessing } +GrafoscopioAbstractNode >> addChild: aBlock ofClass: aClass [ + self subclassResponsibility +] + +{ #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 : #accessing } +GrafoscopioAbstractNode >> created [ + + ^ created +] + +{ #category : #accessing } +GrafoscopioAbstractNode >> created: aTimestamp [ + "I tell when this object was created" + + created := aTimestamp +] + +{ #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 >> initialize [ + "I create a empty new node" + super initialize. + created := DateAndTime now. + edited := DateAndTime now +] + +{ #category : #accessing } +GrafoscopioAbstractNode >> isLeaf [ + ^ self class isLeaf +] + +{ #category : #'as yet unclassified' } +GrafoscopioAbstractNode >> moveDown [ + self subclassResponsibility +] + +{ #category : #accessing } +GrafoscopioAbstractNode >> name [ + ^ name +] + +{ #category : #accessing } +GrafoscopioAbstractNode >> name: aName [ + name := aName +] + +{ #category : #'as yet unclassified' } +GrafoscopioAbstractNode >> order [ + ^ order ifNil: [ 0 ] +] + +{ #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 : #'as yet unclassified' } +GrafoscopioAbstractNode >> updateStamp [ + self edited: DateAndTime now +] diff --git a/src/Grafoscopio/GrafoscopioAbstractText.class.st b/src/Grafoscopio/GrafoscopioAbstractText.class.st index 5158b86..96e1c11 100644 --- a/src/Grafoscopio/GrafoscopioAbstractText.class.st +++ b/src/Grafoscopio/GrafoscopioAbstractText.class.st @@ -1,128 +1,128 @@ -" -This abstract class works for defining the basic type required by Rubric -" -Class { - #name : #GrafoscopioAbstractText, - #superclass : #Object, - #category : #'Grafoscopio-Pillar' -} - -{ #category : #'as yet unclassified' } -GrafoscopioAbstractText >> allRangesOfSubstring: aString [ - ^ { } -] - -{ #category : #converting } -GrafoscopioAbstractText >> asText [ - ^ self -] - -{ #category : #accessing } -GrafoscopioAbstractText >> at: anIndex [ - ^ self subclassResponsibility -] - -{ #category : #accessing } -GrafoscopioAbstractText >> attributesAt: characterIndex [ - "Answer the code for characters in the run beginning at characterIndex." - "NB: no senders any more (supplanted by #attributesAt:forStyle: but retained for the moment in order not to break user code that may exist somewhere that still calls this" - | attributes | -" self size = 0 - ifTrue: [^ Array with: (TextFontChange new fontNumber: 1)]." - self size = 0 ifTrue: [ ^#()]. - attributes := self runs at: characterIndex. - ^ attributes -] - -{ #category : #accessing } -GrafoscopioAbstractText >> attributesAt: anInteger forStyle: aTextStyle [ - | attributes size | - size := self size. - (size = 0 or: [ size < anInteger ]) - ifTrue: [ ^ Array - with: (TextFontChange new fontNumber: aTextStyle defaultFontIndex) ]. "null text tolerates access" - attributes := self runs at: anInteger. - ^ attributes -] - -{ #category : #copying } -GrafoscopioAbstractText >> copy [ - ^ self -] - -{ #category : #copying } -GrafoscopioAbstractText >> copyFrom: start to: stop [ - ^ self subclassResponsibility -] - -{ #category : #testing } -GrafoscopioAbstractText >> ifNotEmpty: isNotEmptyBlock ifEmpty: isEmptyBlock [ - ^ self isEmpty - ifTrue: [ isEmptyBlock value ] - ifFalse: [ isNotEmptyBlock value ] -] - -{ #category : #testing } -GrafoscopioAbstractText >> isEmpty [ - ^ self size = 0 -] - -{ #category : #testing } -GrafoscopioAbstractText >> isText [ - ^ true -] - -{ #category : #accessing } -GrafoscopioAbstractText >> last [ - ^ self at: self size -] - -{ #category : #copying } -GrafoscopioAbstractText >> notEmpty [ - ^ self isEmpty not -] - -{ #category : #removing } -GrafoscopioAbstractText >> removeAttribute: anAttr [ - ^ self removeAttribute: anAttr from: 1 to: self size -] - -{ #category : #removing } -GrafoscopioAbstractText >> removeAttribute: att from: start to: stop [ - self subclassResponsibility -] - -{ #category : #editing } -GrafoscopioAbstractText >> replaceFrom: anInteger to: anInteger2 with: aCollection [ - self subclassResponsibility -] - -{ #category : #emphasis } -GrafoscopioAbstractText >> runLengthFor: characterIndex [ - ^ self runs runLengthFor: characterIndex -] - -{ #category : #accessing } -GrafoscopioAbstractText >> runs [ - self subclassResponsibility -] - -{ #category : #accessing } -GrafoscopioAbstractText >> runs: anArray [ - self subclassResponsibility "runs := RunArray new: self size withAll: anArray . " -] - -{ #category : #accessing } -GrafoscopioAbstractText >> size [ - ^ self string size -] - -{ #category : #accessing } -GrafoscopioAbstractText >> string [ - ^ self subclassResponsibility -] - -{ #category : #accessing } -GrafoscopioAbstractText >> text: aParam [ - self subclassResponsibility -] +" +This abstract class works for defining the basic type required by Rubric +" +Class { + #name : #GrafoscopioAbstractText, + #superclass : #Object, + #category : #'Grafoscopio-Pillar' +} + +{ #category : #'as yet unclassified' } +GrafoscopioAbstractText >> allRangesOfSubstring: aString [ + ^ { } +] + +{ #category : #converting } +GrafoscopioAbstractText >> asText [ + ^ self +] + +{ #category : #accessing } +GrafoscopioAbstractText >> at: anIndex [ + ^ self subclassResponsibility +] + +{ #category : #accessing } +GrafoscopioAbstractText >> attributesAt: characterIndex [ + "Answer the code for characters in the run beginning at characterIndex." + "NB: no senders any more (supplanted by #attributesAt:forStyle: but retained for the moment in order not to break user code that may exist somewhere that still calls this" + | attributes | +" self size = 0 + ifTrue: [^ Array with: (TextFontChange new fontNumber: 1)]." + self size = 0 ifTrue: [ ^#()]. + attributes := self runs at: characterIndex. + ^ attributes +] + +{ #category : #accessing } +GrafoscopioAbstractText >> attributesAt: anInteger forStyle: aTextStyle [ + | attributes size | + size := self size. + (size = 0 or: [ size < anInteger ]) + ifTrue: [ ^ Array + with: (TextFontChange new fontNumber: aTextStyle defaultFontIndex) ]. "null text tolerates access" + attributes := self runs at: anInteger. + ^ attributes +] + +{ #category : #copying } +GrafoscopioAbstractText >> copy [ + ^ self +] + +{ #category : #copying } +GrafoscopioAbstractText >> copyFrom: start to: stop [ + ^ self subclassResponsibility +] + +{ #category : #testing } +GrafoscopioAbstractText >> ifNotEmpty: isNotEmptyBlock ifEmpty: isEmptyBlock [ + ^ self isEmpty + ifTrue: [ isEmptyBlock value ] + ifFalse: [ isNotEmptyBlock value ] +] + +{ #category : #testing } +GrafoscopioAbstractText >> isEmpty [ + ^ self size = 0 +] + +{ #category : #testing } +GrafoscopioAbstractText >> isText [ + ^ true +] + +{ #category : #accessing } +GrafoscopioAbstractText >> last [ + ^ self at: self size +] + +{ #category : #copying } +GrafoscopioAbstractText >> notEmpty [ + ^ self isEmpty not +] + +{ #category : #removing } +GrafoscopioAbstractText >> removeAttribute: anAttr [ + ^ self removeAttribute: anAttr from: 1 to: self size +] + +{ #category : #removing } +GrafoscopioAbstractText >> removeAttribute: att from: start to: stop [ + self subclassResponsibility +] + +{ #category : #editing } +GrafoscopioAbstractText >> replaceFrom: anInteger to: anInteger2 with: aCollection [ + self subclassResponsibility +] + +{ #category : #emphasis } +GrafoscopioAbstractText >> runLengthFor: characterIndex [ + ^ self runs runLengthFor: characterIndex +] + +{ #category : #accessing } +GrafoscopioAbstractText >> runs [ + self subclassResponsibility +] + +{ #category : #accessing } +GrafoscopioAbstractText >> runs: anArray [ + self subclassResponsibility "runs := RunArray new: self size withAll: anArray . " +] + +{ #category : #accessing } +GrafoscopioAbstractText >> size [ + ^ self string size +] + +{ #category : #accessing } +GrafoscopioAbstractText >> string [ + ^ self subclassResponsibility +] + +{ #category : #accessing } +GrafoscopioAbstractText >> text: aParam [ + self subclassResponsibility +] diff --git a/src/Grafoscopio/GrafoscopioAttributeBranchVisitor.class.st b/src/Grafoscopio/GrafoscopioAttributeBranchVisitor.class.st index d2592ee..dd5df65 100644 --- a/src/Grafoscopio/GrafoscopioAttributeBranchVisitor.class.st +++ b/src/Grafoscopio/GrafoscopioAttributeBranchVisitor.class.st @@ -1,146 +1,160 @@ -Class { - #name : #GrafoscopioAttributeBranchVisitor, - #superclass : #Object, - #instVars : [ - 'attributes', - 'level', - 'listLevel', - 'index', - 'text', - 'styler' - ], - #category : #'Grafoscopio-Pillar-Style' -} - -{ #category : #accessing } -GrafoscopioAttributeBranchVisitor >> analyzeBranch: aBranch at: anIndex [ - index := anIndex. - ^ [ aBranch - inject: OrderedCollection new - into: [ :attrs :node | - node accept: self. - attrs addAll: (self attributesAt: node). - attrs ] ] - ensure: [ index := 0 ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> attributes [ - ^ attributes -] - -{ #category : #accessing } -GrafoscopioAttributeBranchVisitor >> attributesAt: aNode [ - ^ (attributes detect: [ :a | a first = aNode ]) second -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> attributesAt: aNode ifAbsentPut: aBlock [ - ^ attributes - detect: [ :a | a first = aNode ] - ifFound: [ :a | a second ] - ifNone: [ | val | - val := aBlock value. - attributes - add: - {aNode. - val}. - val ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> index: anIndex [ - index := anIndex -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> initialize [ - attributes := OrderedCollection new. - styler := GrafoscopioPillarStyler defaultStyler. -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> text: aGrafoscopioPillarASText [ - text := aGrafoscopioPillarASText -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitCodeblock: aPRCodeblock [ - self - attributesAt: aPRCodeblock - ifAbsentPut: [ styler attributesForCodeBlock: aPRCodeblock at: index ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitCommentedLine: aPRCommentedLine [ - self attributesAt: aPRCommentedLine ifAbsentPut: [ styler default ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitDocument: aPRDocument [ - self attributesAt: aPRDocument ifAbsentPut: [ styler attributesForDocument: aPRDocument ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitExternalLink: aPRExternalLink [ - self - attributesAt: aPRExternalLink - ifAbsentPut: [ styler attributesForExternalLink: aPRExternalLink ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitFigure: aPRFigure [ - self attributesAt: aPRFigure ifAbsentPut: [ styler attributesForFigure: aPRFigure ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitFormatText: aGrafoscopioFormatTextNode [ - self attributesAt: aGrafoscopioFormatTextNode - ifAbsentPut: [ styler attributesForFormatText: aGrafoscopioFormatTextNode ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitHeader: aPRHeader [ - self - attributesAt: aPRHeader - ifAbsentPut: [ styler attributesForHeader: aPRHeader ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitInternalLink: aPRInternalLink [ - self - attributesAt: aPRInternalLink - ifAbsentPut: [ styler attributesForInternalLink: aPRInternalLink ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitLineBreak: aPRLineBreak [ - self attributesAt: aPRLineBreak - ifAbsentPut: [ styler attributesForLineBreak: aPRLineBreak ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitListItem: aPRListItem [ - self - attributesAt: aPRListItem - ifAbsentPut: [ styler attributesForListItem: aPRListItem at: index ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitParagraph: aPRParagraph [ - self - attributesAt: aPRParagraph - ifAbsentPut: [ styler attributesForParagraph: aPRParagraph ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitText: aPRText [ - self attributesAt: aPRText - ifAbsentPut: [ styler attributesForText: aPRText ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioAttributeBranchVisitor >> visitUnorderedList: aPRUnorderedList [ - self attributesAt: aPRUnorderedList - ifAbsentPut: [ styler attributesForUnorderedList: aPRUnorderedList ] -] +Class { + #name : #GrafoscopioAttributeBranchVisitor, + #superclass : #Object, + #instVars : [ + 'attributes', + 'level', + 'listLevel', + 'index', + 'text', + 'styler' + ], + #category : #'Grafoscopio-Pillar-Style' +} + +{ #category : #accessing } +GrafoscopioAttributeBranchVisitor >> analyzeBranch: aBranch at: anIndex [ + index := anIndex. + ^ [ aBranch + inject: OrderedCollection new + into: [ :attrs :node | + node accept: self. + attrs addAll: (self attributesAt: node). + attrs ] ] + ensure: [ index := 0 ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> attributes [ + ^ attributes +] + +{ #category : #accessing } +GrafoscopioAttributeBranchVisitor >> attributesAt: aNode [ + ^ (attributes detect: [ :a | a first = aNode ]) second +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> attributesAt: aNode force: aBlock [ + ^ attributes + detect: [ :a | a first = aNode ] + ifFound: [ :a | a at:2 put: aBlock value ] + ifNone: [ | val | + val := aBlock value. + attributes + add: + {aNode. + val}. + val ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> attributesAt: aNode ifAbsentPut: aBlock [ + ^ attributes + detect: [ :a | a first = aNode ] + ifFound: [ :a | a second ] + ifNone: [ | val | + val := aBlock value. + attributes + add: + {aNode. + val}. + val ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> index: anIndex [ + index := anIndex +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> initialize [ + attributes := OrderedCollection new. + styler := GrafoscopioPillarStyler defaultStyler. +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> text: aGrafoscopioPillarASText [ + text := aGrafoscopioPillarASText +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitCodeblock: aPRCodeblock [ + self + attributesAt: aPRCodeblock + ifAbsentPut: [ styler attributesForCodeBlock: aPRCodeblock at: index ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitCommentedLine: aPRCommentedLine [ + self attributesAt: aPRCommentedLine ifAbsentPut: [ styler default ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitDocument: aPRDocument [ + self attributesAt: aPRDocument ifAbsentPut: [ styler attributesForDocument: aPRDocument ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitExternalLink: aPRExternalLink [ + self + attributesAt: aPRExternalLink + ifAbsentPut: [ styler attributesForExternalLink: aPRExternalLink ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitFigure: aPRFigure [ + self attributesAt: aPRFigure ifAbsentPut: [ styler attributesForFigure: aPRFigure ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitFormatText: aGrafoscopioFormatTextNode [ + self attributesAt: aGrafoscopioFormatTextNode + ifAbsentPut: [ styler attributesForFormatText: aGrafoscopioFormatTextNode ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitHeader: aPRHeader [ + self + attributesAt: aPRHeader + ifAbsentPut: [ styler attributesForHeader: aPRHeader ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitInternalLink: aPRInternalLink [ + self + attributesAt: aPRInternalLink + ifAbsentPut: [ styler attributesForInternalLink: aPRInternalLink ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitLineBreak: aPRLineBreak [ + self attributesAt: aPRLineBreak + ifAbsentPut: [ styler attributesForLineBreak: aPRLineBreak ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitListItem: aPRListItem [ + self + attributesAt: aPRListItem + force: [ styler attributesForListItem: aPRListItem at: index ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitParagraph: aPRParagraph [ + self + attributesAt: aPRParagraph + ifAbsentPut: [ styler attributesForParagraph: aPRParagraph ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitText: aPRText [ + self attributesAt: aPRText + ifAbsentPut: [ styler attributesForText: aPRText ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioAttributeBranchVisitor >> visitUnorderedList: aPRUnorderedList [ + self attributesAt: aPRUnorderedList + ifAbsentPut: [ styler attributesForUnorderedList: aPRUnorderedList ] +] diff --git a/src/Grafoscopio/GrafoscopioBranchNode.class.st b/src/Grafoscopio/GrafoscopioBranchNode.class.st index 14e9503..e57d533 100644 --- a/src/Grafoscopio/GrafoscopioBranchNode.class.st +++ b/src/Grafoscopio/GrafoscopioBranchNode.class.st @@ -1,87 +1,87 @@ -" -Branch node. this kind of node has no content -" -Class { - #name : #GrafoscopioBranchNode, - #superclass : #GrafoscopioLeafNode, - #instVars : [ - 'children' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #'instance creation' } -GrafoscopioBranchNode class >> isLeaf [ - ^ false -] - -{ #category : #accessing } -GrafoscopioBranchNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitBranchNode: self. - -] - -{ #category : #accessing } -GrafoscopioBranchNode >> acceptsChildsOfClass: aClass [ - ^ {GrafoscopioBranchNode. - GrafoscopioUnitNode} includes: aClass -] - -{ #category : #accessing } -GrafoscopioBranchNode >> addAtBeginningChild: aBlock ofClass: aClass [ - (self acceptsChildsOfClass: aClass) - ifTrue: [| child | - child := aBlock value. - child parent: self. - self children addFirst: child] -] - -{ #category : #accessing } -GrafoscopioBranchNode >> addChild: aBlock ofClass: aClass [ - (self acceptsChildsOfClass: aClass) - ifTrue: [ | child | - child := aBlock value. - child parent: self. - self children add: child ] -] - -{ #category : #accessing } -GrafoscopioBranchNode >> children [ - "Returns the receivers list of children" - - ^ children ifNil: [ children := OrderedCollection new ] -] - -{ #category : #accessing } -GrafoscopioBranchNode >> children: aCollection [ - "Sets the receivers children" - - aCollection do: [:currentNode | currentNode parent: self ]. - children := aCollection. -] - -{ #category : #accessing } -GrafoscopioBranchNode >> isLeaf [ - ^ false -] - -{ #category : #'as yet unclassified' } -GrafoscopioBranchNode >> moveDown: 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 min: children size) -] - -{ #category : #'as yet unclassified' } -GrafoscopioBranchNode >> 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 : #'as yet unclassified' } -GrafoscopioBranchNode >> remove: aGrafoscopioTextNode [ - children remove: aGrafoscopioTextNode -] +" +Branch node. this kind of node has no content +" +Class { + #name : #GrafoscopioBranchNode, + #superclass : #GrafoscopioLeafNode, + #instVars : [ + 'children' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #'instance creation' } +GrafoscopioBranchNode class >> isLeaf [ + ^ false +] + +{ #category : #accessing } +GrafoscopioBranchNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitBranchNode: self. + +] + +{ #category : #accessing } +GrafoscopioBranchNode >> acceptsChildsOfClass: aClass [ + ^ {GrafoscopioBranchNode. + GrafoscopioUnitNode} includes: aClass +] + +{ #category : #accessing } +GrafoscopioBranchNode >> addAtBeginningChild: aBlock ofClass: aClass [ + (self acceptsChildsOfClass: aClass) + ifTrue: [| child | + child := aBlock value. + child parent: self. + self children addFirst: child] +] + +{ #category : #accessing } +GrafoscopioBranchNode >> addChild: aBlock ofClass: aClass [ + (self acceptsChildsOfClass: aClass) + ifTrue: [ | child | + child := aBlock value. + child parent: self. + self children add: child ] +] + +{ #category : #accessing } +GrafoscopioBranchNode >> children [ + "Returns the receivers list of children" + + ^ children ifNil: [ children := OrderedCollection new ] +] + +{ #category : #accessing } +GrafoscopioBranchNode >> children: aCollection [ + "Sets the receivers children" + + aCollection do: [:currentNode | currentNode parent: self ]. + children := aCollection. +] + +{ #category : #accessing } +GrafoscopioBranchNode >> isLeaf [ + ^ false +] + +{ #category : #'as yet unclassified' } +GrafoscopioBranchNode >> moveDown: 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 min: children size) +] + +{ #category : #'as yet unclassified' } +GrafoscopioBranchNode >> 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 : #'as yet unclassified' } +GrafoscopioBranchNode >> remove: aGrafoscopioTextNode [ + children remove: aGrafoscopioTextNode +] diff --git a/src/Grafoscopio/GrafoscopioButtonModel.class.st b/src/Grafoscopio/GrafoscopioButtonModel.class.st index 2e0db13..89c9516 100644 --- a/src/Grafoscopio/GrafoscopioButtonModel.class.st +++ b/src/Grafoscopio/GrafoscopioButtonModel.class.st @@ -1,86 +1,86 @@ -" -I'm just a prototype of how morphic buttons can be embedded -inside a Grafoscopio notebook UI via Spec. -I'm not used in the actual UI, but I'm more a remainder of directions -to explore. -" -Class { - #name : #GrafoscopioButtonModel, - #superclass : #ComposablePresenter, - #instVars : [ - 'button' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #specs } -GrafoscopioButtonModel class >> defaultSpec [ - - ^ SpecLayout composed add: #button -] - -{ #category : #accessing } -GrafoscopioButtonModel >> body [ - ^ self button -] - -{ #category : #accessing } -GrafoscopioButtonModel >> button [ - ^ button -] - -{ #category : #accessing } -GrafoscopioButtonModel >> button: anObject [ - button := anObject -] - -{ #category : #accessing } -GrafoscopioButtonModel >> cancel [ - ^ cancel -] - -{ #category : #accessing } -GrafoscopioButtonModel >> cancel: anObject [ - cancel := anObject -] - -{ #category : #api } -GrafoscopioButtonModel >> content: anAssoc [ - button label: anAssoc key. - button action: anAssoc value. -] - -{ #category : #accessing } -GrafoscopioButtonModel >> find [ - ^ find -] - -{ #category : #accessing } -GrafoscopioButtonModel >> find: anObject [ - find := anObject -] - -{ #category : #initalize } -GrafoscopioButtonModel >> initializeWidgets [ - button := self newButton. -] - -{ #category : #accessing } -GrafoscopioButtonModel >> ok [ - ^ ok -] - -{ #category : #accessing } -GrafoscopioButtonModel >> ok: anObject [ - ok := anObject -] - -{ #category : #accessing } -GrafoscopioButtonModel >> replace [ - ^ replace -] - -{ #category : #accessing } -GrafoscopioButtonModel >> replace: anObject [ - replace := anObject -] +" +I'm just a prototype of how morphic buttons can be embedded +inside a Grafoscopio notebook UI via Spec. +I'm not used in the actual UI, but I'm more a remainder of directions +to explore. +" +Class { + #name : #GrafoscopioButtonModel, + #superclass : #ComposablePresenter, + #instVars : [ + 'button' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #specs } +GrafoscopioButtonModel class >> defaultSpec [ + + ^ SpecLayout composed add: #button +] + +{ #category : #accessing } +GrafoscopioButtonModel >> body [ + ^ self button +] + +{ #category : #accessing } +GrafoscopioButtonModel >> button [ + ^ button +] + +{ #category : #accessing } +GrafoscopioButtonModel >> button: anObject [ + button := anObject +] + +{ #category : #accessing } +GrafoscopioButtonModel >> cancel [ + ^ cancel +] + +{ #category : #accessing } +GrafoscopioButtonModel >> cancel: anObject [ + cancel := anObject +] + +{ #category : #api } +GrafoscopioButtonModel >> content: anAssoc [ + button label: anAssoc key. + button action: anAssoc value. +] + +{ #category : #accessing } +GrafoscopioButtonModel >> find [ + ^ find +] + +{ #category : #accessing } +GrafoscopioButtonModel >> find: anObject [ + find := anObject +] + +{ #category : #initalize } +GrafoscopioButtonModel >> initializeWidgets [ + button := self newButton. +] + +{ #category : #accessing } +GrafoscopioButtonModel >> ok [ + ^ ok +] + +{ #category : #accessing } +GrafoscopioButtonModel >> ok: anObject [ + ok := anObject +] + +{ #category : #accessing } +GrafoscopioButtonModel >> replace [ + ^ replace +] + +{ #category : #accessing } +GrafoscopioButtonModel >> replace: anObject [ + replace := anObject +] diff --git a/src/Grafoscopio/GrafoscopioCodeModel.class.st b/src/Grafoscopio/GrafoscopioCodeModel.class.st index 5b2434a..368cf46 100644 --- a/src/Grafoscopio/GrafoscopioCodeModel.class.st +++ b/src/Grafoscopio/GrafoscopioCodeModel.class.st @@ -1,64 +1,64 @@ -" -I represent a Pharo code node inside a Grafoscopio notebook as a -embedded interactive Playground. -" -Class { - #name : #GrafoscopioCodeModel, - #superclass : #ComposablePresenter, - #instVars : [ - 'body' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #specs } -GrafoscopioCodeModel class >> defaultSpec [ - ^ SpecLayout composed add: #body -] - -{ #category : #accessing } -GrafoscopioCodeModel >> body [ - ^ body -] - -{ #category : #accessing } -GrafoscopioCodeModel >> body: anObject [ - body := anObject -] - -{ #category : #API } -GrafoscopioCodeModel >> content: aGrafoscopioNodeContent [ - body text: aGrafoscopioNodeContent -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeModel >> extractHtmlImages [ - "comment stating purpose of message" - |imgSoup imgHost imgList src| - - imgList := Set new. - imgSoup := Soup fromString: self body. - (imgSoup findAllTags: 'img') do: [ :each| - src := (each attributeAt: 'src') asUrl. - (src host) ifNil: [src host: self links last asUrl removeLastPathSegment]. - imgList add: src. - "imgList add: (each attributeAt: 'src') asUrl." - - "OSProcess waitForCommand: 'wget ', (each attributeAt: 'src')." - "imgHost := self links last removeLastPathSegment." - - "imgPath:= ((each attributeAt: 'src') asUrl). " - "ZnEasy getJpeg: (imgHost , imgPath) asUrl." - - "OSProcess waitForCommand: ('mkdir ', imgPath)." - - "Transcript show: ' wget ', imgPath , '/',(each attributeAt: 'src'). " - ]. - ^imgList . -] - -{ #category : #initialization } -GrafoscopioCodeModel >> initializeWidgets [ - - body := self newCode. -] +" +I represent a Pharo code node inside a Grafoscopio notebook as a +embedded interactive Playground. +" +Class { + #name : #GrafoscopioCodeModel, + #superclass : #ComposablePresenter, + #instVars : [ + 'body' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #specs } +GrafoscopioCodeModel class >> defaultSpec [ + ^ SpecLayout composed add: #body +] + +{ #category : #accessing } +GrafoscopioCodeModel >> body [ + ^ body +] + +{ #category : #accessing } +GrafoscopioCodeModel >> body: anObject [ + body := anObject +] + +{ #category : #API } +GrafoscopioCodeModel >> content: aGrafoscopioNodeContent [ + body text: aGrafoscopioNodeContent +] + +{ #category : #'as yet unclassified' } +GrafoscopioCodeModel >> extractHtmlImages [ + "comment stating purpose of message" + |imgSoup imgHost imgList src| + + imgList := Set new. + imgSoup := Soup fromString: self body. + (imgSoup findAllTags: 'img') do: [ :each| + src := (each attributeAt: 'src') asUrl. + (src host) ifNil: [src host: self links last asUrl removeLastPathSegment]. + imgList add: src. + "imgList add: (each attributeAt: 'src') asUrl." + + "OSProcess waitForCommand: 'wget ', (each attributeAt: 'src')." + "imgHost := self links last removeLastPathSegment." + + "imgPath:= ((each attributeAt: 'src') asUrl). " + "ZnEasy getJpeg: (imgHost , imgPath) asUrl." + + "OSProcess waitForCommand: ('mkdir ', imgPath)." + + "Transcript show: ' wget ', imgPath , '/',(each attributeAt: 'src'). " + ]. + ^imgList . +] + +{ #category : #initialization } +GrafoscopioCodeModel >> initializeWidgets [ + + body := self newCode. +] diff --git a/src/Grafoscopio/GrafoscopioCodeNode.class.st b/src/Grafoscopio/GrafoscopioCodeNode.class.st deleted file mode 100644 index c69bf85..0000000 --- a/src/Grafoscopio/GrafoscopioCodeNode.class.st +++ /dev/null @@ -1,24 +0,0 @@ -" -This kind of a leafNodes holds code text. -" -Class { - #name : #GrafoscopioCodeNode, - #superclass : #GrafoscopioTextNode, - #category : #'Grafoscopio-Model' -} - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode class >> icon [ - ^ self iconNamed:#objects -] - -{ #category : #'as yet unclassified' } -GrafoscopioCodeNode class >> nameForSelection [ - ^ 'New Code Node' -] - -{ #category : #accessing } -GrafoscopioCodeNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitCodeNode: self. - -] diff --git a/src/Grafoscopio/GrafoscopioDocs.class.st b/src/Grafoscopio/GrafoscopioDocs.class.st index 05d8797..cba8389 100644 --- a/src/Grafoscopio/GrafoscopioDocs.class.st +++ b/src/Grafoscopio/GrafoscopioDocs.class.st @@ -1,66 +1,66 @@ -" -I define the documentation for the Grafoscopio package. -" -Class { - #name : #GrafoscopioDocs, - #superclass : #GrafoscopioDocumentation, - #category : #Grafoscopio -} - -{ #category : #operation } -GrafoscopioDocs class >> devNotes [ - "I'm just an alias to ease the operation. I need to know wich is the index of the notebook - I want to return, as defined on instance initialize method" - | docs | - docs := self newDefault. - ^ (docs localPlace fullName, '/', (docs documents at: 5)) asFileReference. -] - -{ #category : #initialization } -GrafoscopioDocs class >> initialize [ - self update -] - -{ #category : #operation } -GrafoscopioDocs class >> manual [ - "I'm just an alias to ease the operation. I need to know wich is the index of the notebook - I want to open, as defined on method." - | docs | - docs := self newDefault. - ^ (docs localPlace fullName, '/', (docs documents at: 2)) asFileReference. -] - -{ #category : #operation } -GrafoscopioDocs class >> openPDFManual [ - "I open the documentation in PDF format." - | pdfManual | - pdfManual := FileLocator imageDirectory asFileReference / 'Grafoscopio/Docs/En/Books/Manual/manual.pdf'. - WebBrowser openOn: pdfManual fullName. -] - -{ #category : #operation } -GrafoscopioDocs class >> tutorial [ - "I'm just an alias to ease the operation. I need to know wich is the index of the notebook - I want to return, as defined on instance initialize method" - | docs | - docs := self newDefault. - ^ (docs localPlace fullName, '/', (docs documents at: 1)) asFileReference. -] - -{ #category : #initialization } -GrafoscopioDocs >> initialize [ - "I model the important documents that belong to the Grafoscopio documentation. - When more documents become more mature and usable, I will include them." - - super initialize. - name := 'grafoscopio'. - repository := (FossilRepo new remote: 'http://mutabit.com/repos.fossil/grafoscopio'). - localPlace := FileLocator workingDirectory asFileReference /'Grafoscopio'. - self - addDocument: 'Docs/Es/Tutoriales/tutorial.ston'; - addDocument: 'Docs/En/Books/Manual/manual.ston'; - addDocument: 'uv/Docs/En/Books/Manual/manual.pdf'; - addDocument: 'Docs/En/Books/DataActivism/techniques-for-datactivism.ston'; - addDocument: 'Docs/En/dev-notes.ston'; - addDocument: 'Docs/En/Books/SpecIUFramework/spec-ui-framework.ston'. -] +" +I define the documentation for the Grafoscopio package. +" +Class { + #name : #GrafoscopioDocs, + #superclass : #GrafoscopioDocumentation, + #category : #Grafoscopio +} + +{ #category : #operation } +GrafoscopioDocs class >> devNotes [ + "I'm just an alias to ease the operation. I need to know wich is the index of the notebook + I want to return, as defined on instance initialize method" + | docs | + docs := self newDefault. + ^ (docs localPlace fullName, '/', (docs documents at: 5)) asFileReference. +] + +{ #category : #initialization } +GrafoscopioDocs class >> initialize [ + self update +] + +{ #category : #operation } +GrafoscopioDocs class >> manual [ + "I'm just an alias to ease the operation. I need to know wich is the index of the notebook + I want to open, as defined on method." + | docs | + docs := self newDefault. + ^ (docs localPlace fullName, '/', (docs documents at: 2)) asFileReference. +] + +{ #category : #operation } +GrafoscopioDocs class >> openPDFManual [ + "I open the documentation in PDF format." + | pdfManual | + pdfManual := FileLocator imageDirectory asFileReference / 'Grafoscopio/Docs/En/Books/Manual/manual.pdf'. + WebBrowser openOn: pdfManual fullName. +] + +{ #category : #operation } +GrafoscopioDocs class >> tutorial [ + "I'm just an alias to ease the operation. I need to know wich is the index of the notebook + I want to return, as defined on instance initialize method" + | docs | + docs := self newDefault. + ^ (docs localPlace fullName, '/', (docs documents at: 1)) asFileReference. +] + +{ #category : #initialization } +GrafoscopioDocs >> initialize [ + "I model the important documents that belong to the Grafoscopio documentation. + When more documents become more mature and usable, I will include them." + + super initialize. + name := 'grafoscopio'. + repository := (FossilRepo new remote: 'http://mutabit.com/repos.fossil/grafoscopio'). + localPlace := FileLocator workingDirectory asFileReference /'Grafoscopio'. + self + addDocument: 'Docs/Es/Tutoriales/tutorial.ston'; + addDocument: 'Docs/En/Books/Manual/manual.ston'; + addDocument: 'uv/Docs/En/Books/Manual/manual.pdf'; + addDocument: 'Docs/En/Books/DataActivism/techniques-for-datactivism.ston'; + addDocument: 'Docs/En/dev-notes.ston'; + addDocument: 'Docs/En/Books/SpecIUFramework/spec-ui-framework.ston'. +] diff --git a/src/Grafoscopio/GrafoscopioDocumentEditionPerspective.class.st b/src/Grafoscopio/GrafoscopioDocumentEditionPerspective.class.st deleted file mode 100644 index edcfc2c..0000000 --- a/src/Grafoscopio/GrafoscopioDocumentEditionPerspective.class.st +++ /dev/null @@ -1,151 +0,0 @@ -Class { - #name : #GrafoscopioDocumentEditionPerspective, - #superclass : #GrafoscopioPerspective, - #instVars : [ - 'tree', - 'document' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #accessing } -GrafoscopioDocumentEditionPerspective class >> defaultSpec [ - ^ SpBoxLayout newVertical - add: #toolbar height: self toolbarHeight; - add: - (SpBoxLayout newHorizontal - add: #tree width: 100; - add: #viewport; - yourself) yourself -] - -{ #category : #accessing } -GrafoscopioDocumentEditionPerspective class >> icon [ - ^ self iconNamed: #merge -] - -{ #category : #'adding - convenience' } -GrafoscopioDocumentEditionPerspective >> addAtBeginningNewNodeOfClass: aClass [ - self - addAtBeginningOf: (tree selectedItem ifNil: [ document ]) - aNodeOfClass: aClass -] - -{ #category : #'adding - base' } -GrafoscopioDocumentEditionPerspective >> addAtBeginningOf: aNode aNodeOfClass: aClass [ - aNode - addAtBeginningChild: [ self instantiateNode: aClass ] - ofClass: aClass. - self modelChanged -] - -{ #category : #'adding - convenience' } -GrafoscopioDocumentEditionPerspective >> addAtLastNewNodeOfClass: aClass [ - self - addAtLastOf: (tree selectedItem ifNil: [ document ]) - aNodeOfClass: aClass -] - -{ #category : #'adding - base' } -GrafoscopioDocumentEditionPerspective >> addAtLastOf: aNode aNodeOfClass: aClass [ - aNode - addChild: [ self instantiateNode: aClass ] - ofClass: aClass. - self modelChanged -] - -{ #category : #'adding - convenience' } -GrafoscopioDocumentEditionPerspective >> addNewNodeAtBeginningOf: aNode [ - self addAtBeginningOf: aNode aNodeOfClass: self chooseKindsOfNode -] - -{ #category : #'adding - convenience' } -GrafoscopioDocumentEditionPerspective >> addNewNodeAtLastOf: aNode [ - self addAtLastOf: aNode aNodeOfClass: self chooseKindsOfNode -] - -{ #category : #'adding - convenience' } -GrafoscopioDocumentEditionPerspective >> addNewNodeOfClass: aClass [ - ^ self addAtLastNewNodeOfClass: aClass -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> createDefaultViewportVisitor [ - ^ GrafoscopioViewportVisitor new -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> createViewport [ - ^ self renderViewport: document -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> informNodeHasChanged: aNode [ - | path | - path := tree selection selectedPath. - tree roots: {document}. - tree selectPath: path. - - -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> initializeWidgets [ - super initializeWidgets. - tree := self newTreeTable. - tree - addColumn: (SpStringTableColumn evaluated: #name); - children: [ :node | - node isLeaf - ifTrue: [ {} ] - ifFalse: [ node children reject:[: a | a isLeaf ] ]]. - tree activateOnDoubleClick. - tree whenSelectionChangedDo: [ : a | self renderViewport: a selectedItem ]. - tree whenActivatedDo: [ : a | self tooglePresenterForEdition: a ]. -] - -{ #category : #'as yet unclassified' } -GrafoscopioDocumentEditionPerspective >> instantiateNode: aClass [ - aClass class = GrafoscopioUnitNode species - ifTrue: [ | name | - name := UIManager default - request: 'Unit name' - initialAnswer: 'New unit'. - ^ aClass new - name: name; - yourself ]. - ^ aClass new - -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> modelChanged [ -| path | - viewport := self createViewport. - path := tree selection selectedPath. - tree roots: {document}. - tree selectPath: path. - self needRebuild: false. - self buildWithSpec -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> renderViewport: aNode [ - ^ self createDefaultViewportVisitor createViewportFor: ( aNode ifNil: [ document ]) into: self -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> setModelBeforeInitialization: aDomainObject [ - super setModelBeforeInitialization: aDomainObject. - document := aDomainObject . -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> tooglePresenterForEdition: anObect [ - self halt. -] - -{ #category : #initialization } -GrafoscopioDocumentEditionPerspective >> viewport [ - ^ viewport -] diff --git a/src/Grafoscopio/GrafoscopioDocumentRichTextEditionPerspective.class.st b/src/Grafoscopio/GrafoscopioDocumentRichTextEditionPerspective.class.st new file mode 100644 index 0000000..bb0cc15 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioDocumentRichTextEditionPerspective.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GrafoscopioDocumentRichTextEditionPerspective, + #superclass : #GrafoscopioDocumentTextEditionPerspective, + #category : #'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioDocumentRichTextEditionPerspective class >> icon [ + ^ self iconNamed: #merge +] + +{ #category : #initialization } +GrafoscopioDocumentRichTextEditionPerspective >> aboutToBeUninstalledFrom: aTreeNotebook [ + +] + +{ #category : #initialization } +GrafoscopioDocumentRichTextEditionPerspective >> createDefaultViewportVisitor [ + ^ GrafoscopioViewportRichTextVisitor new +] diff --git a/src/Grafoscopio/GrafoscopioDocumentTextEditionPerspective.class.st b/src/Grafoscopio/GrafoscopioDocumentTextEditionPerspective.class.st new file mode 100644 index 0000000..bcfaf26 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioDocumentTextEditionPerspective.class.st @@ -0,0 +1,189 @@ +Class { + #name : #GrafoscopioDocumentTextEditionPerspective, + #superclass : #GrafoscopioPerspective, + #instVars : [ + 'tree', + 'document', + 'buildingVisitor' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioDocumentTextEditionPerspective class >> buildCommandsGroupWith: presenterInstance forRoot: rootCommandGroup [ + rootCommandGroup + register: (self buildMenuBarGroupWith: presenterInstance)"; + register: (self buildContextualMenuGroupWith: presenterInstance)" +] + +{ #category : #accessing } +GrafoscopioDocumentTextEditionPerspective class >> buildContextualMenuGroupWith: presenterInstance [ + ^ (CmCommandGroup named: 'Context Menu') asSpecGroup + register: (self buildEditionGroupWith: presenterInstance); + register: (self buildAddingGroupWith: presenterInstance); + register: (self buildRemovingGroupWith: presenterInstance); + yourself +] + +{ #category : #accessing } +GrafoscopioDocumentTextEditionPerspective class >> buildMenuBarGroupWith: presenterInstance [ + ^ (CmCommandGroup named: 'Context Menu') asSpecGroup + register: + (CmBlockCommand new + name: 'Inspect node'; + block: + [ :context | context viewport selectedPage activePresenter inspectNode ]; + asSpecCommand); + register: + (CmBlockCommand new + name: 'Inspect runs'; + block: + [ :context | context viewport selectedPage activePresenter inspectRuns ]; + asSpecCommand); + yourself +] + +{ #category : #accessing } +GrafoscopioDocumentTextEditionPerspective class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: #toolbar height: self toolbarHeight; + add: + (SpBoxLayout newHorizontal + add: #tree width: 100; + add: #viewport; + yourself) yourself +] + +{ #category : #accessing } +GrafoscopioDocumentTextEditionPerspective class >> icon [ + ^ self iconNamed: #edit +] + +{ #category : #'adding - convenience' } +GrafoscopioDocumentTextEditionPerspective >> aboutToBeUninstalledFrom: aTreeNotebook [ + aTreeNotebook removePerspective: self +] + +{ #category : #'adding - convenience' } +GrafoscopioDocumentTextEditionPerspective >> addAtLastNewNodeOfClass: aClass [ + self + addAtLastOf: (tree selectedItem ifNil: [ document ]) + aNodeOfClass: aClass +] + +{ #category : #'adding - base' } +GrafoscopioDocumentTextEditionPerspective >> addAtLastOf: aNode aNodeOfClass: aClass [ + aNode + addChild: [ self instantiateNode: aClass ] + ofClass: aClass. + self modelChanged +] + +{ #category : #'adding - convenience' } +GrafoscopioDocumentTextEditionPerspective >> addNewNodeOfClass: aClass [ + ^ self addAtLastNewNodeOfClass: aClass +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> buildingVisitor [ + ^ buildingVisitor + ifNil: [ buildingVisitor := self createDefaultViewportVisitor ] +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> buildingVisitor: aVisitor [ + buildingVisitor := aVisitor +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> createDefaultViewportVisitor [ + ^ GrafoscopioViewportTextVisitor new +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> createViewport [ + ^ self renderViewport: document +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> informNodeHasChanged: aNode [ + | path | + path := tree selection selectedPath. + tree roots: {document}. + tree selectPath: path. + + +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> initializeWidgets [ + super initializeWidgets. + tree := self newTreeTable. + tree + addColumn: (SpStringTableColumn evaluated: #name); + children: [ :node | + node isLeaf + ifTrue: [ {} ] + ifFalse: [ node children ] ]. + tree activateOnDoubleClick. + tree + whenSelectionChangedDo: [ :a | self renderViewport: a selectedItem ]. + tree whenActivatedDo: [ : a | self requestNewNameFor: a selectedItem ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioDocumentTextEditionPerspective >> instantiateNode: aClass [ + | name | + name := UIManager default + request: 'Please, name the item' + initialAnswer: 'New item'. + ^ aClass new + name: name; + yourself +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> modelChanged [ + | path | + path := tree selection selectedPath. + tree roots: {document}. + tree selectPath: path. + self needRebuild: false. + self buildWithSpec +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> nameChanged [ + | path | + path := tree selection selectedPath. + tree roots: {document}. + tree buildWithSpec. + tree selectPath: path +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> renderViewport: aNode [ + ^ self buildingVisitor createViewportFor: ( aNode ifNil: [ document ]) into: self +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> requestNewNameFor: aNode [ + | name | + name := UIManager default + request: 'Please, name the node' + initialAnswer: aNode name asString. + name ifNotNil: [ aNode name: name ]. + self buildingVisitor renamePageFor: aNode. + self nameChanged . +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> setModelBeforeInitialization: aDomainObject [ + super setModelBeforeInitialization: aDomainObject. + document := aDomainObject . +] + +{ #category : #initialization } +GrafoscopioDocumentTextEditionPerspective >> viewport [ + ^ viewport +] diff --git a/src/Grafoscopio/GrafoscopioExportPerspective.class.st b/src/Grafoscopio/GrafoscopioExportPerspective.class.st index 0540ecc..07361f3 100644 --- a/src/Grafoscopio/GrafoscopioExportPerspective.class.st +++ b/src/Grafoscopio/GrafoscopioExportPerspective.class.st @@ -1,15 +1,15 @@ -Class { - #name : #GrafoscopioExportPerspective, - #superclass : #GrafoscopioPerspective, - #category : #'Grafoscopio-New-UI' -} - -{ #category : #accessing } -GrafoscopioExportPerspective class >> icon [ - ^ self iconNamed: #export -] - -{ #category : #initialization } -GrafoscopioExportPerspective >> createViewport [ - ^ self newLabel -] +Class { + #name : #GrafoscopioExportPerspective, + #superclass : #GrafoscopioPerspective, + #category : #'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioExportPerspective class >> icon [ + ^ self iconNamed: #export +] + +{ #category : #initialization } +GrafoscopioExportPerspective >> createViewport [ + ^ self newLabel +] diff --git a/src/Grafoscopio/GrafoscopioFmtAnchorAsBody.class.st b/src/Grafoscopio/GrafoscopioFmtAnchorAsBody.class.st index 638f87b..dd40c93 100644 --- a/src/Grafoscopio/GrafoscopioFmtAnchorAsBody.class.st +++ b/src/Grafoscopio/GrafoscopioFmtAnchorAsBody.class.st @@ -1,14 +1,14 @@ -Class { - #name : #GrafoscopioFmtAnchorAsBody, - #superclass : #GrafoscopioFmtUrlAsBody, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtAnchorAsBody >> beInstalledIn: anExternalLink [ - anExternalLink children isEmpty ifFalse: [ ^ self ]. - self - installTextNodeAtRightWithValue: anExternalLink anchor asString - in: anExternalLink - -] +Class { + #name : #GrafoscopioFmtAnchorAsBody, + #superclass : #GrafoscopioFmtUrlAsBody, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtAnchorAsBody >> beInstalledIn: anExternalLink [ + anExternalLink children isEmpty ifFalse: [ ^ self ]. + self + installTextNodeAtRightWithValue: anExternalLink anchor asString + in: anExternalLink + +] diff --git a/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft.class.st b/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft.class.st index 1201915..163a484 100644 --- a/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft.class.st +++ b/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft.class.st @@ -1,10 +1,11 @@ -Class { - #name : #GrafoscopioFmtAnchorOnTheLeft, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtAnchorOnTheLeft >> beInstalledIn: aNode [ - self installTextNodeAtLeftWithValue: (Character value: 1) asString in: aNode -] +Class { + #name : #GrafoscopioFmtAnchorOnTheLeft, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtAnchorOnTheLeft >> beInstalledIn: aNode [ + + self installTextNodeAtLeftWithValue: (Character value: 1) asString in: aNode +] diff --git a/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft2.class.st b/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft2.class.st index 0b5178b..94bbe5e 100644 --- a/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft2.class.st +++ b/src/Grafoscopio/GrafoscopioFmtAnchorOnTheLeft2.class.st @@ -1,13 +1,13 @@ -Class { - #name : #GrafoscopioFmtAnchorOnTheLeft2, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtAnchorOnTheLeft2 >> beInstalledIn: aNode [ - aNode language originalName = 'pharo-image' - ifTrue: [ - aNode propertyAt: #text put: aNode text. - aNode text: (Character value: 1) asString ] -] +Class { + #name : #GrafoscopioFmtAnchorOnTheLeft2, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtAnchorOnTheLeft2 >> beInstalledIn: aNode [ + aNode language originalName = 'pharo-image' + ifTrue: [ + aNode propertyAt: #text put: aNode text. + aNode text: (Character value: 1) asString ] +] diff --git a/src/Grafoscopio/GrafoscopioFmtBeginningLinebreak.class.st b/src/Grafoscopio/GrafoscopioFmtBeginningLinebreak.class.st index 604d1d0..280c2a4 100644 --- a/src/Grafoscopio/GrafoscopioFmtBeginningLinebreak.class.st +++ b/src/Grafoscopio/GrafoscopioFmtBeginningLinebreak.class.st @@ -1,10 +1,11 @@ -Class { - #name : #GrafoscopioFmtBeginningLinebreak, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtBeginningLinebreak >> beInstalledIn: aNode [ - self installTextNodeAtLeftWithValue: OSPlatform current lineEnding in: aNode -] +Class { + #name : #GrafoscopioFmtBeginningLinebreak, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtBeginningLinebreak >> beInstalledIn: aNode [ + " Should be using OSPlatform current lineEnding, but our text presenter does not support to have 2 character representation " + self installTextNodeAtLeftWithValue: String cr in: aNode +] diff --git a/src/Grafoscopio/GrafoscopioFmtDoubleLinebreak.class.st b/src/Grafoscopio/GrafoscopioFmtDoubleLinebreak.class.st index c4151c7..0c0a983 100644 --- a/src/Grafoscopio/GrafoscopioFmtDoubleLinebreak.class.st +++ b/src/Grafoscopio/GrafoscopioFmtDoubleLinebreak.class.st @@ -1,11 +1,12 @@ -Class { - #name : #GrafoscopioFmtDoubleLinebreak, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtDoubleLinebreak >> beInstalledIn: aNode [ - self installTextNodeAtLeftWithValue: OSPlatform current lineEnding in: aNode. - self installTextNodeAtRightWithValue: OSPlatform current lineEnding in: aNode -] +Class { + #name : #GrafoscopioFmtDoubleLinebreak, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtDoubleLinebreak >> beInstalledIn: aNode [ + " Should be using OSPlatform current lineEnding, but our text presenter does not support to have 2 character representation " + self installTextNodeAtLeftWithValue: String cr in: aNode. + self installTextNodeAtRightWithValue: String cr in: aNode +] diff --git a/src/Grafoscopio/GrafoscopioFmtEndingLinebreak.class.st b/src/Grafoscopio/GrafoscopioFmtEndingLinebreak.class.st index ba9b562..a00c771 100644 --- a/src/Grafoscopio/GrafoscopioFmtEndingLinebreak.class.st +++ b/src/Grafoscopio/GrafoscopioFmtEndingLinebreak.class.st @@ -1,15 +1,16 @@ -Class { - #name : #GrafoscopioFmtEndingLinebreak, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtEndingLinebreak >> beInstalledIn: aNode [ - self installTextNodeAtRightWithValue: OSPlatform current lineEnding in: aNode -] - -{ #category : #'target resize' } -GrafoscopioFmtEndingLinebreak >> value: aString [ - ^aString , OSPlatform current lineEnding -] +Class { + #name : #GrafoscopioFmtEndingLinebreak, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtEndingLinebreak >> beInstalledIn: aNode [ + " Should be using OSPlatform current lineEnding, but our text presenter does not support to have 2 character representation " + self installTextNodeAtRightWithValue: String cr in: aNode +] + +{ #category : #'target resize' } +GrafoscopioFmtEndingLinebreak >> value: aString [ + ^aString , OSPlatform current lineEnding +] diff --git a/src/Grafoscopio/GrafoscopioFmtEndingSpace.class.st b/src/Grafoscopio/GrafoscopioFmtEndingSpace.class.st index 43b3428..4b5e016 100644 --- a/src/Grafoscopio/GrafoscopioFmtEndingSpace.class.st +++ b/src/Grafoscopio/GrafoscopioFmtEndingSpace.class.st @@ -1,12 +1,12 @@ -Class { - #name : #GrafoscopioFmtEndingSpace, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtEndingSpace >> beInstalledIn: aNode [ - self - installTextNodeAtRightWithValue: String space - in: aNode -] +Class { + #name : #GrafoscopioFmtEndingSpace, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtEndingSpace >> beInstalledIn: aNode [ + self + installTextNodeAtRightWithValue: String space + in: aNode +] diff --git a/src/Grafoscopio/GrafoscopioFmtUrlAsBody.class.st b/src/Grafoscopio/GrafoscopioFmtUrlAsBody.class.st index b7d1124..e474a22 100644 --- a/src/Grafoscopio/GrafoscopioFmtUrlAsBody.class.st +++ b/src/Grafoscopio/GrafoscopioFmtUrlAsBody.class.st @@ -1,14 +1,14 @@ -Class { - #name : #GrafoscopioFmtUrlAsBody, - #superclass : #GrafoscopioFormat, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #'target resize' } -GrafoscopioFmtUrlAsBody >> beInstalledIn: anExternalLink [ - anExternalLink children isEmpty ifFalse: [ ^ self ]. - self - installTextNodeAtRightWithValue: anExternalLink reference asString - in: anExternalLink - -] +Class { + #name : #GrafoscopioFmtUrlAsBody, + #superclass : #GrafoscopioFormat, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #'target resize' } +GrafoscopioFmtUrlAsBody >> beInstalledIn: anExternalLink [ + anExternalLink children isEmpty ifFalse: [ ^ self ]. + self + installTextNodeAtRightWithValue: anExternalLink reference asString + in: anExternalLink + +] diff --git a/src/Grafoscopio/GrafoscopioFormat.class.st b/src/Grafoscopio/GrafoscopioFormat.class.st index 98dd7ab..6b5f5e3 100644 --- a/src/Grafoscopio/GrafoscopioFormat.class.st +++ b/src/Grafoscopio/GrafoscopioFormat.class.st @@ -1,41 +1,41 @@ -Class { - #name : #GrafoscopioFormat, - #superclass : #Object, - #classInstVars : [ - 'instance' - ], - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #accessing } -GrafoscopioFormat class >> instance [ - ^ instance ifNil: [ instance := self new ] -] - -{ #category : #'target resize' } -GrafoscopioFormat >> beInstalledIn: aNode [ - self subclassResponsibility . - -] - -{ #category : #'target resize' } -GrafoscopioFormat >> installTextNodeAtLeftWithValue: aString in: aNode [ - | newTextChild | - newTextChild := self newTextChildFor: aString in: aNode. - aNode children: {newTextChild} , aNode children -] - -{ #category : #'target resize' } -GrafoscopioFormat >> installTextNodeAtRightWithValue: aString in: aNode [ - | newTextChild | - newTextChild := self newTextChildFor: aString in: aNode. - aNode children: aNode children , {newTextChild} -] - -{ #category : #'target resize' } -GrafoscopioFormat >> newTextChildFor: aString in: aNode [ - ^ GrafoscopioFormatTextNode new - text: aString; - parent: aNode; - yourself -] +Class { + #name : #GrafoscopioFormat, + #superclass : #Object, + #classInstVars : [ + 'instance' + ], + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #accessing } +GrafoscopioFormat class >> instance [ + ^ instance ifNil: [ instance := self new ] +] + +{ #category : #'target resize' } +GrafoscopioFormat >> beInstalledIn: aNode [ + self subclassResponsibility . + +] + +{ #category : #'target resize' } +GrafoscopioFormat >> installTextNodeAtLeftWithValue: aString in: aNode [ + | newTextChild | + newTextChild := self newTextChildFor: aString in: aNode. + aNode children: {newTextChild} , aNode children +] + +{ #category : #'target resize' } +GrafoscopioFormat >> installTextNodeAtRightWithValue: aString in: aNode [ + | newTextChild | + newTextChild := self newTextChildFor: aString in: aNode. + aNode children: aNode children , {newTextChild} +] + +{ #category : #'target resize' } +GrafoscopioFormat >> newTextChildFor: aString in: aNode [ + ^ GrafoscopioFormatTextNode new + text: aString; + parent: aNode; + yourself +] diff --git a/src/Grafoscopio/GrafoscopioFormatTextNode.class.st b/src/Grafoscopio/GrafoscopioFormatTextNode.class.st index 5337c54..165b236 100644 --- a/src/Grafoscopio/GrafoscopioFormatTextNode.class.st +++ b/src/Grafoscopio/GrafoscopioFormatTextNode.class.st @@ -1,16 +1,16 @@ -Class { - #name : #GrafoscopioFormatTextNode, - #superclass : #PRText, - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #visiting } -GrafoscopioFormatTextNode >> accept: aVisitor [ - aVisitor visitFormatText: self -] - -{ #category : #visiting } -GrafoscopioFormatTextNode >> printOn: aStream [ - super printOn: aStream. - aStream nextPutAll: ' format text: '; print: self text -] +Class { + #name : #GrafoscopioFormatTextNode, + #superclass : #PRText, + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #visiting } +GrafoscopioFormatTextNode >> accept: aVisitor [ + aVisitor visitFormatText: self +] + +{ #category : #visiting } +GrafoscopioFormatTextNode >> printOn: aStream [ + super printOn: aStream. + aStream nextPutAll: ' format text: '; print: self text +] diff --git a/src/Grafoscopio/GrafoscopioLeafNode.class.st b/src/Grafoscopio/GrafoscopioLeafNode.class.st index fcc2149..3264037 100644 --- a/src/Grafoscopio/GrafoscopioLeafNode.class.st +++ b/src/Grafoscopio/GrafoscopioLeafNode.class.st @@ -1,65 +1,65 @@ -" -Leaf node. Any content node is leaf -" -Class { - #name : #GrafoscopioLeafNode, - #superclass : #GrafoscopioAbstractNode, - #instVars : [ - 'parent' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #testing } -GrafoscopioLeafNode class >> isLeaf [ - ^ true -] - -{ #category : #accessing } -GrafoscopioLeafNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitLeafNode: self. - -] - -{ #category : #accessing } -GrafoscopioLeafNode >> addAtBeginningChild: aBlock ofClass: aClass [ - self error: 'Leaf nodes are abstract. ' -] - -{ #category : #accessing } -GrafoscopioLeafNode >> addChild: aBlock ofClass: aClass [ - self error: 'Leaf nodes are abstract. ' -] - -{ #category : #accessing } -GrafoscopioLeafNode >> level [ - "Returns the level of the node. See the setter message for details" - - ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioLeafNode >> moveDown [ - parent moveDown: self. -] - -{ #category : #'as yet unclassified' } -GrafoscopioLeafNode >> moveUp [ - parent moveUp: self. -] - -{ #category : #accessing } -GrafoscopioLeafNode >> parent [ - "Returns the parent of the current node" - ^ parent -] - -{ #category : #accessing } -GrafoscopioLeafNode >> parent: aNode [ - parent := aNode -] - -{ #category : #'as yet unclassified' } -GrafoscopioLeafNode >> remove [ - parent remove: self. -] +" +Leaf node. Any content node is leaf +" +Class { + #name : #GrafoscopioLeafNode, + #superclass : #GrafoscopioAbstractNode, + #instVars : [ + 'parent' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #testing } +GrafoscopioLeafNode class >> isLeaf [ + ^ true +] + +{ #category : #accessing } +GrafoscopioLeafNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitLeafNode: self. + +] + +{ #category : #accessing } +GrafoscopioLeafNode >> addAtBeginningChild: aBlock ofClass: aClass [ + self error: 'Leaf nodes are abstract. ' +] + +{ #category : #accessing } +GrafoscopioLeafNode >> addChild: aBlock ofClass: aClass [ + self error: 'Leaf nodes are abstract. ' +] + +{ #category : #accessing } +GrafoscopioLeafNode >> level [ + "Returns the level of the node. See the setter message for details" + + ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioLeafNode >> moveDown [ + parent moveDown: self. +] + +{ #category : #'as yet unclassified' } +GrafoscopioLeafNode >> moveUp [ + parent moveUp: self. +] + +{ #category : #accessing } +GrafoscopioLeafNode >> parent [ + "Returns the parent of the current node" + ^ parent +] + +{ #category : #accessing } +GrafoscopioLeafNode >> parent: aNode [ + parent := aNode +] + +{ #category : #'as yet unclassified' } +GrafoscopioLeafNode >> remove [ + parent remove: self. +] diff --git a/src/Grafoscopio/GrafoscopioLinksList.class.st b/src/Grafoscopio/GrafoscopioLinksList.class.st index 282eae0..8b41b4b 100644 --- a/src/Grafoscopio/GrafoscopioLinksList.class.st +++ b/src/Grafoscopio/GrafoscopioLinksList.class.st @@ -1,51 +1,51 @@ -" -I model the links of a GrafoscopioNode. - -I'm responsable for showing the links and selecting them. -" -Class { - #name : #GrafoscopioLinksList, - #superclass : #ComposablePresenter, - #instVars : [ - 'links' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #specs } -GrafoscopioLinksList class >> defaultSpec [ - ^ SpLayout composed - add: #links; - yourself -] - -{ #category : #accessing } -GrafoscopioLinksList >> content: aGrafoscopioNode [ - links items: aGrafoscopioNode links -] - -{ #category : #initialization } -GrafoscopioLinksList >> initializeWidgets [ - links := self newList. - self focusOrder add: links -] - -{ #category : #accessing } -GrafoscopioLinksList >> links [ - ^ links -] - -{ #category : #accessing } -GrafoscopioLinksList >> links: anObject [ - links := anObject -] - -{ #category : #api } -GrafoscopioLinksList >> title [ - ^ 'Node links list' -] - -{ #category : #'api-events' } -GrafoscopioLinksList >> whenSelectedItemChanged: aBlock [ - links whenSelectedItemChanged: aBlock -] +" +I model the links of a GrafoscopioNode. + +I'm responsable for showing the links and selecting them. +" +Class { + #name : #GrafoscopioLinksList, + #superclass : #ComposablePresenter, + #instVars : [ + 'links' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #specs } +GrafoscopioLinksList class >> defaultSpec [ + ^ SpLayout composed + add: #links; + yourself +] + +{ #category : #accessing } +GrafoscopioLinksList >> content: aGrafoscopioNode [ + links items: aGrafoscopioNode links +] + +{ #category : #initialization } +GrafoscopioLinksList >> initializeWidgets [ + links := self newList. + self focusOrder add: links +] + +{ #category : #accessing } +GrafoscopioLinksList >> links [ + ^ links +] + +{ #category : #accessing } +GrafoscopioLinksList >> links: anObject [ + links := anObject +] + +{ #category : #api } +GrafoscopioLinksList >> title [ + ^ 'Node links list' +] + +{ #category : #'api-events' } +GrafoscopioLinksList >> whenSelectedItemChanged: aBlock [ + links whenSelectedItemChanged: aBlock +] diff --git a/src/Grafoscopio/GrafoscopioNewCodeModel.class.st b/src/Grafoscopio/GrafoscopioNewCodeModel.class.st deleted file mode 100644 index 58b3e45..0000000 --- a/src/Grafoscopio/GrafoscopioNewCodeModel.class.st +++ /dev/null @@ -1,105 +0,0 @@ -Class { - #name : #GrafoscopioNewCodeModel, - #superclass : #GrafoscopioNewTextModel, - #instVars : [ - 'preview', - 'previewButton' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #specs } -GrafoscopioNewCodeModel class >> defaultSpec [ - ^ SpBoxLayout newVertical - add: #body; - yourself -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> content: aGrafoscopioNodeContent [ - self layout: (self createLayoutFor: aGrafoscopioNodeContent). - body text: (aGrafoscopioNodeContent ifNil: [ '' ]) -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> createLayoutFor: aGrafoscopioNodeContent [ - ^ SpBoxLayout newVertical - add: - (SpBoxLayout newHorizontal - add: - (SpBoxLayout newVertical - add: #up; - add: #down; - add: #delete; - yourself) - width: 30; - add: - (SpBoxLayout newHorizontal - add: #body; - add: #previewButton width: 30; - add: #preview; - yourself); - yourself) - height: (self heightFor: aGrafoscopioNodeContent) -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> editionLayoutFor: aGrafoscopioNodeContent [ - ^ SpBoxLayout newVertical - add: - (SpBoxLayout newHorizontal - add: - (SpBoxLayout newVertical - add: #up; - add: #down; - add: #delete; - yourself) - width: 30; - add: - (SpBoxLayout newHorizontal - add: #body; - add: #previewButton width: 30; - add: #preview; - yourself); - yourself) - height: (self heightFor: aGrafoscopioNodeContent) -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> initializeWidgets [ - super initializeWidgets. - previewButton := self newButton. - previewButton icon: (self iconNamed: #smallFind). - previewButton action: [ self previewCode ]. - preview := self newLabel -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> newTextComponent [ - ^ self newCode - whenTextChangedDo: [ model text: body text ]; - autoAccept: true; - yourself -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> normalLayoutFor: aGrafoscopioNodeContent [ - ^ SpBoxLayout newVertical - add: - (SpBoxLayout newHorizontal - add: - (SpBoxLayout newHorizontal - add: #body; - add: #previewButton width: 30; - add: #preview; - yourself); - yourself) - height: (self heightFor: aGrafoscopioNodeContent) -] - -{ #category : #initialization } -GrafoscopioNewCodeModel >> previewCode [ - [ preview label: (self class compiler evaluate: body text) asString ] - on: Error - do: [ :e | preview label: e asString] -] diff --git a/src/Grafoscopio/GrafoscopioNewTextInputModel.class.st b/src/Grafoscopio/GrafoscopioNewTextInputModel.class.st deleted file mode 100644 index 687e894..0000000 --- a/src/Grafoscopio/GrafoscopioNewTextInputModel.class.st +++ /dev/null @@ -1,83 +0,0 @@ -Class { - #name : #GrafoscopioNewTextInputModel, - #superclass : #SpDynamicPresenter, - #instVars : [ - '#body', - '#model => SpObservableSlot', - '#onModifyNodeLocationDo' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #specs } -GrafoscopioNewTextInputModel class >> defaultSpec [ - ^ SpBoxLayout newVertical - add: #body height: 300; - yourself -] - -{ #category : #API } -GrafoscopioNewTextInputModel >> content: aGrafoscopioNodeContent [ - self layout: (self createLayoutFor: aGrafoscopioNodeContent). - body text: (aGrafoscopioNodeContent ifNil: [ '' ]) -] - -{ #category : #API } -GrafoscopioNewTextInputModel >> createLayoutFor: aGrafoscopioNodeContent [ - ^ SpBoxLayout newVertical - add: #body - height: (self heightFor: aGrafoscopioNodeContent) -] - -{ #category : #'as yet unclassified' } -GrafoscopioNewTextInputModel >> heightFor: aGrafoscopioNodeContent [ - ^ aGrafoscopioNodeContent - ifNil: [ 100 ] - ifNotNil: [ (aGrafoscopioNodeContent asString lines size * self class toolbarHeight) max: 100 ] -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> informModification [ - onModifyNodeLocationDo - ifNotNil: [ onModifyNodeLocationDo cull: self ] -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> initialize [ - super initialize. -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> initializePrivateAnnouncements [ - super initializePrivateAnnouncements. - self property: #model whenChangedDo: [ self modelChanged ] -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> initializeWidgets [ - body := self newTextComponent. - -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> model: aModel [ - model := aModel -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> modelChanged [ - self content: model text. -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> newTextComponent [ - ^ self newTextInput whenTextChangedDo: [ - model text: body text. - self informModification ]; - yourself -] - -{ #category : #initialization } -GrafoscopioNewTextInputModel >> onModifyNodeLocationDo: aBlock [ - onModifyNodeLocationDo := aBlock -] diff --git a/src/Grafoscopio/GrafoscopioNewTextModel.class.st b/src/Grafoscopio/GrafoscopioNewTextModel.class.st index fcc4e63..904e3bb 100644 --- a/src/Grafoscopio/GrafoscopioNewTextModel.class.st +++ b/src/Grafoscopio/GrafoscopioNewTextModel.class.st @@ -1,78 +1,77 @@ -Class { - #name : #GrafoscopioNewTextModel, - #superclass : #GrafoscopioNewTextInputModel, - #instVars : [ - 'up', - 'down', - 'delete', - 'editionMode', - 'lastHeightUsed' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #initialization } -GrafoscopioNewTextModel >> createLayoutFor: aGrafoscopioNodeContent [ - lastHeightUsed := (self heightFor: aGrafoscopioNodeContent). - ^ editionMode - ifTrue: [ self editionLayoutFor: aGrafoscopioNodeContent ] - ifFalse: [ self normalLayoutFor: aGrafoscopioNodeContent ] -] - -{ #category : #initialization } -GrafoscopioNewTextModel >> editionLayoutFor: aGrafoscopioNodeContent [ - ^ SpBoxLayout newVertical - add: - (SpBoxLayout newHorizontal - add: - (SpBoxLayout newVertical - add: #up; - add: #down; - add: #delete; - yourself) - width: 30; - add: #body; - yourself) - height: (self heightFor: aGrafoscopioNodeContent) -] - -{ #category : #initialization } -GrafoscopioNewTextModel >> initializeWidgets [ - super initializeWidgets . - editionMode := true. - up := self newButton icon: (self iconNamed: #up) ; color: Color transparent ; yourself . - down := self newButton icon: (self iconNamed: #down) ; color: Color transparent ; yourself . - delete := self newButton icon: (self iconNamed: #delete) ; color: Color transparent ; yourself . - up action: [ model moveUp. self informModification. ]. - down action: [ model moveDown. self informModification.]. - delete action: [ model remove .self informModification. ] - -] - -{ #category : #initialization } -GrafoscopioNewTextModel >> newTextComponent [ - ^ self newText - whenTextChangedDo: [ self textChanged ]; - autoAccept: true; - yourself -] - -{ #category : #initialization } -GrafoscopioNewTextModel >> normalLayoutFor: aGrafoscopioNodeContent [ - ^ SpBoxLayout newVertical - add: #body - height: (self heightFor: aGrafoscopioNodeContent) -] - -{ #category : #initialization } -GrafoscopioNewTextModel >> textChanged [ - model text: body text. - (lastHeightUsed - (self heightFor: model text )) abs - > (lastHeightUsed * 0.1) - ifTrue: [ self modelChanged ] -] - -{ #category : #initialization } -GrafoscopioNewTextModel >> toogleEditionMode [ - editionMode := editionMode not. -] +Class { + #name : #GrafoscopioNewTextModel, + #superclass : #SpPresenterWithModel, + #instVars : [ + 'body', + 'onModifyNodeLocationDo' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #specs } +GrafoscopioNewTextModel class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: #body; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioNewTextModel >> body [ + ^ body +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> contextMenuFromCommandsGroup: aBlock [ + ^ body contextMenuFromCommandsGroup: aBlock +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> informModification [ + onModifyNodeLocationDo + ifNotNil: [ onModifyNodeLocationDo cull: self ] +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> initialize [ + super initialize. +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> initializeWidgets [ + body := self newText. + body text: self model text. + body whenTextChangedDo: [ self informModification ] +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> inspectNRun [ + body text + inspectRunAt: + (body selectionInterval + ifNil: [ body cursorPositionIndex ] + ifNotNil: [ :i | i first ]) +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> inspectNode [ + body text + inspectNodeAt: + (body selectionInterval + ifNil: [ body cursorPositionIndex ] + ifNotNil: [ :i | i first ]) +] + +{ #category : #'as yet unclassified' } +GrafoscopioNewTextModel >> inspectRuns [ + ^ self inspectNRun +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> modelChanged [ + body text: self model text. +] + +{ #category : #initialization } +GrafoscopioNewTextModel >> onModifyNodeLocationDo: aBlock [ + onModifyNodeLocationDo := aBlock +] diff --git a/src/Grafoscopio/GrafoscopioNode.class.st b/src/Grafoscopio/GrafoscopioNode.class.st index e39f0b0..66e3410 100644 --- a/src/Grafoscopio/GrafoscopioNode.class.st +++ b/src/Grafoscopio/GrafoscopioNode.class.st @@ -1,1199 +1,1199 @@ -" -An UbakyeNode is and administrator of all node operations in a tree. - -Instance Variables - node: - -node - - xxxxx - -" -Class { - #name : #GrafoscopioNode, - #superclass : #Object, - #instVars : [ - 'header', - 'headers', - 'created', - 'edited', - 'selected', - 'key', - 'icon', - 'body', - 'tags', - 'children', - 'parent', - 'node', - 'level', - 'nodesInPreorder', - 'links', - 'output' - ], - #classInstVars : [ - 'clipboard' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #utility } -GrafoscopioNode 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 } -GrafoscopioNode class >> clipboard [ - ^ clipboard -] - -{ #category : #accessing } -GrafoscopioNode class >> clipboard: anObject [ - clipboard := anObject -] - -{ #category : #utility } -GrafoscopioNode 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 } -GrafoscopioNode 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 } -GrafoscopioNode >> addLink: anUrl [ - "anUrl is a string" - - (self links includes: anUrl) - ifFalse: [ self links add: anUrl ] - -] - -{ #category : #adding } -GrafoscopioNode >> 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 : #'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 - created: DateAndTime now printString; - header: 'newNode'; - body: ''. - 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> asSton [ - "Exports current tree as STON format" - | stonOutput | - - stonOutput := '' writeStream. - stonOutput nextPutAll: (STON toStringPretty: self "flatten"). - ^stonOutput contents - -] - -{ #category : #exporting } -GrafoscopioNode >> asStonFromRoot [ - "Exports current tree as STON format" - | stonOutput | - - stonOutput := '' writeStream. - self flatten. - stonOutput nextPutAll: (STON toStringPretty: self children). - ^stonOutput contents - -] - -{ #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" - - ^ body - -] - -{ #category : #accessing } -GrafoscopioNode >> body: anObject [ - body := anObject -] - -{ #category : #exporting } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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' } -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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> children [ - "Returns the receivers list of children" - - ^ children ifNil: [ children := OrderedCollection new ] -] - -{ #category : #accessing } -GrafoscopioNode >> children: aCollection [ - "Sets the receivers children" - - aCollection do: [:currentNode | currentNode parent: self ]. - children := aCollection. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNode >> content [ - ^ body -] - -{ #category : #'add/remove nodes' } -GrafoscopioNode >> copyToClipboard [ - self class clipboard: self subtreeCopy. - - -] - -{ #category : #accessing } -GrafoscopioNode >> created [ - - ^ created -] - -{ #category : #accessing } -GrafoscopioNode >> created: aTimestamp [ - "I tell when this object was created" - - created := aTimestamp -] - -{ #category : #operation } -GrafoscopioNode >> 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 : #utility } -GrafoscopioNode >> deleteReferencesToRoot: aRootNode [ - - | sparseTree | - sparseTree := self preorderTraversal. -] - -{ #category : #movement } -GrafoscopioNode >> 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 : #'as yet unclassified' } -GrafoscopioNode >> 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 : #accessing } -GrafoscopioNode >> edited [ - ^ edited -] - -{ #category : #accessing } -GrafoscopioNode >> 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 : #'custom markup' } -GrafoscopioNode >> 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' } -GrafoscopioNode >> 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' } -GrafoscopioNode >> embeddedNodes [ - ^ self children select: [:each | each headerStartsWith: '%embed'] -] - -{ #category : #exporting } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> find: aString andReplaceWith: anotherString [ - anotherString ifNil: [ ^ self ]. - self body: ((self body) copyReplaceAll: aString with: anotherString) -] - -{ #category : #exporting } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> hasChildren [ - (self children size > 0) - ifTrue: [ ^true ] - ifFalse: [ ^false ] -] - -{ #category : #accessing } -GrafoscopioNode >> header [ - "Returns the receiver header" - - ^ header -] - -{ #category : #accessing } -GrafoscopioNode >> header: anObject [ - "Sets the receivers header" - - header := anObject -] - -{ #category : #exporting } -GrafoscopioNode >> 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' } -GrafoscopioNode >> headerStartsWith: aString [ - ^ (self header findString: aString) = 1 -] - -{ #category : #accessing } -GrafoscopioNode >> headers [ - "I returns the headers of the receiver children" - ^ self children collect: [:currentNode | currentNode header ] -] - -{ #category : #operation } -GrafoscopioNode >> 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 } -GrafoscopioNode >> htmlToMarkdownSubtree [ - "I convert self and childern nodes body from HTML format to Pandoc's Markdown." - self preorderTraversal do: [ :each | each htmlToMarkdown ] -] - -{ #category : #accessing } -GrafoscopioNode >> icon [ - "Returns the receivers icon" - - ^icon -] - -{ #category : #accessing } -GrafoscopioNode >> icon: aSymbol [ - "Sets the receivers icon" - - icon := aSymbol -] - -{ #category : #accessing } -GrafoscopioNode >> id [ - ^id -] - -{ #category : #accessing } -GrafoscopioNode >> id: aChecksum [ - "I'm a unique identifier that changes when node content changes (i.e. header, body, links)." - - id := aChecksum -] - -{ #category : #importing } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> initialize [ - "I create a empty new node" - - super initialize. - self - header: 'newHeader'; - tagAs: 'text'; - body: ''. - id := UUID new. - created := DateAndTime now. - edited := DateAndTime now -] - -{ #category : #accessing } -GrafoscopioNode >> isEmpty [ - body ifNil: [ ^ true ] ifNotNil: [ ^ false ] -] - -{ #category : #operation } -GrafoscopioNode >> isSavedAfterLastEdition [ - | root | - root := self root. - root edited ifNil: [ ^ false ]. - ^ self unsavedNodes isEmpty. - "self unsavedNodes isEmpty ifFalse: [ ^ self unsavedNodes inspect ]" -] - -{ #category : #testing } -GrafoscopioNode >> isSelected [ - self selected ifNil: [ ^ false ]. - ^ self selected. -] - -{ #category : #operation } -GrafoscopioNode >> isTaggedAs: aString [ - self tags ifEmpty: [ self tagAs: 'text' ]. - ^ self tags includes: aString -] - -{ #category : #exporting } -GrafoscopioNode >> 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 } -GrafoscopioNode >> key [ - "Returns a unique key identifying the receiver in the help system" - - ^key -] - -{ #category : #accessing } -GrafoscopioNode >> key: aUniqueKey [ - "Sets a unique key identifying the receiver in the help system" - - key := aUniqueKey -] - -{ #category : #accessing } -GrafoscopioNode >> lastLink [ - self links ifNil: [ ^ '' ]. - self links ifEmpty: [ ^ '' ]. - ^ self links last -] - -{ #category : #accessing } -GrafoscopioNode >> lastNetLink [ - ^ self links detect: [ :l | l asZnUrl ] -] - -{ #category : #accessing } -GrafoscopioNode >> level [ - "Returns the level of the node. See the setter message for details" - - ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] -] - -{ #category : #accessing } -GrafoscopioNode >> links [ - "I model local or remote links that are associated to a particular node." - ^ links ifNil: [ ^ links := OrderedCollection new ] -] - -{ #category : #accessing } -GrafoscopioNode >> links: anObject [ - self links add: anObject -] - -{ #category : #operation } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> margin [ - "I define the same margin of the page used for PDF exportations" - - ^'2 cm' -] - -{ #category : #exporting } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> metadata [ - | mnode | - mnode := self root preorderTraversal - detect: [ :n | n headerStartsWith: '%metadata' ] - ifNone: [ ^ nil ]. - ^ mnode output. - -] - -{ #category : #exporting } -GrafoscopioNode >> 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 } -GrafoscopioNode >> moveDown [ - "Moves the current node a place before in the children collection where is located" - - self parent moveDown: self -] - -{ #category : #movement } -GrafoscopioNode >> moveDown: 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 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' } -GrafoscopioNode >> newNode [ - node := Dictionary newFrom: { - #header -> 'newHeadline'. - #body -> ''. - #children -> #()}. - ^ node. - -] - -{ #category : #accessing } -GrafoscopioNode >> output [ - (self isTaggedAs: 'código') - ifFalse: [ ^ self ]. - self body ifNil: [ ^ nil ]. - ^ OpalCompiler new - source: self body; - evaluate -] - -{ #category : #utility } -GrafoscopioNode >> pandocOptions [ - self metadata ifNil: [ ^ nil ]. - self metadata at: 'pandocOptions' ifAbsent: [ ^ '' ]. - ^ self metadata at: 'pandocOptions' -] - -{ #category : #utility } -GrafoscopioNode >> 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 : #accessing } -GrafoscopioNode >> parent [ - "Returns the parent of the current node" - ^ parent -] - -{ #category : #accessing } -GrafoscopioNode >> 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 : #'add/remove nodes' } -GrafoscopioNode >> 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 } -GrafoscopioNode >> preorderTraversal [ - | nodesInPreorder | - nodesInPreorder := OrderedCollection new. - self buildPreorderCollection: nodesInPreorder. - ^ nodesInPreorder -] - -{ #category : #movement } -GrafoscopioNode >> 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 : #exporting } -GrafoscopioNode >> 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' } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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' } -GrafoscopioNode >> 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 } -GrafoscopioNode >> replaceAccentedHTMLChars [ - self body: (self body copyReplaceAll: 'í' with: 'í' ) -] - -{ #category : #accessing } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 } -GrafoscopioNode >> saveContent: anObject [ - "Sets the receivers body to the given object" - - body := anObject -] - -{ #category : #operation } -GrafoscopioNode >> selectMarkupSubtreesToExport [ - ^ (self root preorderTraversal) select: [ :each | each linksToMarkupFile ]. -] - -{ #category : #accessing } -GrafoscopioNode >> specModelClass [ - (self isTaggedAs: 'código') - ifTrue: [ ^ GrafoscopioCodeModel ]. - (self isTaggedAs: 'johan') - ifTrue: [ ^ GrafoscopioButtonModel ]. - "por defecto" - ^ GrafoscopioTextModel -] - -{ #category : #operation } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 : #accessing } -GrafoscopioNode >> tagAs: 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" - aTag = 'código' ifTrue: [ ^ self toggleCodeText ]. - (self tags includes: aTag) - ifFalse: [ self tags add: aTag ]. - ^ self - -] - -{ #category : #accessing } -GrafoscopioNode >> tags [ - "I returns the receiver tags." - | migration | - tags isString - ifTrue: [ - migration := tags. - tags := OrderedCollection new. - self tagAs: migration ]. - tags ifNil: [ - tags := OrderedCollection new. - self tagAs: 'text' ]. - ^ tags -] - -{ #category : #accessing } -GrafoscopioNode >> tags: aCollection [ - tags := aCollection -] - -{ #category : #accessing } -GrafoscopioNode >> title [ - "Returns the receiver header" - - ^ header -] - -{ #category : #operation } -GrafoscopioNode >> 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 } -GrafoscopioNode >> 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 : #'as yet unclassified' } -GrafoscopioNode >> updateEditionTimestamp [ - self edited: DateAndTime now printString -] - -{ #category : #importing } -GrafoscopioNode >> uploadBodyFrom: fileLocator filteredFor: selectedLink [ - self body: fileLocator contents -] - -{ #category : #operation } -GrafoscopioNode >> visitLastLink [ - self lastLink = '' - ifTrue: [ self inform: 'This node has no associated links to visit'. ^ self ]. - [WebBrowser openOn: self lastLink] fork -] - -{ #category : #'as yet unclassified' } -GrafoscopioNode >> wrapBodyLines [ - "I convert the node body from HTML format to Pandoc's Markdown." - - self wrapBodyLines: 80 -] - -{ #category : #'as yet unclassified' } -GrafoscopioNode >> 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 ] ] ] -] +" +An UbakyeNode is and administrator of all node operations in a tree. + +Instance Variables + node: + +node + - xxxxx + +" +Class { + #name : #GrafoscopioNode, + #superclass : #Object, + #instVars : [ + 'header', + 'headers', + 'created', + 'edited', + 'selected', + 'key', + 'icon', + 'body', + 'tags', + 'children', + 'parent', + 'node', + 'level', + 'nodesInPreorder', + 'links', + 'output' + ], + #classInstVars : [ + 'clipboard' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #utility } +GrafoscopioNode 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 } +GrafoscopioNode class >> clipboard [ + ^ clipboard +] + +{ #category : #accessing } +GrafoscopioNode class >> clipboard: anObject [ + clipboard := anObject +] + +{ #category : #utility } +GrafoscopioNode 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 } +GrafoscopioNode 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 } +GrafoscopioNode >> addLink: anUrl [ + "anUrl is a string" + + (self links includes: anUrl) + ifFalse: [ self links add: anUrl ] + +] + +{ #category : #adding } +GrafoscopioNode >> 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 : #'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 + created: DateAndTime now printString; + header: 'newNode'; + body: ''. + 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> asSton [ + "Exports current tree as STON format" + | stonOutput | + + stonOutput := '' writeStream. + stonOutput nextPutAll: (STON toStringPretty: self "flatten"). + ^stonOutput contents + +] + +{ #category : #exporting } +GrafoscopioNode >> asStonFromRoot [ + "Exports current tree as STON format" + | stonOutput | + + stonOutput := '' writeStream. + self flatten. + stonOutput nextPutAll: (STON toStringPretty: self children). + ^stonOutput contents + +] + +{ #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" + + ^ body + +] + +{ #category : #accessing } +GrafoscopioNode >> body: anObject [ + body := anObject +] + +{ #category : #exporting } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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' } +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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> children [ + "Returns the receivers list of children" + + ^ children ifNil: [ children := OrderedCollection new ] +] + +{ #category : #accessing } +GrafoscopioNode >> children: aCollection [ + "Sets the receivers children" + + aCollection do: [:currentNode | currentNode parent: self ]. + children := aCollection. +] + +{ #category : #'as yet unclassified' } +GrafoscopioNode >> content [ + ^ body +] + +{ #category : #'add/remove nodes' } +GrafoscopioNode >> copyToClipboard [ + self class clipboard: self subtreeCopy. + + +] + +{ #category : #accessing } +GrafoscopioNode >> created [ + + ^ created +] + +{ #category : #accessing } +GrafoscopioNode >> created: aTimestamp [ + "I tell when this object was created" + + created := aTimestamp +] + +{ #category : #operation } +GrafoscopioNode >> 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 : #utility } +GrafoscopioNode >> deleteReferencesToRoot: aRootNode [ + + | sparseTree | + sparseTree := self preorderTraversal. +] + +{ #category : #movement } +GrafoscopioNode >> 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 : #'as yet unclassified' } +GrafoscopioNode >> 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 : #accessing } +GrafoscopioNode >> edited [ + ^ edited +] + +{ #category : #accessing } +GrafoscopioNode >> 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 : #'custom markup' } +GrafoscopioNode >> 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' } +GrafoscopioNode >> 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' } +GrafoscopioNode >> embeddedNodes [ + ^ self children select: [:each | each headerStartsWith: '%embed'] +] + +{ #category : #exporting } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> find: aString andReplaceWith: anotherString [ + anotherString ifNil: [ ^ self ]. + self body: ((self body) copyReplaceAll: aString with: anotherString) +] + +{ #category : #exporting } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> hasChildren [ + (self children size > 0) + ifTrue: [ ^true ] + ifFalse: [ ^false ] +] + +{ #category : #accessing } +GrafoscopioNode >> header [ + "Returns the receiver header" + + ^ header +] + +{ #category : #accessing } +GrafoscopioNode >> header: anObject [ + "Sets the receivers header" + + header := anObject +] + +{ #category : #exporting } +GrafoscopioNode >> 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' } +GrafoscopioNode >> headerStartsWith: aString [ + ^ (self header findString: aString) = 1 +] + +{ #category : #accessing } +GrafoscopioNode >> headers [ + "I returns the headers of the receiver children" + ^ self children collect: [:currentNode | currentNode header ] +] + +{ #category : #operation } +GrafoscopioNode >> 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 } +GrafoscopioNode >> htmlToMarkdownSubtree [ + "I convert self and childern nodes body from HTML format to Pandoc's Markdown." + self preorderTraversal do: [ :each | each htmlToMarkdown ] +] + +{ #category : #accessing } +GrafoscopioNode >> icon [ + "Returns the receivers icon" + + ^icon +] + +{ #category : #accessing } +GrafoscopioNode >> icon: aSymbol [ + "Sets the receivers icon" + + icon := aSymbol +] + +{ #category : #accessing } +GrafoscopioNode >> id [ + ^id +] + +{ #category : #accessing } +GrafoscopioNode >> id: aChecksum [ + "I'm a unique identifier that changes when node content changes (i.e. header, body, links)." + + id := aChecksum +] + +{ #category : #importing } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> initialize [ + "I create a empty new node" + + super initialize. + self + header: 'newHeader'; + tagAs: 'text'; + body: ''. + id := UUID new. + created := DateAndTime now. + edited := DateAndTime now +] + +{ #category : #accessing } +GrafoscopioNode >> isEmpty [ + body ifNil: [ ^ true ] ifNotNil: [ ^ false ] +] + +{ #category : #operation } +GrafoscopioNode >> isSavedAfterLastEdition [ + | root | + root := self root. + root edited ifNil: [ ^ false ]. + ^ self unsavedNodes isEmpty. + "self unsavedNodes isEmpty ifFalse: [ ^ self unsavedNodes inspect ]" +] + +{ #category : #testing } +GrafoscopioNode >> isSelected [ + self selected ifNil: [ ^ false ]. + ^ self selected. +] + +{ #category : #operation } +GrafoscopioNode >> isTaggedAs: aString [ + self tags ifEmpty: [ self tagAs: 'text' ]. + ^ self tags includes: aString +] + +{ #category : #exporting } +GrafoscopioNode >> 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 } +GrafoscopioNode >> key [ + "Returns a unique key identifying the receiver in the help system" + + ^key +] + +{ #category : #accessing } +GrafoscopioNode >> key: aUniqueKey [ + "Sets a unique key identifying the receiver in the help system" + + key := aUniqueKey +] + +{ #category : #accessing } +GrafoscopioNode >> lastLink [ + self links ifNil: [ ^ '' ]. + self links ifEmpty: [ ^ '' ]. + ^ self links last +] + +{ #category : #accessing } +GrafoscopioNode >> lastNetLink [ + ^ self links detect: [ :l | l asZnUrl ] +] + +{ #category : #accessing } +GrafoscopioNode >> level [ + "Returns the level of the node. See the setter message for details" + + ^ parent ifNil: [ 0 ] ifNotNil: [ 1 + parent level ] +] + +{ #category : #accessing } +GrafoscopioNode >> links [ + "I model local or remote links that are associated to a particular node." + ^ links ifNil: [ ^ links := OrderedCollection new ] +] + +{ #category : #accessing } +GrafoscopioNode >> links: anObject [ + self links add: anObject +] + +{ #category : #operation } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> margin [ + "I define the same margin of the page used for PDF exportations" + + ^'2 cm' +] + +{ #category : #exporting } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> metadata [ + | mnode | + mnode := self root preorderTraversal + detect: [ :n | n headerStartsWith: '%metadata' ] + ifNone: [ ^ nil ]. + ^ mnode output. + +] + +{ #category : #exporting } +GrafoscopioNode >> 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 } +GrafoscopioNode >> moveDown [ + "Moves the current node a place before in the children collection where is located" + + self parent moveDown: self +] + +{ #category : #movement } +GrafoscopioNode >> moveDown: 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 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' } +GrafoscopioNode >> newNode [ + node := Dictionary newFrom: { + #header -> 'newHeadline'. + #body -> ''. + #children -> #()}. + ^ node. + +] + +{ #category : #accessing } +GrafoscopioNode >> output [ + (self isTaggedAs: 'código') + ifFalse: [ ^ self ]. + self body ifNil: [ ^ nil ]. + ^ OpalCompiler new + source: self body; + evaluate +] + +{ #category : #utility } +GrafoscopioNode >> pandocOptions [ + self metadata ifNil: [ ^ nil ]. + self metadata at: 'pandocOptions' ifAbsent: [ ^ '' ]. + ^ self metadata at: 'pandocOptions' +] + +{ #category : #utility } +GrafoscopioNode >> 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 : #accessing } +GrafoscopioNode >> parent [ + "Returns the parent of the current node" + ^ parent +] + +{ #category : #accessing } +GrafoscopioNode >> 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 : #'add/remove nodes' } +GrafoscopioNode >> 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 } +GrafoscopioNode >> preorderTraversal [ + | nodesInPreorder | + nodesInPreorder := OrderedCollection new. + self buildPreorderCollection: nodesInPreorder. + ^ nodesInPreorder +] + +{ #category : #movement } +GrafoscopioNode >> 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 : #exporting } +GrafoscopioNode >> 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' } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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' } +GrafoscopioNode >> 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 } +GrafoscopioNode >> replaceAccentedHTMLChars [ + self body: (self body copyReplaceAll: 'í' with: 'í' ) +] + +{ #category : #accessing } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 } +GrafoscopioNode >> saveContent: anObject [ + "Sets the receivers body to the given object" + + body := anObject +] + +{ #category : #operation } +GrafoscopioNode >> selectMarkupSubtreesToExport [ + ^ (self root preorderTraversal) select: [ :each | each linksToMarkupFile ]. +] + +{ #category : #accessing } +GrafoscopioNode >> specModelClass [ + (self isTaggedAs: 'código') + ifTrue: [ ^ GrafoscopioCodeModel ]. + (self isTaggedAs: 'johan') + ifTrue: [ ^ GrafoscopioButtonModel ]. + "por defecto" + ^ GrafoscopioTextModel +] + +{ #category : #operation } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 : #accessing } +GrafoscopioNode >> tagAs: 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" + aTag = 'código' ifTrue: [ ^ self toggleCodeText ]. + (self tags includes: aTag) + ifFalse: [ self tags add: aTag ]. + ^ self + +] + +{ #category : #accessing } +GrafoscopioNode >> tags [ + "I returns the receiver tags." + | migration | + tags isString + ifTrue: [ + migration := tags. + tags := OrderedCollection new. + self tagAs: migration ]. + tags ifNil: [ + tags := OrderedCollection new. + self tagAs: 'text' ]. + ^ tags +] + +{ #category : #accessing } +GrafoscopioNode >> tags: aCollection [ + tags := aCollection +] + +{ #category : #accessing } +GrafoscopioNode >> title [ + "Returns the receiver header" + + ^ header +] + +{ #category : #operation } +GrafoscopioNode >> 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 } +GrafoscopioNode >> 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 : #'as yet unclassified' } +GrafoscopioNode >> updateEditionTimestamp [ + self edited: DateAndTime now printString +] + +{ #category : #importing } +GrafoscopioNode >> uploadBodyFrom: fileLocator filteredFor: selectedLink [ + self body: fileLocator contents +] + +{ #category : #operation } +GrafoscopioNode >> visitLastLink [ + self lastLink = '' + ifTrue: [ self inform: 'This node has no associated links to visit'. ^ self ]. + [WebBrowser openOn: self lastLink] fork +] + +{ #category : #'as yet unclassified' } +GrafoscopioNode >> wrapBodyLines [ + "I convert the node body from HTML format to Pandoc's Markdown." + + self wrapBodyLines: 80 +] + +{ #category : #'as yet unclassified' } +GrafoscopioNode >> 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 ] ] ] +] diff --git a/src/Grafoscopio/GrafoscopioNodeTest.class.st b/src/Grafoscopio/GrafoscopioNodeTest.class.st index 06a9b22..e1bc480 100644 --- a/src/Grafoscopio/GrafoscopioNodeTest.class.st +++ b/src/Grafoscopio/GrafoscopioNodeTest.class.st @@ -1,171 +1,171 @@ -" -I test the main functionality of the GrafoscopioNode class. -" -Class { - #name : #GrafoscopioNodeTest, - #superclass : #TestCase, - #category : #'Grafoscopio-Model' -} - -{ #category : #tests } -GrafoscopioNodeTest >> dummyHtml [ - | txt | - txt := ' - - '. - ^ txt -] - -{ #category : #tests } -GrafoscopioNodeTest >> newTestTree [ - | node0 node1 node2 node3 node4 | - node0 := GrafoscopioTextNode new - created: DateAndTime now; - header: 'Arbol principal'. - node1 := GrafoscopioTextNode new - created: DateAndTime now; - header: 'Markup'; - body: 'I am just a node with markup'; - tagAs: 'text'; - links: 'temp.md'. - node2 := GrafoscopioTextNode new - created: DateAndTime now ; - header: '%output Code'; - tagAs: 'código'; - body: '(ConfigurationOfGrafoscopio>>#version14:) sourceCode'. - node3 := GrafoscopioTextNode new - created: DateAndTime now ; - header: '%invisible'; - tagAs: 'text'; - body: 'Just testing'. - node1 addNode: node3. - node4 := GrafoscopioTextNode new - created: DateAndTime now ; - header: 'Something'; - tagAs: 'text'; - body: '

else

'. - node1 addNode: node4. - node1 addNode: node2. - node0 addNode: node1. - ^ node0 -] - -{ #category : #tests } -GrafoscopioNodeTest >> testAddingChildren [ - | tree nnode orig | - tree := self newTestTree. - nnode := GrafoscopioTextNode new. - orig := tree children size. - tree addNode: nnode. - self assert: tree children size equals: orig + 1. - -] - -{ #category : #tests } -GrafoscopioNodeTest >> testDemoteNode [ - | tree child1 child2 | - tree := GrafoscopioTextNode new. - child1 := GrafoscopioTextNode new. - child2 := GrafoscopioTextNode new. - tree - addNode: child1; - addNode: child2. - child2 demote. - self assert: child2 level equals: child1 level + 1 - - -] - -{ #category : #tests } -GrafoscopioNodeTest >> testFindAndReplace [ - | tree | - tree := GrafoscopioTextNode new. - tree body: 'I''m only a test node.'. - tree find: 'only' andReplaceWith: 'JUST'. - self assert: (tree body findString: 'JUST') > 0. - -] - -{ #category : #tests } -GrafoscopioNodeTest >> testHasMarkdownSubtreesToExport [ - "Because becomeDefaultTestTree contains at least one non empty 'links' object that - points to a relative path in the file system, ending in '.md' or '.markdown' the - result of this test is true. - Please see look #becomeDefaultTestTree message to see the details that makes this test true." - | tree | - tree := self newTestTree. - self assert: tree selectMarkupSubtreesToExport isNotEmpty equals: true. - -] - -{ #category : #tests } -GrafoscopioNodeTest >> testInitializeIsOk [ - self shouldnt: [ GrafoscopioTextNode new ] raise: Error -] - -{ #category : #tests } -GrafoscopioNodeTest >> testPromoteNode [ - | tree child1 child2 | - tree := GrafoscopioTextNode new. - child1 := GrafoscopioTextNode new. - child2 := GrafoscopioTextNode new. - tree addNode: child1. - child1 addNode: child2. - child2 promote. - self assert: child2 level equals: child1 level - - -] - -{ #category : #'tests-utility' } -GrafoscopioNodeTest >> testRemoveLeadingLineNumbersSized [ - | copiedCode testNode | - copiedCode := ' - 1var tree = d3.layout.tree() - 2 .sort(null) - 3 .size([size.height, size.width - maxLabelLength*options.fontSize]) - 4 .children(function(d) - 5 { - 6 return (!d.contents || d.contents.length === 0) ? null : d.contents; - 7 }); - 8 - 9var nodes = tree.nodes(treeData); -10var links = tree.links(nodes); -11 '. - testNode := GrafoscopioTextNode new - body: copiedCode. - testNode removeLeadingLineNumbersSized: 3. - self assert: testNode body equals: ' -var tree = d3.layout.tree() - .sort(null) - .size([size.height, size.width - maxLabelLength*options.fontSize]) - .children(function(d) - { - return (!d.contents || d.contents.length === 0) ? null : d.contents; - }); - -var nodes = tree.nodes(treeData); -var links = tree.links(nodes); - -' -] - -{ #category : #tests } -GrafoscopioNodeTest >> testRemovingChildren [ - | tree orig | - tree := self newTestTree.. - orig := tree children size. - orig > 0 ifTrue: [ tree removeNode: (tree children at: 1) ]. - self assert: tree children size equals: orig - 1. - -] - -{ #category : #tests } -GrafoscopioNodeTest >> testSanitizedLink [ - | node link | - - link := 'docutopia://hackbo:hackbot'. - node := GrafoscopioTextNode new links: link. - self assert: (node sanitizeDefaultLink = 'https://docutopia.tupale.co/hackbo:hackbot') equals: true - -] +" +I test the main functionality of the GrafoscopioNode class. +" +Class { + #name : #GrafoscopioNodeTest, + #superclass : #TestCase, + #category : #'Grafoscopio-Model' +} + +{ #category : #tests } +GrafoscopioNodeTest >> dummyHtml [ + | txt | + txt := ' + + '. + ^ txt +] + +{ #category : #tests } +GrafoscopioNodeTest >> newTestTree [ + | node0 node1 node2 node3 node4 | + node0 := GrafoscopioTextNode new + created: DateAndTime now; + header: 'Arbol principal'. + node1 := GrafoscopioTextNode new + created: DateAndTime now; + header: 'Markup'; + body: 'I am just a node with markup'; + tagAs: 'text'; + links: 'temp.md'. + node2 := GrafoscopioTextNode new + created: DateAndTime now ; + header: '%output Code'; + tagAs: 'código'; + body: '(ConfigurationOfGrafoscopio>>#version14:) sourceCode'. + node3 := GrafoscopioTextNode new + created: DateAndTime now ; + header: '%invisible'; + tagAs: 'text'; + body: 'Just testing'. + node1 addNode: node3. + node4 := GrafoscopioTextNode new + created: DateAndTime now ; + header: 'Something'; + tagAs: 'text'; + body: '

else

'. + node1 addNode: node4. + node1 addNode: node2. + node0 addNode: node1. + ^ node0 +] + +{ #category : #tests } +GrafoscopioNodeTest >> testAddingChildren [ + | tree nnode orig | + tree := self newTestTree. + nnode := GrafoscopioTextNode new. + orig := tree children size. + tree addNode: nnode. + self assert: tree children size equals: orig + 1. + +] + +{ #category : #tests } +GrafoscopioNodeTest >> testDemoteNode [ + | tree child1 child2 | + tree := GrafoscopioTextNode new. + child1 := GrafoscopioTextNode new. + child2 := GrafoscopioTextNode new. + tree + addNode: child1; + addNode: child2. + child2 demote. + self assert: child2 level equals: child1 level + 1 + + +] + +{ #category : #tests } +GrafoscopioNodeTest >> testFindAndReplace [ + | tree | + tree := GrafoscopioTextNode new. + tree body: 'I''m only a test node.'. + tree find: 'only' andReplaceWith: 'JUST'. + self assert: (tree body findString: 'JUST') > 0. + +] + +{ #category : #tests } +GrafoscopioNodeTest >> testHasMarkdownSubtreesToExport [ + "Because becomeDefaultTestTree contains at least one non empty 'links' object that + points to a relative path in the file system, ending in '.md' or '.markdown' the + result of this test is true. + Please see look #becomeDefaultTestTree message to see the details that makes this test true." + | tree | + tree := self newTestTree. + self assert: tree selectMarkupSubtreesToExport isNotEmpty equals: true. + +] + +{ #category : #tests } +GrafoscopioNodeTest >> testInitializeIsOk [ + self shouldnt: [ GrafoscopioTextNode new ] raise: Error +] + +{ #category : #tests } +GrafoscopioNodeTest >> testPromoteNode [ + | tree child1 child2 | + tree := GrafoscopioTextNode new. + child1 := GrafoscopioTextNode new. + child2 := GrafoscopioTextNode new. + tree addNode: child1. + child1 addNode: child2. + child2 promote. + self assert: child2 level equals: child1 level + + +] + +{ #category : #'tests-utility' } +GrafoscopioNodeTest >> testRemoveLeadingLineNumbersSized [ + | copiedCode testNode | + copiedCode := ' + 1var tree = d3.layout.tree() + 2 .sort(null) + 3 .size([size.height, size.width - maxLabelLength*options.fontSize]) + 4 .children(function(d) + 5 { + 6 return (!d.contents || d.contents.length === 0) ? null : d.contents; + 7 }); + 8 + 9var nodes = tree.nodes(treeData); +10var links = tree.links(nodes); +11 '. + testNode := GrafoscopioTextNode new + body: copiedCode. + testNode removeLeadingLineNumbersSized: 3. + self assert: testNode body equals: ' +var tree = d3.layout.tree() + .sort(null) + .size([size.height, size.width - maxLabelLength*options.fontSize]) + .children(function(d) + { + return (!d.contents || d.contents.length === 0) ? null : d.contents; + }); + +var nodes = tree.nodes(treeData); +var links = tree.links(nodes); + +' +] + +{ #category : #tests } +GrafoscopioNodeTest >> testRemovingChildren [ + | tree orig | + tree := self newTestTree.. + orig := tree children size. + orig > 0 ifTrue: [ tree removeNode: (tree children at: 1) ]. + self assert: tree children size equals: orig - 1. + +] + +{ #category : #tests } +GrafoscopioNodeTest >> testSanitizedLink [ + | node link | + + link := 'docutopia://hackbo:hackbot'. + node := GrafoscopioTextNode new links: link. + self assert: (node sanitizeDefaultLink = 'https://docutopia.tupale.co/hackbo:hackbot') equals: true + +] diff --git a/src/Grafoscopio/GrafoscopioNotebook.class.st b/src/Grafoscopio/GrafoscopioNotebook.class.st index 953a52c..2f4ce62 100644 --- a/src/Grafoscopio/GrafoscopioNotebook.class.st +++ b/src/Grafoscopio/GrafoscopioNotebook.class.st @@ -1,1243 +1,1243 @@ -" -I am a Grafoscopio Notebook. - -Example: -| testTree nb | -testTree := GrafoscopioNode new becomeDefaultTestTree. -nb := GrafoscopioNotebook new. -nb notebookContent: testTree. -nb openWithSpec -" -Class { - #name : #GrafoscopioNotebook, - #superclass : #ComposablePresenter, - #instVars : [ - 'tree', - 'header', - 'body', - 'links', - 'windowMainMenu', - 'workingFile', - 'notebook', - 'debugMessage', - 'imagesList', - 'exporting' - ], - #classInstVars : [ - 'recents' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #utility } -GrafoscopioNotebook class >> SHA1For: aFile is: aSHA1String [ - "I verify that a file has the same signature that the one in a given string, - returning true in that case or false otherwise" - ^ (SHA1 new hashMessage: aFile asFileReference binaryReadStream contents) hex = aSHA1String - -] - -{ #category : #specs } -GrafoscopioNotebook class >> defaultSpec [ - - ^ SpecLayout composed - newColumn: [:tcol| - tcol newRow: [ :wrow | wrow add: #windowMainMenu ] height: (self toolbarHeight); - newRow: [:row | - row newColumn: [ :tc | - tc add: #tree - ] width: 300. - row newColumn: [ :bc | - bc newRow: [ :bcr | bcr add: #header ] height: self toolbarHeight. - bc add: #body; add: #links height: self toolbarHeight ]]] -] - -{ #category : #'instance creation' } -GrafoscopioNotebook class >> initialize [ - recents := Set new. -] - -{ #category : #'instance creation' } -GrafoscopioNotebook class >> newDefault [ - ^ self new. -] - -{ #category : #'instance creation' } -GrafoscopioNotebook class >> open: aFileReference [ - self newDefault openFromFile: aFileReference -] - -{ #category : #'instance creation' } -GrafoscopioNotebook class >> recents [ - ^ recents -] - -{ #category : #'instance creation' } -GrafoscopioNotebook class >> registerRecent: aFileReference [ - recents add: aFileReference -] - -{ #category : #utilities } -GrafoscopioNotebook >> addCommandFrom: dictionary into: stream [ - dictionary keysAndValuesDo: [ :k :v | - k = 'thisNotebook' - ifTrue: [ - stream nextPutAll: (GrafoscopioUtils perform: v on: self) ]] -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> addNode [ - self currentNode addNodeAfterMe. - self notebookContent: notebook. -] - -{ #category : #persistence } -GrafoscopioNotebook >> askToSaveBeforeClosing [ - - | saveChanges | - - saveChanges := UIManager default - question: 'Do you want to save changes in the notebook before closing?' - title: 'Save changes before closing?'. - saveChanges ifNil: [ ^ self notebook unsavedNodes inspect ]. - ^ saveChanges -] - -{ #category : #operation } -GrafoscopioNotebook >> autoSaveBodyOf: aNode [ - | playground bodyContents | - bodyContents := aNode body. - self body class = GrafoscopioTextModel - ifTrue: [ self body body - whenTextChangedDo: [ :arg | - aNode body: arg. - "self body body whenTextIsAccepted: [:bodyText | - self inform: bodyText. - aNode updateEditionTimestamp ]." - bodyContents = arg - ifFalse: [ "self inform: arg.""aNode updateEditionTimestamp" ] ] ]. - self body body class = GlamourPresentationModel - ifFalse: [ ^ self ]. - playground := self body body glmPres. - playground - onChangeOfPort: #text - act: [ :x | - aNode body: (x pane port: #entity) value content - "aNode updateEditionTimestamp." - "self inform: aNode edited" ] -] - -{ #category : #accessing } -GrafoscopioNotebook >> body [ - ^ body -] - -{ #category : #accessing } -GrafoscopioNotebook >> body: anObject [ - body := anObject -] - -{ #category : #utilities } -GrafoscopioNotebook >> checksumForRootSubtree [ - "I return the checksum (crypto hash) of the workingFile where this notebook is being stored. - I'm useful for data provenance and traceability of derivated files coming from this source - notebook." - self workingFile ifNil: [ ^ self ]. - self workingFile contents = '' ifTrue: [ ^ self ]. - ^ GrafoscopioUtils checksumFor: self workingFile -] - -{ #category : #'as yet unclassified' } -GrafoscopioNotebook >> content [ - self shouldBeImplemented. -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> copyNodeToClipboard [ - tree highlightedItem content copyToClipboard. - self notebookContent: notebook. -] - -{ #category : #persistence } -GrafoscopioNotebook >> createNewExample [ - | node0 node1 | - node0 := GrafoscopioNode new - created: DateAndTime now printString; - header: 'Arbol principal'; - tagAs: 'código'; - yourself. - node1 := GrafoscopioNode new - created: DateAndTime now printString; - header: 'Node 1'; - body: ''; - tagAs: 'text'; - yourself. - node0 addNode: node1. - ^ node0 -] - -{ #category : #operation } -GrafoscopioNotebook >> currentNode [ - ^ tree highlightedItem - ifNil: [ notebook children - ifEmpty: [ notebook root ] - ifNotEmpty: [ notebook children first ] ] - ifNotNil: [ :v | v content ] -] - -{ #category : #operation } -GrafoscopioNotebook >> currentNodeContent [ - ^ self currentNode content -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> cutNodeToClipboard [ - self copyNodeToClipboard; removeNode. -] - -{ #category : #accessing } -GrafoscopioNotebook >> debugMessage [ - ^ debugMessage ifNil: [ self defineDebugMessageUI ] -] - -{ #category : #accessing } -GrafoscopioNotebook >> debugMessage: aGrafoscopioNodeSelector [ - "I define a message that can be used for debugging purposes in the current notebook." - debugMessage := aGrafoscopioNodeSelector -] - -{ #category : #operation } -GrafoscopioNotebook >> debugWithSelector: aSymbol [ - "I invoke a message to debug in the current node. In the future the debugging scope can be changed to - include different elements instead of the current node." - | currentNode nodeContent | - currentNode := tree highlightedItem. - currentNode ifNil: [ ^ self ]. - nodeContent := currentNode content. - ^ (nodeContent perform: aSymbol asSymbol) inspect -] - -{ #category : #utilities } -GrafoscopioNotebook >> defineDebugMessageUI [ - | answer | - answer := UIManager default - request: 'Define debug message to be send to a selected node in this notebook.' - initialAnswer: 'messageNoDebugSelector'. - self debugMessage: answer -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> demoteNode [ - | editedNode | - editedNode := tree highlightedItem content. - editedNode demote. - self notebookContent: notebook. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNotebook >> downloadImages [ - "I download all images in a notebook into a local folder that respects relative paths. - So if a image refers to http://mysite.com/uploads/chap1/myimage.png, it will be stored - into: 'uploads/chap1/myimage.png' in the same folder where the notebook is stored. - This is helpful for notebooks conversions that expect to have local images in particular - locations." - - | parentFolder | - parentFolder := self workingFile parent. - self. - ^ self imagesList do: [ :each | | relativePathString link | - link := each contents asUrl. - relativePathString := link directory. - relativePathString ifNotEmpty: [ - GrafoscopioUtils ensureCreateDirectory: relativePathString into: parentFolder ]] -] - -{ #category : #persistence } -GrafoscopioNotebook >> ensureNotExporting [ - self isAlreadyExporting - ifTrue: [ ^ self error: ' Already exporting! Please wait ' ] -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportAllSubtreesAsMarkup [ - | toBeExported | - toBeExported := self notebook selectMarkupSubtreesToExport. - toBeExported ifEmpty: [ ^ self ]. - toBeExported do: [ :each | self subtreeAsMarkdownFileFor: each ]. - self inform: toBeExported size asString , ' exported markdown subtrees.' -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportAsHTML [ - "I export the current tree/document to a HTML file, using pandoc external app. - I suppose pandoc is already installed and available in the system." - - | htmlFile | - self markdownFile exists - ifTrue: [ self markdownFile delete ]. - self exportAsMarkdown. - htmlFile := self htmlFile. - htmlFile exists - ifTrue: [ htmlFile delete ]. - self - exportUsing: - {'--standalone'. - self markdownFile fullName. - '--output'. - htmlFile fullName} - output: htmlFile fullName - " - - - Smalltalk platformName = 'Win32' - ifTrue: [WinProcess createProcess: 'pandoc --standalone ', self markdownFile fullName, ' -o ', htmlFile]." -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportAsLaTeX [ - "I export the current tree/document to a LaTeX file, using pandoc external app. - I suppose pandoc is already installed and available in the system." - | texFile | - self markdownFile exists ifTrue: [ self markdownFile delete ]. - self halt. - - "self exportAsMarkdown.""<- This violates the separation of concenrs. Markdown exportation should - be explicit. There is still the issue of how to deal with desynchronization between a notebook - which has unsaved changes as markdown.... TO BE REVIWED!" - texFile := self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.tex'. - texFile asFileReference exists ifTrue: [ texFile asFileReference delete ]. - "OSProcess command: 'pandoc --standalone ', self markdownFile fullName, ' -o ', texFile." - self inform: ('File exported as: ', String cr, texFile). -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportAsMarkdown [ - "I export the current working tree/document to a markdown file." - workingFile - ifNil: [ self inform: 'File NOT exported. Please save the notebook on hard drive first' ] - ifNotNil: [ self exportNode: (self notebook) asMarkdownIn: (self markdownFile) ] -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportAsPDF [ - "I export the current tree/document to a PDF file, using pandoc and LaTeX external apps. - The latex engine used is xelatex, to minimize errors and warnings related with UTF8 support. - I suppose all them are already installed and defined in the system." - - | pandocCommonCommand | - self ensureNotExporting. - self exportAsMarkdown. - self pdfFile ensureDelete. - pandocCommonCommand := self pandocOptionsComputed , ' ' - , self markdownFile fullName , ' --output ' , self pdfFile fullName. - ^ self - exportUsing: ((' ' split: pandocCommonCommand) reject: #isEmpty) - output: self pdfFile fullName -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportAsSton: aNotebook on: aFileStream [ - aNotebook flatten. - self notebook root updateEditionTimestamp. - (STON writer on: aFileStream) - newLine: String crlf; - prettyPrint: true; - keepNewLines: true; - nextPut: aNotebook children. - -] - -{ #category : #utility } -GrafoscopioNotebook >> exportLinkContent [ - Transcript show: (self currentNodeContent asMarkdown) -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportNode: aGrafoscopioNode asMarkdownIn: aFile [ - "I export the current tree/document to a markdown file" - aFile ensureDelete. - aFile - ensureCreateFile; - writeStreamDo: [:stream | - stream - nextPutAll: - ('---', String cr, - 'exportedFrom: ', self checksumForRootSubtree, String cr) withInternetLineEndings. - aGrafoscopioNode metadataAsYamlIn: stream. - stream - nextPutAll: - ('---', String cr, String cr) withInternetLineEndings, - aGrafoscopioNode asMarkdown ]. - self inform: 'Exported as: ', String cr, aFile fullName -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportUsing: arguments [ - self ensureNotExporting. - exporting := (#pandoc command arguments: arguments) future. - exporting - onSuccessDo: [ :val | - exporting := nil. - self - inform: 'File exported as: ' , String cr , self pdfFile fullName ]. - exporting - onFailureDo: [ :e | - exporting := nil. - self - inform: 'Error exporting, ' , self pdfFile fullName , ': ' , e messageText ] -] - -{ #category : #persistence } -GrafoscopioNotebook >> exportUsing: arguments output: aName [ - self ensureNotExporting. - exporting := (#pandoc command arguments: arguments) future. - exporting - onSuccessDo: [ :val | - exporting := nil. - self inform: 'File exported as: ' , String cr , aName ]. - exporting - onFailureDo: [ :e | - exporting := nil. - self inform: 'Error exporting, ' , aName , ': ' , e messageText ]. - ^ exporting -] - -{ #category : #api } -GrafoscopioNotebook >> extent [ - ^900@500 -] - -{ #category : #'as yet unclassified' } -GrafoscopioNotebook >> findAndReplace [ - | currentNode replaceGUI findString replaceString | - currentNode := tree highlightedItem content. - replaceGUI := GrafoscopioReplace new. - replaceGUI openWithSpec. - replaceGUI ok - on: [ ] - do: [ - findString := replaceGUI returnValues at: 'find'. - replaceString := replaceGUI returnValues at: 'replace'. - currentNode find: findString andReplaceWith: replaceString. ] - -] - -{ #category : #testing } -GrafoscopioNotebook >> hasAWorkingFileDefined [ - self workingFile ifNil: [ ^ false ] ifNotNil: [ ^ true ] -] - -{ #category : #accessing } -GrafoscopioNotebook >> header [ - ^ header -] - -{ #category : #accessing } -GrafoscopioNotebook >> header: anObject [ - header := anObject -] - -{ #category : #persistence } -GrafoscopioNotebook >> htmlFile [ - ^ (self markdownFile parent fullName , '/' - , self markdownFile basenameWithoutExtension , '.html') - asFileReference -] - -{ #category : #operation } -GrafoscopioNotebook >> htmlToMarkdown [ - self currentNodeContent htmlToMarkdown. - self updateBodyFor: self currentNode -] - -{ #category : #operation } -GrafoscopioNotebook >> htmlToMarkdownSubtree [ - self currentNodeContent htmlToMarkdownSubtree. - self updateBodyFor: self currentNode -] - -{ #category : #accessing } -GrafoscopioNotebook >> imagesList [ - imagesList ifNil: [ ^ #('No images list for this notebook') ]. - ^ imagesList -] - -{ #category : #accessing } -GrafoscopioNotebook >> imagesList: anObject [ - self halt. - imagesList := anObject -] - -{ #category : #'as yet unclassified' } -GrafoscopioNotebook >> importImages [ - self imagesList: (Pandoc listImagesFrom: self markdownFile). - self inform: 'All notebook images has been imported.', String cr, 'Now you can list and download them.' -] - -{ #category : #operation } -GrafoscopioNotebook >> importLinkContent [ - "I see if a node link is an url located at 'http://ws.stfx.eu', wich means that is a shared - workspace, and convert the node body to an interactive playground" - | currentNode nodeContent | - currentNode := tree highlightedItem. - currentNode ifNil: [ ^ self ]. - nodeContent := currentNode content. - nodeContent importPlaygroundLink. - nodeContent importHtmlLink. - self updateBodyFor: currentNode -] - -{ #category : #initialization } -GrafoscopioNotebook >> initialize [ - super initialize. - self - notebook: (self createNewExample ); - title: ' New | Grafoscopio notebook'. - self notebookContent: self notebook. - -] - -{ #category : #initialization } -GrafoscopioNotebook >> initializePresenter [ - tree whenHighlightedItemChanged: [ :item | - tree highlightedItem ifNotNil: [self updateBodyFor: item]]. - tree whenTreeUpdated: [ :item | item ifNotNil: [self updateBodyFor: item]]. - header whenTextChanged: [ :arg | - (tree highlightedItem content header) = arg - ifFalse: [ - tree highlightedItem content header: arg. - tree highlightedItem content updateEditionTimestamp. - tree roots: tree roots]]. - links whenTextChanged: [ :arg | - tree highlightedItem content addLink: arg. - tree highlightedItem content updateEditionTimestamp. - ] -] - -{ #category : #initialization } -GrafoscopioNotebook >> initializeWidgets [ - windowMainMenu := self topBar. - header := self newTextInput. - header autoAccept: true. - body := self newText. - body class traceCr. - body disable. - body text: '<- Select a node'. - body autoAccept: true. - links := self newTextInput. - tree := self newTree. - tree - childrenBlock: [ :node | node children ]; - displayBlock: [ :node | node title ]. - self focusOrder - add: tree; - add: header; - add: body; - add: links. - self askOkToClose: true -] - -{ #category : #persistence } -GrafoscopioNotebook >> isAlreadyExporting [ - ^ exporting isNotNil -] - -{ #category : #persistence } -GrafoscopioNotebook >> isSaved [ - "I tell if a notebook has been saved in a persistence storage, including last editions." - ^ self hasAWorkingFileDefined and: [self isSavedAfterLastEdition ]. -] - -{ #category : #testing } -GrafoscopioNotebook >> isSavedAfterLastEdition [ - ^ self notebook isSavedAfterLastEdition -] - -{ #category : #accessing } -GrafoscopioNotebook >> links [ - ^ links -] - -{ #category : #accessing } -GrafoscopioNotebook >> links: anObject [ - links := anObject -] - -{ #category : #'as yet unclassified' } -GrafoscopioNotebook >> linksList [ - | currentNode | - currentNode := tree highlightedItem content. - GrafoscopioLinksList new - content: currentNode; - openWithSpec - -] - -{ #category : #utilities } -GrafoscopioNotebook >> listImagesUI [ - ListPresenter new - title: 'Images files list'; - items: self imagesList ; - openWithSpec -] - -{ #category : #persistence } -GrafoscopioNotebook >> loadFromFile: aFileReference [ - "I load the contents of aFileReference into a GrafoscopioNotebook, without opening it." - (aFileReference basename endsWith: 'ston') ifFalse: [ ^ self ]. - self workingFile: aFileReference. - self notebook: ((STON fromString: self workingFile contents) at: 1) parent. - self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'. - self notebookContent: self notebook. -] - -{ #category : #persistence } -GrafoscopioNotebook >> markdownFile [ - "I define the location of the markdown file where the notebook will be exported" - | markdownFile | - markdownFile := (((workingFile parent) / workingFile basenameWithoutExtension) fullName, '.markdown') asFileReference. - ^ markdownFile -] - -{ #category : #utilities } -GrafoscopioNotebook >> markdownFileChecksum [ - self workingFile ifNil: [ ^ self ]. - self workingFile contents = '' ifTrue: [ ^ self ]. - ^ GrafoscopioUtils checksumFor: self markdownFile -] - -{ #category : #utilities } -GrafoscopioNotebook >> markdownFileChecksumUpto: anInteger [ - "I cut the markdownFileCheckup upto a given integer. - Type coersion is needed, because this message argument can be read from a string in %metadata nodes. - Maybe the way used by playgrounds to import text as commands can be useful here." - ^ self markdownFileChecksum copyFrom: 1 to: anInteger asInteger. -] - -{ #category : #utilities } -GrafoscopioNotebook >> metadata [ - ^ self notebook metadata -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> moveSelectedNodeDown [ - | editedNode | - editedNode := tree selectedItem content. - editedNode moveDown. - self notebookContent: notebook -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> moveSelectedNodeUp [ - | editedNode | - editedNode := tree highlightedItem content. - editedNode moveUp. - self notebookContent: notebook -] - -{ #category : #utilities } -GrafoscopioNotebook >> navigateRelativePathFor: aFileString [ - "Given a relative path according to location of the notebook's workingFile, - I navigate to that file if exist and create it, including subdirectories if it does not exist. - If the relative path is located in a subdirectory that shares the route with the notebooks working - file, it must start with the folders name, - without using './' to point the same shared root " - - | finalLocation pathSegments | - aFileString ifEmpty: [ ^ self ]. - aFileString asUrl host ifNotNil: [ ^self ]. - finalLocation := workingFile parent. - pathSegments := aFileString splitOn: '/'. - pathSegments allButLastDo: [ :segment | - (segment = '..') - ifTrue: [ finalLocation := finalLocation parent ] - ifFalse: [ - finalLocation := finalLocation / segment. - finalLocation exists ifFalse: [ finalLocation ensureCreateDirectory ]]]. - finalLocation := finalLocation / (pathSegments last). - finalLocation exists ifFalse: [ finalLocation ensureCreateFile ]. - ^ finalLocation -] - -{ #category : #accessing } -GrafoscopioNotebook >> notebook [ - ^ notebook -] - -{ #category : #accessing } -GrafoscopioNotebook >> notebook: anObject [ - notebook := anObject -] - -{ #category : #api } -GrafoscopioNotebook >> notebookContent: aTree [ - tree - roots: (aTree children collect: [ :gfcNode | gfcNode asTreeNodePresenter ]). - tree selectedIndex: (tree selectedIndex min: aTree children size) -] - -{ #category : #initialization } -GrafoscopioNotebook >> notebookSubMenu [ - ^ MenuPresenter new - addGroup: [ :group | - group - addItem: [ :item | - item - name: 'Save'; - icon: (Smalltalk ui icons iconNamed: #smallSave); - shortcut: $s command; - action: [ self saveWorkingNotebook ] ]. - group - addItem: [ :item | - item - name: 'Save as...'; - icon: (Smalltalk ui icons iconNamed: #smallSaveAs); - action: [ self saveToFileUI ] ]. - group - addItem: [ :item | - item - name: 'Import images'; - icon: (Smalltalk ui icons iconNamed: #processBrowser); - action: [ self importImages ] ]. - group - addItem: [ :item | - item - name: 'See images list'; - icon: (Smalltalk ui icons iconNamed: #processBrowser); - action: [ self listImagesUI ] ]. - group - addItem: [ :item | - item - name: 'Download images'; - icon: (Smalltalk ui icons iconNamed: #processBrowser); - action: [ self downloadImages ] ]. - group - addItem: [ :item | - item - name: 'Export as markdown'; - icon: (Smalltalk ui icons iconNamed: #smallSaveAs); - action: [ self exportAsMarkdown ] ]. - group - addItem: [ :item | - item - name: 'Export as html'; - icon: (Smalltalk ui icons iconNamed: #smallWindow); - action: [ self exportAsHTML ] ]. - group - addItem: [ :item | - item - name: 'Export as LaTeX'; - icon: (Smalltalk ui icons iconNamed: #smallPrint); - action: [ self exportAsLaTeX ] ]. - group - addItem: [ :item | - item - name: 'Export as pdf'; - icon: (Smalltalk ui icons iconNamed: #smallPrint); - action: [ self exportAsPDF ] ]. - group - addItem: [ :item | - item - name: 'See html'; - icon: (self iconNamed: #smallInspectIt); - action: [ self seeHtml ] ]. - group - addItem: [ :item | - item - name: 'See pdf'; - icon: (Smalltalk ui icons iconNamed: #smallInspectIt); - action: [ self seePdf ] ]. - - group - addItem: [ :item | - item - name: 'Define debug message...'; - icon: Smalltalk ui icons glamorousBug; - action: [ self defineDebugMessageUI ] ] ] -] - -{ #category : #'event handling' } -GrafoscopioNotebook >> okToChange [ - - self isSaved ifTrue: [ ^ true ] ifFalse: [ ^ self askToSaveBeforeClosing ] -] - -{ #category : #persistence } -GrafoscopioNotebook >> openDefault [ -"I open a new default notebook" - - ^ self class new openWithSpec. -] - -{ #category : #persistence } -GrafoscopioNotebook >> openFromFile: aFileReference [ - self class registerRecent: aFileReference. - self loadFromFile: aFileReference. - ^ self openWithSpec. -] - -{ #category : #persistence } -GrafoscopioNotebook >> openFromFileSelector [ - - | file nb | - file := UIManager default - chooseExistingFileReference:'Choose a file' - extensions: #('ston') - path: FileLocator documents. - file ifNil: [ - self inform: 'No file selected'. - ^ self ]. - self workingFile: file. - nb := self class new. - nb openFromFile: self workingFile. - GfUIHelpers updateRecentNotebooksWith: workingFile -] - -{ #category : #persistence } -GrafoscopioNotebook >> openFromUrl: url [ -"Opens a tree from a file named aFileName" - - | fileName sanitized | - sanitized := GrafoscopioUtils sanitize: url. - fileName := sanitized segments last. - GrafoscopioUtils - downloadingFrom: sanitized - withMessage: 'Downloading document...' - into: FileLocator temp. - self class new openFromFile: (FileLocator temp / fileName) -] - -{ #category : #persistence } -GrafoscopioNotebook >> openFromUrlUI [ - "This method generates the UI for the openFromUrl: method, it asks for a URL from the user" - - | fileUrl | - "GrafoscopioBrowser configureSettings." - fileUrl := UIManager default - textEntry: 'Enter the URL' - title: 'Open notebook from URL'. - fileUrl isNil ifTrue: [ ^nil ]. - self class new openFromUrl: fileUrl -] - -{ #category : #utilities } -GrafoscopioNotebook >> pandocOptions [ - ^ self notebook pandocOptions -] - -{ #category : #utilities } -GrafoscopioNotebook >> pandocOptionsComputed [ - "I convert the pandoc options array into a single line that can be used with the pandoc command." - - | result | - result := '' writeStream. - self pandocOptions ifNil: [ ^ '' ]. - self pandocOptions - do: [ :option | - option isDictionary - ifTrue: [ - self addCommandFrom: option into: result ] - ifFalse: [ - result - nextPutAll: option] ]. - ^ result contents -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> pasteNodeFromClipboard [ - tree highlightedItem content pasteFromClipboard. - self notebookContent: notebook. -] - -{ #category : #persistence } -GrafoscopioNotebook >> pdfFile [ - "I define the location of the markdown file where the notebook will be exported" - | pdfFile | - pdfFile := (self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.pdf') asFileReference. - ^ pdfFile. - -] - -{ #category : #initialization } -GrafoscopioNotebook >> projectSubMenu [ - ^ MenuModel new - addGroup: [ :group | - group - addItem: [ :item | - item - name: 'Activate remote repository...'; - icon: Smalltalk ui icons smallPushpinIcon; - action: [ self inform: 'To be implemented ...' ] ]. - group - addItem: [ :item | - item - name: 'Activate local repository...'; - icon: Smalltalk ui icons homeIcon; - action: [ self inform: 'To be implemented ...' ] ]. - group - addItem: [ :item | - item - name: 'Add file...'; - icon: Smalltalk ui icons newerPackagesAvailableIcon; - action: [ self inform: 'To be implemented ...' ] ]. - group - addItem: [ :item | - item - name: 'Delete file...'; - icon: Smalltalk ui icons packageDeleteIcon; - action: [ self inform: 'To be implemented ...' ] ]. - group - addItem: [ :item | - item - name: 'Commit to repository'; - icon: Smalltalk ui icons smallScreenshotIcon; - action: [ self inform: 'To be implemented ...' ] ]. - group - addItem: [ :item | - item - name: 'Credentials'; - icon: (self iconNamed: #userIcon); - action: [ self inform: 'To be implemented ...' ] ] ] -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> promoteNode [ - | editedNote | - editedNote := tree selectedItem content. - editedNote promote. - self notebookContent: notebook -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> removeNode [ - | contentToDelete parentContent newSelectedContent children | - 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 ] ] - ifFalse: [ newSelectedContent := parentContent ]. - contentToDelete parent removeNode: contentToDelete. - self notebookContent: notebook -] - -{ #category : #persistence } -GrafoscopioNotebook >> saveToFile: aFileReference [ - "I save the current tree/document to a file and update storage timestamp." - - aFileReference ifNil: [ self inform: 'No file selected for saving. Save NOT done.'. ^ self ]. - workingFile := aFileReference. - self workingFile ensureDelete. - self workingFile writeStreamDo: [:stream | - self exportAsSton: self notebook on: stream ]. - self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'. - self inform: ('File saved at: ', String cr, self workingFile fullName). - GfUIHelpers updateRecentNotebooksWith: aFileReference. -] - -{ #category : #persistence } -GrafoscopioNotebook >> saveToFileUI [ - - | file | - - file := UIManager default - chooseForSaveFileReference: 'Save notebook to file as...' - extensions: #('ston') - path: (workingFile ifNotNil: [ workingFile parent ] ifNil: [ FileLocator documents ] ). - file - ifNil: [ self inform: 'Saving to file cancelled'. ^ self ] - ifNotNil:[self saveToFile: file]. -] - -{ #category : #persistence } -GrafoscopioNotebook >> saveWorkingNotebook [ - "Saves the current tree to the user predefined file location used when he/she opened it." - self workingFile - ifNil: [ self saveToFileUI ] - ifNotNil: [ self saveToFile: workingFile ]. - self notebook root updateEditionTimestamp. - GfUIHelpers updateRecentNotebooksWith: workingFile - - -] - -{ #category : #inspecting } -GrafoscopioNotebook >> seeHtml [ - self pdfFile exists - ifTrue: [ (#open command argument: self htmlFile fullName) schedule ] -] - -{ #category : #persistence } -GrafoscopioNotebook >> seePdf [ - self exportAsPDF - onSuccessDo: [ :v | (#open command argument: self pdfFile fullName) schedule ] -] - -{ #category : #persistence } -GrafoscopioNotebook >> subtreeAsMarkdown [ - | currentNode | - currentNode := tree highlightedItem content. - self inform: ('Exported as: ', String cr, (self subtreeAsMarkdownFileFor: currentNode) fullName ) -] - -{ #category : #persistence } -GrafoscopioNotebook >> subtreeAsMarkdownFileFor: aNode [ - | exportedFile | - aNode links ifEmpty: [ ^ self ]. - exportedFile:= self navigateRelativePathFor: aNode links last. - exportedFile class = self class ifTrue: [ ^ self ]. - self exportNode: aNode asMarkdownIn: exportedFile. - ^ exportedFile -] - -{ #category : #'editing nodes' } -GrafoscopioNotebook >> toggleCodeNode [ - | currentNode | - currentNode := tree highlightedItem. - currentNode content toggleCodeText. - self updateBodyFor: currentNode. -] - -{ #category : #initialization } -GrafoscopioNotebook >> topBar [ - ^ MenuPresenter new - addGroup: [ :group | - group - addItem: [ :item | - item - name: 'Notebook'; - icon: (self iconNamed: #smallObjects); - subMenu: self notebookSubMenu ]. - group - addItem: [ :item | - item - name: 'Project'; - icon: (self iconNamed: #catalog); - subMenu: self projectSubMenu ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: nil; - description: 'Save notebook'; - icon: (self iconNamed: #glamorousSave); - action: [ self saveWorkingNotebook ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Export all Markdown subtrees'; - icon: (self iconNamed: #glamorousMore); - action: [ self exportAllSubtreesAsMarkup ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Cut'; - icon: (self iconNamed: #smallCut); - action: [ self cutNodeToClipboard ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Copy'; - icon: (self iconNamed: #smallCopy); - action: [ self copyNodeToClipboard ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Paste'; - icon: (self iconNamed: #smallPaste); - action: [ self pasteNodeFromClipboard ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Find & Replace'; - icon: (self iconNamed: #smallFind); - action: [ self findAndReplace ] ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: nil; - description: 'Add node'; - icon: MendaIcons new plusIcon; - action: [ self addNode ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Delete node'; - icon: MendaIcons new minusIcon; - action: [ self removeNode ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Move node up'; - icon: MendaIcons new arrowUpIcon; - action: [ self moveSelectedNodeUp ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Move node down'; - icon: MendaIcons new arrowDownIcon; - action: [ self moveSelectedNodeDown ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Move node left'; - icon: MendaIcons new arrowLeftIcon; - action: [ self promoteNode ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Move node right'; - icon: MendaIcons new arrowRightIcon; - action: [ self demoteNode ] ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: nil; - description: 'Toggle: code <--> text'; - icon: MendaIcons new smalltalkCodeIcon; - action: [ self toggleCodeNode ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'List node links'; - icon: (self iconNamed: #tinyMenu); - action: [ self linksList ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Visit link'; - icon: (self iconNamed: #glamorousRight); - action: [ self visitNodeLink ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Import link content'; - icon: (self iconNamed: #glamorousOpenFromUrl); - action: [ self importLinkContent ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Export link content'; - icon: (self iconNamed: #glamorousSaveToUrl); - action: [ self exportLinkContent ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'HTML to Markdown'; - icon: (self iconNamed: #smallProfile); - action: [ self htmlToMarkdown ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'HTML to Markdown subtree'; - icon: (self iconNamed: #hierarchy); - action: [ self htmlToMarkdownSubtree ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Tag as...'; - icon: MendaIcons new tagAddIcon; - action: [ self inform: 'To be implemented...' ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Untag ....'; - icon: MendaIcons new tagMinusIcon; - action: [ self inform: 'To be implemented...' ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Edit tags...'; - icon: FontAwesomeIcons new tagsIcon; - action: [ self inform: 'To be implemented...' ] ] ]; - addGroup: [ :debug | - debug - addItem: [ :item | - item - name: nil; - description: 'Debug'; - icon: - (self iconNamed: #glamorousBug); - action: [ self debugWithSelector: self debugMessage ] ] ] -] - -{ #category : #accessing } -GrafoscopioNotebook >> tree [ - ^ tree -] - -{ #category : #accessing } -GrafoscopioNotebook >> tree: anObject [ - tree := anObject -] - -{ #category : #operation } -GrafoscopioNotebook >> updateBodyFor: aNodeContainer [ - | aNode | - self needRebuild: false. - tree needRebuild: false. - body needRebuild: true. - aNode := aNodeContainer content. - header text: aNode header. - body := self instantiate: aNode specModelClass new. - body content: aNode body. - links text: aNode lastLink. - - self buildWithSpecLayout: self class defaultSpec -] - -{ #category : #operation } -GrafoscopioNotebook >> visitNodeLink [ - - tree highlightedItem content visitLastLink. -] - -{ #category : #accessing } -GrafoscopioNotebook >> windowMainMenu [ - ^ windowMainMenu -] - -{ #category : #accessing } -GrafoscopioNotebook >> windowMainMenu: anObject [ - windowMainMenu := anObject -] - -{ #category : #accessing } -GrafoscopioNotebook >> workingFile [ - ^ workingFile -] - -{ #category : #accessing } -GrafoscopioNotebook >> workingFile: aFileReference [ - workingFile := aFileReference. -] - -{ #category : #'as yet unclassified' } -GrafoscopioNotebook >> wrapBodyLines [ - self currentNodeContent wrapBodyLines. - self updateBodyFor: self currentNode -] +" +I am a Grafoscopio Notebook. + +Example: +| testTree nb | +testTree := GrafoscopioNode new becomeDefaultTestTree. +nb := GrafoscopioNotebook new. +nb notebookContent: testTree. +nb openWithSpec +" +Class { + #name : #GrafoscopioNotebook, + #superclass : #ComposablePresenter, + #instVars : [ + 'tree', + 'header', + 'body', + 'links', + 'windowMainMenu', + 'workingFile', + 'notebook', + 'debugMessage', + 'imagesList', + 'exporting' + ], + #classInstVars : [ + 'recents' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #utility } +GrafoscopioNotebook class >> SHA1For: aFile is: aSHA1String [ + "I verify that a file has the same signature that the one in a given string, + returning true in that case or false otherwise" + ^ (SHA1 new hashMessage: aFile asFileReference binaryReadStream contents) hex = aSHA1String + +] + +{ #category : #specs } +GrafoscopioNotebook class >> defaultSpec [ + + ^ SpecLayout composed + newColumn: [:tcol| + tcol newRow: [ :wrow | wrow add: #windowMainMenu ] height: (self toolbarHeight); + newRow: [:row | + row newColumn: [ :tc | + tc add: #tree + ] width: 300. + row newColumn: [ :bc | + bc newRow: [ :bcr | bcr add: #header ] height: self toolbarHeight. + bc add: #body; add: #links height: self toolbarHeight ]]] +] + +{ #category : #'instance creation' } +GrafoscopioNotebook class >> initialize [ + recents := Set new. +] + +{ #category : #'instance creation' } +GrafoscopioNotebook class >> newDefault [ + ^ self new. +] + +{ #category : #'instance creation' } +GrafoscopioNotebook class >> open: aFileReference [ + self newDefault openFromFile: aFileReference +] + +{ #category : #'instance creation' } +GrafoscopioNotebook class >> recents [ + ^ recents +] + +{ #category : #'instance creation' } +GrafoscopioNotebook class >> registerRecent: aFileReference [ + recents add: aFileReference +] + +{ #category : #utilities } +GrafoscopioNotebook >> addCommandFrom: dictionary into: stream [ + dictionary keysAndValuesDo: [ :k :v | + k = 'thisNotebook' + ifTrue: [ + stream nextPutAll: (GrafoscopioUtils perform: v on: self) ]] +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> addNode [ + self currentNode addNodeAfterMe. + self notebookContent: notebook. +] + +{ #category : #persistence } +GrafoscopioNotebook >> askToSaveBeforeClosing [ + + | saveChanges | + + saveChanges := UIManager default + question: 'Do you want to save changes in the notebook before closing?' + title: 'Save changes before closing?'. + saveChanges ifNil: [ ^ self notebook unsavedNodes inspect ]. + ^ saveChanges +] + +{ #category : #operation } +GrafoscopioNotebook >> autoSaveBodyOf: aNode [ + | playground bodyContents | + bodyContents := aNode body. + self body class = GrafoscopioTextModel + ifTrue: [ self body body + whenTextChangedDo: [ :arg | + aNode body: arg. + "self body body whenTextIsAccepted: [:bodyText | + self inform: bodyText. + aNode updateEditionTimestamp ]." + bodyContents = arg + ifFalse: [ "self inform: arg.""aNode updateEditionTimestamp" ] ] ]. + self body body class = GlamourPresentationModel + ifFalse: [ ^ self ]. + playground := self body body glmPres. + playground + onChangeOfPort: #text + act: [ :x | + aNode body: (x pane port: #entity) value content + "aNode updateEditionTimestamp." + "self inform: aNode edited" ] +] + +{ #category : #accessing } +GrafoscopioNotebook >> body [ + ^ body +] + +{ #category : #accessing } +GrafoscopioNotebook >> body: anObject [ + body := anObject +] + +{ #category : #utilities } +GrafoscopioNotebook >> checksumForRootSubtree [ + "I return the checksum (crypto hash) of the workingFile where this notebook is being stored. + I'm useful for data provenance and traceability of derivated files coming from this source + notebook." + self workingFile ifNil: [ ^ self ]. + self workingFile contents = '' ifTrue: [ ^ self ]. + ^ GrafoscopioUtils checksumFor: self workingFile +] + +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> content [ + self shouldBeImplemented. +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> copyNodeToClipboard [ + tree highlightedItem content copyToClipboard. + self notebookContent: notebook. +] + +{ #category : #persistence } +GrafoscopioNotebook >> createNewExample [ + | node0 node1 | + node0 := GrafoscopioNode new + created: DateAndTime now printString; + header: 'Arbol principal'; + tagAs: 'código'; + yourself. + node1 := GrafoscopioNode new + created: DateAndTime now printString; + header: 'Node 1'; + body: ''; + tagAs: 'text'; + yourself. + node0 addNode: node1. + ^ node0 +] + +{ #category : #operation } +GrafoscopioNotebook >> currentNode [ + ^ tree highlightedItem + ifNil: [ notebook children + ifEmpty: [ notebook root ] + ifNotEmpty: [ notebook children first ] ] + ifNotNil: [ :v | v content ] +] + +{ #category : #operation } +GrafoscopioNotebook >> currentNodeContent [ + ^ self currentNode content +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> cutNodeToClipboard [ + self copyNodeToClipboard; removeNode. +] + +{ #category : #accessing } +GrafoscopioNotebook >> debugMessage [ + ^ debugMessage ifNil: [ self defineDebugMessageUI ] +] + +{ #category : #accessing } +GrafoscopioNotebook >> debugMessage: aGrafoscopioNodeSelector [ + "I define a message that can be used for debugging purposes in the current notebook." + debugMessage := aGrafoscopioNodeSelector +] + +{ #category : #operation } +GrafoscopioNotebook >> debugWithSelector: aSymbol [ + "I invoke a message to debug in the current node. In the future the debugging scope can be changed to + include different elements instead of the current node." + | currentNode nodeContent | + currentNode := tree highlightedItem. + currentNode ifNil: [ ^ self ]. + nodeContent := currentNode content. + ^ (nodeContent perform: aSymbol asSymbol) inspect +] + +{ #category : #utilities } +GrafoscopioNotebook >> defineDebugMessageUI [ + | answer | + answer := UIManager default + request: 'Define debug message to be send to a selected node in this notebook.' + initialAnswer: 'messageNoDebugSelector'. + self debugMessage: answer +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> demoteNode [ + | editedNode | + editedNode := tree highlightedItem content. + editedNode demote. + self notebookContent: notebook. +] + +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> downloadImages [ + "I download all images in a notebook into a local folder that respects relative paths. + So if a image refers to http://mysite.com/uploads/chap1/myimage.png, it will be stored + into: 'uploads/chap1/myimage.png' in the same folder where the notebook is stored. + This is helpful for notebooks conversions that expect to have local images in particular + locations." + + | parentFolder | + parentFolder := self workingFile parent. + self. + ^ self imagesList do: [ :each | | relativePathString link | + link := each contents asUrl. + relativePathString := link directory. + relativePathString ifNotEmpty: [ + GrafoscopioUtils ensureCreateDirectory: relativePathString into: parentFolder ]] +] + +{ #category : #persistence } +GrafoscopioNotebook >> ensureNotExporting [ + self isAlreadyExporting + ifTrue: [ ^ self error: ' Already exporting! Please wait ' ] +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportAllSubtreesAsMarkup [ + | toBeExported | + toBeExported := self notebook selectMarkupSubtreesToExport. + toBeExported ifEmpty: [ ^ self ]. + toBeExported do: [ :each | self subtreeAsMarkdownFileFor: each ]. + self inform: toBeExported size asString , ' exported markdown subtrees.' +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportAsHTML [ + "I export the current tree/document to a HTML file, using pandoc external app. + I suppose pandoc is already installed and available in the system." + + | htmlFile | + self markdownFile exists + ifTrue: [ self markdownFile delete ]. + self exportAsMarkdown. + htmlFile := self htmlFile. + htmlFile exists + ifTrue: [ htmlFile delete ]. + self + exportUsing: + {'--standalone'. + self markdownFile fullName. + '--output'. + htmlFile fullName} + output: htmlFile fullName + " + + + Smalltalk platformName = 'Win32' + ifTrue: [WinProcess createProcess: 'pandoc --standalone ', self markdownFile fullName, ' -o ', htmlFile]." +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportAsLaTeX [ + "I export the current tree/document to a LaTeX file, using pandoc external app. + I suppose pandoc is already installed and available in the system." + | texFile | + self markdownFile exists ifTrue: [ self markdownFile delete ]. + self halt. + + "self exportAsMarkdown.""<- This violates the separation of concenrs. Markdown exportation should + be explicit. There is still the issue of how to deal with desynchronization between a notebook + which has unsaved changes as markdown.... TO BE REVIWED!" + texFile := self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.tex'. + texFile asFileReference exists ifTrue: [ texFile asFileReference delete ]. + "OSProcess command: 'pandoc --standalone ', self markdownFile fullName, ' -o ', texFile." + self inform: ('File exported as: ', String cr, texFile). +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportAsMarkdown [ + "I export the current working tree/document to a markdown file." + workingFile + ifNil: [ self inform: 'File NOT exported. Please save the notebook on hard drive first' ] + ifNotNil: [ self exportNode: (self notebook) asMarkdownIn: (self markdownFile) ] +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportAsPDF [ + "I export the current tree/document to a PDF file, using pandoc and LaTeX external apps. + The latex engine used is xelatex, to minimize errors and warnings related with UTF8 support. + I suppose all them are already installed and defined in the system." + + | pandocCommonCommand | + self ensureNotExporting. + self exportAsMarkdown. + self pdfFile ensureDelete. + pandocCommonCommand := self pandocOptionsComputed , ' ' + , self markdownFile fullName , ' --output ' , self pdfFile fullName. + ^ self + exportUsing: ((' ' split: pandocCommonCommand) reject: #isEmpty) + output: self pdfFile fullName +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportAsSton: aNotebook on: aFileStream [ + aNotebook flatten. + self notebook root updateEditionTimestamp. + (STON writer on: aFileStream) + newLine: String crlf; + prettyPrint: true; + keepNewLines: true; + nextPut: aNotebook children. + +] + +{ #category : #utility } +GrafoscopioNotebook >> exportLinkContent [ + Transcript show: (self currentNodeContent asMarkdown) +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportNode: aGrafoscopioNode asMarkdownIn: aFile [ + "I export the current tree/document to a markdown file" + aFile ensureDelete. + aFile + ensureCreateFile; + writeStreamDo: [:stream | + stream + nextPutAll: + ('---', String cr, + 'exportedFrom: ', self checksumForRootSubtree, String cr) withInternetLineEndings. + aGrafoscopioNode metadataAsYamlIn: stream. + stream + nextPutAll: + ('---', String cr, String cr) withInternetLineEndings, + aGrafoscopioNode asMarkdown ]. + self inform: 'Exported as: ', String cr, aFile fullName +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportUsing: arguments [ + self ensureNotExporting. + exporting := (#pandoc command arguments: arguments) future. + exporting + onSuccessDo: [ :val | + exporting := nil. + self + inform: 'File exported as: ' , String cr , self pdfFile fullName ]. + exporting + onFailureDo: [ :e | + exporting := nil. + self + inform: 'Error exporting, ' , self pdfFile fullName , ': ' , e messageText ] +] + +{ #category : #persistence } +GrafoscopioNotebook >> exportUsing: arguments output: aName [ + self ensureNotExporting. + exporting := (#pandoc command arguments: arguments) future. + exporting + onSuccessDo: [ :val | + exporting := nil. + self inform: 'File exported as: ' , String cr , aName ]. + exporting + onFailureDo: [ :e | + exporting := nil. + self inform: 'Error exporting, ' , aName , ': ' , e messageText ]. + ^ exporting +] + +{ #category : #api } +GrafoscopioNotebook >> extent [ + ^900@500 +] + +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> findAndReplace [ + | currentNode replaceGUI findString replaceString | + currentNode := tree highlightedItem content. + replaceGUI := GrafoscopioReplace new. + replaceGUI openWithSpec. + replaceGUI ok + on: [ ] + do: [ + findString := replaceGUI returnValues at: 'find'. + replaceString := replaceGUI returnValues at: 'replace'. + currentNode find: findString andReplaceWith: replaceString. ] + +] + +{ #category : #testing } +GrafoscopioNotebook >> hasAWorkingFileDefined [ + self workingFile ifNil: [ ^ false ] ifNotNil: [ ^ true ] +] + +{ #category : #accessing } +GrafoscopioNotebook >> header [ + ^ header +] + +{ #category : #accessing } +GrafoscopioNotebook >> header: anObject [ + header := anObject +] + +{ #category : #persistence } +GrafoscopioNotebook >> htmlFile [ + ^ (self markdownFile parent fullName , '/' + , self markdownFile basenameWithoutExtension , '.html') + asFileReference +] + +{ #category : #operation } +GrafoscopioNotebook >> htmlToMarkdown [ + self currentNodeContent htmlToMarkdown. + self updateBodyFor: self currentNode +] + +{ #category : #operation } +GrafoscopioNotebook >> htmlToMarkdownSubtree [ + self currentNodeContent htmlToMarkdownSubtree. + self updateBodyFor: self currentNode +] + +{ #category : #accessing } +GrafoscopioNotebook >> imagesList [ + imagesList ifNil: [ ^ #('No images list for this notebook') ]. + ^ imagesList +] + +{ #category : #accessing } +GrafoscopioNotebook >> imagesList: anObject [ + self halt. + imagesList := anObject +] + +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> importImages [ + self imagesList: (Pandoc listImagesFrom: self markdownFile). + self inform: 'All notebook images has been imported.', String cr, 'Now you can list and download them.' +] + +{ #category : #operation } +GrafoscopioNotebook >> importLinkContent [ + "I see if a node link is an url located at 'http://ws.stfx.eu', wich means that is a shared + workspace, and convert the node body to an interactive playground" + | currentNode nodeContent | + currentNode := tree highlightedItem. + currentNode ifNil: [ ^ self ]. + nodeContent := currentNode content. + nodeContent importPlaygroundLink. + nodeContent importHtmlLink. + self updateBodyFor: currentNode +] + +{ #category : #initialization } +GrafoscopioNotebook >> initialize [ + super initialize. + self + notebook: (self createNewExample ); + title: ' New | Grafoscopio notebook'. + self notebookContent: self notebook. + +] + +{ #category : #initialization } +GrafoscopioNotebook >> initializePresenter [ + tree whenHighlightedItemChanged: [ :item | + tree highlightedItem ifNotNil: [self updateBodyFor: item]]. + tree whenTreeUpdated: [ :item | item ifNotNil: [self updateBodyFor: item]]. + header whenTextChanged: [ :arg | + (tree highlightedItem content header) = arg + ifFalse: [ + tree highlightedItem content header: arg. + tree highlightedItem content updateEditionTimestamp. + tree roots: tree roots]]. + links whenTextChanged: [ :arg | + tree highlightedItem content addLink: arg. + tree highlightedItem content updateEditionTimestamp. + ] +] + +{ #category : #initialization } +GrafoscopioNotebook >> initializeWidgets [ + windowMainMenu := self topBar. + header := self newTextInput. + header autoAccept: true. + body := self newText. + body class traceCr. + body disable. + body text: '<- Select a node'. + body autoAccept: true. + links := self newTextInput. + tree := self newTree. + tree + childrenBlock: [ :node | node children ]; + displayBlock: [ :node | node title ]. + self focusOrder + add: tree; + add: header; + add: body; + add: links. + self askOkToClose: true +] + +{ #category : #persistence } +GrafoscopioNotebook >> isAlreadyExporting [ + ^ exporting isNotNil +] + +{ #category : #persistence } +GrafoscopioNotebook >> isSaved [ + "I tell if a notebook has been saved in a persistence storage, including last editions." + ^ self hasAWorkingFileDefined and: [self isSavedAfterLastEdition ]. +] + +{ #category : #testing } +GrafoscopioNotebook >> isSavedAfterLastEdition [ + ^ self notebook isSavedAfterLastEdition +] + +{ #category : #accessing } +GrafoscopioNotebook >> links [ + ^ links +] + +{ #category : #accessing } +GrafoscopioNotebook >> links: anObject [ + links := anObject +] + +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> linksList [ + | currentNode | + currentNode := tree highlightedItem content. + GrafoscopioLinksList new + content: currentNode; + openWithSpec + +] + +{ #category : #utilities } +GrafoscopioNotebook >> listImagesUI [ + ListPresenter new + title: 'Images files list'; + items: self imagesList ; + openWithSpec +] + +{ #category : #persistence } +GrafoscopioNotebook >> loadFromFile: aFileReference [ + "I load the contents of aFileReference into a GrafoscopioNotebook, without opening it." + (aFileReference basename endsWith: 'ston') ifFalse: [ ^ self ]. + self workingFile: aFileReference. + self notebook: ((STON fromString: self workingFile contents) at: 1) parent. + self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'. + self notebookContent: self notebook. +] + +{ #category : #persistence } +GrafoscopioNotebook >> markdownFile [ + "I define the location of the markdown file where the notebook will be exported" + | markdownFile | + markdownFile := (((workingFile parent) / workingFile basenameWithoutExtension) fullName, '.markdown') asFileReference. + ^ markdownFile +] + +{ #category : #utilities } +GrafoscopioNotebook >> markdownFileChecksum [ + self workingFile ifNil: [ ^ self ]. + self workingFile contents = '' ifTrue: [ ^ self ]. + ^ GrafoscopioUtils checksumFor: self markdownFile +] + +{ #category : #utilities } +GrafoscopioNotebook >> markdownFileChecksumUpto: anInteger [ + "I cut the markdownFileCheckup upto a given integer. + Type coersion is needed, because this message argument can be read from a string in %metadata nodes. + Maybe the way used by playgrounds to import text as commands can be useful here." + ^ self markdownFileChecksum copyFrom: 1 to: anInteger asInteger. +] + +{ #category : #utilities } +GrafoscopioNotebook >> metadata [ + ^ self notebook metadata +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> moveSelectedNodeDown [ + | editedNode | + editedNode := tree selectedItem content. + editedNode moveDown. + self notebookContent: notebook +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> moveSelectedNodeUp [ + | editedNode | + editedNode := tree highlightedItem content. + editedNode moveUp. + self notebookContent: notebook +] + +{ #category : #utilities } +GrafoscopioNotebook >> navigateRelativePathFor: aFileString [ + "Given a relative path according to location of the notebook's workingFile, + I navigate to that file if exist and create it, including subdirectories if it does not exist. + If the relative path is located in a subdirectory that shares the route with the notebooks working + file, it must start with the folders name, + without using './' to point the same shared root " + + | finalLocation pathSegments | + aFileString ifEmpty: [ ^ self ]. + aFileString asUrl host ifNotNil: [ ^self ]. + finalLocation := workingFile parent. + pathSegments := aFileString splitOn: '/'. + pathSegments allButLastDo: [ :segment | + (segment = '..') + ifTrue: [ finalLocation := finalLocation parent ] + ifFalse: [ + finalLocation := finalLocation / segment. + finalLocation exists ifFalse: [ finalLocation ensureCreateDirectory ]]]. + finalLocation := finalLocation / (pathSegments last). + finalLocation exists ifFalse: [ finalLocation ensureCreateFile ]. + ^ finalLocation +] + +{ #category : #accessing } +GrafoscopioNotebook >> notebook [ + ^ notebook +] + +{ #category : #accessing } +GrafoscopioNotebook >> notebook: anObject [ + notebook := anObject +] + +{ #category : #api } +GrafoscopioNotebook >> notebookContent: aTree [ + tree + roots: (aTree children collect: [ :gfcNode | gfcNode asTreeNodePresenter ]). + tree selectedIndex: (tree selectedIndex min: aTree children size) +] + +{ #category : #initialization } +GrafoscopioNotebook >> notebookSubMenu [ + ^ MenuPresenter new + addGroup: [ :group | + group + addItem: [ :item | + item + name: 'Save'; + icon: (Smalltalk ui icons iconNamed: #smallSave); + shortcut: $s command; + action: [ self saveWorkingNotebook ] ]. + group + addItem: [ :item | + item + name: 'Save as...'; + icon: (Smalltalk ui icons iconNamed: #smallSaveAs); + action: [ self saveToFileUI ] ]. + group + addItem: [ :item | + item + name: 'Import images'; + icon: (Smalltalk ui icons iconNamed: #processBrowser); + action: [ self importImages ] ]. + group + addItem: [ :item | + item + name: 'See images list'; + icon: (Smalltalk ui icons iconNamed: #processBrowser); + action: [ self listImagesUI ] ]. + group + addItem: [ :item | + item + name: 'Download images'; + icon: (Smalltalk ui icons iconNamed: #processBrowser); + action: [ self downloadImages ] ]. + group + addItem: [ :item | + item + name: 'Export as markdown'; + icon: (Smalltalk ui icons iconNamed: #smallSaveAs); + action: [ self exportAsMarkdown ] ]. + group + addItem: [ :item | + item + name: 'Export as html'; + icon: (Smalltalk ui icons iconNamed: #smallWindow); + action: [ self exportAsHTML ] ]. + group + addItem: [ :item | + item + name: 'Export as LaTeX'; + icon: (Smalltalk ui icons iconNamed: #smallPrint); + action: [ self exportAsLaTeX ] ]. + group + addItem: [ :item | + item + name: 'Export as pdf'; + icon: (Smalltalk ui icons iconNamed: #smallPrint); + action: [ self exportAsPDF ] ]. + group + addItem: [ :item | + item + name: 'See html'; + icon: (self iconNamed: #smallInspectIt); + action: [ self seeHtml ] ]. + group + addItem: [ :item | + item + name: 'See pdf'; + icon: (Smalltalk ui icons iconNamed: #smallInspectIt); + action: [ self seePdf ] ]. + + group + addItem: [ :item | + item + name: 'Define debug message...'; + icon: Smalltalk ui icons glamorousBug; + action: [ self defineDebugMessageUI ] ] ] +] + +{ #category : #'event handling' } +GrafoscopioNotebook >> okToChange [ + + self isSaved ifTrue: [ ^ true ] ifFalse: [ ^ self askToSaveBeforeClosing ] +] + +{ #category : #persistence } +GrafoscopioNotebook >> openDefault [ +"I open a new default notebook" + + ^ self class new openWithSpec. +] + +{ #category : #persistence } +GrafoscopioNotebook >> openFromFile: aFileReference [ + self class registerRecent: aFileReference. + self loadFromFile: aFileReference. + ^ self openWithSpec. +] + +{ #category : #persistence } +GrafoscopioNotebook >> openFromFileSelector [ + + | file nb | + file := UIManager default + chooseExistingFileReference:'Choose a file' + extensions: #('ston') + path: FileLocator documents. + file ifNil: [ + self inform: 'No file selected'. + ^ self ]. + self workingFile: file. + nb := self class new. + nb openFromFile: self workingFile. + GfUIHelpers updateRecentNotebooksWith: workingFile +] + +{ #category : #persistence } +GrafoscopioNotebook >> openFromUrl: url [ +"Opens a tree from a file named aFileName" + + | fileName sanitized | + sanitized := GrafoscopioUtils sanitize: url. + fileName := sanitized segments last. + GrafoscopioUtils + downloadingFrom: sanitized + withMessage: 'Downloading document...' + into: FileLocator temp. + self class new openFromFile: (FileLocator temp / fileName) +] + +{ #category : #persistence } +GrafoscopioNotebook >> openFromUrlUI [ + "This method generates the UI for the openFromUrl: method, it asks for a URL from the user" + + | fileUrl | + "GrafoscopioBrowser configureSettings." + fileUrl := UIManager default + textEntry: 'Enter the URL' + title: 'Open notebook from URL'. + fileUrl isNil ifTrue: [ ^nil ]. + self class new openFromUrl: fileUrl +] + +{ #category : #utilities } +GrafoscopioNotebook >> pandocOptions [ + ^ self notebook pandocOptions +] + +{ #category : #utilities } +GrafoscopioNotebook >> pandocOptionsComputed [ + "I convert the pandoc options array into a single line that can be used with the pandoc command." + + | result | + result := '' writeStream. + self pandocOptions ifNil: [ ^ '' ]. + self pandocOptions + do: [ :option | + option isDictionary + ifTrue: [ + self addCommandFrom: option into: result ] + ifFalse: [ + result + nextPutAll: option] ]. + ^ result contents +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> pasteNodeFromClipboard [ + tree highlightedItem content pasteFromClipboard. + self notebookContent: notebook. +] + +{ #category : #persistence } +GrafoscopioNotebook >> pdfFile [ + "I define the location of the markdown file where the notebook will be exported" + | pdfFile | + pdfFile := (self markdownFile parent fullName,'/', self markdownFile basenameWithoutExtension, '.pdf') asFileReference. + ^ pdfFile. + +] + +{ #category : #initialization } +GrafoscopioNotebook >> projectSubMenu [ + ^ MenuModel new + addGroup: [ :group | + group + addItem: [ :item | + item + name: 'Activate remote repository...'; + icon: Smalltalk ui icons smallPushpinIcon; + action: [ self inform: 'To be implemented ...' ] ]. + group + addItem: [ :item | + item + name: 'Activate local repository...'; + icon: Smalltalk ui icons homeIcon; + action: [ self inform: 'To be implemented ...' ] ]. + group + addItem: [ :item | + item + name: 'Add file...'; + icon: Smalltalk ui icons newerPackagesAvailableIcon; + action: [ self inform: 'To be implemented ...' ] ]. + group + addItem: [ :item | + item + name: 'Delete file...'; + icon: Smalltalk ui icons packageDeleteIcon; + action: [ self inform: 'To be implemented ...' ] ]. + group + addItem: [ :item | + item + name: 'Commit to repository'; + icon: Smalltalk ui icons smallScreenshotIcon; + action: [ self inform: 'To be implemented ...' ] ]. + group + addItem: [ :item | + item + name: 'Credentials'; + icon: (self iconNamed: #userIcon); + action: [ self inform: 'To be implemented ...' ] ] ] +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> promoteNode [ + | editedNote | + editedNote := tree selectedItem content. + editedNote promote. + self notebookContent: notebook +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> removeNode [ + | contentToDelete parentContent newSelectedContent children | + 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 ] ] + ifFalse: [ newSelectedContent := parentContent ]. + contentToDelete parent removeNode: contentToDelete. + self notebookContent: notebook +] + +{ #category : #persistence } +GrafoscopioNotebook >> saveToFile: aFileReference [ + "I save the current tree/document to a file and update storage timestamp." + + aFileReference ifNil: [ self inform: 'No file selected for saving. Save NOT done.'. ^ self ]. + workingFile := aFileReference. + self workingFile ensureDelete. + self workingFile writeStreamDo: [:stream | + self exportAsSton: self notebook on: stream ]. + self title: self workingFile basenameWithIndicator, ' | Grafoscopio notebook'. + self inform: ('File saved at: ', String cr, self workingFile fullName). + GfUIHelpers updateRecentNotebooksWith: aFileReference. +] + +{ #category : #persistence } +GrafoscopioNotebook >> saveToFileUI [ + + | file | + + file := UIManager default + chooseForSaveFileReference: 'Save notebook to file as...' + extensions: #('ston') + path: (workingFile ifNotNil: [ workingFile parent ] ifNil: [ FileLocator documents ] ). + file + ifNil: [ self inform: 'Saving to file cancelled'. ^ self ] + ifNotNil:[self saveToFile: file]. +] + +{ #category : #persistence } +GrafoscopioNotebook >> saveWorkingNotebook [ + "Saves the current tree to the user predefined file location used when he/she opened it." + self workingFile + ifNil: [ self saveToFileUI ] + ifNotNil: [ self saveToFile: workingFile ]. + self notebook root updateEditionTimestamp. + GfUIHelpers updateRecentNotebooksWith: workingFile + + +] + +{ #category : #inspecting } +GrafoscopioNotebook >> seeHtml [ + self pdfFile exists + ifTrue: [ (#open command argument: self htmlFile fullName) schedule ] +] + +{ #category : #persistence } +GrafoscopioNotebook >> seePdf [ + self exportAsPDF + onSuccessDo: [ :v | (#open command argument: self pdfFile fullName) schedule ] +] + +{ #category : #persistence } +GrafoscopioNotebook >> subtreeAsMarkdown [ + | currentNode | + currentNode := tree highlightedItem content. + self inform: ('Exported as: ', String cr, (self subtreeAsMarkdownFileFor: currentNode) fullName ) +] + +{ #category : #persistence } +GrafoscopioNotebook >> subtreeAsMarkdownFileFor: aNode [ + | exportedFile | + aNode links ifEmpty: [ ^ self ]. + exportedFile:= self navigateRelativePathFor: aNode links last. + exportedFile class = self class ifTrue: [ ^ self ]. + self exportNode: aNode asMarkdownIn: exportedFile. + ^ exportedFile +] + +{ #category : #'editing nodes' } +GrafoscopioNotebook >> toggleCodeNode [ + | currentNode | + currentNode := tree highlightedItem. + currentNode content toggleCodeText. + self updateBodyFor: currentNode. +] + +{ #category : #initialization } +GrafoscopioNotebook >> topBar [ + ^ MenuPresenter new + addGroup: [ :group | + group + addItem: [ :item | + item + name: 'Notebook'; + icon: (self iconNamed: #smallObjects); + subMenu: self notebookSubMenu ]. + group + addItem: [ :item | + item + name: 'Project'; + icon: (self iconNamed: #catalog); + subMenu: self projectSubMenu ] ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Save notebook'; + icon: (self iconNamed: #glamorousSave); + action: [ self saveWorkingNotebook ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Export all Markdown subtrees'; + icon: (self iconNamed: #glamorousMore); + action: [ self exportAllSubtreesAsMarkup ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Cut'; + icon: (self iconNamed: #smallCut); + action: [ self cutNodeToClipboard ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Copy'; + icon: (self iconNamed: #smallCopy); + action: [ self copyNodeToClipboard ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Paste'; + icon: (self iconNamed: #smallPaste); + action: [ self pasteNodeFromClipboard ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Find & Replace'; + icon: (self iconNamed: #smallFind); + action: [ self findAndReplace ] ] ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Add node'; + icon: MendaIcons new plusIcon; + action: [ self addNode ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Delete node'; + icon: MendaIcons new minusIcon; + action: [ self removeNode ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Move node up'; + icon: MendaIcons new arrowUpIcon; + action: [ self moveSelectedNodeUp ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Move node down'; + icon: MendaIcons new arrowDownIcon; + action: [ self moveSelectedNodeDown ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Move node left'; + icon: MendaIcons new arrowLeftIcon; + action: [ self promoteNode ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Move node right'; + icon: MendaIcons new arrowRightIcon; + action: [ self demoteNode ] ] ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Toggle: code <--> text'; + icon: MendaIcons new smalltalkCodeIcon; + action: [ self toggleCodeNode ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'List node links'; + icon: (self iconNamed: #tinyMenu); + action: [ self linksList ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Visit link'; + icon: (self iconNamed: #glamorousRight); + action: [ self visitNodeLink ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Import link content'; + icon: (self iconNamed: #glamorousOpenFromUrl); + action: [ self importLinkContent ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Export link content'; + icon: (self iconNamed: #glamorousSaveToUrl); + action: [ self exportLinkContent ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'HTML to Markdown'; + icon: (self iconNamed: #smallProfile); + action: [ self htmlToMarkdown ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'HTML to Markdown subtree'; + icon: (self iconNamed: #hierarchy); + action: [ self htmlToMarkdownSubtree ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Tag as...'; + icon: MendaIcons new tagAddIcon; + action: [ self inform: 'To be implemented...' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Untag ....'; + icon: MendaIcons new tagMinusIcon; + action: [ self inform: 'To be implemented...' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Edit tags...'; + icon: FontAwesomeIcons new tagsIcon; + action: [ self inform: 'To be implemented...' ] ] ]; + addGroup: [ :debug | + debug + addItem: [ :item | + item + name: nil; + description: 'Debug'; + icon: + (self iconNamed: #glamorousBug); + action: [ self debugWithSelector: self debugMessage ] ] ] +] + +{ #category : #accessing } +GrafoscopioNotebook >> tree [ + ^ tree +] + +{ #category : #accessing } +GrafoscopioNotebook >> tree: anObject [ + tree := anObject +] + +{ #category : #operation } +GrafoscopioNotebook >> updateBodyFor: aNodeContainer [ + | aNode | + self needRebuild: false. + tree needRebuild: false. + body needRebuild: true. + aNode := aNodeContainer content. + header text: aNode header. + body := self instantiate: aNode specModelClass new. + body content: aNode body. + links text: aNode lastLink. + + self buildWithSpecLayout: self class defaultSpec +] + +{ #category : #operation } +GrafoscopioNotebook >> visitNodeLink [ + + tree highlightedItem content visitLastLink. +] + +{ #category : #accessing } +GrafoscopioNotebook >> windowMainMenu [ + ^ windowMainMenu +] + +{ #category : #accessing } +GrafoscopioNotebook >> windowMainMenu: anObject [ + windowMainMenu := anObject +] + +{ #category : #accessing } +GrafoscopioNotebook >> workingFile [ + ^ workingFile +] + +{ #category : #accessing } +GrafoscopioNotebook >> workingFile: aFileReference [ + workingFile := aFileReference. +] + +{ #category : #'as yet unclassified' } +GrafoscopioNotebook >> wrapBodyLines [ + self currentNodeContent wrapBodyLines. + self updateBodyFor: self currentNode +] diff --git a/src/Grafoscopio/GrafoscopioNotebookTest.class.st b/src/Grafoscopio/GrafoscopioNotebookTest.class.st index 3ddee46..7bd9f2a 100644 --- a/src/Grafoscopio/GrafoscopioNotebookTest.class.st +++ b/src/Grafoscopio/GrafoscopioNotebookTest.class.st @@ -1,8 +1,8 @@ -" -I test the main functionality of the GrafoscopioNode class. -" -Class { - #name : #GrafoscopioNotebookTest, - #superclass : #TestCase, - #category : #'Grafoscopio-UI' -} +" +I test the main functionality of the GrafoscopioNode class. +" +Class { + #name : #GrafoscopioNotebookTest, + #superclass : #TestCase, + #category : #'Grafoscopio-UI' +} diff --git a/src/Grafoscopio/GrafoscopioPerspective.class.st b/src/Grafoscopio/GrafoscopioPerspective.class.st index 165acac..c2762e9 100644 --- a/src/Grafoscopio/GrafoscopioPerspective.class.st +++ b/src/Grafoscopio/GrafoscopioPerspective.class.st @@ -1,196 +1,207 @@ -Class { - #name : #GrafoscopioPerspective, - #superclass : #SpPresenterWithModel, - #instVars : [ - 'toolbar', - 'viewport' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective class >> defaultPerspective [ - ^ GrafoscopioDocumentEditionPerspective -] - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective class >> defaultSpec [ - ^ SpBoxLayout newVertical - add: #toolbar height: self toolbarHeight; - add: #viewport; - yourself -] - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective class >> isAbstract [ - ^ self = GrafoscopioPerspective -] - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective class >> perspectives [ - ^ self allSubclasses select: [ : c | c isAbstract not ] -] - -{ #category : #initialization } -GrafoscopioPerspective >> aboutToBeUninstalledFrom: aTreeNotebook [ -] - -{ #category : #initialization } -GrafoscopioPerspective >> addItemTo: aGroup [ - aGroup - addItem: [ :item | - item - name: 'Dynamic'; - icon: (self iconNamed: #delete); - action: [ aGroup menuItems remove: item. - self needRebuild: false. - self buildWithSpec ] ]. - self needRebuild: false. - self buildWithSpec -] - -{ #category : #initialization } -GrafoscopioPerspective >> addingMenu [ - | menu | - menu := self newMenu. - self kindsOfNode - do: [ :n | - menu - addItem: [ :item | - item - name: n nameForSelection; - icon: n icon; - action: [ self addNewNodeOfClass: n ] ] ]. - ^ menu -] - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective >> chooseKindsOfNode [ - | idx values | - values := self kindsOfNode. - idx := UIManager default - chooseFrom: values - lines: {} - title: 'What kind of node? '. - ^ idx = 0 - ifTrue: [ nil ] - ifFalse: [ values at: idx ] -] - -{ #category : #initialization } -GrafoscopioPerspective >> createToolbar [ - | aMenu | - aMenu := self newMenuBar - addGroup: [ :group | - group - addItem: [ :item | - item - name: 'File'; - icon: (self iconNamed: #openIcon); - subMenu: self subMenu ]. - group - addItem: [ :item | - item - name: nil; - description: 'Open file'; - icon: (self iconNamed: #openIcon); - action: [ self inform: 'Open File' ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Save File'; - icon: (self iconNamed: #smallSaveIcon); - action: [ self inform: 'Save File' ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Print file'; - icon: (self iconNamed: #smallPrintIcon); - action: [ self inform: 'Print file' ] ] ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: nil; - description: 'Undo'; - icon: (self iconNamed: #smallUndoIcon); - action: [ self inform: 'Undo' ] ]. - group - addItem: [ :item | - item - name: nil; - description: 'Redo'; - icon: (self iconNamed: #smallRedoIcon); - action: [ self inform: 'Redo' ] ]. - group - addItem: [ :item | - item - name: ''; - icon: (self iconNamed: #add); - subMenu: self addingMenu ]. - - ]; - addGroup: [ :group | - group - addItem: [ :item | - item - name: nil; - description: 'Add menu item'; - icon: (self iconNamed: #add); - action: [ self addItemTo: group ] ] ]. - aMenu color: Color transparent. - ^ aMenu -] - -{ #category : #initialization } -GrafoscopioPerspective >> createViewport [ - self subclassResponsibility -] - -{ #category : #initialization } -GrafoscopioPerspective >> initializeWidgets [ - super initializeWidgets. - toolbar := self createToolbar. - viewport := self createViewport -] - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective >> kindsOfNode [ - ^ GrafoscopioAbstractNode allSubclasses select: [ :c | c showInMenu ] -] - -{ #category : #initialization } -GrafoscopioPerspective >> subMenu [ - ^ self newMenu - addItem: [ :item | - item - name: 'Open'; - icon: (self iconNamed: #openIcon); - shortcut: $o meta; - action: [ self inform: 'Open' ] ]; - addItem: [ :item | - item - name: 'Save'; - icon: (self iconNamed: #smallSaveIcon); - shortcut: $s meta; - action: [ self inform: 'Save' ] ]; - addItem: [ :item | - item - name: 'Print'; - shortcut: $p meta; - icon: (self iconNamed: #smallPrintIcon); - action: [ self inform: 'Print' ] ]; - addItem: [ :item | - item - name: 'Close'; - shortcut: $c meta; - icon: (self iconNamed: #smallCancelIcon); - action: [ self inform: 'Kill' ] ]; - yourself -] - -{ #category : #'as yet unclassified' } -GrafoscopioPerspective >> updateModel: aModel [ -] +Class { + #name : #GrafoscopioPerspective, + #superclass : #SpPresenterWithModel, + #instVars : [ + 'toolbar', + 'viewport' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> defaultPerspective [ + ^ GrafoscopioDocumentTextEditionPerspective +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: #toolbar height: self toolbarHeight; + add: #viewport; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> isAbstract [ + ^ self = GrafoscopioPerspective +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective class >> perspectives [ + ^ self allSubclasses select: [ : c | c isAbstract not ] +] + +{ #category : #initialization } +GrafoscopioPerspective >> aboutToBeInstalled: aTreeNotebook [ +] + +{ #category : #initialization } +GrafoscopioPerspective >> aboutToBeUninstalledFrom: aTreeNotebook [ +] + +{ #category : #initialization } +GrafoscopioPerspective >> addItemTo: aGroup [ + aGroup + addItem: [ :item | + item + name: 'Dynamic'; + icon: (self iconNamed: #delete); + action: [ aGroup menuItems remove: item. + self needRebuild: false. + self buildWithSpec ] ]. + self needRebuild: false. + self buildWithSpec +] + +{ #category : #initialization } +GrafoscopioPerspective >> addingMenu [ + | menu | + menu := self newMenu. + self kindsOfNode + do: [ :n | + menu + addItem: [ :item | + item + name: n nameForSelection; + icon: n icon; + action: [ self addNewNodeOfClass: n ] ] ]. + ^ menu +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective >> chooseKindsOfNode [ + | idx values | + values := self kindsOfNode. + idx := UIManager default + chooseFrom: values + lines: {} + title: 'What kind of node? '. + ^ idx = 0 + ifTrue: [ nil ] + ifFalse: [ values at: idx ] +] + +{ #category : #initialization } +GrafoscopioPerspective >> createToolbar [ + | aMenu | + aMenu := self newMenuBar + addGroup: [ :group | + group + addItem: [ :item | + item + name: 'File'; + icon: (self iconNamed: #openIcon); + subMenu: self subMenu ]. + group + addItem: [ :item | + item + name: nil; + description: 'Open file'; + icon: (self iconNamed: #openIcon); + action: [ self inform: 'Open File' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Save File'; + icon: (self iconNamed: #smallSaveIcon); + action: [ self inform: 'Save File' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Print file'; + icon: (self iconNamed: #smallPrintIcon); + action: [ self inform: 'Print file' ] ] ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Undo'; + icon: (self iconNamed: #smallUndoIcon); + action: [ self inform: 'Undo' ] ]. + group + addItem: [ :item | + item + name: nil; + description: 'Redo'; + icon: (self iconNamed: #smallRedoIcon); + action: [ self inform: 'Redo' ] ]. + group + addItem: [ :item | + item + name: ''; + icon: (self iconNamed: #add); + subMenu: self addingMenu ]. + + ]; + addGroup: [ :group | + group + addItem: [ :item | + item + name: nil; + description: 'Add menu item'; + icon: (self iconNamed: #add); + action: [ self addItemTo: group ] ] ]. + aMenu color: Color transparent. + ^ aMenu +] + +{ #category : #initialization } +GrafoscopioPerspective >> createViewport [ + self subclassResponsibility +] + +{ #category : #initialization } +GrafoscopioPerspective >> initializeWidgets [ + super initializeWidgets. + toolbar := self createToolbar. + viewport := self createViewport +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective >> kindsOfNode [ + ^ GrafoscopioAbstractNode allSubclasses select: [ :c | c showInMenu ] +] + +{ #category : #initialization } +GrafoscopioPerspective >> subMenu [ + ^ self newMenu + addItem: [ :item | + item + name: 'Open'; + icon: (self iconNamed: #openIcon); + shortcut: $o meta; + action: [ self inform: 'Open' ] ]; + addItem: [ :item | + item + name: 'Save'; + icon: (self iconNamed: #smallSaveIcon); + shortcut: $s meta; + action: [ self inform: 'Save' ] ]; + addItem: [ :item | + item + name: 'Print'; + shortcut: $p meta; + icon: (self iconNamed: #smallPrintIcon); + action: [ self inform: 'Print' ] ]; + addItem: [ :item | + item + name: 'Close'; + shortcut: $c meta; + icon: (self iconNamed: #smallCancelIcon); + action: [ self inform: 'Kill' ] ]; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPerspective >> updateModel: aModel [ +] + +{ #category : #initialization } +GrafoscopioPerspective >> viewport: aViewport [ + + viewport := aViewport. + self buildWithSpec +] diff --git a/src/Grafoscopio/GrafoscopioPillarASText.class.st b/src/Grafoscopio/GrafoscopioPillarASText.class.st index a1c3dd7..0e41de1 100644 --- a/src/Grafoscopio/GrafoscopioPillarASText.class.st +++ b/src/Grafoscopio/GrafoscopioPillarASText.class.st @@ -1,526 +1,539 @@ -" -Pillar ASText delegates the text behavior into a pillar AST . -" -Class { - #name : #GrafoscopioPillarASText, - #superclass : #GrafoscopioAbstractText, - #instVars : [ - 'ast', - 'stringDecorator', - 'lastNode', - 'runs' - ], - #category : #'Grafoscopio-Pillar' -} - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText class >> now [ -| pillarAst text presenter | -pillarAst := GrafoscopioPillarASText pillarExample . -text := GrafoscopioPillarASText new. -text ast: pillarAst. - -presenter := SpTextPresenter new text: text ; yourself . -presenter autoAccept: true. -presenter whenTextChangedDo: [ : v | presenter violentForceRecompose ]. -presenter openWithSpec. - -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText class >> openExample [ - ^ self new - ast: self pillarExample; - yourself -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText class >> pillarExample [ - ^ PRPillarParser - parse: - '!!About Pillar - - -[[[label=script1|caption=My script that works|language=pharo-image -PolymorphSystemSettings pharoLogoForm -]]] - - - -Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). -It is composed of several modules such as importers, transformers, document model and outputers. - -This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. -The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! - -This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. - -Pillar was sponsored by *ESUG>http://www.esug.org*. - -!!!Introduction - -Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. - -+An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ - -Pillar has many features, helpful tools, and documentation: -- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; -- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; -%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). -- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* -- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* -- a cheat sheet (see Chapter *@chacheat*). - - -!!!Pillar users - -@pillarUSERS - -This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: - -- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), -- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, -- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, -- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). - - -!!! Pillar future features - -Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. -Its architecture is a lot cleaner. - -Still some issues are missing. Here is a little list of features that we are working on or will soon: - -- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. -- Markdown syntax. -- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. -- Better table support. - -!!! Conclusion - - -Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements. -!!About Pillar - - -[[[label=script1|caption=My script that works|language=pharo-image -PolymorphSystemSettings pharoLogoForm -]]] - - - -Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). -It is composed of several modules such as importers, transformers, document model and outputers. - -This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. -The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! - -This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. - -Pillar was sponsored by *ESUG>http://www.esug.org*. - -!!!Introduction - -Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. - -+An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ - -Pillar has many features, helpful tools, and documentation: -- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; -- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; -%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). -- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* -- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* -- a cheat sheet (see Chapter *@chacheat*). - - -!!!Pillar users - -@pillarUSERS - -This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: - -- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), -- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, -- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, -- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). - - -!!! Pillar future features - -Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. -Its architecture is a lot cleaner. - -Still some issues are missing. Here is a little list of features that we are working on or will soon: - -- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. -- Markdown syntax. -- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. -- Better table support. - -!!! Conclusion - - -Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements. -!!About Pillar - - -[[[label=script1|caption=My script that works|language=pharo-image -PolymorphSystemSettings pharoLogoForm -]]] - - - -Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). -It is composed of several modules such as importers, transformers, document model and outputers. - -This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. -The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! - -This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. - -Pillar was sponsored by *ESUG>http://www.esug.org*. - -!!!Introduction - -Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. - -+An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ - -Pillar has many features, helpful tools, and documentation: -- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; -- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; -%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). -- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* -- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* -- a cheat sheet (see Chapter *@chacheat*). - - -!!!Pillar users - -@pillarUSERS - -This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: - -- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), -- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, -- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, -- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). - - -!!! Pillar future features - -Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. -Its architecture is a lot cleaner. - -Still some issues are missing. Here is a little list of features that we are working on or will soon: - -- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. -- Markdown syntax. -- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. -- Better table support. - -!!! Conclusion - - -Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements. -!!About Pillar - - -[[[label=script1|caption=My script that works|language=pharo-image -PolymorphSystemSettings pharoLogoForm -]]] - - - -Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). -It is composed of several modules such as importers, transformers, document model and outputers. - -This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. -The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! - -This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. - -Pillar was sponsored by *ESUG>http://www.esug.org*. - -!!!Introduction - -Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. - -+An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ - -Pillar has many features, helpful tools, and documentation: -- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; -- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; -%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). -- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* -- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* -- a cheat sheet (see Chapter *@chacheat*). - - -!!!Pillar users - -@pillarUSERS - -This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: - -- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), -- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, -- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, -- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). - - -!!! Pillar future features - -Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. -Its architecture is a lot cleaner. - -Still some issues are missing. Here is a little list of features that we are working on or will soon: - -- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. -- Markdown syntax. -- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. -- Better table support. - -!!! Conclusion - - -Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements.' -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> ast [ - ^ ast -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> ast: aPRDocument [ - ast := aPRDocument. - aPRDocument accept: GrafoscopioPillarTextAnnotator new. - ast start: aPRDocument children first start. - ast stop: aPRDocument children last stop. - stringDecorator := GrafoscopioPillarASTextStringDecorator new - text: self; - yourself -] - -{ #category : #accessing } -GrafoscopioPillarASText >> at: anInteger [ - | node | - (anInteger > (self size +1 ) or: [ anInteger < 1 ]) - ifTrue: [ ^ self errorSubscriptBounds: anInteger ]. - node := self detectAstNodeFor: anInteger in: ast. - ^ node text at: anInteger - node textStart +1 -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> attributesAt: characterIndex do: aBlockClosure [ - self size = 0 ifTrue:[^self]. - (self runs at: characterIndex) do: aBlockClosure -] - -{ #category : #copying } -GrafoscopioPillarASText >> copyFrom: from to: to [ - ^ (ast textStart = from and: [ ast textStop = (to + 1) ]) - ifTrue: [ self ] - ifFalse: [ GrafoscopioPillarASTextStringProjectionDecorator new - text: self; - - from: from; - to: to; - yourself ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> detectAstNodeFor: anInteger [ - ^ self detectAstNodeFor: anInteger in: ast -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> detectAstNodeFor: anInteger in: aNode [ - " The size should match " - (anInteger between: aNode textStart and: aNode textStop) - ifFalse: [ self error: 'Cannot find a node for ' , anInteger asString ]. - - " if the node is the same as the last, or if it is further (probably we are just iterating the thing) we try to reuse the last result or to go throug as a list. " - lastNode ifNotNil: [ - (anInteger between: lastNode textStart and: lastNode textStop -1 ) ifTrue: [ - ^ lastNode. - ]. - anInteger >= lastNode textStop ifTrue: [ - [(lastNode := lastNode next) notNil ] whileTrue: [ - (anInteger between: lastNode textStart and: lastNode textStop -1) ifTrue:[ ^ lastNode ] - ] - ] - ]. - - ^ aNode hasChildren - ifTrue: [ aNode children - detect: [ :c | anInteger between: c textStart and: c textStop -1 ] - ifFound: [ :n | self detectAstNodeFor: anInteger in: n ] - ifNone: [ self error: 'whut?' ] ] - ifFalse: [ lastNode := aNode ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> detectAstNodesBetween: from and: to in: aNode [ - | current nodes | - nodes := OrderedCollection new. - current := self detectAstNodeFor: from. - from > to - ifTrue: [ nodes add: current ] - ifFalse: [ [ current isNotNil and: [ current textStart <= to ] ] - whileTrue: [ nodes add: current. - current := current next ] ]. - ^ nodes - - - - "| children childrenStream currentNode | - aNode hasChildren ifFalse: [ ^ {aNode} ]. - childrenStream := aNode children readStream. - children := OrderedCollection new. - - [ childrenStream atEnd not and: [ (currentNode :=childrenStream next) textStart <= to ] ] whileTrue:[ - ((currentNode textStart >= from) or: [ currentNode textStop >= from ]) ifTrue: [ - children addAll: (self detectAstNodesBetween: from and: to in: currentNode ) . - ]. - ]. - ^ children." -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> detectFullBranchFor: anId [ - ^ self detectFullBranchFor: anId in: ast. -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> detectFullBranchFor: anInteger in: aNode [ - | branch basenode | - basenode := (self detectAstNodeFor: anInteger) . - branch := OrderedCollection new. - [basenode notNil ] whileTrue:[ branch add: basenode. basenode := basenode parent ]. - ^ branch reverse. -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> errorSubscriptBounds: aSomedthing [ - self halt. -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> extractStringFrom: aPosition to: anOtherPosition [ - | from to nodes preffix suffix | - nodes := self detectAstNodesBetween: aPosition and: anOtherPosition in: ast. - from := (aPosition - nodes first textStart +1 ) . - nodes size = 1 ifTrue: [ - ^ nodes first text copyFrom: from to: from + anOtherPosition - aPosition . - ]. - to := nodes last textStop - anOtherPosition. - - preffix := nodes first text copyFrom: from to: nodes first textSize. - suffix := nodes last text copyFrom: 1 to: to. - ^ String - streamContents: [ :str | - str nextPutAll: preffix. - (nodes copyFrom: 2 to: nodes size) - inject: str - into: [ :stream :each | - stream nextPutAll: each text. - stream ]. - str nextPutAll: suffix ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> fontAt: characterIndex withStyle: aTextStyle [ - "Answer the fontfor characters in the run beginning at characterIndex." - | attributes font | - self size = 0 ifTrue: [^ aTextStyle defaultFont]. "null text tolerates access" - attributes := runs at: characterIndex. - font := aTextStyle defaultFont. "default" - attributes do: - [:att | att forFontInStyle: aTextStyle do: [:f | font := f]]. - ^ font -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> rangeOf: aTextURL startingAt: anInteger [ - ^ self runs rangeOf: aTextURL startingAt: anInteger -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> reannotate: aPRText [ - | current previous parents | - - aPRText textStop: aPRText textStart + aPRText text size. - aPRText markAsDirty. - parents := Set new. - parents add: aPRText parent. - current := aPRText next. - - previous := aPRText. - - [ current isNotNil ] - whileTrue: [ - current markAsDirty. - current textStart: previous textStop. - current textStop: current textStart + current text size. - previous := current. - current := current next. - self ]. - - - self resetRuns. -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASText >> removeAttribute: att from: start to: stop [ - -] - -{ #category : #editing } -GrafoscopioPillarASText >> replaceFrom: start to: stop with: aCollection [ - | nodes realStart realStop newText node | - ((stop - start )> 1 and:[ aCollection isEmpty ]) ifTrue: [ ^self ]. - nodes := self detectAstNodesBetween: start and: stop in: ast. - self assert: nodes size <= 1. - node := nodes first. - realStart := (start - (node textStart) )+ 1 . - realStop := stop - (node textStart ) + 1 . - newText := (node text copyReplaceFrom: realStart to: realStop with: aCollection). - node isText ifTrue: [ - node text: newText - ] ifFalse: [ - node := self detectAstNodeFor: node textStart - 1. - node text: node text, aCollection. - nodes := { node }. - ]. - nodes first markAsDirty. - self reannotate: nodes first. - -] - -{ #category : #accessing } -GrafoscopioPillarASText >> resetRuns [ - ^ runs := GrafoscopioPillarRuns new - ast: self; - yourself -] - -{ #category : #accessing } -GrafoscopioPillarASText >> runs [ - ^ runs - ifNil: [ runs := GrafoscopioPillarRuns new - ast: self; - yourself ] -] - -{ #category : #accessing } -GrafoscopioPillarASText >> size [ - ^ ast textSize -] - -{ #category : #accessing } -GrafoscopioPillarASText >> string [ - ^ stringDecorator -] +" +Pillar ASText delegates the text behavior into a pillar AST . +" +Class { + #name : #GrafoscopioPillarASText, + #superclass : #GrafoscopioAbstractText, + #instVars : [ + 'ast', + 'lastNode', + 'runs' + ], + #category : #'Grafoscopio-Pillar' +} + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText class >> openExample [ + ^ self new + ast: self pillarExample; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText class >> pillarExample [ + ^ PRPillarParser + parse: + '!!About Pillar + + +[[[label=script1|caption=My script that works|language=pharo +PolymorphSystemSettings pharoLogoForm +]]] + + + +Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). +It is composed of several modules such as importers, transformers, document model and outputers. + +This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. +The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! + +This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. + +Pillar was sponsored by *ESUG>http://www.esug.org*. + +!!!Introduction + +Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. + ++An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ + +Pillar has many features, helpful tools, and documentation: +- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; +- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; +%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). +- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* +- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* +- a cheat sheet (see Chapter *@chacheat*). + + +!!!Pillar users + +@pillarUSERS + +This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: + +- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), +- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, +- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, +- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). + + +!!! Pillar future features + +Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. +Its architecture is a lot cleaner. + +Still some issues are missing. Here is a little list of features that we are working on or will soon: + +- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. +- Markdown syntax. +- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. +- Better table support. + +!!! Conclusion + + +Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements. +!!About Pillar + + +[[[label=script1|caption=My script that works|language=pharo +PolymorphSystemSettings pharoLogoForm +]]] + + + +Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). +It is composed of several modules such as importers, transformers, document model and outputers. + +This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. +The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! + +This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. + +Pillar was sponsored by *ESUG>http://www.esug.org*. + +!!!Introduction + +Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. + ++An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ + +Pillar has many features, helpful tools, and documentation: +- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; +- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; +%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). +- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* +- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* +- a cheat sheet (see Chapter *@chacheat*). + + +!!!Pillar users + +@pillarUSERS + +This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: + +- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), +- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, +- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, +- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). + + +!!! Pillar future features + +Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. +Its architecture is a lot cleaner. + +Still some issues are missing. Here is a little list of features that we are working on or will soon: + +- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. +- Markdown syntax. +- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. +- Better table support. + +!!! Conclusion + + +Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements. +!!About Pillar + + +[[[label=script1|caption=My script that works|language=pharo +PolymorphSystemSettings pharoLogoForm +]]] + + + +Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). +It is composed of several modules such as importers, transformers, document model and outputers. + +This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. +The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! + +This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. + +Pillar was sponsored by *ESUG>http://www.esug.org*. + +!!!Introduction + +Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. + ++An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ + +Pillar has many features, helpful tools, and documentation: +- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; +- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; +%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). +- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* +- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* +- a cheat sheet (see Chapter *@chacheat*). + + +!!!Pillar users + +@pillarUSERS + +This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: + +- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), +- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, +- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, +- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). + + +!!! Pillar future features + +Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. +Its architecture is a lot cleaner. + +Still some issues are missing. Here is a little list of features that we are working on or will soon: + +- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. +- Markdown syntax. +- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. +- Better table support. + +!!! Conclusion + + +Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements. +!!About Pillar + + +[[[label=script1|caption=My script that works|language=pharo +PolymorphSystemSettings pharoLogoForm +]]] + + + +Pillar is a system to manage documents (books, presentations, and web sites). From a common format, it is able to generate documents in multiple formats (html, markdown, latex, AsciiDoc). +It is composed of several modules such as importers, transformers, document model and outputers. + +This book describes Pillar in its current version 7.0. Pillar is currently developed and masintained by Stéphane Ducasse and Guillermo Polito. +The original author of Pillar was Damien Cassou. Many people have also contributed to Pillar: Ben Coman, Guillermo Polito, Lukas Renggli (original author of the PierCMS from which a first version of Pillar has been extracted), Benjamin van Ryseghem, Cyril Ferlicot-Delbecque, Thibault Arloing, Yann Dubois, Quentin Ducasse and Asbathou Sama Biyalou. Special thanks to Asbathou Sama Biyalou! + +This book adapts, extends, and clarifies the chapter explaining Pillar in the ''Enterprise Pharo: a Web Perspective'' book. + +Pillar was sponsored by *ESUG>http://www.esug.org*. + +!!!Introduction + +Pillar (hosted at *http://github.com/pillar-markup*) is a markup language and associated tools to write and generate documentation, books (such as this one), web sites, and slide-based presentations. The Pillar screenshot in Figure *@voyageDocExample* shows the HTML version of chapter Voyage. + ++An example Pillar output>file://figures/voyageDocExample-small.png|label=voyageDocExample|width=60+ + +Pillar has many features, helpful tools, and documentation: +- simple markup syntax with references, tables, pictures, captions, syntax-highlighted code blocks; +- export documents to HTML, LaTeX, Markdown, AsciiDoc, ePuB and Pillar itself, and presentations to Beamer and Deck.js; +%- customization of the export through a dedicated STON configuration file (see chapter Missing Chapter *@cha:ston*) and Mustache templates (see chapter *@templating*). +- many tests with good coverage (94% with more than a 2100 executed tests), which are regularly run by a *continuous integration job>https://ci.inria.fr/pharo-contribution/job/Pillar* +- a command-line interface and dedicated plugins for several text editors: *Emacs>https://github.com/pillar-markup/pillar-mode*, *Vim>https://github.com/cdlm/vim-pillar*, *TextMate>https://github.com/Uko/Pillar.tmbundle*, and *Atom>https://github.com/Uko/language-pillar* +- a cheat sheet (see Chapter *@chacheat*). + + +!!!Pillar users + +@pillarUSERS + +This book was written in Pillar itself. If you want to see how Pillar is used, have a look at its source code (*http://github.com/SquareBracketAssociates/Booklet-PublishingAPillarBooklet*), or check the following other real-world projects: + +- the Updated Pharo by Example book (*https://github.com/SquareBracketAssociates/UpdatedPharoByExample*), +- the Pharo MOOC - Massive open online course (*https://github.com/SquareBracketAssociates/PharoMooc*, +- Any of the Pharo booklets (*https://github.com/SquareBracketAssociates/Booklet-XXXX*, +- the PillarHub open-access shared blog (*http://pillarhub.pharocloud.com*). + + +!!! Pillar future features + +Pillar 70 saw some major refactorings and cleaning: it does not rely on Grease and Magritte anymore. +Its architecture is a lot cleaner. + +Still some issues are missing. Here is a little list of features that we are working on or will soon: + +- Incremental recompilation. Since we remove the use of make (so that Windows users can use Pillar) we should introduce a way to avoid to recompile complete book when just one chapter changed. +- Markdown syntax. +- Release of Ecstatic. Pillar supports the deployment of web sites named Ecstatic and we are working on a second version of Ecstatic. +- Better table support. + +!!! Conclusion + + +Pillar is still in active development: maintainers keep improving its implementation. The current version of Pillar is Pillar 70. This booklet only documents Pillar 70. This booklet will be synchronised with future enhancements.' +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> addAttribute: aTextFontChange [ + +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> addAttribute: att from: start to: stop [ + +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> ast [ + ^ ast +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> ast: aPRDocument [ + self resetRuns. + lastNode := nil. + ast := aPRDocument. + aPRDocument accept: GrafoscopioPillarTextAnnotator new. + ast start: aPRDocument children first start. + ast stop: aPRDocument children last stop. + +] + +{ #category : #accessing } +GrafoscopioPillarASText >> at: anInteger [ + | node | + (anInteger > (self size +1 ) or: [ anInteger < 1 ]) + ifTrue: [ ^ self errorSubscriptBounds: anInteger ]. + node := self detectAstNodeFor: anInteger in: ast. + ^ node text at: anInteger - node textStart +1 +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> attributesAt: characterIndex do: aBlockClosure [ + self size = 0 ifTrue:[^self]. + (self runs at: characterIndex) do: aBlockClosure +] + +{ #category : #copying } +GrafoscopioPillarASText >> copyFrom: from to: to [ + ^ (ast textStart = from and: [ ast textStop = (to + 1) ]) + ifTrue: [ self ] + ifFalse: [ GrafoscopioPillarASTextStringProjectionAdapter new + text: self; + + from: from; + to: to; + yourself ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> detectAstNodeFor: anInteger [ + ^ self detectAstNodeFor: anInteger in: ast +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> detectAstNodeFor: anInteger in: aNode [ + " The size should match " + (anInteger between: aNode textStart and: aNode textStop) + ifFalse: [ self error: 'Cannot find a node for ' , anInteger asString ]. + + " if the node is the same as the last, or if it is further (probably we are just iterating the thing) we try to reuse the last result or to go throug as a list. " + lastNode ifNotNil: [ + (anInteger between: lastNode textStart and: lastNode textStop -1 ) ifTrue: [ + ^ lastNode. + ]. + anInteger >= lastNode textStop ifTrue: [ + [(lastNode := lastNode next) notNil ] whileTrue: [ + (anInteger between: lastNode textStart and: lastNode textStop -1) ifTrue:[ ^ lastNode ] + ] + ] + ]. + + ^ aNode hasChildren + ifTrue: [ aNode children + detect: [ :c | anInteger between: c textStart and: c textStop -1 ] + ifFound: [ :n | self detectAstNodeFor: anInteger in: n ] + ifNone: [ self error: 'whut?' ] ] + ifFalse: [ lastNode := aNode ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> detectAstNodesBetween: from and: to in: aNode [ + | current nodes | + nodes := OrderedCollection new. + current := self detectAstNodeFor: from. + from > to + ifTrue: [ nodes add: current ] + ifFalse: [ [ current isNotNil and: [ current textStart <= to ] ] + whileTrue: [ nodes add: current. + current := current next ] ]. + ^ nodes + + + + "| children childrenStream currentNode | + aNode hasChildren ifFalse: [ ^ {aNode} ]. + childrenStream := aNode children readStream. + children := OrderedCollection new. + + [ childrenStream atEnd not and: [ (currentNode :=childrenStream next) textStart <= to ] ] whileTrue:[ + ((currentNode textStart >= from) or: [ currentNode textStop >= from ]) ifTrue: [ + children addAll: (self detectAstNodesBetween: from and: to in: currentNode ) . + ]. + ]. + ^ children." +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> detectFullBranchFor: anId [ + ^ self detectFullBranchFor: anId in: ast. +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> detectFullBranchFor: anInteger in: aNode [ + | branch basenode | + basenode := (self detectAstNodeFor: anInteger) . + branch := OrderedCollection new. + [basenode notNil ] whileTrue:[ branch add: basenode. basenode := basenode parent ]. + ^ branch reverse. +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> errorSubscriptBounds: aSomedthing [ + self halt. +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> extractStringFrom: aPosition to: anOtherPosition [ + | from to nodes preffix suffix | + nodes := self detectAstNodesBetween: aPosition and: anOtherPosition in: ast. + from := (aPosition - nodes first textStart +1 ) . + nodes size = 1 ifTrue: [ + ^ nodes first text copyFrom: from to: from + anOtherPosition - aPosition . + ]. + to := nodes last textStop - anOtherPosition. + + preffix := nodes first text copyFrom: from to: nodes first textSize. + suffix := nodes last text copyFrom: 1 to: to. + ^ String + streamContents: [ :str | + str nextPutAll: preffix. + (nodes allButFirst) + inject: str + into: [ :stream :each | + stream nextPutAll: each text. + stream ]. + str nextPutAll: suffix ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> fontAt: characterIndex withStyle: aTextStyle [ + "Answer the fontfor characters in the run beginning at characterIndex." + | attributes font | + self size = 0 ifTrue: [^ aTextStyle defaultFont]. "null text tolerates access" + attributes := runs at: characterIndex. + font := aTextStyle defaultFont. "default" + attributes do: + [:att | att forFontInStyle: aTextStyle do: [:f | font := f]]. + ^ font +] + +{ #category : #copying } +GrafoscopioPillarASText >> inspectNodeAt: anIndex [ + (self detectAstNodeFor: anIndex) inspect +] + +{ #category : #copying } +GrafoscopioPillarASText >> inspectRunAt: anIndex [ + (self runs at: anIndex) inspect +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> pillarText [ + ^ PRPillarWriter write: ast +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> rangeOf: aTextURL startingAt: anInteger [ + ^ self runs rangeOf: aTextURL startingAt: anInteger +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> reannotate: aPRText [ + | current previous parents | + + aPRText textStop: aPRText textStart + aPRText text size. + aPRText markAsDirty. + parents := Set new. + parents add: aPRText parent. + current := aPRText next. + + previous := aPRText. + + [ current isNotNil ] + whileTrue: [ + current markAsDirty. + current textStart: previous textStop. + current textStop: current textStart + current text size. + previous := current. + current := current next. + self ]. + + + self resetRuns. +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASText >> removeAttribute: att from: start to: stop [ + +] + +{ #category : #editing } +GrafoscopioPillarASText >> replaceFrom: start to: stop with: aCollection [ + | nodes realStart realStop newText node | + ((stop - start )> 1 and:[ aCollection isEmpty ]) ifTrue: [ ^self ]. + nodes := self detectAstNodesBetween: start and: stop in: ast. + self assert: nodes size <= 1. + node := nodes first. + realStart := (start - (node textStart) )+ 1 . + realStop := stop - (node textStart ) + 1 . + newText := (node text copyReplaceFrom: realStart to: realStop with: aCollection). + node isText ifTrue: [ + node text: newText + ] ifFalse: [ + node := self detectAstNodeFor: node textStart - 1. + node text: node text, aCollection. + nodes := { node }. + ]. + nodes first markAsDirty. + self reannotate: nodes first. + +] + +{ #category : #accessing } +GrafoscopioPillarASText >> resetRuns [ + "^ runs := RunArray new." + + ^ runs := GrafoscopioPillarRuns new + ast: self; + yourself +] + +{ #category : #accessing } +GrafoscopioPillarASText >> runs [ + ^ runs + ifNil: [ self resetRuns. + runs ] +] + +{ #category : #accessing } +GrafoscopioPillarASText >> size [ + ^ ast textSize +] + +{ #category : #accessing } +GrafoscopioPillarASText >> string [ + ^ GrafoscopioPillarASTextStringAdapter new + text: self; + yourself +] diff --git a/src/Grafoscopio/GrafoscopioPillarASTextNotebook.class.st b/src/Grafoscopio/GrafoscopioPillarASTextNotebook.class.st new file mode 100644 index 0000000..f287172 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioPillarASTextNotebook.class.st @@ -0,0 +1,80 @@ +Class { + #name : #GrafoscopioPillarASTextNotebook, + #superclass : #SpPresenterWithModel, + #instVars : [ + 'sidebar', + 'viewport', + 'model', + 'empty', + 'perspectives' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #specs } +GrafoscopioPillarASTextNotebook class >> defaultSpec [ + ^ SpBoxLayout newHorizontal + add: + (SpBoxLayout newVertical + add: #empty height: self toolbarHeight; + add: #sidebar; + yourself) + width: 51; + add: #viewport; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextNotebook >> basicInstallPerspective: aPerspective [ + viewport ifNotNil: [ viewport aboutToBeUninstalledFrom: self ]. + viewport := self perspectives + at: aPerspective + ifAbsentPut: [ self instantiate: aPerspective on: model document ]. +] + +{ #category : #initialization } +GrafoscopioPillarASTextNotebook >> createDefaultComponent [ + ^ self basicInstallPerspective: GrafoscopioPerspective defaultPerspective +] + +{ #category : #initialization } +GrafoscopioPillarASTextNotebook >> initializeWidgets [ + super initializeWidgets. + sidebar := self sidebar. + self createDefaultComponent. + empty := self newLabel. +] + +{ #category : #initialization } +GrafoscopioPillarASTextNotebook >> installPerspective: aPerspective [ + self basicInstallPerspective: aPerspective . + self modelChanged +] + +{ #category : #initialization } +GrafoscopioPillarASTextNotebook >> modelChanged [ + viewport modelChanged. + self needRebuild: false. + self buildWithSpec +] + +{ #category : #initialization } +GrafoscopioPillarASTextNotebook >> perspectives [ + ^ perspectives ifNil: [ perspectives := Dictionary new ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextNotebook >> setModelBeforeInitialization: aDomainObject [ + super setModelBeforeInitialization: aDomainObject. + model := aDomainObject +] + +{ #category : #initialization } +GrafoscopioPillarASTextNotebook >> sidebar [ + | bar | + bar := self instantiate: SpSidebar. + GrafoscopioPerspective perspectives + do: + [ :p | bar addAction: [ self installPerspective: p ] icon: p icon ]. + ^ bar +] diff --git a/src/Grafoscopio/GrafoscopioPillarASTextStringAdapter.class.st b/src/Grafoscopio/GrafoscopioPillarASTextStringAdapter.class.st new file mode 100644 index 0000000..01dde20 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioPillarASTextStringAdapter.class.st @@ -0,0 +1,205 @@ +Class { + #name : #GrafoscopioPillarASTextStringAdapter, + #superclass : #ProtoObject, + #instVars : [ + 'text' + ], + #category : #'Grafoscopio-Pillar' +} + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> allRangesOfSubstring: aSubstring [ + + ^ Array streamContents: [:s | | start subSize | + start := 1. + subSize := aSubstring size. + [start isZero] + whileFalse: [ start := self findString: aSubstring startingAt: start. + start > 0 + ifTrue: [s nextPut: (start to: start + subSize - 1). + start := start + subSize]]] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> asString [ + ^ self +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringAdapter >> at: aNumber [ + ^ text at: aNumber +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringAdapter >> at: aNumber put: aChar [ + self halt +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> copyFrom: one to: two [ + ^ text copyFrom: one to: two +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> findString: key startingAt: start [ + ^ self findString: key startingAt: start caseSensitive: true +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> findString: key startingAt: start caseSensitive: caseSensitive [ + "Answer the index in this String at which the substring key first occurs, + at or beyond start. The match can be case-sensitive or not. If no match + is found, zero will be returned." + + "IMPLEMENTATION NOTE: do not use CaseSensitiveOrder because it is broken for WideString + This is a temporary work around until Wide CaseSensitiveOrder search is fixed + Code should revert to: + caseSensitive + ifTrue: [^ self findSubstring: key in: self startingAt: start matchTable: CaseSensitiveOrder] + ifFalse: [^ self findSubstring: key in: self startingAt: start matchTable: CaseInsensitiveOrder]" + + ^ caseSensitive + ifTrue: [ WideString new + findSubstring: key + in: self + startingAt: start + matchTable: nil ] + ifFalse: [ WideString new + findSubstring: key + in: self + startingAt: start + matchTable: String newCaseInsensitiveOrder ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> ifEmpty: aBlock [ + "Evaluate the given block, answering its value if the receiver is empty, otherwise answer the receiver." + + "Note that the fact that this method returns its receiver in case the receiver is not empty allows one to write expressions like the following ones: self classifyMethodAs: (myProtocol ifEmpty: ['As yet unclassified'])" + + ^ self isEmpty + ifTrue: [ aBlock value ] + ifFalse: [ self ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock [ + "Evaluate emptyBlock if I'm empty, notEmptyBlock otherwise" + "If the notEmptyBlock has an argument, eval with the receiver as its argument" + + ^ self isEmpty + ifTrue: [ emptyBlock value ] + ifFalse: [ notEmptyBlock cull: self ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> indexOf: aCharacter startingAt: anInteger ifAbsent: aBlockClosure [ + | current index | + current := text detectAstNodeFor: anInteger. + index := current text + indexOf: aCharacter + startingAt: anInteger - current textStart + 1. + current := current next. + [ current isNotNil ] + whileTrue: [ index := current text indexOf: aCharacter. + index = 0 + ifTrue: [ current := current next ] + ifFalse: [ ^ current textStart + index ] ]. + ^ aBlockClosure value +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> isByteString [ + ^ false +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> isEmpty [ + ^ text isEmpty +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringAdapter >> isReadOnlyObject [ + "Answer if the receiver is read-only. + If the VM supports read-only objects it will not write to read-only objects. + An attempt to write to an instance variable of a read-only object will + cause the VM to send attemptToAssign:withIndex: to the read-only object. + An attempt to modify a read-only object in a primitive will cause the + primitive to fail with a #'no modification' error code." + + ^self class isImmediateClass +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> isString [ + ^ true +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> isWideString [ + ^ true +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> lastIndexOf: anElement startingAt: lastIndex ifAbsent: exceptionBlock [ + "Answer the index of the last occurence of anElement within the + receiver. If the receiver does not contain anElement, answer the + result of evaluating the argument, exceptionBlock." + lastIndex to: 1 by: -1 do: [ :index | + (self at: index) = anElement + ifTrue: [ ^ index ] ]. + ^ exceptionBlock value +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringAdapter >> notEmpty [ + ^ text notEmpty +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> printOn: aStream [ + aStream + nextPutAll: 'StringAdaptor('; + nextPutAll: self size asString; + nextPutAll: ')' +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> printString [ + ^ String streamContents: [ :str | self printOn: str ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> readStream [ + ^ ReadStream on: self +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringAdapter >> size [ + ^ text size +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> string [ + ^ text extractStringFrom: 1 to: self size +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringAdapter >> text: aGFPText [ + text := aGFPText +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> trimBoth [ + ^ self +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> yourself [ + ^ self +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringAdapter >> ~= anOther [ + ^ (self == anOther) not +] diff --git a/src/Grafoscopio/GrafoscopioPillarASTextStringDecorator.class.st b/src/Grafoscopio/GrafoscopioPillarASTextStringDecorator.class.st deleted file mode 100644 index ce5946b..0000000 --- a/src/Grafoscopio/GrafoscopioPillarASTextStringDecorator.class.st +++ /dev/null @@ -1,74 +0,0 @@ -Class { - #name : #GrafoscopioPillarASTextStringDecorator, - #superclass : #Object, - #instVars : [ - 'text' - ], - #category : #'Grafoscopio-Pillar' -} - -{ #category : #accessing } -GrafoscopioPillarASTextStringDecorator >> at: aNumber [ - ^ text at: aNumber -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringDecorator >> at: aNumber put: aChar [ - self halt -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringDecorator >> copyFrom: one to: two [ - ^ text copyFrom: one to: two -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringDecorator >> indexOf: aCharacter startingAt: anInteger ifAbsent: aBlockClosure [ - | current index | - current := text detectAstNodeFor: anInteger. - index := current text - indexOf: aCharacter - startingAt: anInteger - current textStart + 1. - current := current next. - [ current isNotNil ] - whileTrue: [ index := current text indexOf: aCharacter. - index = 0 - ifTrue: [ current := current next ] - ifFalse: [ ^ current textStart + index ] ]. - ^ aBlockClosure value -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringDecorator >> isByteString [ - ^ false -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringDecorator >> isEmpty [ - ^ text isEmpty -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringDecorator >> isWideString [ - ^ true -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringDecorator >> notEmpty [ - ^ text notEmpty -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringDecorator >> size [ - ^ text size -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringDecorator >> string [ - self shouldBeImplemented. -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringDecorator >> text: aGFPText [ - text := aGFPText -] diff --git a/src/Grafoscopio/GrafoscopioPillarASTextStringProjectionAdapter.class.st b/src/Grafoscopio/GrafoscopioPillarASTextStringProjectionAdapter.class.st new file mode 100644 index 0000000..1600398 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioPillarASTextStringProjectionAdapter.class.st @@ -0,0 +1,69 @@ +Class { + #name : #GrafoscopioPillarASTextStringProjectionAdapter, + #superclass : #GrafoscopioPillarASTextStringAdapter, + #instVars : [ + 'from', + 'to', + 'cached' + ], + #category : #'Grafoscopio-Pillar' +} + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> asText [ + ^ self string asText +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringProjectionAdapter >> at: anInteger [ + ^ self cached at: anInteger +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringProjectionAdapter >> cached [ + ^ cached ifNil: [ cached := text extractStringFrom: from to: to ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> copyFrom: one to: two [ + ^ self class new + text: text; + from: from + one; + to: from + two; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> from: anInteger [ + from := anInteger +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> isEmpty [ + ^ to <= from +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> printOn: aStream [ + aStream + nextPutAll: 'SubStringAdapter('; + nextPutAll: from asString; + nextPutAll: ':'; + nextPutAll: to asString; + nextPutAll: ')' +] + +{ #category : #accessing } +GrafoscopioPillarASTextStringProjectionAdapter >> size [ + ^ to - from +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> string [ + ^ text extractStringFrom: from to: to +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarASTextStringProjectionAdapter >> to: anInteger [ + to := anInteger +] diff --git a/src/Grafoscopio/GrafoscopioPillarASTextStringProjectionDecorator.class.st b/src/Grafoscopio/GrafoscopioPillarASTextStringProjectionDecorator.class.st deleted file mode 100644 index 1fea36c..0000000 --- a/src/Grafoscopio/GrafoscopioPillarASTextStringProjectionDecorator.class.st +++ /dev/null @@ -1,64 +0,0 @@ -Class { - #name : #GrafoscopioPillarASTextStringProjectionDecorator, - #superclass : #GrafoscopioPillarASTextStringDecorator, - #instVars : [ - 'from', - 'to', - 'cached' - ], - #category : #'Grafoscopio-Pillar' -} - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> allRangesOfSubstring: aString [ - ^ { } -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> asText [ - ^ self string asText -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringProjectionDecorator >> at: anInteger [ - ^ self cached at: anInteger -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringProjectionDecorator >> cached [ - ^ cached ifNil: [ cached := text extractStringFrom: from to: to ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> copyFrom: one to: two [ - ^ self class new - text: text; - from: from + one; - to: from + two; - yourself -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> from: anInteger [ - from := anInteger -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> isEmpty [ - ^ from <= to -] - -{ #category : #accessing } -GrafoscopioPillarASTextStringProjectionDecorator >> size [ - ^ to - from -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> string [ - ^ text extractStringFrom: from to: to -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarASTextStringProjectionDecorator >> to: anInteger [ - to := anInteger -] diff --git a/src/Grafoscopio/GrafoscopioPillarNode.class.st b/src/Grafoscopio/GrafoscopioPillarNode.class.st new file mode 100644 index 0000000..8bcc25d --- /dev/null +++ b/src/Grafoscopio/GrafoscopioPillarNode.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GrafoscopioPillarNode, + #superclass : #GrafoscopioLeafNode, + #instVars : [ + 'ast', + 'astText' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #testing } +GrafoscopioPillarNode class >> icon [ + ^ self iconNamed: #workspace +] + +{ #category : #testing } +GrafoscopioPillarNode class >> nameForSelection [ + ^ 'New Pillar node' +] + +{ #category : #testing } +GrafoscopioPillarNode class >> showInMenu [ + ^ true +] + +{ #category : #accessing } +GrafoscopioPillarNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitPillarNode: self. + +] + +{ #category : #accessing } +GrafoscopioPillarNode >> text [ + ^ astText +] + +{ #category : #accessing } +GrafoscopioPillarNode >> text: anAst [ + astText := anAst +] diff --git a/src/Grafoscopio/GrafoscopioPillarRuns.class.st b/src/Grafoscopio/GrafoscopioPillarRuns.class.st index 4c869d6..8698038 100644 --- a/src/Grafoscopio/GrafoscopioPillarRuns.class.st +++ b/src/Grafoscopio/GrafoscopioPillarRuns.class.st @@ -1,80 +1,74 @@ -Class { - #name : #GrafoscopioPillarRuns, - #superclass : #Object, - #instVars : [ - 'ast', - 'lastBranch', - 'lastAttributes', - 'visitor' - ], - #category : #'Grafoscopio-Pillar-Style' -} - -{ #category : #accessing } -GrafoscopioPillarRuns >> ast: anAst [ - ast := anAst. - visitor := GrafoscopioAttributeBranchVisitor new. - visitor text: ast -] - -{ #category : #'basic api' } -GrafoscopioPillarRuns >> at: anIndex [ - | newBranch | - anIndex > ast size - ifTrue: [ ^ Array empty ]. - newBranch := ast detectFullBranchFor: anIndex. - newBranch = lastBranch - ifTrue: [ ^ lastAttributes ]. - lastAttributes := self - calculateAttributesForBranch: newBranch - at: anIndex. - lastBranch := (newBranch - anySatisfy: [ :n | n isMemberOf: PRCodeblock ]) - ifTrue: [ nil ] - ifFalse: [ newBranch ]. - ^ lastAttributes -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarRuns >> calculateAttributesForBranch: aCollection at: anIndex [ - ^ visitor analyzeBranch: aCollection at: anIndex. -] - -{ #category : #'basic api' } -GrafoscopioPillarRuns >> isEmpty [ - ^ ast isNil or: [ ast isEmpty ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarRuns >> rangeOf: aTextURL startingAt: anInteger [ - self assert: (aTextURL isKindOf: TextAction). - - ((self at: anInteger) contains: [ : a | (a isKindOf: TextAction)]) ifTrue: [ - (ast extractStringFrom: anInteger to: (ast detectAstNodeFor: anInteger ) textStop) traceCr. - ^ anInteger to: (ast detectAstNodeFor: anInteger ) textStop - ]. - ^ 0 to: 0 -] - -{ #category : #'basic api' } -GrafoscopioPillarRuns >> reset [ - lastAttributes := nil. - lastBranch := nil. -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarRuns >> runLengthFor: anInteger [ - | node | - anInteger > ast size - ifTrue: [ ^ 0 ]. - node := ast detectAstNodeFor: anInteger. - ^ (node isKindOf: PRCodeblock) - ifTrue: [ 1 ] - ifFalse: [ node textStop - anInteger ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarRuns >> withStartStopAndValueDo: aBlockClosure [ -thisContext sender asString traceCr. - 'Should implement' traceCr. -] +Class { + #name : #GrafoscopioPillarRuns, + #superclass : #Object, + #instVars : [ + 'ast', + 'lastBranch', + 'lastAttributes', + 'visitor' + ], + #category : #'Grafoscopio-Pillar-Style' +} + +{ #category : #accessing } +GrafoscopioPillarRuns >> ast: anAst [ + ast := anAst. + visitor := GrafoscopioAttributeBranchVisitor new. + visitor text: ast +] + +{ #category : #'basic api' } +GrafoscopioPillarRuns >> at: anIndex [ + | newBranch | + anIndex > ast size + ifTrue: [ ^ Array empty ]. + newBranch := ast detectFullBranchFor: anIndex. + "newBranch = lastBranch + ifTrue: [ ^ lastAttributes ]." + lastAttributes := self + calculateAttributesForBranch: newBranch + at: anIndex. + lastBranch := (newBranch + anySatisfy: [ :n | n isMemberOf: PRCodeblock ]) + ifTrue: [ nil ] + ifFalse: [ newBranch ]. + ^ lastAttributes +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarRuns >> calculateAttributesForBranch: aCollection at: anIndex [ + ^ visitor analyzeBranch: aCollection at: anIndex. +] + +{ #category : #'basic api' } +GrafoscopioPillarRuns >> isEmpty [ + ^ ast isNil or: [ ast isEmpty ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarRuns >> rangeOf: aTextURL startingAt: anInteger [ + self assert: (aTextURL isKindOf: TextAction). + + ((self at: anInteger) contains: [ : a | (a isKindOf: TextAction)]) ifTrue: [ + (ast extractStringFrom: anInteger to: (ast detectAstNodeFor: anInteger ) textStop) traceCr. + ^ anInteger to: (ast detectAstNodeFor: anInteger ) textStop + ]. + ^ 0 to: 0 +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarRuns >> runLengthFor: anInteger [ + | node | + anInteger > ast size + ifTrue: [ ^ 0 ]. + node := ast detectAstNodeFor: anInteger. + ^ (node isKindOf: PRCodeblock) + ifTrue: [ 1 ] + ifFalse: [ node textStop - anInteger ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarRuns >> withStartStopAndValueDo: aBlockClosure [ +thisContext sender asString traceCr. + 'Should implement' traceCr. +] diff --git a/src/Grafoscopio/GrafoscopioPillarStyler.class.st b/src/Grafoscopio/GrafoscopioPillarStyler.class.st index 9e4bcb3..945b721 100644 --- a/src/Grafoscopio/GrafoscopioPillarStyler.class.st +++ b/src/Grafoscopio/GrafoscopioPillarStyler.class.st @@ -1,104 +1,104 @@ -Class { - #name : #GrafoscopioPillarStyler, - #superclass : #Object, - #instVars : [ - 'pharoStyler' - ], - #category : #'Grafoscopio-Pillar-Style' -} - -{ #category : #accessing } -GrafoscopioPillarStyler class >> defaultStyler [ - ^ self new -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForCodeBlock: aPRCodeblock at: index [ - - aPRCodeblock language originalName = 'pharo-image' - ifTrue: [ ^ {(TextAnchor new - anchoredMorph: (self class compiler evaluate: (aPRCodeblock propertyAt: #text)))} ]. - aPRCodeblock language originalName = 'pharo' - ifTrue: [ | runs | - runs := (self pharoStyler - privateStyle: aPRCodeblock text asText , '.') runs. - ^ runs at: index - aPRCodeblock textStart ]. - ^{} -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForDocument: aDocument [ - ^ self default -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForExternalLink: aPRExternalLink [ - ^ {(TextColor new color: (Color fromHexString: '03A9F4')). - TextEmphasis underlined. - (TextAction new - actOnClickBlock: [ self inform: 'Should be going to ' , aPRExternalLink reference ])} -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForFigure: aPRFigure [ - ^ self default -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForFormatText: aGrafoscopioFormatTextNode [ - ^ self attributesForText: aGrafoscopioFormatTextNode -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForHeader: aPRHeader [ - ^ {(TextFontReference - toFont: - (LogicalFont - familyName: 'Source Code Pro' - pointSize: (20 - (aPRHeader level * 5) max: 10))) . TextEmphasis bold} -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForInternalLink: aPRInternalLink [ - ^ self attributesForExternalLink: aPRInternalLink -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForLineBreak: aPRLineBreak [ - ^ self default -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForListItem: aPRListItem at: index [ - ^ (aPRListItem text at: index - aPRListItem textStart + 1) = Character home - ifTrue: [ {(TextIndent tabs: aPRListItem level). - (TextAnchor new - anchoredMorph: (self iconNamed: #menuPin); - yourself)} ] ifFalse: [ { } ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForParagraph: aParagraph [ - ^ self default -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForText: aText [ - ^ self default -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> attributesForUnorderedList: aList [ - ^ self default -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> default [ - ^ Array empty -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarStyler >> pharoStyler [ - ^ pharoStyler - ifNil: [ pharoStyler := SHRBTextStyler new isForWorkspace: true; yourself] -] +Class { + #name : #GrafoscopioPillarStyler, + #superclass : #Object, + #instVars : [ + 'pharoStyler' + ], + #category : #'Grafoscopio-Pillar-Style' +} + +{ #category : #accessing } +GrafoscopioPillarStyler class >> defaultStyler [ + ^ self new +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForCodeBlock: aPRCodeblock at: index [ + + aPRCodeblock language originalName = 'pharo-image' + ifTrue: [ ^ {(TextAnchor new + anchoredMorph: (self class compiler evaluate: (aPRCodeblock propertyAt: #text)))} ]. + aPRCodeblock language originalName = 'pharo' + ifTrue: [ | runs | + runs := (self pharoStyler + privateStyle: aPRCodeblock text asText , '.') runs. + ^ runs at: index - aPRCodeblock textStart ]. + ^{} +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForDocument: aDocument [ + ^ self default +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForExternalLink: aPRExternalLink [ + ^ {(TextColor new color: (Color fromHexString: '03A9F4')). + TextEmphasis underlined. + (TextAction new + actOnClickBlock: [ self inform: 'Should be going to ' , aPRExternalLink reference ])} +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForFigure: aPRFigure [ + ^ self default +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForFormatText: aGrafoscopioFormatTextNode [ + ^ self attributesForText: aGrafoscopioFormatTextNode +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForHeader: aPRHeader [ + ^ {(TextFontReference + toFont: + (LogicalFont + familyName: 'Source Code Pro' + pointSize: (20 - (aPRHeader level * 5) max: 10))) . TextEmphasis bold} +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForInternalLink: aPRInternalLink [ + ^ self attributesForExternalLink: aPRInternalLink +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForLineBreak: aPRLineBreak [ + ^ self default +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForListItem: aPRListItem at: index [ + ^ (aPRListItem text at: index - aPRListItem textStart + 1) = Character home + ifTrue: [ {(TextIndent tabs: aPRListItem parent level). + (TextAnchor new + anchoredMorph: (self iconNamed: #menuPin); + yourself)} ] ifFalse: [ { } ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForParagraph: aParagraph [ + ^ self default +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForText: aText [ + ^ self default +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> attributesForUnorderedList: aList [ + ^ self default +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> default [ + ^ Array empty +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarStyler >> pharoStyler [ + ^ pharoStyler + ifNil: [ pharoStyler := SHRBTextStyler new isForWorkspace: true; yourself] +] diff --git a/src/Grafoscopio/GrafoscopioPillarTextAnnotator.class.st b/src/Grafoscopio/GrafoscopioPillarTextAnnotator.class.st index f6ec298..fcc9802 100644 --- a/src/Grafoscopio/GrafoscopioPillarTextAnnotator.class.st +++ b/src/Grafoscopio/GrafoscopioPillarTextAnnotator.class.st @@ -1,65 +1,65 @@ -Class { - #name : #GrafoscopioPillarTextAnnotator, - #superclass : #PRVisitor, - #instVars : [ - 'texts', - 'formatter', - 'lastStop' - ], - #category : #'Grafoscopio-Pillar' -} - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> formatter: aFormatter [ - formatter := aFormatter -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> initialize [ - super initialize. - texts := OrderedCollection new. - formatter := GrafoscopioTextFormatter default. -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> visitCodeblock: aTextObject [ - self visitText: aTextObject -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> visitCommentedLine: aTextObject [ - self visitText: aTextObject -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> visitDocument: aDoc [ - lastStop := 1 . - aDoc accept: formatter. - super visitDocument: aDoc. - -] - -{ #category : #'as yet unclassified' } -GrafoscopioPillarTextAnnotator >> visitFormatText: aGrafoscopioFormatTextNode [ - " for the annotator, a format text is a much as regular text" - self visitText: aGrafoscopioFormatTextNode -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> visitLineBreak: anObject [ - ^ self visitText: anObject -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> visitRaw: aTextObject [ - self visitText: aTextObject -] - -{ #category : #'visiting-document' } -GrafoscopioPillarTextAnnotator >> visitText: aTextObject [ - texts ifNotEmpty: [ texts last next: aTextObject ]. - texts add: aTextObject. - aTextObject propertyAt: #textStart put: lastStop. - lastStop := lastStop + aTextObject text size. - aTextObject propertyAt: #textStop put: lastStop . -] +Class { + #name : #GrafoscopioPillarTextAnnotator, + #superclass : #PRVisitor, + #instVars : [ + 'texts', + 'formatter', + 'lastStop' + ], + #category : #'Grafoscopio-Pillar' +} + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> formatter: aFormatter [ + formatter := aFormatter +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> initialize [ + super initialize. + texts := OrderedCollection new. + formatter := GrafoscopioTextFormatter default. +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> visitCodeblock: aTextObject [ + self visitText: aTextObject +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> visitCommentedLine: aTextObject [ + self visitText: aTextObject +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> visitDocument: aDoc [ + lastStop := 1 . + aDoc accept: formatter. + super visitDocument: aDoc. + +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarTextAnnotator >> visitFormatText: aGrafoscopioFormatTextNode [ + " for the annotator, a format text is a much as regular text" + self visitText: aGrafoscopioFormatTextNode +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> visitLineBreak: anObject [ + ^ self visitText: anObject +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> visitRaw: aTextObject [ + self visitText: aTextObject +] + +{ #category : #'visiting-document' } +GrafoscopioPillarTextAnnotator >> visitText: aTextObject [ + texts ifNotEmpty: [ texts last next: aTextObject ]. + texts add: aTextObject. + aTextObject propertyAt: #textStart put: lastStop. + lastStop := lastStop + aTextObject text size. + aTextObject propertyAt: #textStop put: lastStop . +] diff --git a/src/Grafoscopio/GrafoscopioPillarToTextDecorator.class.st b/src/Grafoscopio/GrafoscopioPillarToTextDecorator.class.st new file mode 100644 index 0000000..63c4285 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioPillarToTextDecorator.class.st @@ -0,0 +1,37 @@ +Class { + #name : #GrafoscopioPillarToTextDecorator, + #superclass : #Object, + #instVars : [ + 'pillarNode' + ], + #category : #'Grafoscopio-Pillar' +} + +{ #category : #'instance creation' } +GrafoscopioPillarToTextDecorator class >> on: aGFPillarNode [ + ^ self new + pillarNode: aGFPillarNode; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioPillarToTextDecorator >> name [ + ^ pillarNode name +] + +{ #category : #accessing } +GrafoscopioPillarToTextDecorator >> pillarNode: aNode [ + pillarNode := aNode +] + +{ #category : #accessing } +GrafoscopioPillarToTextDecorator >> text [ + ^ pillarNode text pillarText +] + +{ #category : #accessing } +GrafoscopioPillarToTextDecorator >> text: aText [ + | parsed | + parsed := PRPillarParser parse: aText. + ^ pillarNode text ast: parsed +] diff --git a/src/Grafoscopio/GrafoscopioProject.class.st b/src/Grafoscopio/GrafoscopioProject.class.st index 8497e1f..c36303a 100644 --- a/src/Grafoscopio/GrafoscopioProject.class.st +++ b/src/Grafoscopio/GrafoscopioProject.class.st @@ -1,26 +1,31 @@ -Class { - #name : #GrafoscopioProject, - #superclass : #Object, - #instVars : [ - 'document', - 'dictionary' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #'as yet unclassified' } -GrafoscopioProject >> document [ - ^ document -] - -{ #category : #initialization } -GrafoscopioProject >> initialize [ - super initialize. - document := GrafoscopioRootNode new. - dictionary := GrafoscopioRootNode new -] - -{ #category : #initialization } -GrafoscopioProject >> name: aName [ - document name: aName -] +Class { + #name : #GrafoscopioProject, + #superclass : #Object, + #instVars : [ + 'document', + 'dictionary' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #'as yet unclassified' } +GrafoscopioProject >> acceptVisitor: aGrafoscopioViewportTextVisitor [ + aGrafoscopioViewportTextVisitor visitGrafoscopioProject: self +] + +{ #category : #'as yet unclassified' } +GrafoscopioProject >> document [ + ^ document +] + +{ #category : #initialization } +GrafoscopioProject >> initialize [ + super initialize. + document := GrafoscopioRootNode new. + dictionary := GrafoscopioRootNode new +] + +{ #category : #initialization } +GrafoscopioProject >> name: aName [ + document name: aName +] diff --git a/src/Grafoscopio/GrafoscopioReplace.class.st b/src/Grafoscopio/GrafoscopioReplace.class.st index c266ef2..7eed324 100644 --- a/src/Grafoscopio/GrafoscopioReplace.class.st +++ b/src/Grafoscopio/GrafoscopioReplace.class.st @@ -1,122 +1,122 @@ -" -I'm a tool for finding and replacing text ocurrences in notebook -nodes. -" -Class { - #name : #GrafoscopioReplace, - #superclass : #ComposablePresenter, - #instVars : [ - 'find', - 'replace', - 'ok', - 'cancel' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #specs } -GrafoscopioReplace class >> defaultSpec [ - ^ SpecLayout composed - newColumn: [ :column | - column - add: #find; - add: #replace; - newRow: [ :row | row add: #ok; add: #cancel ] height: self toolbarHeight ]; - yourself -] - -{ #category : #accessing } -GrafoscopioReplace >> cancel [ - ^ cancel -] - -{ #category : #accessing } -GrafoscopioReplace >> cancel: anObject [ - cancel := anObject -] - -{ #category : #accessing } -GrafoscopioReplace >> find [ - ^ find -] - -{ #category : #accessing } -GrafoscopioReplace >> find: anObject [ - find := anObject -] - -{ #category : #initialization } -GrafoscopioReplace >> initializePresenter [ - "Here we need to deal with changes in the OK button to send messages about the - state of the 'find' and 'replace' variables or do nothing if 'Cancel' is pressed." - ok whenStateChangedDo: [ - Transcript show: 'OK Clicked!' ] - -] - -{ #category : #initialization } -GrafoscopioReplace >> initializeWidgets [ - find := self newTextInput - ghostText: 'Text to find...'; - autoAccept: true. - replace := self newTextInput - ghostText: 'Text to replace...'; - autoAccept: true. - ok := self newButton - label: 'OK'; - state: false; - action: [ - ok state: true. - self window close. ] . - cancel := self newButton - label: 'Cancel'; - action: [ - cancel state: true. - self window close ]. - self focusOrder - add: find; - add: replace; - add: ok; - add: cancel. - self askOkToClose: true -] - -{ #category : #accessing } -GrafoscopioReplace >> ok [ - ^ ok -] - -{ #category : #accessing } -GrafoscopioReplace >> ok: anObject [ - ok := anObject -] - -{ #category : #private } -GrafoscopioReplace >> okToChange [ - ^ true -] - -{ #category : #accessing } -GrafoscopioReplace >> replace [ - ^ replace -] - -{ #category : #accessing } -GrafoscopioReplace >> replace: anObject [ - replace := anObject -] - -{ #category : #initialization } -GrafoscopioReplace >> returnValues [ - self ok state - ifFalse: [ ^ self ] - ifTrue: [^ Dictionary new - at: 'find' put: find getText; - at: 'replace' put: replace getText; - yourself] -] - -{ #category : #api } -GrafoscopioReplace >> title [ - ^ 'Find & Replace' -] +" +I'm a tool for finding and replacing text ocurrences in notebook +nodes. +" +Class { + #name : #GrafoscopioReplace, + #superclass : #ComposablePresenter, + #instVars : [ + 'find', + 'replace', + 'ok', + 'cancel' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #specs } +GrafoscopioReplace class >> defaultSpec [ + ^ SpecLayout composed + newColumn: [ :column | + column + add: #find; + add: #replace; + newRow: [ :row | row add: #ok; add: #cancel ] height: self toolbarHeight ]; + yourself +] + +{ #category : #accessing } +GrafoscopioReplace >> cancel [ + ^ cancel +] + +{ #category : #accessing } +GrafoscopioReplace >> cancel: anObject [ + cancel := anObject +] + +{ #category : #accessing } +GrafoscopioReplace >> find [ + ^ find +] + +{ #category : #accessing } +GrafoscopioReplace >> find: anObject [ + find := anObject +] + +{ #category : #initialization } +GrafoscopioReplace >> initializePresenter [ + "Here we need to deal with changes in the OK button to send messages about the + state of the 'find' and 'replace' variables or do nothing if 'Cancel' is pressed." + ok whenStateChangedDo: [ + Transcript show: 'OK Clicked!' ] + +] + +{ #category : #initialization } +GrafoscopioReplace >> initializeWidgets [ + find := self newTextInput + ghostText: 'Text to find...'; + autoAccept: true. + replace := self newTextInput + ghostText: 'Text to replace...'; + autoAccept: true. + ok := self newButton + label: 'OK'; + state: false; + action: [ + ok state: true. + self window close. ] . + cancel := self newButton + label: 'Cancel'; + action: [ + cancel state: true. + self window close ]. + self focusOrder + add: find; + add: replace; + add: ok; + add: cancel. + self askOkToClose: true +] + +{ #category : #accessing } +GrafoscopioReplace >> ok [ + ^ ok +] + +{ #category : #accessing } +GrafoscopioReplace >> ok: anObject [ + ok := anObject +] + +{ #category : #private } +GrafoscopioReplace >> okToChange [ + ^ true +] + +{ #category : #accessing } +GrafoscopioReplace >> replace [ + ^ replace +] + +{ #category : #accessing } +GrafoscopioReplace >> replace: anObject [ + replace := anObject +] + +{ #category : #initialization } +GrafoscopioReplace >> returnValues [ + self ok state + ifFalse: [ ^ self ] + ifTrue: [^ Dictionary new + at: 'find' put: find getText; + at: 'replace' put: replace getText; + yourself] +] + +{ #category : #api } +GrafoscopioReplace >> title [ + ^ 'Find & Replace' +] diff --git a/src/Grafoscopio/GrafoscopioRootNode.class.st b/src/Grafoscopio/GrafoscopioRootNode.class.st index 2880cc1..362e910 100644 --- a/src/Grafoscopio/GrafoscopioRootNode.class.st +++ b/src/Grafoscopio/GrafoscopioRootNode.class.st @@ -1,68 +1,68 @@ -" -This is a root node. It represents a document. -" -Class { - #name : #GrafoscopioRootNode, - #superclass : #GrafoscopioAbstractNode, - #instVars : [ - 'children' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #accessing } -GrafoscopioRootNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitRootNode: self. - -] - -{ #category : #accessing } -GrafoscopioRootNode >> acceptsChildsOfClass: aClass [ - ^ {GrafoscopioUnitNode} includes: aClass -] - -{ #category : #accessing } -GrafoscopioRootNode >> addAtBeginningChild: aBlock ofClass: aClass [ - (self acceptsChildsOfClass: aClass) - ifTrue: [ | child | - child := aBlock value. - child parent: self. - self children addFirst: child ] -] - -{ #category : #accessing } -GrafoscopioRootNode >> addChild: aBlock ofClass: aClass [ - (self acceptsChildsOfClass: aClass) - ifTrue: [ | child | - child := aBlock value. - child parent: self. - self children add: child ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioRootNode >> children [ - ^ children -] - -{ #category : #accessing } -GrafoscopioRootNode >> initialize [ - super initialize. - children := SortedCollection new - sortBlock: [ :a :b | a order < b order ]; - yourself -] - -{ #category : #accessing } -GrafoscopioRootNode >> level [ - ^ 1 -] - -{ #category : #accessing } -GrafoscopioRootNode >> text [ - ^ name -] - -{ #category : #accessing } -GrafoscopioRootNode >> text: aText [ - name := aText -] +" +This is a root node. It represents a document. +" +Class { + #name : #GrafoscopioRootNode, + #superclass : #GrafoscopioAbstractNode, + #instVars : [ + 'children' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #accessing } +GrafoscopioRootNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitRootNode: self. + +] + +{ #category : #accessing } +GrafoscopioRootNode >> acceptsChildsOfClass: aClass [ + ^ {GrafoscopioUnitNode} includes: aClass +] + +{ #category : #accessing } +GrafoscopioRootNode >> addAtBeginningChild: aBlock ofClass: aClass [ + (self acceptsChildsOfClass: aClass) + ifTrue: [ | child | + child := aBlock value. + child parent: self. + self children addFirst: child ] +] + +{ #category : #accessing } +GrafoscopioRootNode >> addChild: aBlock ofClass: aClass [ + (self acceptsChildsOfClass: aClass) + ifTrue: [ | child | + child := aBlock value. + child parent: self. + self children add: child ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioRootNode >> children [ + ^ children +] + +{ #category : #accessing } +GrafoscopioRootNode >> initialize [ + super initialize. + children := SortedCollection new + sortBlock: [ :a :b | a order < b order ]; + yourself +] + +{ #category : #accessing } +GrafoscopioRootNode >> level [ + ^ 1 +] + +{ #category : #accessing } +GrafoscopioRootNode >> text [ + ^ name +] + +{ #category : #accessing } +GrafoscopioRootNode >> text: aText [ + name := aText +] diff --git a/src/Grafoscopio/GrafoscopioTextFormatter.class.st b/src/Grafoscopio/GrafoscopioTextFormatter.class.st index f155343..0ee1a82 100644 --- a/src/Grafoscopio/GrafoscopioTextFormatter.class.st +++ b/src/Grafoscopio/GrafoscopioTextFormatter.class.st @@ -1,124 +1,124 @@ -" -I am in charge of formatting a document in terms of text. -Our text model does not support break lines or tabbings as text attribute, but as text content. (By example, our header needs to have a line break before and after by default. This kind of things can be done in rubbric by adding the character cr. Still, our pillar AST does not have this breakline for the header, and we do not wan to add it, since it would affect the parsing. No, what we want is to be able to format and modify the text before returning it when being displayed, composed, scanned, etc. So for doing so we add the text formatter. -This text formatter contains a dictionary that allows the user to add as many as needed for each class. -This class provides as extention point: - - #formatsFor: anObject > Returning a collection of formats for the given object (the default behaviour stores it by class). - #Redefine the specific visit methods for specific class behaviour. - - -) -" -Class { - #name : #GrafoscopioTextFormatter, - #superclass : #PRVisitor, - #instVars : [ - 'formats', - 'stack' - ], - #classInstVars : [ - 'default' - ], - #category : #'Grafoscopio-Pillar-TextFormat' -} - -{ #category : #accessing } -GrafoscopioTextFormatter class >> buildDefault [ - | new | - new := self new. - new registerFormat: GrafoscopioFmtDoubleLinebreak instance forClass: PRHeader. - new registerFormat: GrafoscopioFmtAnchorOnTheLeft instance forClass: PRListItem. - new registerFormat: GrafoscopioFmtBeginningLinebreak instance forClass: PRListItem. - -" new registerFormat: GrafoscopioFmtDoubleLinebreak instance forClass: PRCodeblock ." - new registerFormat: GrafoscopioFmtAnchorOnTheLeft2 instance forClass: PRCodeblock . - new registerFormat: GrafoscopioFmtEndingLinebreak instance forClass: PRParagraph . - new registerFormat: GrafoscopioFmtBeginningLinebreak instance forClass: PRList. - new registerFormat: GrafoscopioFmtUrlAsBody instance forClass: PRExternalLink . - new registerFormat: GrafoscopioFmtEndingSpace instance forClass: PRExternalLink. - new registerFormat: GrafoscopioFmtUrlAsBody instance forClass: PRMailLink. - new registerFormat: GrafoscopioFmtEndingSpace instance forClass: PRMailLink. - new registerFormat: GrafoscopioFmtAnchorAsBody instance forClass: PRInternalLink. - new registerFormat: GrafoscopioFmtEndingSpace instance forClass: PRInternalLink. - ^ new -] - -{ #category : #accessing } -GrafoscopioTextFormatter class >> buildEmpty [ - | new | - new := self new. - - ^ new -] - -{ #category : #accessing } -GrafoscopioTextFormatter class >> default [ - ^ self buildDefault -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> format: aNode [ - (self formatsFor: aNode) do: [ :f | f beInstalledIn: aNode ] -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> formatsFor: aNode [ - ^ formats at: aNode ifAbsent: [ self formatsForClass: aNode class ] -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> formatsForClass: aNodeClass [ - ^ formats at: aNodeClass ifAbsent: [ Array empty ] -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> initialize [ - super initialize . - formats := Dictionary new. - stack := Stack new. -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> registerFormat: aFormat for: aNode [ - ^ (formats at: aNode ifAbsentPut: [ OrderedCollection new ]) - add: aFormat -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> registerFormat: aFormat forClass: aNodeClass [ - ^ self registerFormat: aFormat for: aNodeClass -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> visitDocument: aDocument [ - self assert: stack isEmpty. - super visitDocument: aDocument. - self assert: stack isEmpty -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> visitDocumentGroup: aNode [ - aNode parent: (stack ifNotEmpty: [stack first] ifEmpty:[ nil ]). - self format: aNode. - stack push: aNode. - super visitDocumentGroup: aNode. - stack pop -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> visitDocumentItem: anItem [ - self format: anItem. - anItem parent: stack first. -] - -{ #category : #initialization } -GrafoscopioTextFormatter >> visitEmptyParagraph: aParagraph [ - stack top children last = aParagraph - ifTrue: [ stack top children: stack top children allButLast ]. -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextFormatter >> visitFormatText: aGrafoscopioFormatTextNode [ - " No way we format the formatted text. " -] +" +I am in charge of formatting a document in terms of text. +Our text model does not support break lines or tabbings as text attribute, but as text content. (By example, our header needs to have a line break before and after by default. This kind of things can be done in rubbric by adding the character cr. Still, our pillar AST does not have this breakline for the header, and we do not wan to add it, since it would affect the parsing. No, what we want is to be able to format and modify the text before returning it when being displayed, composed, scanned, etc. So for doing so we add the text formatter. +This text formatter contains a dictionary that allows the user to add as many as needed for each class. +This class provides as extention point: + + #formatsFor: anObject > Returning a collection of formats for the given object (the default behaviour stores it by class). + #Redefine the specific visit methods for specific class behaviour. + + +) +" +Class { + #name : #GrafoscopioTextFormatter, + #superclass : #PRVisitor, + #instVars : [ + 'formats', + 'stack' + ], + #classInstVars : [ + 'default' + ], + #category : #'Grafoscopio-Pillar-TextFormat' +} + +{ #category : #accessing } +GrafoscopioTextFormatter class >> buildDefault [ + | new | + new := self new. + new registerFormat: GrafoscopioFmtDoubleLinebreak instance forClass: PRHeader. + new registerFormat: GrafoscopioFmtAnchorOnTheLeft instance forClass: PRListItem. + new registerFormat: GrafoscopioFmtBeginningLinebreak instance forClass: PRListItem. + +" new registerFormat: GrafoscopioFmtDoubleLinebreak instance forClass: PRCodeblock ." + new registerFormat: GrafoscopioFmtAnchorOnTheLeft2 instance forClass: PRCodeblock . + new registerFormat: GrafoscopioFmtEndingLinebreak instance forClass: PRParagraph . + new registerFormat: GrafoscopioFmtBeginningLinebreak instance forClass: PRList. + new registerFormat: GrafoscopioFmtUrlAsBody instance forClass: PRExternalLink . + new registerFormat: GrafoscopioFmtEndingSpace instance forClass: PRExternalLink. + new registerFormat: GrafoscopioFmtUrlAsBody instance forClass: PRMailLink. + new registerFormat: GrafoscopioFmtEndingSpace instance forClass: PRMailLink. + new registerFormat: GrafoscopioFmtAnchorAsBody instance forClass: PRInternalLink. + new registerFormat: GrafoscopioFmtEndingSpace instance forClass: PRInternalLink. + ^ new +] + +{ #category : #accessing } +GrafoscopioTextFormatter class >> buildEmpty [ + | new | + new := self new. + + ^ new +] + +{ #category : #accessing } +GrafoscopioTextFormatter class >> default [ + ^ self buildDefault +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> format: aNode [ + (self formatsFor: aNode) do: [ :f | f beInstalledIn: aNode ] +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> formatsFor: aNode [ + ^ formats at: aNode ifAbsent: [ self formatsForClass: aNode class ] +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> formatsForClass: aNodeClass [ + ^ formats at: aNodeClass ifAbsent: [ Array empty ] +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> initialize [ + super initialize . + formats := Dictionary new. + stack := Stack new. +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> registerFormat: aFormat for: aNode [ + ^ (formats at: aNode ifAbsentPut: [ OrderedCollection new ]) + add: aFormat +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> registerFormat: aFormat forClass: aNodeClass [ + ^ self registerFormat: aFormat for: aNodeClass +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> visitDocument: aDocument [ + self assert: stack isEmpty. + super visitDocument: aDocument. + self assert: stack isEmpty +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> visitDocumentGroup: aNode [ + aNode parent: (stack ifNotEmpty: [stack first] ifEmpty:[ nil ]). + self format: aNode. + stack push: aNode. + super visitDocumentGroup: aNode. + stack pop +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> visitDocumentItem: anItem [ + self format: anItem. + anItem parent: stack first. +] + +{ #category : #initialization } +GrafoscopioTextFormatter >> visitEmptyParagraph: aParagraph [ + stack top children last = aParagraph + ifTrue: [ stack top children: stack top children allButLast ]. +] + +{ #category : #'as yet unclassified' } +GrafoscopioTextFormatter >> visitFormatText: aGrafoscopioFormatTextNode [ + " No way we format the formatted text. " +] diff --git a/src/Grafoscopio/GrafoscopioTextModel.class.st b/src/Grafoscopio/GrafoscopioTextModel.class.st index 616cc3f..ae2284c 100644 --- a/src/Grafoscopio/GrafoscopioTextModel.class.st +++ b/src/Grafoscopio/GrafoscopioTextModel.class.st @@ -1,39 +1,39 @@ -" -I represent a text node inside a Grafoscopio notebook. -Usually my content is markdown text. -" -Class { - #name : #GrafoscopioTextModel, - #superclass : #ComposablePresenter, - #instVars : [ - 'body' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #specs } -GrafoscopioTextModel class >> defaultSpec [ - ^ SpecLayout composed add: #body -] - -{ #category : #accessing } -GrafoscopioTextModel >> body [ - ^ body -] - -{ #category : #accessing } -GrafoscopioTextModel >> body: anObject [ - body := anObject -] - -{ #category : #API } -GrafoscopioTextModel >> content: aGrafoscopioNodeContent [ - body text: aGrafoscopioNodeContent -] - -{ #category : #initialization } -GrafoscopioTextModel >> initializeWidgets [ - - body := self newText. - body autoAccept: true. -] +" +I represent a text node inside a Grafoscopio notebook. +Usually my content is markdown text. +" +Class { + #name : #GrafoscopioTextModel, + #superclass : #ComposablePresenter, + #instVars : [ + 'body' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #specs } +GrafoscopioTextModel class >> defaultSpec [ + ^ SpecLayout composed add: #body +] + +{ #category : #accessing } +GrafoscopioTextModel >> body [ + ^ body +] + +{ #category : #accessing } +GrafoscopioTextModel >> body: anObject [ + body := anObject +] + +{ #category : #API } +GrafoscopioTextModel >> content: aGrafoscopioNodeContent [ + body text: aGrafoscopioNodeContent +] + +{ #category : #initialization } +GrafoscopioTextModel >> initializeWidgets [ + + body := self newText. + body autoAccept: true. +] diff --git a/src/Grafoscopio/GrafoscopioTextNode.class.st b/src/Grafoscopio/GrafoscopioTextNode.class.st index e6e9b5c..7f605f7 100644 --- a/src/Grafoscopio/GrafoscopioTextNode.class.st +++ b/src/Grafoscopio/GrafoscopioTextNode.class.st @@ -1,42 +1,42 @@ -" -This kind of a leafNodes holds plain text. -" -Class { - #name : #GrafoscopioTextNode, - #superclass : #GrafoscopioLeafNode, - #instVars : [ - 'text' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #accessing } -GrafoscopioTextNode class >> icon [ - ^ self iconNamed: #workspace -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode class >> nameForSelection [ - ^ 'New text node' -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode class >> showInMenu [ - ^ true -] - -{ #category : #accessing } -GrafoscopioTextNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitTextNode: self. - -] - -{ #category : #'as yet unclassified' } -GrafoscopioTextNode >> text [ - ^ text -] - -{ #category : #accessing } -GrafoscopioTextNode >> text: aString [ - text := aString -] +" +This kind of a leafNodes holds plain text. +" +Class { + #name : #GrafoscopioTextNode, + #superclass : #GrafoscopioLeafNode, + #instVars : [ + 'text' + ], + #category : #'Grafoscopio-Model' +} + +{ #category : #accessing } +GrafoscopioTextNode class >> icon [ + ^ self iconNamed: #workspace +] + +{ #category : #'as yet unclassified' } +GrafoscopioTextNode class >> nameForSelection [ + ^ 'New text node' +] + +{ #category : #'as yet unclassified' } +GrafoscopioTextNode class >> showInMenu [ + ^ true +] + +{ #category : #accessing } +GrafoscopioTextNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitTextNode: self. + +] + +{ #category : #'as yet unclassified' } +GrafoscopioTextNode >> text [ + ^ text +] + +{ #category : #accessing } +GrafoscopioTextNode >> text: aString [ + text := aString +] diff --git a/src/Grafoscopio/GrafoscopioTreeNotebook.class.st b/src/Grafoscopio/GrafoscopioTreeNotebook.class.st index 0a990db..b324c7d 100644 --- a/src/Grafoscopio/GrafoscopioTreeNotebook.class.st +++ b/src/Grafoscopio/GrafoscopioTreeNotebook.class.st @@ -1,80 +1,86 @@ -Class { - #name : #GrafoscopioTreeNotebook, - #superclass : #SpPresenterWithModel, - #instVars : [ - 'sidebar', - 'viewport', - 'model', - 'empty', - 'perspectives' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #specs } -GrafoscopioTreeNotebook class >> defaultSpec [ - ^ SpBoxLayout newHorizontal - add: - (SpBoxLayout newVertical - add: #empty height: self toolbarHeight; - add: #sidebar; - yourself) - width: 51; - add: #viewport; - yourself -] - -{ #category : #'as yet unclassified' } -GrafoscopioTreeNotebook >> basicInstallPerspective: aPerspective [ - viewport ifNotNil: [ viewport aboutToBeUninstalledFrom: self ]. - viewport := self perspectives - at: aPerspective - ifAbsentPut: [ self instantiate: aPerspective on: model document ]. -] - -{ #category : #initialization } -GrafoscopioTreeNotebook >> createDefaultComponent [ - ^ self basicInstallPerspective: GrafoscopioPerspective defaultPerspective -] - -{ #category : #initialization } -GrafoscopioTreeNotebook >> initializeWidgets [ - super initializeWidgets. - sidebar := self sidebar. - self createDefaultComponent. - empty := self newLabel. -] - -{ #category : #initialization } -GrafoscopioTreeNotebook >> installPerspective: aPerspective [ - self basicInstallPerspective: aPerspective . - self modelChanged -] - -{ #category : #initialization } -GrafoscopioTreeNotebook >> modelChanged [ - viewport modelChanged. - self needRebuild: false. - self buildWithSpec -] - -{ #category : #initialization } -GrafoscopioTreeNotebook >> perspectives [ - ^ perspectives ifNil: [ perspectives := Dictionary new ] -] - -{ #category : #'as yet unclassified' } -GrafoscopioTreeNotebook >> setModelBeforeInitialization: aDomainObject [ - super setModelBeforeInitialization: aDomainObject. - model := aDomainObject -] - -{ #category : #initialization } -GrafoscopioTreeNotebook >> sidebar [ - | bar | - bar := self instantiate: SpSidebar. - GrafoscopioPerspective perspectives - do: - [ :p | bar addAction: [ self installPerspective: p ] icon: p icon ]. - ^ bar -] +Class { + #name : #GrafoscopioTreeNotebook, + #superclass : #SpPresenterWithModel, + #instVars : [ + 'sidebar', + 'viewport', + 'model', + 'empty', + 'perspectives' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #specs } +GrafoscopioTreeNotebook class >> defaultSpec [ + ^ SpBoxLayout newHorizontal + add: + (SpBoxLayout newVertical + add: #empty height: self toolbarHeight; + add: #sidebar; + yourself) + width: 51; + add: #viewport; + yourself +] + +{ #category : #'as yet unclassified' } +GrafoscopioTreeNotebook >> basicInstallPerspective: aPerspective [ + viewport ifNotNil: [ viewport aboutToBeUninstalledFrom: self ]. + viewport := self perspectives + at: aPerspective + ifAbsentPut: [ self instantiate: aPerspective on: model document ]. + viewport aboutToBeInstalled: self. +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> createDefaultComponent [ + ^ self basicInstallPerspective: GrafoscopioPerspective defaultPerspective +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> initializeWidgets [ + super initializeWidgets. + sidebar := self sidebar. + self createDefaultComponent. + empty := self newLabel. +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> installPerspective: aPerspective [ + self basicInstallPerspective: aPerspective . + self modelChanged +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> modelChanged [ + viewport modelChanged. + self needRebuild: false. + self buildWithSpec +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> perspectives [ + ^ perspectives ifNil: [ perspectives := Dictionary new ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioTreeNotebook >> removePerspective: aPerspective [ + perspectives removeKeyAtValue: aPerspective +] + +{ #category : #'as yet unclassified' } +GrafoscopioTreeNotebook >> setModelBeforeInitialization: aDomainObject [ + super setModelBeforeInitialization: aDomainObject. + model := aDomainObject +] + +{ #category : #initialization } +GrafoscopioTreeNotebook >> sidebar [ + | bar | + bar := self instantiate: SpSidebar. + GrafoscopioPerspective perspectives + do: + [ :p | bar addAction: [ self installPerspective: p ] icon: p icon ]. + ^ bar +] diff --git a/src/Grafoscopio/GrafoscopioUnitNode.class.st b/src/Grafoscopio/GrafoscopioUnitNode.class.st index 066958f..d3acfa1 100644 --- a/src/Grafoscopio/GrafoscopioUnitNode.class.st +++ b/src/Grafoscopio/GrafoscopioUnitNode.class.st @@ -1,41 +1,41 @@ -Class { - #name : #GrafoscopioUnitNode, - #superclass : #GrafoscopioBranchNode, - #category : #'Grafoscopio-Model' -} - -{ #category : #'instance creation' } -GrafoscopioUnitNode class >> icon [ - ^ self iconNamed: #smallHierarchyBrowser -] - -{ #category : #'instance creation' } -GrafoscopioUnitNode class >> nameForSelection [ - ^ 'New unit' -] - -{ #category : #'instance creation' } -GrafoscopioUnitNode class >> showInMenu [ - ^ true -] - -{ #category : #accessing } -GrafoscopioUnitNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitUnitNode: self. - -] - -{ #category : #accessing } -GrafoscopioUnitNode >> acceptsChildsOfClass: aClass [ - ^ aClass isLeaf or: [ aClass = self class ] -] - -{ #category : #accessing } -GrafoscopioUnitNode >> text [ - ^ name -] - -{ #category : #accessing } -GrafoscopioUnitNode >> text: aText [ - name := aText -] +Class { + #name : #GrafoscopioUnitNode, + #superclass : #GrafoscopioBranchNode, + #category : #'Grafoscopio-Model' +} + +{ #category : #'instance creation' } +GrafoscopioUnitNode class >> icon [ + ^ self iconNamed: #smallHierarchyBrowser +] + +{ #category : #'instance creation' } +GrafoscopioUnitNode class >> nameForSelection [ + ^ 'New unit' +] + +{ #category : #'instance creation' } +GrafoscopioUnitNode class >> showInMenu [ + ^ true +] + +{ #category : #accessing } +GrafoscopioUnitNode >> acceptVisitor: aGrafoscopioVisitor [ + aGrafoscopioVisitor visitUnitNode: self. + +] + +{ #category : #accessing } +GrafoscopioUnitNode >> acceptsChildsOfClass: aClass [ + ^ aClass isLeaf or: [ aClass = self class ] +] + +{ #category : #accessing } +GrafoscopioUnitNode >> text [ + ^ name +] + +{ #category : #accessing } +GrafoscopioUnitNode >> text: aText [ + name := aText +] diff --git a/src/Grafoscopio/GrafoscopioUrlNode.class.st b/src/Grafoscopio/GrafoscopioUrlNode.class.st deleted file mode 100644 index 868c08a..0000000 --- a/src/Grafoscopio/GrafoscopioUrlNode.class.st +++ /dev/null @@ -1,97 +0,0 @@ -" -URL Node downloads content for rendering. -" -Class { - #name : #GrafoscopioUrlNode, - #superclass : #GrafoscopioLeafNode, - #instVars : [ - 'link' - ], - #category : #'Grafoscopio-Model' -} - -{ #category : #'instance creation' } -GrafoscopioUrlNode class >> icon [ - ^ self iconNamed: #remote -] - -{ #category : #'instance creation' } -GrafoscopioUrlNode class >> nameForSelection [ - ^ 'New URL Node' -] - -{ #category : #'instance creation' } -GrafoscopioUrlNode class >> new [ - ^ super new - content; - yourself -] - -{ #category : #'instance creation' } -GrafoscopioUrlNode class >> showInMenu [ - ^ true -] - -{ #category : #'as yet unclassified' } -GrafoscopioUrlNode >> acceptVisitor: aGrafoscopioVisitor [ - aGrafoscopioVisitor visitUrlNode: self. - -] - -{ #category : #'as yet unclassified' } -GrafoscopioUrlNode >> content [ - ^ (self url - 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') asString -] - -{ #category : #'as yet unclassified' } -GrafoscopioUrlNode >> shouldAskBeforeRemove [ - ^ false -] - -{ #category : #content } -GrafoscopioUrlNode >> text [ - ^ self content -] - -{ #category : #'as yet unclassified' } -GrafoscopioUrlNode >> text: aString [ - -] - -{ #category : #'as yet unclassified' } -GrafoscopioUrlNode >> url [ - ^ link ifNil: [ link := self getUrl ] -] diff --git a/src/Grafoscopio/GrafoscopioViewportRichTextVisitor.class.st b/src/Grafoscopio/GrafoscopioViewportRichTextVisitor.class.st new file mode 100644 index 0000000..2d33c23 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioViewportRichTextVisitor.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GrafoscopioViewportRichTextVisitor, + #superclass : #GrafoscopioViewportTextVisitor, + #category : #'Grafoscopio-New-UI' +} + +{ #category : #'as yet unclassified' } +GrafoscopioViewportRichTextVisitor >> visitPillarNode: aNode [ + self + showPageFor: aNode + ifProviderDoestExistInstall: [ | text | + text := presenter instantiate: GrafoscopioNewTextModel on: aNode. + text + contextMenuFromCommandsGroup: [ presenter rootCommandsGroup / 'Context Menu' ]. + text + onModifyNodeLocationDo: [ presenter informNodeHasChanged: aNode ]. + text ] +] diff --git a/src/Grafoscopio/GrafoscopioViewportTextVisitor.class.st b/src/Grafoscopio/GrafoscopioViewportTextVisitor.class.st new file mode 100644 index 0000000..53a0a83 --- /dev/null +++ b/src/Grafoscopio/GrafoscopioViewportTextVisitor.class.st @@ -0,0 +1,60 @@ +Class { + #name : #GrafoscopioViewportTextVisitor, + #superclass : #GrafoscopioVisitor, + #instVars : [ + 'viewport', + 'stack', + 'presenter', + 'pages' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #visiting } +GrafoscopioViewportTextVisitor >> createViewportFor: aDocumentNode into: aPresenter [ + presenter := aPresenter. + aDocumentNode acceptVisitor: self. + ^ viewport +] + +{ #category : #visiting } +GrafoscopioViewportTextVisitor >> initialize [ + super initialize. + pages := Dictionary new +] + +{ #category : #'as yet unclassified' } +GrafoscopioViewportTextVisitor >> renamePageFor: aNode [ + pages at: aNode ifPresent: [ : p | p title: aNode name ] +] + +{ #category : #'as yet unclassified' } +GrafoscopioViewportTextVisitor >> showPageFor: aNode ifProviderDoestExistInstall: aBlock [ + | page | + page := pages at: aNode ifAbsentPut: [ aBlock value ]. + presenter viewport: page +] + +{ #category : #'as yet unclassified' } +GrafoscopioViewportTextVisitor >> visitPillarNode: aGrafoscopioPillarNode [ + self + showPageFor: aGrafoscopioPillarNode + ifProviderDoestExistInstall: [ | text | + text := presenter + instantiate: GrafoscopioNewTextModel + on: (GrafoscopioPillarToTextDecorator on: aGrafoscopioPillarNode). + text + onModifyNodeLocationDo: [ :model | model model text: model body text ]. + text ] +] + +{ #category : #visiting } +GrafoscopioViewportTextVisitor >> visitTextNode: aNode [ + self + showPageFor: aNode + ifProviderDoestExistInstall: [ | text | + text := presenter instantiate: GrafoscopioNewTextModel on: aNode. + text + onModifyNodeLocationDo: [ :model | model model text: model body text ]. + text ] +] diff --git a/src/Grafoscopio/GrafoscopioViewportVisitor.class.st b/src/Grafoscopio/GrafoscopioViewportVisitor.class.st deleted file mode 100644 index bd0045b..0000000 --- a/src/Grafoscopio/GrafoscopioViewportVisitor.class.st +++ /dev/null @@ -1,77 +0,0 @@ -Class { - #name : #GrafoscopioViewportVisitor, - #superclass : #GrafoscopioVisitor, - #instVars : [ - 'viewport', - 'stack', - 'items', - 'presenter' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #visiting } -GrafoscopioViewportVisitor >> createViewportFor: aDocumentNode into: aPresenter [ - presenter := aPresenter. - items := OrderedCollection new. - aDocumentNode acceptVisitor: self. - viewport := aPresenter viewport - ifNil: [ aPresenter instantiate: SpComponentListPresenter ]. - viewport items: items. - ^ viewport -] - -{ #category : #visiting } -GrafoscopioViewportVisitor >> visitCodeNode: aNode [ - | code | - code := presenter instantiate: GrafoscopioNewCodeModel. - code onModifyNodeLocationDo: [ presenter informNodeHasChanged: aNode ]. - code model: aNode. - items add: code. -] - -{ #category : #visiting } -GrafoscopioViewportVisitor >> visitRootNode: aNode [ - | text | - text := presenter instantiate: GrafoscopioNewTextInputModel. - text model: aNode. - text onModifyNodeLocationDo: [ presenter informNodeHasChanged: aNode ]. - items add: text. - super visitRootNode: aNode. -] - -{ #category : #visiting } -GrafoscopioViewportVisitor >> visitTextNode: aNode [ - | text | - text := presenter instantiate: GrafoscopioNewTextModel. - text onModifyNodeLocationDo: [ presenter informNodeHasChanged: aNode ]. - text model: aNode. - items add: text. -] - -{ #category : #visiting } -GrafoscopioViewportVisitor >> visitUnitNode: aNode [ - | text | - text := presenter instantiate: GrafoscopioNewTextInputModel. - text model: aNode. - text onModifyNodeLocationDo: [ presenter informNodeHasChanged: aNode ]. - items add: text. - items add: (presenter newButton - label: ' Add node on the beginning '; - action: [ presenter addNewNodeAtBeginningOf: aNode ]; - yourself). - super visitUnitNode: aNode. - items add: (presenter newButton - label: ' Add node on the end '; - action: [ presenter addNewNodeAtLastOf: aNode ]; - yourself) -] - -{ #category : #visiting } -GrafoscopioViewportVisitor >> visitUrlNode: aNode [ - | text | - text := presenter instantiate: GrafoscopioNewTextInputModel. - text onModifyNodeLocationDo: [ presenter informNodeHasChanged: aNode ]. - text model: aNode. - items add: text. -] diff --git a/src/Grafoscopio/GrafoscopioVisitor.class.st b/src/Grafoscopio/GrafoscopioVisitor.class.st index c696934..bb52b06 100644 --- a/src/Grafoscopio/GrafoscopioVisitor.class.st +++ b/src/Grafoscopio/GrafoscopioVisitor.class.st @@ -1,48 +1,44 @@ -Class { - #name : #GrafoscopioVisitor, - #superclass : #Object, - #category : #'Grafoscopio-Model' -} - -{ #category : #visiting } -GrafoscopioVisitor >> visitBranchNode: aNode [ - self visitNode: aNode. - aNode children do: [ : c | c acceptVisitor: self ]. -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitCodeNode: aNode [ - self visitNode: aNode -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitLeafNode: aNode [ - self visitNode: aNode -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitNode: aNode [ - " nothing to do here" -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitRootNode: aNode [ - self visitNode: aNode. - aNode children do: [ : c | c acceptVisitor: self ]. -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitTextNode: aNode [ - self visitNode: aNode -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitUnitNode: aNode [ - self visitNode: aNode. - aNode children do: [ : c | c acceptVisitor: self ]. -] - -{ #category : #visiting } -GrafoscopioVisitor >> visitUrlNode: aNode [ - self visitNode: aNode -] +Class { + #name : #GrafoscopioVisitor, + #superclass : #Object, + #category : #'Grafoscopio-Model' +} + +{ #category : #visiting } +GrafoscopioVisitor >> visitBranchNode: aNode [ + self visitNode: aNode. + aNode children do: [ : c | c acceptVisitor: self ]. +] + +{ #category : #'as yet unclassified' } +GrafoscopioVisitor >> visitGrafoscopioProject: aGrafoscopioProject [ + self visitNode: aGrafoscopioProject . + aGrafoscopioProject document acceptVisitor: self. +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitLeafNode: aNode [ + self visitNode: aNode +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitNode: aNode [ + " nothing to do here" +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitRootNode: aNode [ + self visitNode: aNode. + aNode children do: [ : c | c acceptVisitor: self ]. +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitTextNode: aNode [ + self visitNode: aNode +] + +{ #category : #visiting } +GrafoscopioVisitor >> visitUnitNode: aNode [ + self visitNode: aNode. + aNode children do: [ : c | c acceptVisitor: self ]. +] diff --git a/src/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st b/src/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st index 6371f5d..bd2f8d6 100644 --- a/src/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st +++ b/src/Grafoscopio/GrafoscopioZoaPediaPerspective.class.st @@ -1,15 +1,15 @@ -Class { - #name : #GrafoscopioZoaPediaPerspective, - #superclass : #GrafoscopioPerspective, - #category : #'Grafoscopio-New-UI' -} - -{ #category : #accessing } -GrafoscopioZoaPediaPerspective class >> icon [ - ^ self iconNamed: #edit -] - -{ #category : #initialization } -GrafoscopioZoaPediaPerspective >> createViewport [ - ^ self newLabel -] +Class { + #name : #GrafoscopioZoaPediaPerspective, + #superclass : #GrafoscopioPerspective, + #category : #'Grafoscopio-New-UI' +} + +{ #category : #accessing } +GrafoscopioZoaPediaPerspective class >> icon [ + ^ self iconNamed: #edit +] + +{ #category : #initialization } +GrafoscopioZoaPediaPerspective >> createViewport [ + ^ self newLabel +] diff --git a/src/Grafoscopio/ManifestGrafoscopio.class.st b/src/Grafoscopio/ManifestGrafoscopio.class.st index 9207d50..2f4776c 100644 --- a/src/Grafoscopio/ManifestGrafoscopio.class.st +++ b/src/Grafoscopio/ManifestGrafoscopio.class.st @@ -1,63 +1,63 @@ -" -I store metadata for this package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser -" -Class { - #name : #ManifestGrafoscopio, - #superclass : #PackageManifest, - #category : #Grafoscopio -} - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBAssignmentInIfTrueRuleV1FalsePositive [ - ^ #(#(#(#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' } -ManifestGrafoscopio class >> ruleRBBadMessageRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#'GrafoscopioDocumentation class' #openNotebookFrom:At: #true)) #'2017-04-05T18:01:21.892153-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBBooleanPrecedenceRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#'GrafoscopioNode class' #cleanTreeRootReferences #true)) #'2017-03-27T22:18:17.447627-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBCascadedNextPutAllsRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#GrafoscopioTextNode #metadataAsYamlIn: #false)) #'2017-04-20T19:01:59.286212-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBClassNameInSelectorRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#'Documentation class' #current #true)) #'2016-10-07T19:39:23.013722-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBEqualsTrueRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#'GrafoscopioDocumentation class' #listOutdated #true)) #'2016-10-09T10:16:31.841951-05:00') #(#(#RGMethodDefinition #(#'GrafoscopioDocumentation class' #listOutdatedDocsIn: #true)) #'2017-04-05T17:50:59.032741-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBLongMethodsRuleV1FalsePositive [ - ^ #(#(#(#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' } -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 #(#GrafoscopioNewNotebook #topBar #false)) #'2016-12-17T18:51:40.617924-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBShouldntRaiseErrorRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#GrafoscopioNodeTest #testInitializeIsOk #false)) #'2017-01-31T12:49:28.199769-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleRBStringConcatenationRuleV1FalsePositive [ - ^ #(#(#(#RGMethodDefinition #(#'FileUtils class' #createFilesNamed:endingWith:intoFolder: #true)) #'2017-01-01T21:48:26.712639-05:00') ) -] - -{ #category : #'code-critics' } -ManifestGrafoscopio class >> ruleSmTMethodTestedRuleV1FalsePositive [ - ^ #(#(#(#RGClassDefinition #(#GrafoscopioTextNode)) #'2017-10-31T19:59:03.294735-05:00') ) -] +" +I store metadata for this package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser +" +Class { + #name : #ManifestGrafoscopio, + #superclass : #PackageManifest, + #category : #Grafoscopio +} + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBAssignmentInIfTrueRuleV1FalsePositive [ + ^ #(#(#(#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' } +ManifestGrafoscopio class >> ruleRBBadMessageRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#'GrafoscopioDocumentation class' #openNotebookFrom:At: #true)) #'2017-04-05T18:01:21.892153-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBBooleanPrecedenceRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#'GrafoscopioNode class' #cleanTreeRootReferences #true)) #'2017-03-27T22:18:17.447627-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBCascadedNextPutAllsRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#GrafoscopioTextNode #metadataAsYamlIn: #false)) #'2017-04-20T19:01:59.286212-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBClassNameInSelectorRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#'Documentation class' #current #true)) #'2016-10-07T19:39:23.013722-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBEqualsTrueRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#'GrafoscopioDocumentation class' #listOutdated #true)) #'2016-10-09T10:16:31.841951-05:00') #(#(#RGMethodDefinition #(#'GrafoscopioDocumentation class' #listOutdatedDocsIn: #true)) #'2017-04-05T17:50:59.032741-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBLongMethodsRuleV1FalsePositive [ + ^ #(#(#(#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' } +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 #(#GrafoscopioNewNotebook #topBar #false)) #'2016-12-17T18:51:40.617924-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBShouldntRaiseErrorRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#GrafoscopioNodeTest #testInitializeIsOk #false)) #'2017-01-31T12:49:28.199769-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleRBStringConcatenationRuleV1FalsePositive [ + ^ #(#(#(#RGMethodDefinition #(#'FileUtils class' #createFilesNamed:endingWith:intoFolder: #true)) #'2017-01-01T21:48:26.712639-05:00') ) +] + +{ #category : #'code-critics' } +ManifestGrafoscopio class >> ruleSmTMethodTestedRuleV1FalsePositive [ + ^ #(#(#(#RGClassDefinition #(#GrafoscopioTextNode)) #'2017-10-31T19:59:03.294735-05:00') ) +] diff --git a/src/Grafoscopio/MendaIcons.class.st b/src/Grafoscopio/MendaIcons.class.st index d5b11a2..df0ec5d 100644 --- a/src/Grafoscopio/MendaIcons.class.st +++ b/src/Grafoscopio/MendaIcons.class.st @@ -1,220 +1,220 @@ -" -Please comment me using the following template inspired by Class Responsibility Collaborator (CRC) design: - -I'm MendaIcons the provider of this icons on this environment. - -My main responsibility is to support some menda icons insideCallback pharo environment, for that I convert and package png images to the pharo image. - -For the Collaborators Part: State my main collaborators and one line about how I interact with them. - -Public API and Key Messages - -- message one -- message two -- what is the way to create instances is a plus. - - One simple example is simply gorgeous. - -Internal Representation and Key Implementation Points. - - - Implementation Points -" -Class { - #name : #MendaIcons, - #superclass : #Object, - #instVars : [ - 'icons' - ], - #classVars : [ - 'Current' - ], - #category : #'Grafoscopio-UI' -} - -{ #category : #'instance creation' } -MendaIcons class >> current [ - ^ Current ifNil: [ Current := self new ] -] - -{ #category : #'accessing - icons' } -MendaIcons >> arrowDownIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self arrowDownIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> arrowDownIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowDown.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAICAYAAADJEc7MAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAALJJREFUGJV9zjEKwkAUBND5i3ZaeJBAcgavIIjX2GwK8QbZ -/bmGjSDYaq5gE/Aa9hIyNkZkXf0w1fCGD5IIIexUNSOJf6nrOvfeb0lCXuBK8m6MWVprOyTO -e5+LSAtgDiA31tqO5EZEFsMwtE3TZDFS1WJEJNfOuZsBAOfc4RdW1YLkZURVVR0BQEi+l0MI -KxHZj2/3fT81xpxj9AVjLCITkrMYJWGEmUI/4Qd+lGV5SvVPkLiZ0qKAa5MAAAAASUVORK5C -YII=' -] - -{ #category : #'accessing - icons' } -MendaIcons >> arrowLeftIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self arrowLeftIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> arrowLeftIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowLeft.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAOCAYAAAASVl2WAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAK1JREFUGJV9kcFpAmEQhb835rpo2giYHsSLBQhiH3NLCf/+ -20XIRexAy7CLkAKEfZ42xNXswJy+j4H3Bts821rr0jayzXhqrTvbn8DHgzBASd+2VzEFM/MS -UxAgpiDArGmafyFA2L5KMjCz/TJOFJl5tL0H5hFxLqW83wkAmXkYJEmnv9JvikGStJB06rpu -CTw22bbtVtKX7Z+IWN8VNbr02vf95umjbFNKebPNDbaypHslWRyiAAAAAElFTkSuQmCC' -] - -{ #category : #'accessing - icons' } -MendaIcons >> arrowRightIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self arrowRightIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> arrowRightIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowRight.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAOCAYAAAASVl2WAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAMVJREFUGJV9kTFqw0AQRf/M2pWsQK4RMDit2lTpDSbXkFCV -I6xW13AZcK20ukAgOYb7IPTSxMFWLH8YGHgPZpgRoJTSGtC1Cnmev0ra933/VRTFpyZxMzsA -RzPbN02z/SeUZfnh7k9zkkvSLclPzZxkwMXMtm3X4zi+m9k98OKaZBiGpZktAMzs+0JIKT2G -EDpgBeyqqjosziHQSVoBu7qu3/6WjDFufmF+DiUpZFm2cfdO0t0Uni75DFyFkiRAMcaHuWf9 -APv6mfGc3+HbAAAAAElFTkSuQmCC' -] - -{ #category : #'accessing - icons' } -MendaIcons >> arrowUpIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self arrowUpIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> arrowUpIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowUp.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAICAYAAADJEc7MAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAJRJREFUGJWNy8EJQjEQBNCdVGAdwtca/LYg2EdIsIjNliHi -xQ6sQfRgF4L/LOx4EnJIPg7MaeaBpLRiZnuSn5TSpbWHGXQEcC6l7P6CFXqJyATg1MKhh0hu -AGx7OPRQSukZY7y7+9jCoYd+h5zzw91Hku8aw8wGd7+1UB1VXQG4AliQXAtJUdWDqi5JylzN -bFDVTFK+Zz+PhDBK0j8AAAAASUVORK5CYII=' -] - -{ #category : #'gt-inspector' } -MendaIcons >> gtInspectorIconsIn: composite [ - - composite list - title: 'Icons'; - display: [ - self iconSelectors asSortedCollection - collect: [ :each | each -> (self perform: each) ] ]; - icon: [ :each | each value ]; - format: [ :each | each key ] -] - -{ #category : #initialization } -MendaIcons >> iconSelectors [ - ^ self class selectors select: [ :each | '*Icon' match: each asString ] -] - -{ #category : #initialization } -MendaIcons >> initialize [ - super initialize. - self initializeIcons -] - -{ #category : #initialization } -MendaIcons >> initializeIcons [ - icons := LRUCache new - maximumWeight: 64; - factory: [ :key | self perform: key ]; - yourself -] - -{ #category : #'accessing - icons' } -MendaIcons >> minusIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self minusIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> minusIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/minus.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAADCAYAAACj1j4PAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAADhJREFUCJlj7O7uDmNgYEhjIAEwMTFNZ2FkZJRjYGBwJkXj -379/t7D8////AQMDw05SNDIwMDwEANK6DIovD6CyAAAAAElFTkSuQmCC' -] - -{ #category : #'accessing - icons' } -MendaIcons >> plusIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self plusIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> plusIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/plus.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAKVJREFUKJHVkjEOgkAQRd9suIFcxUhvZeJpCA2FhoJ2uYyN -nEE9CZ5hP4WQkHVJjIWJv5r5839m83dMEjG6ritCCO3U1lVV3WJN9uYCJOVmtgcws01K41Lk -J/i90bz3haQ84rfAeaob4LEcShqyEEI7B7GCJsH1Xz81A+o4ckm7eZOZnYD7cu6ce1rqALz3 -R+AyGQ9lWV5jzR/949qtDkAPryBSmhE5/jJyCd+z1gAAAABJRU5ErkJggg==' -] - -{ #category : #'accessing - icons' } -MendaIcons >> smalltalkCodeIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self smalltalkCodeIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> smalltalkCodeIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/smalltalkCode.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAQhJREFUKJHtkrFOw0AQRGfOJ1FdkSB6pLgN+QAaJCQkfsS1 -ZbcUDm18EvK/UOQPEA1ROiy+gDQBdxz20BwSsiho0jHV7urN7BbLuq4fJc0kbUn2xpiboige -6rq+JdmXZblcrVbnJJeSLMk5ydZKmpGckrwAAEl3AEDyTNInABhjTgBckkRkUkNyG5vTJEkm -zrk1RnLO3SdJMhmGIY2hGyupJwlr7Vue5/uxCQCyLAsA9t57G5f05jfwL/o3HsJo4xshhHDs -vbdd171XVfXxE2qa5iiE4ABMv32G5BwAjDEvAHbOuatxegjhGsAOwHMcLSzJVlJKciOpjwAA -PAHoY/0qaR2vW0hqvwBnzXKb8TsomwAAAABJRU5ErkJggg==' -] - -{ #category : #'accessing - icons' } -MendaIcons >> tagAddIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self tagAddIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> tagAddIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/tagAdd.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAATFJREFUKJF90DFPAjEcBfD3GuByGg1qiH4FRyOrDhjipl9B -HR25+whu0NldJ0dmHJ2c1U0HY6KigKLJwYE8F8GjAf5Jk+bf/tq+slwunwDIwymSNgiCmtsf -lgGQJ7nrDklVa21xFgQASGpKOpXU/bvRn4VNYn4RhuExgKvEc6fiJDyqVCqXAApO1onYJDZ4 -JHdI0j19EjYkraRoUo71zXcsZOOJmJJgrS1KqpL0k3B77xG31zk0Xv7bkiKS+5QEAGM4X3hG -OvODldUOvj4yiLsG9zfLqD/NAcBA0sEIJvFSrusbI2xsveLhLovPhofvdhpxJzUgeVgqlc6S -v4ogCGok91tvXtSs++j1DNotD826P4ZGGd0a3jy/2PPjKIV+34yhqdDJ7LloJkzgtTAMz921 -X92gun92K18+AAAAAElFTkSuQmCC' -] - -{ #category : #'accessing - icons' } -MendaIcons >> tagMinusIcon [ - "Private - Generated method" - ^Form fromBinaryStream: (self tagMinusIconContents base64Decoded readStream) -] - -{ #category : #'private - contents' } -MendaIcons >> tagMinusIconContents [ - "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/tagMinus.png" - ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw -SFlzAAAOxAAADsQBlSsOGwAAAPhJREFUKJGF0bFKQzEUBuD/D9Zy3RQKPsh9gkpx7Cuoo+NN -HqHbvZm76+TYuY4+gqubIDg4iFBbaP8u9ZIcrrcHAiHJl5NzwrquZwBKmCAZvfdLu/4XDkBJ -8toOSYsY46QPAgAkfUmaS1ofMhZ92CXzpxDCPYCX5Ln/4hTeNU3zDGBsau3ELjkwJHlFkvb2 -LuxIRkmrrjr6MCUhxjiRtCBZHLtA0orklJIAIMPl+AOD020G3l4v8Pl+BgA7STctTPH5aF04 -pwz+fA+w+T3ZkbytquohgzazeWWLgPw7AADe+yXJqWlYhtrmdEWSeWhRL0zwZQjh0e7tAVcM -m6R0vbZ+AAAAAElFTkSuQmCC' -] +" +Please comment me using the following template inspired by Class Responsibility Collaborator (CRC) design: + +I'm MendaIcons the provider of this icons on this environment. + +My main responsibility is to support some menda icons insideCallback pharo environment, for that I convert and package png images to the pharo image. + +For the Collaborators Part: State my main collaborators and one line about how I interact with them. + +Public API and Key Messages + +- message one +- message two +- what is the way to create instances is a plus. + + One simple example is simply gorgeous. + +Internal Representation and Key Implementation Points. + + + Implementation Points +" +Class { + #name : #MendaIcons, + #superclass : #Object, + #instVars : [ + 'icons' + ], + #classVars : [ + 'Current' + ], + #category : #'Grafoscopio-UI' +} + +{ #category : #'instance creation' } +MendaIcons class >> current [ + ^ Current ifNil: [ Current := self new ] +] + +{ #category : #'accessing - icons' } +MendaIcons >> arrowDownIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self arrowDownIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> arrowDownIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowDown.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAICAYAAADJEc7MAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAALJJREFUGJV9zjEKwkAUBND5i3ZaeJBAcgavIIjX2GwK8QbZ +/bmGjSDYaq5gE/Aa9hIyNkZkXf0w1fCGD5IIIexUNSOJf6nrOvfeb0lCXuBK8m6MWVprOyTO +e5+LSAtgDiA31tqO5EZEFsMwtE3TZDFS1WJEJNfOuZsBAOfc4RdW1YLkZURVVR0BQEi+l0MI +KxHZj2/3fT81xpxj9AVjLCITkrMYJWGEmUI/4Qd+lGV5SvVPkLiZ0qKAa5MAAAAASUVORK5C +YII=' +] + +{ #category : #'accessing - icons' } +MendaIcons >> arrowLeftIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self arrowLeftIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> arrowLeftIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowLeft.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAOCAYAAAASVl2WAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAK1JREFUGJV9kcFpAmEQhb835rpo2giYHsSLBQhiH3NLCf/+ +20XIRexAy7CLkAKEfZ42xNXswJy+j4H3Bts821rr0jayzXhqrTvbn8DHgzBASd+2VzEFM/MS +UxAgpiDArGmafyFA2L5KMjCz/TJOFJl5tL0H5hFxLqW83wkAmXkYJEmnv9JvikGStJB06rpu +CTw22bbtVtKX7Z+IWN8VNbr02vf95umjbFNKebPNDbaypHslWRyiAAAAAElFTkSuQmCC' +] + +{ #category : #'accessing - icons' } +MendaIcons >> arrowRightIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self arrowRightIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> arrowRightIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowRight.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAOCAYAAAASVl2WAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAMVJREFUGJV9kTFqw0AQRf/M2pWsQK4RMDit2lTpDSbXkFCV +I6xW13AZcK20ukAgOYb7IPTSxMFWLH8YGHgPZpgRoJTSGtC1Cnmev0ra933/VRTFpyZxMzsA +RzPbN02z/SeUZfnh7k9zkkvSLclPzZxkwMXMtm3X4zi+m9k98OKaZBiGpZktAMzs+0JIKT2G +EDpgBeyqqjosziHQSVoBu7qu3/6WjDFufmF+DiUpZFm2cfdO0t0Uni75DFyFkiRAMcaHuWf9 +APv6mfGc3+HbAAAAAElFTkSuQmCC' +] + +{ #category : #'accessing - icons' } +MendaIcons >> arrowUpIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self arrowUpIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> arrowUpIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/arrowUp.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAICAYAAADJEc7MAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAJRJREFUGJWNy8EJQjEQBNCdVGAdwtca/LYg2EdIsIjNliHi +xQ6sQfRgF4L/LOx4EnJIPg7MaeaBpLRiZnuSn5TSpbWHGXQEcC6l7P6CFXqJyATg1MKhh0hu +AGx7OPRQSukZY7y7+9jCoYd+h5zzw91Hku8aw8wGd7+1UB1VXQG4AliQXAtJUdWDqi5JylzN +bFDVTFK+Zz+PhDBK0j8AAAAASUVORK5CYII=' +] + +{ #category : #'gt-inspector' } +MendaIcons >> gtInspectorIconsIn: composite [ + + composite list + title: 'Icons'; + display: [ + self iconSelectors asSortedCollection + collect: [ :each | each -> (self perform: each) ] ]; + icon: [ :each | each value ]; + format: [ :each | each key ] +] + +{ #category : #initialization } +MendaIcons >> iconSelectors [ + ^ self class selectors select: [ :each | '*Icon' match: each asString ] +] + +{ #category : #initialization } +MendaIcons >> initialize [ + super initialize. + self initializeIcons +] + +{ #category : #initialization } +MendaIcons >> initializeIcons [ + icons := LRUCache new + maximumWeight: 64; + factory: [ :key | self perform: key ]; + yourself +] + +{ #category : #'accessing - icons' } +MendaIcons >> minusIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self minusIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> minusIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/minus.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAADCAYAAACj1j4PAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAADhJREFUCJlj7O7uDmNgYEhjIAEwMTFNZ2FkZJRjYGBwJkXj +379/t7D8////AQMDw05SNDIwMDwEANK6DIovD6CyAAAAAElFTkSuQmCC' +] + +{ #category : #'accessing - icons' } +MendaIcons >> plusIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self plusIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> plusIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/plus.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAKVJREFUKJHVkjEOgkAQRd9suIFcxUhvZeJpCA2FhoJ2uYyN +nEE9CZ5hP4WQkHVJjIWJv5r5839m83dMEjG6ritCCO3U1lVV3WJN9uYCJOVmtgcws01K41Lk +J/i90bz3haQ84rfAeaob4LEcShqyEEI7B7GCJsH1Xz81A+o4ckm7eZOZnYD7cu6ce1rqALz3 +R+AyGQ9lWV5jzR/949qtDkAPryBSmhE5/jJyCd+z1gAAAABJRU5ErkJggg==' +] + +{ #category : #'accessing - icons' } +MendaIcons >> smalltalkCodeIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self smalltalkCodeIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> smalltalkCodeIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/smalltalkCode.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAQhJREFUKJHtkrFOw0AQRGfOJ1FdkSB6pLgN+QAaJCQkfsS1 +ZbcUDm18EvK/UOQPEA1ROiy+gDQBdxz20BwSsiho0jHV7urN7BbLuq4fJc0kbUn2xpiboige +6rq+JdmXZblcrVbnJJeSLMk5ydZKmpGckrwAAEl3AEDyTNInABhjTgBckkRkUkNyG5vTJEkm +zrk1RnLO3SdJMhmGIY2hGyupJwlr7Vue5/uxCQCyLAsA9t57G5f05jfwL/o3HsJo4xshhHDs +vbdd171XVfXxE2qa5iiE4ABMv32G5BwAjDEvAHbOuatxegjhGsAOwHMcLSzJVlJKciOpjwAA +PAHoY/0qaR2vW0hqvwBnzXKb8TsomwAAAABJRU5ErkJggg==' +] + +{ #category : #'accessing - icons' } +MendaIcons >> tagAddIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self tagAddIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> tagAddIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/tagAdd.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAATFJREFUKJF90DFPAjEcBfD3GuByGg1qiH4FRyOrDhjipl9B +HR25+whu0NldJ0dmHJ2c1U0HY6KigKLJwYE8F8GjAf5Jk+bf/tq+slwunwDIwymSNgiCmtsf +lgGQJ7nrDklVa21xFgQASGpKOpXU/bvRn4VNYn4RhuExgKvEc6fiJDyqVCqXAApO1onYJDZ4 +JHdI0j19EjYkraRoUo71zXcsZOOJmJJgrS1KqpL0k3B77xG31zk0Xv7bkiKS+5QEAGM4X3hG +OvODldUOvj4yiLsG9zfLqD/NAcBA0sEIJvFSrusbI2xsveLhLovPhofvdhpxJzUgeVgqlc6S +v4ogCGok91tvXtSs++j1DNotD826P4ZGGd0a3jy/2PPjKIV+34yhqdDJ7LloJkzgtTAMz921 +X92gun92K18+AAAAAElFTkSuQmCC' +] + +{ #category : #'accessing - icons' } +MendaIcons >> tagMinusIcon [ + "Private - Generated method" + ^Form fromBinaryStream: (self tagMinusIconContents base64Decoded readStream) +] + +{ #category : #'private - contents' } +MendaIcons >> tagMinusIconContents [ + "Private - Method generated with the content of the file File @ /home/offray/Programas/Grafoscopio/Dev/Common/Icons/Menda/Png/tagMinus.png" + ^ 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlw +SFlzAAAOxAAADsQBlSsOGwAAAPhJREFUKJGF0bFKQzEUBuD/D9Zy3RQKPsh9gkpx7Cuoo+NN +HqHbvZm76+TYuY4+gqubIDg4iFBbaP8u9ZIcrrcHAiHJl5NzwrquZwBKmCAZvfdLu/4XDkBJ +8toOSYsY46QPAgAkfUmaS1ofMhZ92CXzpxDCPYCX5Ln/4hTeNU3zDGBsau3ELjkwJHlFkvb2 +LuxIRkmrrjr6MCUhxjiRtCBZHLtA0orklJIAIMPl+AOD020G3l4v8Pl+BgA7STctTPH5aF04 +pwz+fA+w+T3ZkbytquohgzazeWWLgPw7AADe+yXJqWlYhtrmdEWSeWhRL0zwZQjh0e7tAVcM +m6R0vbZ+AAAAAElFTkSuQmCC' +] diff --git a/src/Grafoscopio/MindMap.class.st b/src/Grafoscopio/MindMap.class.st index e37f9e0..37280a8 100644 --- a/src/Grafoscopio/MindMap.class.st +++ b/src/Grafoscopio/MindMap.class.st @@ -1,185 +1,185 @@ -" -I provide support for minmapping Freemind import and d3js export. -In the future I'll plan to support exporting from Grafoscopio notebooks -to javascript enabled web presentations. -" -Class { - #name : #MindMap, - #superclass : #Object, - #category : #'Grafoscopio-Model' -} - -{ #category : #'as yet unclassified' } -MindMap class >> d3CSS [ - "I define the nodes geometrical form, their text and the links between them." - ^ ' .node circle { - fill: #fff; - stroke: steelblue; - stroke-width: 3px; - } - - .node text { font: 12px sans-serif; } - - .link { - fill: none; - stroke: #ccc; - stroke-width: 2px; - }' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3SVGAppender [ - "I append the SVG working area to the body of our web page and create a group elements - () that will contain the SVG objects (the nodes, text and links)" - - ^'var svg = d3.select("body").append("svg") - .attr("width", width + margin.right + margin.left) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")");' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3TreeDemoData [ - "I'm demo data for the tutorial contain at: https://blog.pixelingene.com/2011/07/building-a-tree-diagram-in-d3-js/" - ^ 'var treeData = { - name: "/", - contents: [ - { - name: "Applications", - contents: [ - { name: "Mail.app" }, - { name: "iPhoto.app" }, - { name: "Keynote.app" }, - { name: "iTunes.app" }, - { name: "XCode.app" }, - { name: "Numbers.app" }, - { name: "Pages.app" } - ] - }, - { - name: "System", - contents: [] - }, - { - name: "Library", - contents: [ - { - name: "Application Support", - contents: [ - { name: "Adobe" }, - { name: "Apple" }, - { name: "Google" }, - { name: "Microsoft" } - ] - }, - { - name: "Languages", - contents: [ - { name: "Ruby" }, - { name: "Python" }, - { name: "Javascript" }, - { name: "C#" } - ] - }, - { - name: "Developer", - contents: [ - { name: "4.2" }, - { name: "4.3" }, - { name: "5.0" }, - { name: "Documentation" } - ] - } - ] - }, - { - name: "opt", - contents: [] - }, - { - name: "Users", - contents: [ - { name: "pavanpodila" }, - { name: "admin" }, - { name: "test-user" } - ] - } - ] -};' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3TreeDiagramProperties [ - ^ 'var margin = {top: 20, right: 120, bottom: 20, left: 120}, - width = 960 - margin.right - margin.left, - height = 500 - margin.top - margin.bottom; - -var i = 0; - -var tree = d3.layout.tree() - .size([height, width]);' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3TreeLinksDrawerFunction [ - ^ 'var diagonal = d3.svg.diagonal() - .projection(function(d) { return [d.y, d.x]; });' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3TreeRootDefinition [ - "For an excellent explanation on whi this, look at: - http://www.d3noob.org/2014/01/tree-diagrams-in-d3js_11.html" - ^ 'root = treeData[0];' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3TreeUpdater [ - ^ 'function update (source) { - // Compute the new tree layout. - var nodes = tree.nodes(root).reverse(), - links = tree.links(nodes); - - // Normalize for fixed-depth. - nodes.forEach(function(d) { d.y = d.depth * 180 }); - - // Declare the nodes - var node = svg.selectAll("g.node") - .data(nodes, function(d) { return d.id || (d.id = ++i); }); - - // Enter the nodes. - var nodeEnter = node.enter().append("g") - .attr("class", "node") - .attr("transform", function(d) { - return "translate(" + d.y + "," + d.x + ")";}); - - nodeEnter.append("circle") - .attr("r",10) - .style("fill", "#fff"); - - nodeEnter.append("text") - .attr("x", function (d) { - return d.children || d._children ? -13 : 13; }) - .attr("dy", ".35em") - .attr("text-anchor", function(d) { - return d.children || d._children ? "end" : "start"; }) - .text(function(d) { return d.name; }) - .style ("fill-opacity", 1); - - // Declare the links - var link = svg.selectAll("path.link") - .data(links, function(d) { return d.targed.id }); - - // Enter the links. - link.enter().insert("path","g") - .attr("class", "link") - .attr("d", diagonal); - - }' -] - -{ #category : #'as yet unclassified' } -MindMap class >> d3TreeUpdaterCaller [ - ^ 'update(root)' -] +" +I provide support for minmapping Freemind import and d3js export. +In the future I'll plan to support exporting from Grafoscopio notebooks +to javascript enabled web presentations. +" +Class { + #name : #MindMap, + #superclass : #Object, + #category : #'Grafoscopio-Model' +} + +{ #category : #'as yet unclassified' } +MindMap class >> d3CSS [ + "I define the nodes geometrical form, their text and the links between them." + ^ ' .node circle { + fill: #fff; + stroke: steelblue; + stroke-width: 3px; + } + + .node text { font: 12px sans-serif; } + + .link { + fill: none; + stroke: #ccc; + stroke-width: 2px; + }' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3SVGAppender [ + "I append the SVG working area to the body of our web page and create a group elements + () that will contain the SVG objects (the nodes, text and links)" + + ^'var svg = d3.select("body").append("svg") + .attr("width", width + margin.right + margin.left) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")");' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3TreeDemoData [ + "I'm demo data for the tutorial contain at: https://blog.pixelingene.com/2011/07/building-a-tree-diagram-in-d3-js/" + ^ 'var treeData = { + name: "/", + contents: [ + { + name: "Applications", + contents: [ + { name: "Mail.app" }, + { name: "iPhoto.app" }, + { name: "Keynote.app" }, + { name: "iTunes.app" }, + { name: "XCode.app" }, + { name: "Numbers.app" }, + { name: "Pages.app" } + ] + }, + { + name: "System", + contents: [] + }, + { + name: "Library", + contents: [ + { + name: "Application Support", + contents: [ + { name: "Adobe" }, + { name: "Apple" }, + { name: "Google" }, + { name: "Microsoft" } + ] + }, + { + name: "Languages", + contents: [ + { name: "Ruby" }, + { name: "Python" }, + { name: "Javascript" }, + { name: "C#" } + ] + }, + { + name: "Developer", + contents: [ + { name: "4.2" }, + { name: "4.3" }, + { name: "5.0" }, + { name: "Documentation" } + ] + } + ] + }, + { + name: "opt", + contents: [] + }, + { + name: "Users", + contents: [ + { name: "pavanpodila" }, + { name: "admin" }, + { name: "test-user" } + ] + } + ] +};' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3TreeDiagramProperties [ + ^ 'var margin = {top: 20, right: 120, bottom: 20, left: 120}, + width = 960 - margin.right - margin.left, + height = 500 - margin.top - margin.bottom; + +var i = 0; + +var tree = d3.layout.tree() + .size([height, width]);' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3TreeLinksDrawerFunction [ + ^ 'var diagonal = d3.svg.diagonal() + .projection(function(d) { return [d.y, d.x]; });' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3TreeRootDefinition [ + "For an excellent explanation on whi this, look at: + http://www.d3noob.org/2014/01/tree-diagrams-in-d3js_11.html" + ^ 'root = treeData[0];' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3TreeUpdater [ + ^ 'function update (source) { + // Compute the new tree layout. + var nodes = tree.nodes(root).reverse(), + links = tree.links(nodes); + + // Normalize for fixed-depth. + nodes.forEach(function(d) { d.y = d.depth * 180 }); + + // Declare the nodes + var node = svg.selectAll("g.node") + .data(nodes, function(d) { return d.id || (d.id = ++i); }); + + // Enter the nodes. + var nodeEnter = node.enter().append("g") + .attr("class", "node") + .attr("transform", function(d) { + return "translate(" + d.y + "," + d.x + ")";}); + + nodeEnter.append("circle") + .attr("r",10) + .style("fill", "#fff"); + + nodeEnter.append("text") + .attr("x", function (d) { + return d.children || d._children ? -13 : 13; }) + .attr("dy", ".35em") + .attr("text-anchor", function(d) { + return d.children || d._children ? "end" : "start"; }) + .text(function(d) { return d.name; }) + .style ("fill-opacity", 1); + + // Declare the links + var link = svg.selectAll("path.link") + .data(links, function(d) { return d.targed.id }); + + // Enter the links. + link.enter().insert("path","g") + .attr("class", "link") + .attr("d", diagonal); + + }' +] + +{ #category : #'as yet unclassified' } +MindMap class >> d3TreeUpdaterCaller [ + ^ 'update(root)' +] diff --git a/src/Grafoscopio/PRCodeblock.extension.st b/src/Grafoscopio/PRCodeblock.extension.st index ae40334..dc62356 100644 --- a/src/Grafoscopio/PRCodeblock.extension.st +++ b/src/Grafoscopio/PRCodeblock.extension.st @@ -1,13 +1,13 @@ -Extension { #name : #PRCodeblock } - -{ #category : #'*Grafoscopio' } -PRCodeblock >> children [ - ^ self - propertyAt: #children - ifAbsentPut: [ { PRText new text: text; parent: self ;yourself } asOrderedCollection ] -] - -{ #category : #'*Grafoscopio' } -PRCodeblock >> children: aCollection [ - self propertyAt:#children put: aCollection. -] +Extension { #name : #PRCodeblock } + +{ #category : #'*Grafoscopio' } +PRCodeblock >> children [ + ^ self + propertyAt: #children + ifAbsentPut: [ { PRText new text: text; parent: self ;yourself } asOrderedCollection ] +] + +{ #category : #'*Grafoscopio' } +PRCodeblock >> children: aCollection [ + self propertyAt:#children put: aCollection. +] diff --git a/src/Grafoscopio/PRDocumentGroup.extension.st b/src/Grafoscopio/PRDocumentGroup.extension.st index 530f38c..be0eba2 100644 --- a/src/Grafoscopio/PRDocumentGroup.extension.st +++ b/src/Grafoscopio/PRDocumentGroup.extension.st @@ -1,8 +1,8 @@ -Extension { #name : #PRDocumentGroup } - -{ #category : #'*Grafoscopio' } -PRDocumentGroup >> children: anArrayOfChildren [ - "Answer the children of the receiver." - - children := anArrayOfChildren asArray -] +Extension { #name : #PRDocumentGroup } + +{ #category : #'*Grafoscopio' } +PRDocumentGroup >> children: anArrayOfChildren [ + "Answer the children of the receiver." + + children := anArrayOfChildren asArray +] diff --git a/src/Grafoscopio/PRDocumentItem.extension.st b/src/Grafoscopio/PRDocumentItem.extension.st index 5292f97..b3a0cdb 100644 --- a/src/Grafoscopio/PRDocumentItem.extension.st +++ b/src/Grafoscopio/PRDocumentItem.extension.st @@ -1,63 +1,63 @@ -Extension { #name : #PRDocumentItem } - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> formats [ - | ancestors | - ancestors := (self parent ifNil: [ {} ] ifNotNil: [ self parent formats ]). - ^ ancestors , (self propertyAt: #gfpFormat ifAbsent: [ Array empty ]) -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> installFormat: aFormat [ - (self propertyAt: #gfpFormat ifAbsentPut: [ OrderedCollection new ]) add: aFormat. -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> isLineBreak [ - ^ false -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> isTextOrLineBreak [ - ^ false -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> markAsDirty [ - self parent ifNotNil: [ :p | p markAsDirty ]. - properties removeKey: #textStart ifAbsent: [ ]. - properties removeKey: #textStop ifAbsent: [ ] -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> parent [ - ^ self propertyAt: #parent ifAbsent: [ nil ] -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> parent: aParent [ - self propertyAt: #parent put: aParent -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> textSize [ - ^ self textStop - self textStart -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> textStart [ - ^ self - propertyAt: #textStart - ifAbsentPut: [ self hasChildren - ifTrue: [ self children first textStart ] - ifFalse: [ 0 ] ] -] - -{ #category : #'*Grafoscopio' } -PRDocumentItem >> textStop [ - ^ self - propertyAt: #textStop - ifAbsentPut: [ self hasChildren - ifTrue: [ self children last textStop ] - ifFalse: [ 0 ] ] -] +Extension { #name : #PRDocumentItem } + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> formats [ + | ancestors | + ancestors := (self parent ifNil: [ {} ] ifNotNil: [ self parent formats ]). + ^ ancestors , (self propertyAt: #gfpFormat ifAbsent: [ Array empty ]) +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> installFormat: aFormat [ + (self propertyAt: #gfpFormat ifAbsentPut: [ OrderedCollection new ]) add: aFormat. +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> isLineBreak [ + ^ false +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> isTextOrLineBreak [ + ^ false +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> markAsDirty [ + self parent ifNotNil: [ :p | p markAsDirty ]. + properties removeKey: #textStart ifAbsent: [ ]. + properties removeKey: #textStop ifAbsent: [ ] +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> parent [ + ^ self propertyAt: #parent ifAbsent: [ nil ] +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> parent: aParent [ + self propertyAt: #parent put: aParent +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> textSize [ + ^ self textStop - self textStart +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> textStart [ + ^ self + propertyAt: #textStart + ifAbsentPut: [ self hasChildren + ifTrue: [ self children first textStart ] + ifFalse: [ 0 ] ] +] + +{ #category : #'*Grafoscopio' } +PRDocumentItem >> textStop [ + ^ self + propertyAt: #textStop + ifAbsentPut: [ self hasChildren + ifTrue: [ self children last textStop ] + ifFalse: [ 0 ] ] +] diff --git a/src/Grafoscopio/PRLineBreak.extension.st b/src/Grafoscopio/PRLineBreak.extension.st index 1d1ff2a..b31e816 100644 --- a/src/Grafoscopio/PRLineBreak.extension.st +++ b/src/Grafoscopio/PRLineBreak.extension.st @@ -1,70 +1,70 @@ -Extension { #name : #PRLineBreak } - -{ #category : #'*Grafoscopio' } -PRLineBreak >> formattedText [ - ^ OSPlatform current lineEnding -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> isLineBreak [ - ^ true -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> isTextOrLineBreak [ - ^ true -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> markAsDirty [ - self parent ifNotNil: [ :p | p markAsDirty ] -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> next [ - ^ self propertyAt: #next ifAbsent: [ nil ] -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> next: aText [ - self propertyAt: #next put: aText -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> text [ - ^ OSPlatform current lineEnding -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> text: aString [ - self shouldBeImplemented. -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> textSize [ - ^ self text size -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> textStart [ - ^ self propertyAt: #textStart -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> textStart: aValue [ - ^ self - propertyAt: #textStart - put: aValue -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> textStop [ - ^ self propertyAt: #textStop -] - -{ #category : #'*Grafoscopio' } -PRLineBreak >> textStop: aValue [ - ^ self - propertyAt: #textStop - put: aValue -] +Extension { #name : #PRLineBreak } + +{ #category : #'*Grafoscopio' } +PRLineBreak >> formattedText [ + ^ OSPlatform current lineEnding +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> isLineBreak [ + ^ true +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> isTextOrLineBreak [ + ^ true +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> markAsDirty [ + self parent ifNotNil: [ :p | p markAsDirty ] +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> next [ + ^ self propertyAt: #next ifAbsent: [ nil ] +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> next: aText [ + self propertyAt: #next put: aText +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> text [ + ^ OSPlatform current lineEnding +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> text: aString [ + self shouldBeImplemented. +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> textSize [ + ^ 1 +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> textStart [ + ^ self propertyAt: #textStart +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> textStart: aValue [ + ^ self + propertyAt: #textStart + put: aValue +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> textStop [ + ^ self propertyAt: #textStop +] + +{ #category : #'*Grafoscopio' } +PRLineBreak >> textStop: aValue [ + ^ self + propertyAt: #textStop + put: aValue +] diff --git a/src/Grafoscopio/PRList.extension.st b/src/Grafoscopio/PRList.extension.st index 56bb072..253a336 100644 --- a/src/Grafoscopio/PRList.extension.st +++ b/src/Grafoscopio/PRList.extension.st @@ -1,16 +1,16 @@ -Extension { #name : #PRList } - -{ #category : #'*Grafoscopio' } -PRList >> level [ - | current level | - current := self. - level := 0. - [ current notNil ] - whileTrue: [ - (current isKindOf: PRList) ifTrue: [ - level := level + 1. - ]. - current := current parent - ]. - ^ level -] +Extension { #name : #PRList } + +{ #category : #'*Grafoscopio' } +PRList >> level [ + | current level | + current := self. + level := 0. + [ current notNil ] + whileTrue: [ + (current isKindOf: PRList) ifTrue: [ + level := level + 1. + ]. + current := current parent + ]. + ^ level +] diff --git a/src/Grafoscopio/PRPillarWriter.extension.st b/src/Grafoscopio/PRPillarWriter.extension.st index f672baa..fceb0cd 100644 --- a/src/Grafoscopio/PRPillarWriter.extension.st +++ b/src/Grafoscopio/PRPillarWriter.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #PRPillarWriter } - -{ #category : #'*Grafoscopio' } -PRPillarWriter >> visitFormatText: aGrafoscopioFormatTextNode [ - " Do nothing " -] +Extension { #name : #PRPillarWriter } + +{ #category : #'*Grafoscopio' } +PRPillarWriter >> visitFormatText: aGrafoscopioFormatTextNode [ + " Do nothing " +] diff --git a/src/Grafoscopio/PRText.extension.st b/src/Grafoscopio/PRText.extension.st index fcc9086..7d952ce 100644 --- a/src/Grafoscopio/PRText.extension.st +++ b/src/Grafoscopio/PRText.extension.st @@ -1,45 +1,45 @@ -Extension { #name : #PRText } - -{ #category : #'*Grafoscopio' } -PRText >> isTextOrLineBreak [ - ^ true -] - -{ #category : #'*Grafoscopio' } -PRText >> markAsDirty [ - self parent ifNotNil: [ :p | p markAsDirty ] -] - -{ #category : #'*Grafoscopio' } -PRText >> next [ - ^ self propertyAt: #next ifAbsent: [ nil ] -] - -{ #category : #'*Grafoscopio' } -PRText >> next: aText [ - self propertyAt: #next put: aText. -] - -{ #category : #'*Grafoscopio' } -PRText >> textStart [ - ^ self propertyAt: #textStart -] - -{ #category : #'*Grafoscopio' } -PRText >> textStart: aValue [ - ^ self - propertyAt: #textStart - put: aValue -] - -{ #category : #'*Grafoscopio' } -PRText >> textStop [ - ^ self propertyAt: #textStop -] - -{ #category : #'*Grafoscopio' } -PRText >> textStop: aValue [ - ^ self - propertyAt: #textStop - put: aValue -] +Extension { #name : #PRText } + +{ #category : #'*Grafoscopio' } +PRText >> isTextOrLineBreak [ + ^ true +] + +{ #category : #'*Grafoscopio' } +PRText >> markAsDirty [ + self parent ifNotNil: [ :p | p markAsDirty ] +] + +{ #category : #'*Grafoscopio' } +PRText >> next [ + ^ self propertyAt: #next ifAbsent: [ nil ] +] + +{ #category : #'*Grafoscopio' } +PRText >> next: aText [ + self propertyAt: #next put: aText. +] + +{ #category : #'*Grafoscopio' } +PRText >> textStart [ + ^ self propertyAt: #textStart +] + +{ #category : #'*Grafoscopio' } +PRText >> textStart: aValue [ + ^ self + propertyAt: #textStart + put: aValue +] + +{ #category : #'*Grafoscopio' } +PRText >> textStop [ + ^ self propertyAt: #textStop +] + +{ #category : #'*Grafoscopio' } +PRText >> textStop: aValue [ + ^ self + propertyAt: #textStop + put: aValue +] diff --git a/src/Grafoscopio/SpSidebar.class.st b/src/Grafoscopio/SpSidebar.class.st index 08a3fea..4e80acd 100644 --- a/src/Grafoscopio/SpSidebar.class.st +++ b/src/Grafoscopio/SpSidebar.class.st @@ -1,44 +1,44 @@ -Class { - #name : #SpSidebar, - #superclass : #SpPresenter, - #instVars : [ - 'container' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #specs } -SpSidebar class >> defaultSpec [ - ^ SpBoxLayout newHorizontal - add: #container ; - yourself -] - -{ #category : #initialization } -SpSidebar >> addAction: aBlock icon: anIcon [ - container - addPresenter: - (self createDefaultPresenter - parent: self; - action: [ :state | - container unselectAll. - aBlock cull: state ]; - icon: anIcon; - yourself) -] - -{ #category : #'as yet unclassified' } -SpSidebar >> buttons [ - ^ container items -] - -{ #category : #initialization } -SpSidebar >> createDefaultPresenter [ - ^ self instantiate: SpSquareButton. -] - -{ #category : #initialization } -SpSidebar >> initializeWidgets [ - super initializeWidgets. - container := self instantiate: SpComponentListPresenter. -] +Class { + #name : #SpSidebar, + #superclass : #SpPresenter, + #instVars : [ + 'container' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #specs } +SpSidebar class >> defaultSpec [ + ^ SpBoxLayout newHorizontal + add: #container ; + yourself +] + +{ #category : #initialization } +SpSidebar >> addAction: aBlock icon: anIcon [ + container + addPresenter: + (self createDefaultPresenter + parent: self; + action: [ :state | + container unselectAll. + aBlock cull: state ]; + icon: anIcon; + yourself) +] + +{ #category : #'as yet unclassified' } +SpSidebar >> buttons [ + ^ container items +] + +{ #category : #initialization } +SpSidebar >> createDefaultPresenter [ + ^ self instantiate: SpSquareButton. +] + +{ #category : #initialization } +SpSidebar >> initializeWidgets [ + super initializeWidgets. + container := self instantiate: SpComponentListPresenter. +] diff --git a/src/Grafoscopio/SpSquareButton.class.st b/src/Grafoscopio/SpSquareButton.class.st index fcd401b..2aa3832 100644 --- a/src/Grafoscopio/SpSquareButton.class.st +++ b/src/Grafoscopio/SpSquareButton.class.st @@ -1,52 +1,52 @@ -Class { - #name : #SpSquareButton, - #superclass : #SpPresenter, - #instVars : [ - 'button', - 'parent' - ], - #category : #'Grafoscopio-New-UI' -} - -{ #category : #specs } -SpSquareButton class >> defaultSpec [ - ^ SpBoxLayout newVertical - add: (SpBoxLayout newHorizontal add: #button width: 50; yourself) height: 50; - yourself -] - -{ #category : #initialization } -SpSquareButton >> action: anAction [ - button - action: [ :state | - state - ifTrue: [ parent buttons - reject: [ :b | b = self ] - thenDo: [ :b | b toggleOff ]. - anAction cull: state ] - ifFalse: [ ] ] -] - -{ #category : #initialization } -SpSquareButton >> icon: icon [ - button icon: icon -] - -{ #category : #initialization } -SpSquareButton >> initializeWidgets [ - super initializeWidgets. - button := self newToggleButton - extent: 90 @ 50; - color: Color transparent; - yourself -] - -{ #category : #'as yet unclassified' } -SpSquareButton >> parent: aSpSidebar [ - parent := aSpSidebar -] - -{ #category : #initialization } -SpSquareButton >> toggleOff [ - button state: false. -] +Class { + #name : #SpSquareButton, + #superclass : #SpPresenter, + #instVars : [ + 'button', + 'parent' + ], + #category : #'Grafoscopio-New-UI' +} + +{ #category : #specs } +SpSquareButton class >> defaultSpec [ + ^ SpBoxLayout newVertical + add: (SpBoxLayout newHorizontal add: #button width: 50; yourself) height: 50; + yourself +] + +{ #category : #initialization } +SpSquareButton >> action: anAction [ + button + action: [ :state | + state + ifTrue: [ parent buttons + reject: [ :b | b = self ] + thenDo: [ :b | b toggleOff ]. + anAction cull: state ] + ifFalse: [ ] ] +] + +{ #category : #initialization } +SpSquareButton >> icon: icon [ + button icon: icon +] + +{ #category : #initialization } +SpSquareButton >> initializeWidgets [ + super initializeWidgets. + button := self newToggleButton + extent: 90 @ 50; + color: Color transparent; + yourself +] + +{ #category : #'as yet unclassified' } +SpSquareButton >> parent: aSpSidebar [ + parent := aSpSidebar +] + +{ #category : #initialization } +SpSquareButton >> toggleOff [ + button state: false. +] diff --git a/src/Grafoscopio/Text.extension.st b/src/Grafoscopio/Text.extension.st index a4c6da8..9e79b39 100644 --- a/src/Grafoscopio/Text.extension.st +++ b/src/Grafoscopio/Text.extension.st @@ -1,6 +1,6 @@ -Extension { #name : #Text } - -{ #category : #'*Grafoscopio' } -Text >> allSegmentsOfLinesDo: aBlockClosure [ - -] +Extension { #name : #Text } + +{ #category : #'*Grafoscopio' } +Text >> allSegmentsOfLinesDo: aBlockClosure [ + +] diff --git a/src/Grafoscopio/TextPresenter.extension.st b/src/Grafoscopio/TextPresenter.extension.st index f64bd28..470360c 100644 --- a/src/Grafoscopio/TextPresenter.extension.st +++ b/src/Grafoscopio/TextPresenter.extension.st @@ -1,9 +1,9 @@ -Extension { #name : #TextPresenter } - -{ #category : #'*Grafoscopio' } -TextPresenter >> beForGrafoscopio [ - self - isCodeCompletionAllowed: true; - menuHolder: [ self getMenu ]; - isForSmalltalkCode: true -] +Extension { #name : #TextPresenter } + +{ #category : #'*Grafoscopio' } +TextPresenter >> beForGrafoscopio [ + self + isCodeCompletionAllowed: true; + menuHolder: [ self getMenu ]; + isForSmalltalkCode: true +] diff --git a/src/Grafoscopio/TreePresenter.extension.st b/src/Grafoscopio/TreePresenter.extension.st index d4b37a8..9cd14ba 100644 --- a/src/Grafoscopio/TreePresenter.extension.st +++ b/src/Grafoscopio/TreePresenter.extension.st @@ -1,17 +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) ] -] +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) ] +] diff --git a/src/Grafoscopio/package.st b/src/Grafoscopio/package.st index 448d455..f1c9451 100644 --- a/src/Grafoscopio/package.st +++ b/src/Grafoscopio/package.st @@ -1 +1 @@ -Package { #name : #Grafoscopio } +Package { #name : #Grafoscopio }