├── .gitignore ├── LICENSE ├── README.md ├── assets └── screenshots │ ├── edit_sandbox.png │ ├── home.png │ ├── home_neon.png │ ├── index.png │ └── sandbox_plus_sidebar.png ├── bin └── notebook.tcl ├── docs ├── index.quilldoc └── man1 │ └── notebook.manpage ├── lib ├── app_notebook │ ├── dbmanager.tcl │ ├── default.nbk │ ├── help.nbk │ ├── helpbrowser.tcl │ ├── main.tcl │ ├── messagelog.tcl │ ├── missing.gif │ ├── nb2html.tcl │ ├── nb2mediawiki.tcl │ ├── nbactionmanager.tcl │ ├── nbobjects.tcl │ ├── notebook.tcl │ ├── notebookbrowser.tcl │ ├── pageeditor.tcl │ ├── pageviewer.tcl │ ├── pkgIndex.tcl │ ├── pkgModules.tcl │ ├── prefs_dialog.tcl │ ├── renderpane.tcl │ ├── splash.gif │ ├── userprefs.tcl │ └── welcomer.tcl ├── combobox │ ├── ANNOUNCE.txt │ ├── CHANGES.txt │ ├── README.txt │ ├── combobox.n │ ├── combobox.tcl │ ├── combobox.tmml │ ├── example.tcl │ └── pkgIndex.tcl ├── gui │ ├── actionmanager.tcl │ ├── bullet1.gif │ ├── bullet2.gif │ ├── bullet3.gif │ ├── gui.tcl │ ├── icon.tcl │ ├── misc.tcl │ ├── pkgIndex.tcl │ ├── rotext.tcl │ ├── searchentry.tcl │ ├── statusentry.tcl │ ├── tooltip.tcl │ └── windowmanager.tcl ├── markupparser │ ├── README.txt │ ├── markupparser.tcl │ ├── pkgIndex.tcl │ └── pkgModules.tcl ├── notebookdb │ ├── Makefile │ ├── notebookdb.tcl │ ├── notebookdb.test │ ├── pkgIndex.tcl │ └── pkgModules.tcl └── quillinfo │ ├── pkgIndex.tcl │ ├── pkgModules.tcl │ └── quillinfo.tcl ├── project.quill └── test ├── app_notebook ├── all_tests.test └── app_notebook.test ├── markupparser ├── all_tests.test └── markupparser.test └── notebookdb ├── all_tests.test └── notebookdb.test /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | *.html 3 | *.log 4 | *.exe 5 | bin/*macosx* 6 | bin/*linux* 7 | bin/*.kit 8 | *~ 9 | *.bak 10 | Testing.nbk 11 | *.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Will Duquette 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notebook Personal Wiki 2 | 3 | # History 4 | 5 | In 2005 I released Notebook v2.1.3, the latest, most up-to-date version of 6 | an application I'd been working on for about four years. Later, I spent 7 | a great deal of time on Notebook v3, which ultimately failed due to problems 8 | with a significant piece of infrastructure. I stopped working on Notebook 9 | altogether after that; the Notebook v3 project was a dead end, and I'd put 10 | too much effort into v3 to be interested in Notebook v2.1.3. 11 | 12 | Recently, though, I've been interested in getting Notebook v2.1.3 working 13 | again, using all of the latest Tcl/Tk bells and whistles. 14 | 15 | This version is labeled Notebook v2.2.0; it's essentially Notebook v2.1.3 16 | with the minimal changes required to get it working with the latest version 17 | of Tcl/Tk. I've not tested it thoroughly, but feel free to try it; your 18 | data is always saved in a plain text file, so you aren't going to lose anything. 19 | 20 | If you find any bugs, please write me a bug on the Notebook issue tracker 21 | at https://github.com/wduquette/notebook/issues. 22 | 23 | Will Duquette - will@wjduquette.com 24 | 25 | # Screenshots 26 | 27 | Some screenshots from Notebook v2.2.0 taken on Windows XP. 28 | 29 | ![Home page](assets/screenshots/home.png "Home page") 30 | 31 | ![Sandbox page (with Sidebar open)](assets/screenshots/sandbox_plus_sidebar.png "Sandbox page (with Sidebar open)") 32 | 33 | ![Editing Sandbox page](assets/screenshots/edit_sandbox.png "Editing Sandbox page") 34 | 35 | ![Index page](assets/screenshots/index.png "Index page") 36 | 37 | Notebook's Preferences dialog allows various style customization. Fonts are all set to Calibri, except Liberation Mono for "Mono Text". 38 | 39 | ![Home page (neon style)](assets/screenshots/home_neon.png "Home page (neon style)") 40 | -------------------------------------------------------------------------------- /assets/screenshots/edit_sandbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/assets/screenshots/edit_sandbox.png -------------------------------------------------------------------------------- /assets/screenshots/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/assets/screenshots/home.png -------------------------------------------------------------------------------- /assets/screenshots/home_neon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/assets/screenshots/home_neon.png -------------------------------------------------------------------------------- /assets/screenshots/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/assets/screenshots/index.png -------------------------------------------------------------------------------- /assets/screenshots/sandbox_plus_sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/assets/screenshots/sandbox_plus_sidebar.png -------------------------------------------------------------------------------- /bin/notebook.tcl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -*-tcl-*- 3 | # the next line restarts using tclsh\ 4 | exec tclsh "$0" "$@" 5 | 6 | #------------------------------------------------------------------------- 7 | # NAME: notebook.tcl 8 | # 9 | # PROJECT: 10 | # notebook: Your project description 11 | # 12 | # DESCRIPTION: 13 | # Loader script for the notebook(1) tool. 14 | # 15 | #------------------------------------------------------------------------- 16 | 17 | #------------------------------------------------------------------------- 18 | # Prepare to load application 19 | 20 | set bindir [file dirname [info script]] 21 | set libdir [file normalize [file join $bindir .. lib]] 22 | 23 | set auto_path [linsert $auto_path 0 $libdir] 24 | 25 | # -quill-tcl-begin 26 | package require Tcl 8.6.1 27 | # -quill-tcl-end 28 | 29 | # quillinfo(n) is a generated package containing this project's 30 | # metadata. 31 | package require quillinfo 32 | 33 | # If it's a gui, load Tk. 34 | if {[quillinfo isgui notebook]} { 35 | # -quill-tk-begin 36 | package require Tk 8.6.1 37 | # -quill-tk-end 38 | } 39 | 40 | # app_notebook(n) is the package containing the bulk of the 41 | # notebook code. In particular, this package defines the 42 | # "main" procedure. 43 | package require app_notebook 44 | namespace import app_notebook::* 45 | 46 | #------------------------------------------------------------------------- 47 | # Invoke the application 48 | 49 | if {!$tcl_interactive} { 50 | if {[catch { 51 | main $argv 52 | } result eopts]} { 53 | if {[dict get $eopts -errorcode] eq "FATAL"} { 54 | # The application has flagged a FATAL error; display it 55 | # and halt. 56 | puts $result 57 | exit 1 58 | } else { 59 | puts "Unexpected error: $result" 60 | puts "Error Code: ([dict get $eopts -errorcode])\n" 61 | puts [dict get $eopts -errorinfo] 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/index.quilldoc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TBD

6 | 7 | 8 | 9 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/man1/notebook.manpage: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 | 6 | 7 |
8 | 9 | TODO: General description of the notebook(1) application 10 | 11 |
12 | 13 | TODO: Usage information for the notebook(1) application. 14 | 15 |
16 | 17 | TBD

18 | 19 |

20 | 21 | TBD

22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/app_notebook/default.nbk: -------------------------------------------------------------------------------- 1 | # Notebook Database File 2 | 3 | #-------------------------------------------------- 4 | # Home 5 | 6 | page Home {Hi! Welcome to your new Notebook! 7 | 8 | You should probably look at the topics on the [%Help menu|help-introduction%] 9 | to find out more about your Notebook, but here are some things to 10 | get you started. Click on them to see what they are all about. 11 | 12 | * Take a quick [Tour] of the features of your notebook. 13 | 14 | * The [%Index Sidebar|show-index%] can show you an index of all of the pages in your 15 | notebook. Press the Index button on the toolbar (the one with the 16 | "book" icon), or select "Page/Index" from the menu. 17 | 18 | * Create new pages on the [New Pages] page, or anywhere else you like! 19 | 20 | * You can edit any page by pressing the "Edit" button, above, or by selecting 21 | "Page/Edit" from the menu. 22 | 23 | * Try things in the [Sandbox]. 24 | } 1115592699 25 | 26 | 27 | #-------------------------------------------------- 28 | # Index 29 | 30 | page Index {[@pageIndex@]} 1026586734 31 | 32 | 33 | #-------------------------------------------------- 34 | # New Pages 35 | 36 | page {New Pages} {To create a new page, 37 | 38 | * Add a link to it in this page (or on any other page where the link would 39 | be more appropriate): 40 | ** Press the "Edit" button. 41 | ** Go to the bottom of the page (or anywhere, really). 42 | ** Type the page's name in square brackets, &lb;Like This&rb;. 43 | ** Press the "Done" button. 44 | * Click on the link. 45 | * On the Status Line, down at the bottom of the window, Notebook will ask 46 | if you want to create the page. Type "yes" or "y", and press Enter. 47 | 48 | New Pages 49 | 50 | * [Tour] 51 | * [Sandbox]} 1102291321 52 | 53 | 54 | #-------------------------------------------------- 55 | # Recent Changes 56 | 57 | page {Recent Changes} {[@recentChanges@]} 1026585675 58 | 59 | 60 | #-------------------------------------------------- 61 | # Sandbox 62 | 63 | page Sandbox {This is place to experiment with Notebook's way of marking up text. 64 | Edit this page to see how you type things; then save it to see what it looks 65 | like when you're browsing. 66 | 67 | = You Can Have Section Headers = 68 | 69 | * You can have bulleted lists. 70 | * You can type bold, italic, and monospace text, 71 | or any combination. 72 | * You can have inline header text. 73 | * You can have very small text. 74 | * You can strike things out. 75 | 76 | : You can simply indent a paragraph without adding a bullet. 77 | 78 | Paragraphs end with the first blank line, bullet item, or indented line. 79 | No matter how many lines long a paragraph is, Notebook will wrap it for 80 | display so that it looks nice. 81 | 82 | If you leave a space character at the beginning of a paragraph, the whole 83 | paragraph is "Preformatted". That means that it's displayed in a monospace 84 | font, exactly as is. It's conventional to put whitespace at the beginning of 85 | every line of a preformatted paragraph, but that's not necessary. 86 | 87 | Preformatted text is useful for typing in tables and things like that.} 1113419698 88 | 89 | 90 | #-------------------------------------------------- 91 | # Tour 92 | 93 | page Tour {Welcome to a tour of your new Notebook! 94 | 95 | Notebook has many features; this tour will touch on the highlights. You 96 | can find more information about any of them via the [@helpbtn Help@] menu. 97 | 98 | Pages are linked together 99 | 100 | The most important feature of Notebook is that a notebook is a collection 101 | of pages, and the pages are linked together. You've already discovered 102 | this, by clicking [Tour] to get to this page. 103 | 104 | : [Click here to continue...|Tour 2]} 1102291376 105 | 106 | 107 | #-------------------------------------------------- 108 | # Tour 2 109 | 110 | page {Tour 2} {This page is part of a [tour] of Notebook's features. 111 | 112 | Notebook is not WYSIWYG 113 | 114 | WYSIWYG means "What you see is what you get." Most word processors are 115 | WYSIWYG. As you type, and add boldface or italics, you see 116 | them immediately. 117 | 118 | Notebook is different. Instead, you type your pages in plain text, following 119 | a few simple conventions, and Notebook displays them nicely. It's similar to 120 | writing web pages, but Notebook's conventions are much simpler to type than 121 | HTML. 122 | 123 | Why not make it WYSIWYG? Because it comes much easier for you to type and 124 | edit links to other pages, like these: [Home], [Index], [Sandbox]. 125 | 126 | If you press the Edit button (it's the one with the pencil on it), you'll 127 | see what the text of this page really looks like. Press the Cancel 128 | button (the red "X") when you're done, unless you've made changes you want to save. 129 | 130 | : [Click here to continue...|Tour 3]} 1113419804 131 | 132 | 133 | #-------------------------------------------------- 134 | # Tour 3 135 | 136 | page {Tour 3} {This page is part of a [tour] of Notebook's features. 137 | 138 | = You can style your text = 139 | 140 | You can use boldface, italics, and monospace text, or 141 | a combination of all three. 142 | 143 | You can strikeout any text you like. 144 | 145 | You can use a large font for emphasis, or a 146 | small font for incidental notes. 147 | 148 | If you click the Edit button (the one with the pencil on it), you'll see 149 | that the above paragraphs look like this, only not indented: 150 | 151 | You can use boldface, italics, and monospace text, or 152 | a combination of all three. 153 | 154 | You can strikeout any text you like. 155 | 156 | You can use a large font for emphasis, or a 157 | small font for incidental notes. 158 | 159 | If you put whitespace at the beginning of a paragraph, like those two just 160 | above this have, then they get displayed "preformatted", just as you typed them. 161 | 162 | : [Click here to continue...|Tour 4]} 1113419869 163 | 164 | 165 | #-------------------------------------------------- 166 | # Tour 4 167 | 168 | page {Tour 4} {This page is part of a [tour] of Notebook's features. 169 | 170 | You can link your pages together 171 | 172 | To add a link from one page to another, just enclose the page's name in 173 | square brackets, &lb;Like This&rb;. 174 | 175 | This page contains links to your [Home] page and the [Index] page. 176 | 177 | To create a new page, choose a name and add a link to it in the 178 | appropriate page. The [New Pages] page is good choice. Then, just click 179 | on the new link. Notebook will ask if you want to create the page. 180 | For example, [Nonexistent Page] doesn't exist (unless you've created it 181 | yourself). When you click on it, Notebook will inquire (down on the 182 | Status Line, at the bottom of the window) whether you want to create the 183 | page or not. Enter "y" or "n", accordingly, and press Enter. 184 | 185 | Links to nonexistent pages are always shown with red brackets, as you see. 186 | 187 | Sometimes you want to link to a page using something other than the 188 | page's name as the link text. At the bottom of this page, for example, 189 | is a link that says "Click here to continue...". If you edit this page, you'll 190 | see that it looks like this: 191 | 192 | [Click here to continue...|Tour 5] 193 | 194 | The text after the vertical bar is the page name to link to. 195 | 196 | : [Click here to continue...|Tour 5]} 1113419900 197 | 198 | 199 | #-------------------------------------------------- 200 | # Tour 5 201 | 202 | page {Tour 5} {This page is part of a [tour] of Notebook's features. 203 | 204 | You can find your pages 205 | 206 | You can always find any page you create in Notebook, even if you don't 207 | remember how to get there by clicking on links. 208 | 209 | * You can press the Index button (the one with the Book on it), or select 210 | "Page/Index" from the menu, to see an alphabetical list of the pages in your Notebook. 211 | 212 | * You can select "Page/Recent Changes" from the menu to see a list of 213 | all of your pages, starting with the ones you've edited most recently. 214 | 215 | * You can type any text you like in the "Search" field above, and press 216 | Enter, and Notebook 217 | will show you a list of pages that contain it. 218 | 219 | Try it! Once you're done, click the Back button (the one with the arrow 220 | pointing to the left) repeatedly until you're 221 | back to this page--or find [Tour 5] in the index! 222 | 223 | : [Click here to continue...|Tour 6]} 1113419918 224 | 225 | 226 | #-------------------------------------------------- 227 | # Tour 6 228 | 229 | page {Tour 6} {This page is part of a [tour] of Notebook's features. 230 | 231 | You can do magic! 232 | 233 | Notebook is programmed in a language called Tcl. 234 | You can use [@helpbtn "the Tcl language"@] in your pages to do magic. There are 235 | two kinds of magic: [@helpbtn "magic button"@]s and 236 | [@helpbtn "embedded macro"@]s. 237 | Both of them are represented on this page (you'll see them if you edit 238 | this page). 239 | 240 | A magic button looks like a link, but it's colored magenta instead of blue. 241 | Whereas a link just takes you to the named page, a magic button executes 242 | a [@helpbtn "Notebook command"@]. There are lots of magic buttons on 243 | this page; see what happens if you click them. 244 | 245 | An embedded macro is a [@helpbtn "Notebook command"@] that's executed 246 | whenever the page displays. It returns some text that's included into 247 | the page just as though you'd typed it. If you edit this page, then toward 248 | the top you'll see an embedded macro that looks like this: 249 | 250 | : &lb;@helpbtn "the Tcl language"@&rb; 251 | 252 | Here, "helpbtn" is an embedded macro that creates a magic button that pops up 253 | the Help window with the named topic, as if you'd typed this: 254 | 255 | [%the Tcl language|showhelp "the Tcl language"%] 256 | 257 | Of course, the macro is much shorter because it doesn't need to repeat the 258 | topic text. "showhelp" is a command that pops up Notebook's on-line help 259 | and makes it go to the specified topic. 260 | 261 | : [Click here to continue...|Tour 7]} 1102291940 262 | 263 | 264 | #-------------------------------------------------- 265 | # Tour 7 266 | 267 | page {Tour 7} {This page is part of a [tour] of Notebook's features. 268 | 269 | But wait--there's more! 270 | 271 | Notebook can do lots of other things as well. If you create a page you don't 272 | like, you can delete it. If you decide you don't like a page's name, you can 273 | rename it; Notebook will automatically update all of the links. You can 274 | define new [@helpbtn "Notebook commands"@] on your [User Code] page, 275 | and use them in [@helpbtn "embedded macro"@]s and [@helpbtn "magic button"@]s. 276 | 277 | And that means you can do almost anything you can think of.} 1028999577 278 | 279 | 280 | #-------------------------------------------------- 281 | # User Code 282 | 283 | page {User Code} {Use this page to extend Notebook using the 284 | [@helpbtn "the Tcl language"@]. Commands you add here can be used as 285 | [@helpbtn "Magic Button"@]s and [@helpbtn "Embedded Macro"@]s. 286 | 287 | Note that you can intersperse normal prose in between blocks of Tcl code. 288 | 289 | = User Menu = 290 | 291 | The [@helpbtn "User Menu"@] pops up when you right-click or control-click 292 | on a page in the [@helpbtn "Page Browser"@]. You can customize it however you like. 293 | 294 | #Tcl 295 | usermenu { 296 | Back back-page 297 | Home {goto-page Home} 298 | } 299 | #unTcl 300 | 301 | = Edit Menu = 302 | 303 | The [@helpbtn "Edit Menu"@] pops up when you right-click or control-click 304 | on a page in the [@helpbtn "Page Editor"@]. You can customize it however you like. 305 | 306 | #Tcl 307 | editmenu { 308 | Undo undo-change 309 | Redo redo-change 310 | separator {} 311 | Cut cut-string 312 | Copy copy-string 313 | Paste paste-string 314 | "Insert Page..." insert-page 315 | } 316 | #unTcl 317 | 318 | = Example = 319 | 320 | The following [@helpbtn "embedded macro"@] used to be used by the 321 | [Tour] to create a [@helpbtn "magic button"@] that said 322 | "Click here to continue..." and took you to the next page in the tour 323 | when you clicked it. It's no longer needed, because now you can write 324 | such links directly, like this: 325 | 326 | [Click here to continue...|Tour 2] 327 | 328 | Still, it's a nice example of how to write a macro that creates a button. 329 | 330 | #Tcl 331 | proc clickToContinue {name} { 332 | return "\[%Click here to continue...|goto-page [list $name]%\]" 333 | } 334 | #unTcl} 1113420734 335 | 336 | 337 | # End of Notebook Database File 338 | -------------------------------------------------------------------------------- /lib/app_notebook/helpbrowser.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # helpbrowser.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # A browser for on-line help in Notebook format. 10 | # 11 | # LICENSE: 12 | # Copyright (C) 2002,2003,2004,2005 by William H. Duquette. 13 | # This file may be used subject to the terms in license.txt. 14 | # 15 | #----------------------------------------------------------------------- 16 | 17 | #----------------------------------------------------------------------- 18 | # Package Definition 19 | # 20 | # Not a separate package, at the moment. 21 | 22 | #----------------------------------------------------------------------- 23 | # Namespace 24 | 25 | namespace eval ::app_notebook::HelpBrowser:: { 26 | namespace export {[a-z]*} 27 | 28 | # The "" array contains information about the helpbrowser. There 29 | # is only ever one. 30 | # 31 | # (-initialized) True if it's been initialized, false otherwise. 32 | # (-helpfile) The help database file name 33 | variable "" 34 | set (-initialized) 0 35 | set (-helpfile) "" 36 | 37 | } 38 | 39 | #----------------------------------------------------------------------- 40 | # Public Methods 41 | 42 | # helpbrowser helpfile 43 | # 44 | # helpfile: Name of the help database file. 45 | # 46 | # Saves sufficient information to create the help browser when it is 47 | # wanted. 48 | 49 | proc ::app_notebook::HelpBrowser::helpbrowser {helpfile} { 50 | variable "" 51 | 52 | # First, calling this twice is an error. 53 | if {$(-initialized)} { 54 | error "helpbrowser already initialized" 55 | } 56 | 57 | # Next, initialize the object's data structures with defaults 58 | set (-initialized) 1 59 | set (-helpfile) $helpfile 60 | } 61 | 62 | # showhelp ?helppage? 63 | # 64 | # helppage: The name of the page to show; defaults to "Help". 65 | # 66 | # Displays the named page in the help browser, first creating it if 67 | # necessary. 68 | 69 | proc ::app_notebook::HelpBrowser::showhelp {{helppage "Help"}} { 70 | variable "" 71 | 72 | # First, create the browser if need be. 73 | if {[info commands .helpbrowser] eq ""} { 74 | MakeBrowser 75 | } 76 | 77 | # Pop it up 78 | wm deiconify .helpbrowser 79 | raise .helpbrowser 80 | 81 | # Show the help topic. 82 | if {[string tolower $helppage] eq "index"} { 83 | .helpbrowser goto-page "Help" 84 | .helpbrowser show-index 85 | } elseif {[.helpbrowser pageexists $helppage]} { 86 | .helpbrowser goto-page $helppage 87 | } else { 88 | .helpbrowser showsearch $helppage 89 | } 90 | } 91 | 92 | #----------------------------------------------------------------------- 93 | # Private Methods 94 | 95 | # MakeBrowser 96 | # 97 | # Creates the help browser window. 98 | 99 | proc ::app_notebook::HelpBrowser::MakeBrowser {} { 100 | variable "" 101 | 102 | # TBD: Consider passing the help file name and letting the 103 | # notebookbrowser open the file. 104 | try { 105 | set db [dbmanager openfile $(-helpfile)] 106 | } on error errmsg { 107 | error "Notebook Help could not open help file $(-helpfile): $errmsg" 108 | } 109 | 110 | # Create the browser 111 | notebookbrowser .helpbrowser \ 112 | -db $db \ 113 | -title "Notebook Help" \ 114 | -readonly 1 \ 115 | -home Help 116 | 117 | } -------------------------------------------------------------------------------- /lib/app_notebook/main.tcl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | # TITLE: 3 | # main.tcl 4 | # 5 | # PROJECT: 6 | # notebook: Your project description 7 | # 8 | # DESCRIPTION: 9 | # app_notebook(n): main procedure 10 | # 11 | #------------------------------------------------------------------------- 12 | 13 | #------------------------------------------------------------------------- 14 | # Exported Commands 15 | 16 | namespace eval ::app_notebook { 17 | namespace export \ 18 | main 19 | } 20 | 21 | #------------------------------------------------------------------------- 22 | # Commands 23 | 24 | # main argv 25 | # 26 | # Dummy procedure 27 | 28 | proc ::app_notebook::main {argv} { 29 | namespace eval :: { 30 | source [file join $::app_notebook::library notebook.tcl] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/app_notebook/messagelog.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # messagelog.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # This window is created at application start-up, but is immediately 10 | # withdrawn. It contains a scrolling log of all messages written 11 | # to any statusentry in the program, along with all unexpected 12 | # stack traces. 13 | # 14 | # LICENSE: 15 | # Copyright (C) 2003 by William H. Duquette. This file may 16 | # be used subject to the terms in license.txt. 17 | # 18 | #----------------------------------------------------------------------- 19 | 20 | snit::widget messagelog { 21 | # This is a toplevel window 22 | hulltype toplevel 23 | delegate option * to hull 24 | 25 | #------------------------------------------------------------------- 26 | # Type Methods 27 | 28 | typemethod init {} { 29 | messagelog .messagelog 30 | } 31 | 32 | typemethod logmessage {text} { 33 | .messagelog logmessage $text 34 | } 35 | 36 | typemethod logerror {msg einfo ecode} { 37 | .messagelog logerror $msg $einfo $ecode 38 | } 39 | 40 | typemethod show {} { 41 | wm deiconify .messagelog 42 | raise .messagelog 43 | } 44 | 45 | #------------------------------------------------------------------- 46 | # options 47 | 48 | # Number of lines to retain. 49 | option -maxlines 200 50 | 51 | #------------------------------------------------------------------- 52 | # Components 53 | 54 | variable am 55 | variable status 56 | variable log 57 | 58 | #------------------------------------------------------------------- 59 | # Constructor 60 | 61 | constructor {args} { 62 | #--------------------------------------------------------------- 63 | # Preliminaries 64 | 65 | # FIRST, withdraw the window; we'll bring it back when the 66 | # want to see it. 67 | wm withdraw $win 68 | 69 | # NEXT, set the window title 70 | wm title $win "Notebook: Message Log" 71 | 72 | # NEXT, Go ahead and configure the widget options, if any; none are 73 | # delegated to anything but the hull. 74 | $self configurelist $args 75 | 76 | # NEXT, prepare for window closing 77 | wm protocol $win WM_DELETE_WINDOW [list wm withdraw $win] 78 | 79 | #--------------------------------------------------------------- 80 | # Create Components 81 | 82 | # FIRST, create the actionmanager and define the actions. 83 | install am using nbactionmanager %AUTO% \ 84 | -toplevel $win \ 85 | -windowtype messagelog \ 86 | -errorcommand [mymethod ErrorHandler] 87 | 88 | # NEXT, create the statusentry. It provides a GUI for displaying 89 | # status and entering arguments. 90 | install status using statusentry $win.status \ 91 | -errorcommand [mymethod logerror] \ 92 | -messagecommand [mymethod logmessage] 93 | 94 | 95 | # NEXT, create the rotext pane. 96 | install log using rotext $win.text \ 97 | -yscrollcommand "$win.scroll set" \ 98 | -width 80 \ 99 | -height 24 \ 100 | -foreground black \ 101 | -background white \ 102 | -highlightthickness 0 103 | 104 | $log tag configure errortext \ 105 | -foreground darkred 106 | 107 | scrollbar $win.scroll \ 108 | -command "$win.text yview" 109 | 110 | # Next, pack the components 111 | pack $status -side bottom -fill x -expand false 112 | pack $win.scroll -side right -fill y -expand false 113 | pack $log -side top -fill both -expand true 114 | 115 | # Add better navigation keys 116 | bind $win [list $log yview scroll 1 units] 117 | bind $win [list $log yview scroll -1 units] 118 | bind $win [list $log yview scroll 1 pages] 119 | bind $win [list $log yview scroll -1 pages] 120 | 121 | # NEXT, update the action state now that everything's created. 122 | $am updatestate 123 | } 124 | 125 | #------------------------------------------------------------------- 126 | # Private Methods 127 | 128 | # Handles errors from the statusentry and actionmanager components. 129 | method ErrorHandler {msg einfo ecode} { 130 | if {$ecode eq "USER"} { 131 | $status msg $msg 132 | bell 133 | } else { 134 | messagelog logerror $msg $einfo $ecode 135 | 136 | $status msg "$msg -- go to Message Log for more." 137 | } 138 | } 139 | 140 | # Truncates the log if it gets too long. 141 | method Truncate {} { 142 | $log del $options(-maxlines).0 end 143 | } 144 | 145 | #----------------------------------------------------------------------- 146 | # Public Methods 147 | 148 | # Log an error. 149 | method logerror {msg einfo ecode} { 150 | $log ins 1.0 "\n$einfo\n\n" errortext 151 | $log see 1.0 152 | $log mark set insert 1.0 153 | 154 | $self Truncate 155 | bell 156 | } 157 | 158 | # Log a normal message 159 | method logmessage {msg} { 160 | if {$msg ne ""} { 161 | $log ins 1.0 "$msg\n" 162 | $log see 1.0 163 | $log mark set insert 1.0 164 | 165 | $self Truncate 166 | } 167 | } 168 | 169 | #------------------------------------------------------------------- 170 | # Non-Delegated Action Handlers 171 | 172 | # Action: close-window 173 | method close-window {} { 174 | wm withdraw $win 175 | } 176 | 177 | #------------------------------------------------------------------- 178 | # Delegated Action Handlers 179 | 180 | delegate method about-notebook to am 181 | delegate method copy-string to am 182 | delegate method cut-string to am 183 | delegate method edit-preferences to am 184 | delegate method help-on to am 185 | delegate method help-introduction to am 186 | delegate method help-index to am 187 | delegate method help-on-actions to am 188 | delegate method help-on-commands to am 189 | delegate method help-on-markup to am 190 | delegate method new-notebook to am 191 | delegate method notebook-license to am 192 | delegate method notebook-release-notes to am 193 | delegate method open-notebook to am 194 | delegate method paste-string to am 195 | delegate method redo-change to am 196 | delegate method request-action to am 197 | delegate method undo-change to am 198 | delegate method show-version to am 199 | } 200 | 201 | -------------------------------------------------------------------------------- /lib/app_notebook/missing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/lib/app_notebook/missing.gif -------------------------------------------------------------------------------- /lib/app_notebook/nb2html.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # nb2html.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # HTML exportation code for Notebook files. This module 10 | # knows very little about the Notebook application as a whole; 11 | # such intelligence will be provided by hooks. 12 | # 13 | # LICENSE: 14 | # Copyright (C) 2005 by William H. Duquette. This file may 15 | # be used subject to the terms in license.txt. 16 | # 17 | #----------------------------------------------------------------------- 18 | 19 | #----------------------------------------------------------------------- 20 | # Required Packages 21 | 22 | package require markupparser 23 | 24 | 25 | #----------------------------------------------------------------------- 26 | # HTML Type 27 | # 28 | # Creates objects which can translate Notebook markup into HTML. 29 | 30 | namespace eval ::nb2html:: {} 31 | 32 | snit::type ::nb2html::html { 33 | #------------------------------------------------------------------- 34 | # Tables 35 | 36 | # This table indicates the HTML tag(s) for each Notebook 37 | # style code. 38 | typevariable htmlStyles -array { 39 | m1 40 | m0 41 | i1 42 | i0 43 | b1 44 | b0 45 | h1 {} 46 | h0 47 | s1 {} 48 | s0 49 | x1 50 | x0 51 | } 52 | 53 | #------------------------------------------------------------------- 54 | # Options 55 | # 56 | # These options control how Notebook markup is translated into HTML. 57 | 58 | # CSS styles for inclusion in pages or a .css file. By default, 59 | # no styles. 60 | option -css {} 61 | 62 | # Indicates whether hash directives should be included 63 | # in the output. If "unknown", only unknown directives 64 | # are included. If "none", no directives are included; 65 | # if "all", all directives are included. Included 66 | # directives are rendered as preformatted text. 67 | option -showhashes unknown ;# unknown | none | all 68 | 69 | # If "" (the default), link text will be displayed 70 | # verbatim. Otherwise, the link text will be lappended 71 | # to the cmdprefix, which will be evaluated. The 72 | # return value will be included in the output verbatim. 73 | option -linkcmd {} 74 | 75 | # If "", the filename will be used as the Notebook title 76 | # when exporting an entire notebook. 77 | option -nbtitle {} 78 | 79 | #------------------------------------------------------------------- 80 | # Constructor 81 | 82 | # So far, we don't need one. 83 | 84 | #------------------------------------------------------------------- 85 | # Public Methods 86 | 87 | 88 | # htmlpage pagename htmltext args 89 | # 90 | # pagename The page's name 91 | # text HTML text to wrap 92 | # args Options/values (NIY) 93 | # 94 | # Wraps HTML body text in a simple boilerplate HTML page. 95 | method htmlpage {pagename text args} { 96 | if {[llength $args] == 1} { 97 | set args [lindex $args 0] 98 | } 99 | 100 | set out "$pagename" 101 | 102 | if {$options(-css) ne ""} { 103 | append out "\n\n" 104 | } 105 | 106 | append out "\n\n\n" 107 | append out "

$pagename

\n\n" 108 | append out $text 109 | append out "\n\n" 110 | 111 | return $out 112 | } 113 | 114 | # htmltext markup 115 | # 116 | # markup Notebook markup to translate 117 | 118 | method htmltext {markup} { 119 | # Set up for processing. 120 | set result "" 121 | set this(indent) 0 122 | set this(para) : 123 | set this(inpara) 0 124 | set last(indent) 0 125 | set last(para) : 126 | 127 | foreach {tag value} [::markupparser::parse $markup] { 128 | switch -exact $tag { 129 | META { } 130 | BR { 131 | append result
132 | } 133 | PRE { 134 | # Otherwise, just display the preformatted text. 135 | append result "
[AddEscapes $value]
\n" 136 | } 137 | TCL { 138 | # Tcl code is always displayed as preformatted text... 139 | # for now. It would be fun to do code coloring, etc. 140 | append result "
[AddEscapes $value]
\n" 141 | } 142 | DATA { 143 | array set fields $value 144 | 145 | # "html" data is passed through unchanged; other data is 146 | # shown as PRE. 147 | if {$fields(type) eq "html"} { 148 | append result $fields(data) 149 | } else { 150 | append result "
[AddEscapes $fields(data)]
\n" 151 | } 152 | } 153 | HASH { 154 | if {[string match \#pre* $value] || 155 | [string match \#unpre* $value] || 156 | [string match \#Tcl* $value] || 157 | [string match \#unTcl* $value] || 158 | [string match \#data* $value] || 159 | [string match \#undata* $value] || 160 | [string match \#meta* $value] || 161 | [string match \#rem* $value] 162 | } { 163 | if {$options(-showhashes) eq "all"} { 164 | append result "
[AddEscapes $value]
\n" 165 | } 166 | continue 167 | } 168 | 169 | if {[string match \#---* $value]} { 170 | # If last indent was > 0, close the indentation. 171 | set this(indent) 0 172 | for {set i 0} {$i < $last(indent)} {incr i} { 173 | append result "\n\n" 174 | } 175 | array set last [array get this] 176 | 177 | if {$options(-showhashes) eq "all"} { 178 | append result "
[AddEscapes $value]
\n" 179 | } 180 | append result "
\n" 181 | continue 182 | } 183 | 184 | # Otherwise, it's an unknown hash; display as PRE. 185 | if {$options(-showhashes) eq "unknown"} { 186 | append result "
[AddEscapes $value]
\n" 187 | } 188 | } 189 | H { 190 | # Next, if we're indented, close the indentation. 191 | set this(indent) 0 192 | for {set i 0} {$i < $last(indent)} {incr i} { 193 | append result "\n\n" 194 | } 195 | array set last [array get this] 196 | 197 | set lev [lindex $value 0] 198 | set htext [lindex $value 1] 199 | 200 | # Increment level: we want

,

and

201 | # tags. 202 | incr lev 203 | 204 | append result "[AddEscapes $htext]\n" 205 | } 206 | /P { 207 | set this(inpara) 0 208 | foreach letter [array names sty] { 209 | if {$sty($letter)} { 210 | append result $htmlStyles(${letter}0) 211 | } 212 | } 213 | array set last [array get this] 214 | 215 | if {$this(indent) == 0} { 216 | # Then this is the end of a normal paragraph, which 217 | # needs to be closed. 218 | append result "

" 219 | } 220 | } 221 | P { 222 | set this(inpara) 1 223 | 224 | # FIRST, reset all text styles. 225 | array set sty { 226 | m 0 i 0 b 0 227 | h 0 s 0 x 0 228 | } 229 | 230 | # Next, get the paragraph style. 231 | set this(para) [lindex $value 0] 232 | set this(indent) [lindex $value 1] 233 | 234 | # Next, if we're indented less than before, close 235 | # the indentation. 236 | for {set i $this(indent)} {$i < $last(indent)} {incr i} { 237 | append result "\n\n" 238 | } 239 | 240 | # Next, if we're indented more than before, open 241 | # the indentation. 242 | for {set i $last(indent)} {$i < $this(indent)} {incr i} { 243 | append result "\n
    \n" 244 | } 245 | 246 | # Next, if the indentation is 0, start a new 247 | # paragraph; otherwise start a list item. 248 | if {$this(indent) == 0} { 249 | append result "

    " 250 | } elseif {$this(para) eq "*"} { 251 | append result "

  • " 252 | } elseif {$last(para) eq "*" && 253 | $this(para) eq ":"} { 254 | append result "
  • \n" 255 | } 256 | } 257 | TXT { 258 | # Handle Notebook markup pseudo-escapes. 259 | set value [string map [list "&lb;" \[ "&rb;" \]] $value] 260 | 261 | append result $value 262 | } 263 | HTML { 264 | append result $value 265 | } 266 | NL { 267 | append result "\n" 268 | } 269 | STY { 270 | set code [lindex $value 0] 271 | set letter [string index $code 0] 272 | set flag [string index $code 1] 273 | 274 | set sty($letter) $flag 275 | append result $htmlStyles($code) 276 | } 277 | LINK { 278 | set value [::markupparser::normalizeSpace $value] 279 | 280 | if {![regexp {^([^|]+)\|(.*$)} $value dummy linktext name]} { 281 | set linktext $value 282 | set name $value 283 | } 284 | 285 | if {$options(-linkcmd) eq ""} { 286 | append result [AddEscapes $linktext] 287 | continue 288 | } 289 | 290 | set cmd $options(-linkcmd) 291 | lappend cmd $linktext $name 292 | 293 | append result [uplevel \#0 $cmd] 294 | } 295 | BTN { 296 | # Extract just the button text. 297 | set value [lindex [split $value |] 0] 298 | 299 | append result $value 300 | } 301 | OBJECT { 302 | set obj [getobj $value] 303 | 304 | switch -exact [lindex $obj 0] { 305 | image { 306 | set fname [lindex $obj 1] 307 | array set opts [lindex $obj 2] 308 | 309 | append result "\"$opts(-text)\""" 321 | } 322 | error - 323 | default { 324 | append result [ErrText "
    \[!$value!\]
    "] 325 | } 326 | } 327 | } 328 | MACRO { 329 | # There shouldn't be any macros. Format the macro 330 | # in red. 331 | append result [ErrText "\[@$value@\]"] 332 | } 333 | } 334 | } 335 | 336 | # If last indent was > 0, close the indentation. 337 | for {set i 0} {$i < $last(indent)} {incr i} { 338 | append result "
\n\n" 339 | } 340 | 341 | if {$this(inpara)} { 342 | foreach letter [array names sty] { 343 | if {$sty($letter)} { 344 | append result $htmlStyles(${letter}0) 345 | } 346 | } 347 | 348 | if {$this(indent) == 0} { 349 | # Then this is the end of a normal paragraph, which 350 | # needs to be closed. 351 | append result "

" 352 | } 353 | } 354 | 355 | return $result 356 | } 357 | 358 | #------------------------------------------------------------------- 359 | # Utility Procs 360 | 361 | # ErrText text 362 | # 363 | # Wraps the text in a red font. 364 | 365 | proc ErrText {text} { 366 | return "$text" 367 | } 368 | 369 | # AddEscapes text 370 | # 371 | # text plain text to be included in HTML output. 372 | # 373 | # Converts &, <, and > to &, <, and >. 374 | 375 | proc AddEscapes {text} { 376 | regsub -all "&" $text {\&} text 377 | regsub -all "<" $text {\<} text 378 | regsub -all ">" $text {\>} text 379 | 380 | return $text 381 | } 382 | } 383 | 384 | 385 | 386 | 387 | -------------------------------------------------------------------------------- /lib/app_notebook/nb2mediawiki.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # nb2mediawiki.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # MediaWiki markup exportation code for Notebook files. This module 10 | # knows very little about the Notebook application as a whole; 11 | # such intelligence will be provided by hooks. 12 | # 13 | # LICENSE: 14 | # Copyright (C) 2005 by William H. Duquette. This file may 15 | # be used subject to the terms in license.txt. 16 | # 17 | #----------------------------------------------------------------------- 18 | 19 | #----------------------------------------------------------------------- 20 | # Required Packages 21 | 22 | package require markupparser 23 | 24 | #----------------------------------------------------------------------- 25 | # namespace declaration 26 | 27 | namespace eval ::nb2mediawiki:: { 28 | variable wikiStyles 29 | 30 | # This table indicates the MediaWiki equivalents for each 31 | # Notebook style code. 32 | array set wikiStyles { 33 | m1 34 | m0 35 | i1 '' 36 | i0 '' 37 | b1 ''' 38 | b0 ''' 39 | h1 {=== } 40 | h0 { ===} 41 | s1 42 | s0 43 | x1 44 | x0 45 | } 46 | } 47 | 48 | 49 | 50 | #----------------------------------------------------------------------- 51 | # Public functions 52 | 53 | 54 | # wikitext markup args 55 | # 56 | # markup Notebook markup to translate 57 | # args Option/value pairs, as follows: 58 | # 59 | # -embeddedhtml 0|1 60 | # If 1, it's assumed that TXT can contain embedded HTML. 61 | # Otherwise, &, <, and > are quoted. 62 | # 63 | # -showhashes unknown|none|all 64 | # Indicates whether hash directives should be included 65 | # in the output. If "unknown", only unknown directives 66 | # are included. If "none", no directives are included; 67 | # if "all", all directives are included. Included 68 | # directives are rendered as preformatted text. 69 | 70 | proc nb2mediawiki::wikitext {markup args} { 71 | variable wikiStyles 72 | 73 | # Allow the options to be passed as a single argument. 74 | if {[llength $args] == 1} { 75 | set args [lindex $args 0] 76 | } 77 | 78 | # Get default option values 79 | array set opts { 80 | -embeddedhtml 0 81 | -showhashes unknown 82 | } 83 | 84 | # Get the caller's values. 85 | # TBD: check errors better. 86 | array set opts $args 87 | 88 | # Set up for processing. 89 | set result "" 90 | set gotNL 1 91 | 92 | foreach {tag value} [::markupparser::parse $markup] { 93 | switch -exact $tag { 94 | META { } 95 | BR { 96 | append result
97 | } 98 | PRE { 99 | # PRE is used to mark blank lines in the input; 100 | # these should be handled separately. 101 | # TBD: See if this statement can be removed. 102 | if {[regexp {^\s*\n\s*$} $value]} { 103 | append result "\n" 104 | continue 105 | } 106 | 107 | # Otherwise, just display the preformatted text. 108 | append result "
[AddEscapes $value]
\n" 109 | } 110 | TCL { 111 | # Tcl code is always displayed as preformatted text... 112 | # for now. It would be fun to do code coloring, etc. 113 | append result "
[AddEscapes $value]
\n" 114 | } 115 | DATA { 116 | array set fields $value 117 | 118 | # data is shown as PRE. 119 | append result "
[AddEscapes $fields(data)]
\n" 120 | } 121 | HASH { 122 | if {[string match \#pre* $value] || 123 | [string match \#unpre* $value] || 124 | [string match \#Tcl* $value] || 125 | [string match \#unTcl* $value] || 126 | [string match \#data* $value] || 127 | [string match \#undata* $value] || 128 | [string match \#meta* $value] || 129 | [string match \#rem* $value] 130 | } { 131 | if {$opts(-showhashes) eq "all"} { 132 | append result "
[AddEscapes $value]
\n" 133 | } 134 | continue 135 | } 136 | 137 | if {[string match \#---* $value]} { 138 | append result "----\n" 139 | continue 140 | } 141 | 142 | # Otherwise, it's an unknown hash; display as PRE. 143 | if {$opts(-showhashes) eq "unknown"} { 144 | append result "
[AddEscapes $value]
\n" 145 | } 146 | } 147 | H { 148 | set lev [lindex $value 0] 149 | set htext [lindex $value 1] 150 | 151 | set tag [string repeat "=" $lev] 152 | append result "$tag $htext $tag\n" 153 | } 154 | /P { 155 | foreach letter [array names sty] { 156 | if {$sty($letter)} { 157 | append result $wikiStyles($letter$sty($letter)) 158 | } 159 | } 160 | } 161 | P { 162 | # FIRST, reset all text styles. 163 | array set sty { 164 | m 0 i 0 b 0 165 | h 0 s 0 x 0 166 | } 167 | 168 | # Next, get the paragraph style. 169 | set para [lindex $value 0] 170 | set indent [lindex $value 1] 171 | 172 | if {$para eq ":" && !$gotNL} { 173 | append result "
\n" 174 | } elseif {$para eq "*"} { 175 | append result "\n[string repeat * $indent] " 176 | } 177 | 178 | set gotNL 0 179 | } 180 | TXT { 181 | if {!$opts(-embeddedhtml)} { 182 | set value [AddEscapes $value] 183 | } 184 | 185 | set value [string map [list "\n" " " "&lb;" \[ "&rb;" \]] $value] 186 | 187 | append result $value 188 | } 189 | HTML { 190 | append result $value 191 | } 192 | NL { 193 | set gotNL 1 194 | append result "\n\n" 195 | } 196 | STY { 197 | set code [lindex $value 0] 198 | set letter [string index $code 0] 199 | set flag [string index $code 1] 200 | 201 | set sty($letter) $flag 202 | append result $wikiStyles($code) 203 | } 204 | LINK { 205 | set value [::markupparser::normalizeSpace $value] 206 | 207 | # Note: with "|" notation, MediaWiki's format 208 | # puts the page name first and the displayed 209 | # text second, so we need to swap them. 210 | set list [split $value "|"] 211 | 212 | if {[llength $list] == 2} { 213 | set value "[lindex $list 1]|[lindex $list 2]" 214 | } 215 | 216 | append result "\[\[$value\]\]" 217 | } 218 | BTN { 219 | # Extract just the button text. 220 | set value [lindex [split $value |] 0] 221 | 222 | append result $value 223 | } 224 | OBJECT { 225 | set obj [getobj $value] 226 | 227 | switch -exact [lindex $obj 0] { 228 | image { 229 | set fname [lindex $obj 1] 230 | array set opts [lindex $obj 2] 231 | 232 | append result "\[\[Image:$fname\]\]" 233 | } 234 | error - 235 | default { 236 | append result "\[!$value!\]" 237 | } 238 | } 239 | } 240 | MACRO { 241 | # There shouldn't be any macros. 242 | append result "\[@$value@\]" 243 | } 244 | } 245 | } 246 | 247 | return $result 248 | } 249 | 250 | 251 | #----------------------------------------------------------------------- 252 | # Utility Procs 253 | 254 | # AddEscapes text 255 | # 256 | # text plain text to be included in HTML output. 257 | # 258 | # Converts &, <, and > to &, <, and >. 259 | 260 | proc nb2mediawiki::AddEscapes {text} { 261 | regsub -all "&" $text {\&} text 262 | regsub -all "<" $text {\<} text 263 | regsub -all ">" $text {\>} text 264 | 265 | return $text 266 | } 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /lib/app_notebook/nbobjects.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # nbobjects.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # This module contains the parser for Notebook "objects", e.g., 10 | # things found in [!...!] markup. 11 | # 12 | # At some point, this might allow some kind of plug-in 13 | # architecture. 14 | # 15 | #----------------------------------------------------------------------- 16 | 17 | # getobj objstring 18 | # 19 | # Parses objstring and returns a list. The first element is the valid 20 | # object type, or "error". The subsequent elements depend on the 21 | # object type. 22 | 23 | proc getobj {objstring} { 24 | # FIRST, it must be a valid list. 25 | if {[catch {lindex $objstring 0} objectType]} { 26 | return [list error "Invalid object: $objstring"] 27 | } 28 | 29 | # NEXT, we only have one kind of object at the moment. 30 | if {$objectType != "image"} { 31 | return [list error "Unrecognized object type: $objstring"] 32 | } 33 | 34 | return [ParseImageObject $objstring] 35 | } 36 | 37 | # ParseImageObject obj 38 | # 39 | # The first token of obj is "image". The second is the path to the 40 | # image file. The remaining elements are options and values. 41 | # 42 | # Returns a list {image path optlist}. The list contains all 43 | # options allowed for images, with values. 44 | 45 | proc ParseImageObject {obj} { 46 | set result [lrange $obj 0 1] 47 | 48 | array set opts { 49 | -width 0 50 | -height 0 51 | -padwidth 0 52 | -text "" 53 | } 54 | 55 | foreach {opt val} [lrange $obj 2 end] { 56 | if {[info exists opts($opt)]} { 57 | if {$opt eq "-text" || 58 | [regexp {^[0-9]+[cimp]?$} $val]} { 59 | set opts($opt) $val 60 | continue 61 | } 62 | } 63 | 64 | return [list error "Invalid image option: $opt $val"] 65 | } 66 | 67 | lappend result [array get opts] 68 | 69 | return $result 70 | } 71 | -------------------------------------------------------------------------------- /lib/app_notebook/notebook.tcl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | # TITLE: 3 | # notebook.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # Wiki-like personal notebook 10 | # 11 | # LICENSE: 12 | # Copyright (C) 2005 by William H. Duquette. This file may 13 | # be used subject to the terms in license.txt. 14 | 15 | #----------------------------------------------------------------------- 16 | # Catch Mac OS X open events at startup. 17 | # 18 | # We aren't really ready for open events until the end of this file; 19 | # however, we call "update" when we popup the splash screen, so we 20 | # might well receive some. The following definition of 21 | # ::tk::mac::OpenDocument just saves any file names we receive so 22 | # that they can be opened later. We will redefine OpenDocument 23 | # at the end of the file to open notebooks properly. 24 | # 25 | # If we aren't running under Aqua, then this is irrelevant. 26 | 27 | set osxDocsToOpen {} 28 | 29 | if {[tk windowingsystem] eq "aqua"} { 30 | proc ::tk::mac::OpenDocument {args} { 31 | set ::osxDocsToOpen $args 32 | } 33 | } 34 | 35 | #----------------------------------------------------------------------- 36 | # Copy Encodings 37 | 38 | set appdir [file dirname [info script]] 39 | 40 | set encodingGlob [file join $appdir .. encoding *] 41 | set encodings [encoding names] 42 | 43 | foreach encodingFile [glob -nocomplain $encodingGlob] { 44 | set encoding [file rootname [file tail $encodingFile]] 45 | if {[lsearch -exact $encodings $encoding] == -1} { 46 | file copy -force $encodingFile [file join $tcl_library encoding] 47 | } 48 | } 49 | 50 | #----------------------------------------------------------------------- 51 | # Version 52 | 53 | # FIXME 54 | set notebookVersion "V2.2.0" 55 | 56 | #----------------------------------------------------------------------- 57 | # Allow notebook to execute scripts. 58 | 59 | if {"-script" == [lindex $argv 0]} { 60 | set script [lindex $argv 1] 61 | set argv [lreplace $argv 0 1] 62 | source [lindex $script] 63 | exit 64 | } 65 | 66 | #------------------------------------------------------------------------- 67 | # Do the splash screen 68 | 69 | # Withdraw the main window; we might not need it at all. We'll pop it 70 | # back up if we do. 71 | wm withdraw . 72 | 73 | toplevel .splash -background white 74 | wm title .splash "Notebook $notebookVersion" 75 | 76 | image create photo splash.gif \ 77 | -file [file join [file dirname [info script]] splash.gif] 78 | 79 | pack [label .splash.image \ 80 | -foreground black \ 81 | -background white \ 82 | -justify center \ 83 | -image splash.gif] 84 | 85 | set sw [winfo screenwidth .splash] 86 | set sh [winfo screenheight .splash] 87 | set rw [winfo reqwidth .splash] 88 | set rh [winfo reqheight .splash] 89 | 90 | set x [expr ($sw - $rw)/2] 91 | set y [expr ($sh - $rh)/2] 92 | 93 | wm geometry .splash +$x+$y 94 | 95 | update 96 | 97 | #----------------------------------------------------------------------- 98 | # Utility functions 99 | 100 | # Display a message. This should only be used for debugging. 101 | proc msg {messageText {icon "error"}} { 102 | tk_messageBox -icon $icon -parent . \ 103 | -title "Notebook Message" \ 104 | -message $messageText \ 105 | -type ok 106 | } 107 | 108 | # Return the current version 109 | proc version {} { 110 | global notebookVersion 111 | 112 | return $notebookVersion 113 | } 114 | 115 | #----------------------------------------------------------------------- 116 | # Other required packages 117 | 118 | set appdir [file dirname [info script]] 119 | 120 | package require gui 1.0 121 | namespace import ::gui::* 122 | 123 | package require markupparser 124 | package require notebookdb 125 | 126 | # Source in the application files 127 | 128 | source [file join $appdir nb2html.tcl] 129 | source [file join $appdir nb2mediawiki.tcl] 130 | source [file join $appdir userprefs.tcl] 131 | source [file join $appdir renderpane.tcl] 132 | source [file join $appdir nbobjects.tcl] 133 | source [file join $appdir pageeditor.tcl] 134 | source [file join $appdir prefs_dialog.tcl] 135 | 136 | namespace import ::app_notebook::prefs 137 | 138 | source [file join $appdir nbactionmanager.tcl] 139 | source [file join $appdir dbmanager.tcl] 140 | source [file join $appdir messagelog.tcl] 141 | source [file join $appdir welcomer.tcl] 142 | source [file join $appdir pageviewer.tcl] 143 | source [file join $appdir helpbrowser.tcl] 144 | namespace import ::app_notebook::HelpBrowser::* 145 | 146 | source [file join $appdir notebookbrowser.tcl] 147 | 148 | # Create images 149 | image create photo ::app_notebook::missing \ 150 | -file [file join $appdir missing.gif] 151 | 152 | #----------------------------------------------------------------------- 153 | # Mainline code 154 | 155 | # Check the arguments 156 | if {[llength $argv] == 0} { 157 | set notebooks {} 158 | } else { 159 | foreach name $argv { 160 | set name [file normalize $name] 161 | lappend notebooks [file join [pwd] $name] 162 | } 163 | } 164 | 165 | # Read the preferences 166 | prefs load 167 | 168 | # Initialize the help browser 169 | helpbrowser [file join $appdir help.nbk] 170 | 171 | # Initialize the message log 172 | messagelog init 173 | 174 | # Copy the extensions path to the auto_path, and 175 | # try to load the extension packages. 176 | set extpath [string trim [prefs get extpath]] 177 | if {$extpath ne ""} { 178 | lappend auto_path $extpath 179 | } 180 | 181 | if {[catch {package require Img 1.3} result]} { 182 | messagelog logmessage "Img Extension: not loaded" 183 | } else { 184 | messagelog logmessage "Img Extension: loaded" 185 | } 186 | 187 | # Add the initial OS X file names, if any 188 | set notebooks [concat $notebooks $osxDocsToOpen] 189 | 190 | # Create the initial Notebook browsers 191 | notebookbrowser appdir $appdir 192 | 193 | if {[llength $notebooks] == 0} { 194 | welcomer .%AUTO% 195 | destroy .splash 196 | } else { 197 | destroy .splash 198 | foreach notebook $notebooks { 199 | try { 200 | if {[file extension $notebook] eq ""} { 201 | append notebook ".nbk" 202 | } 203 | 204 | set db [dbmanager openfile $notebook] 205 | notebookbrowser .%AUTO% -db $db 206 | } trap notebookdb::loaderror {msg} { 207 | welcomer .%AUTO% -errormsg $msg 208 | } 209 | } 210 | } 211 | 212 | #----------------------------------------------------------------------- 213 | # Redefine ::tk::mac::OpenDocument 214 | # 215 | # See the discussion at the top of this file. 216 | # 217 | # This proc will receive OS X open events after the application has 218 | # gotten initialized, and will open new windows for the specified 219 | # notebook files. 220 | 221 | if {[tk windowingsystem] eq "aqua"} { 222 | proc ::tk::mac::OpenDocument {args} { 223 | foreach name $args { 224 | try { 225 | set newdb [dbmanager openfile $name] 226 | } trap notebookdb::loaderror {msg} { 227 | throw USER $msg 228 | } 229 | 230 | if {$newdb eq ""} { 231 | error "Cancelled." {} USER 232 | } 233 | 234 | notebookbrowser .%AUTO% -db $newdb 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /lib/app_notebook/pageviewer.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # pageviewer.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # A viewer for browsing Notebook pages. 10 | # 11 | # LICENSE: 12 | # Copyright (C) 2005 by William H. Duquette. This file may 13 | # be used subject to the terms in license.txt. 14 | # 15 | #----------------------------------------------------------------------- 16 | 17 | snit::widget pageviewer { 18 | #------------------------------------------------------------------- 19 | # Creation Options 20 | # 21 | # These options should only be set at creation time. This isn't 22 | # enforced, so be careful 23 | 24 | # 1 if we're read-only, and 0 otherwise. 25 | option -readonly 0 26 | 27 | # A command to use for outputting messages. The message will be 28 | # appended. 29 | option -messagecommand {} 30 | 31 | # The notebook file directory, used as the root for relative paths. 32 | option -dbdir {} 33 | 34 | #------------------------------------------------------------------- 35 | # Other options 36 | 37 | # A command that's called when the displayed page changes. The 38 | # command gets one argument, the page name. 39 | option -pagecommand {} 40 | 41 | # A command that's called when we try to display a page that 42 | # doesn't exist. 43 | option -unknowncommand {} 44 | 45 | #------------------------------------------------------------------- 46 | # Components 47 | 48 | # The toplevel window (the notebookbrowser) 49 | variable top 50 | 51 | # The renderpane is the component that actually displays the 52 | # rendered page. 53 | variable rp 54 | 55 | #-------------------------------------------------------------------- 56 | # Other variables 57 | 58 | # The history list is a list of {PageName PagePos} pairs. 59 | variable history {} 60 | 61 | # The histPos is the index of the currently displayed page in the 62 | # history list. It's initially -1 because there's nothing in the 63 | # list. 64 | variable histPos -1 65 | 66 | # A list of recently displayed pages. Each page appears only once, 67 | # regardless of how often (and where) it appears in the history list. 68 | # There will be ten pages listed, maximum; no more than one of them 69 | # are guaranteed to still exist. 70 | variable recentPages {} 71 | 72 | #-------------------------------------------------------------------- 73 | # Constructor 74 | 75 | constructor {args} { 76 | # Get the toplevel widget 77 | set top [winfo toplevel $win] 78 | 79 | # As yet we have no option delegation, so go ahead and get the 80 | # args 81 | $self configurelist $args 82 | 83 | # Create the renderpane. 84 | install rp using renderpane $win.rp \ 85 | -yscrollcommand "$win.scroll set" \ 86 | -querycommand [list $top pageexists] \ 87 | -titlecommand [mymethod TitleHandler] \ 88 | -linkcommand [mymethod LinkHandler] \ 89 | -buttoncommand [mymethod ButtonHandler] \ 90 | -macroerrorcommand [mymethod MacroErrorHandler] \ 91 | -messagecommand $options(-messagecommand) \ 92 | -dbdir $options(-dbdir) 93 | 94 | scrollbar $win.scroll \ 95 | -command "$win.rp yview" 96 | 97 | # Pack them in. 98 | pack $win.scroll -side right -fill y -expand false 99 | pack $rp -side top -fill both -expand true 100 | 101 | #--------------------------------------------------------------- 102 | # User Interactions 103 | 104 | # Add the pageviewer window itself to the renderpane's bindtags, 105 | # so that keys bound to the pageviewer will take effect when the 106 | # renderpane has focus 107 | set tags [bindtags $rp] 108 | lappend tags $win 109 | bindtags $rp $tags 110 | 111 | # Go to the previous page when is pressed. 112 | bind $rp [list $self backpage] 113 | 114 | # User Menu. 115 | menu $rp.user -tearoff no -postcommand [mymethod PostUserMenu] 116 | 117 | $rp.user add command -label "User Menu" 118 | $rp.user add separator 119 | $rp.user add command -label "Dummy" 120 | 121 | bind $rp [list tk_popup $rp.user %X %Y 2] 122 | bind $rp <3> [list tk_popup $rp.user %X %Y 2] 123 | 124 | # Recent Pages Menu. 125 | menu $rp.recent -tearoff no -postcommand [mymethod PostRecent] 126 | 127 | $rp.recent add command -label "Recent Pages" 128 | $rp.recent add separator 129 | $rp.recent add command -label "Dummy" 130 | 131 | bind $rp [list tk_popup $rp.recent %X %Y 2] 132 | 133 | #--------------------------------------------------------------- 134 | # Prepare to receive preferences events; unregister on 135 | # destroy. 136 | prefs register $selfns [mymethod UsePreferences] 137 | } 138 | 139 | destructor { 140 | catch {prefs unregister $selfns} 141 | } 142 | 143 | method UsePreferences {} { 144 | # Update the display when the preferences change. 145 | $self showpage 146 | } 147 | 148 | #------------------------------------------------------------------- 149 | # Public Methods 150 | 151 | delegate method searchfor to rp 152 | 153 | # Returns the name of the currently displayed page, or "" if none. 154 | # Note that during a deletion or page renaming the named page 155 | # is not guaranteed to exist. 156 | method current {} { 157 | return [lindex $history $histPos 0] 158 | } 159 | 160 | # Tells the pageviewer to take the keyboard focus. 161 | # If -friendly, we take the focus only if our toplevel already 162 | # has it (e.g., we take the focus back from the search box). 163 | method focus {{option {}}} { 164 | set focusWin [focus] 165 | 166 | if {$focusWin eq ""} { 167 | return 168 | } 169 | 170 | if {$option ne "-friendly" || 171 | [winfo toplevel $win] eq [winfo toplevel $focusWin]} { 172 | focus $rp 173 | } 174 | } 175 | 176 | # Shows the named page. The -unknowncommand is called if the 177 | # page doesn't exist. 178 | method showpage {{name ""}} { 179 | # FIRST, remember the position for the page we're showing now. 180 | if {$histPos >= 0} { 181 | lset history $histPos 1 [$self GetPos] 182 | } 183 | 184 | # NEXT, default to the current page. 185 | if {$name eq ""} { 186 | set name [$self current] 187 | 188 | if {$name eq ""} { 189 | error "no current page." 190 | } 191 | } 192 | 193 | # NEXT, if the page doesn't exist, call the -unknowncommand. 194 | if {![$top pageexists $name]} { 195 | if {$options(-unknowncommand) ne {}} { 196 | set command $options(-unknowncommand) 197 | lappend command $name 198 | uplevel \#0 $command 199 | } else { 200 | $return -code error -errorcode USER "No such page: '$name'" 201 | } 202 | 203 | return 204 | } 205 | 206 | # NEXT, save the new page in the history list. When we show 207 | # a new page, any pages forward of our position go away. 208 | set history [lrange $history 0 $histPos] 209 | lappend history [list [$top pagename $name] 0] 210 | incr histPos 211 | 212 | # NEXT, display it. 213 | $self DisplayPage 214 | } 215 | 216 | # Steps back one page, if possible 217 | method backpage {} { 218 | # FIRST, remember the position for the page we're showing now. 219 | if {$histPos >= 0} { 220 | lset history $histPos 1 [$self GetPos] 221 | } 222 | 223 | # NEXT, Look back through the history list for a page that exists, 224 | # and is also different than the current page. 225 | set current [lindex $history $histPos 0] 226 | set pos [expr {$histPos - 1}] 227 | 228 | while {$pos >= 0} { 229 | set candidate [lindex $history $pos 0] 230 | 231 | if {[$top pageexists $candidate]} { 232 | 233 | if {![string equal -nocase $candidate $current]} { 234 | break 235 | } 236 | } 237 | 238 | incr pos -1 239 | } 240 | 241 | if {$pos < 0} { 242 | bell 243 | return 244 | } 245 | 246 | set histPos $pos 247 | 248 | $self DisplayPage 249 | } 250 | 251 | # Steps forward one page, if possible 252 | method forwardpage {} { 253 | # FIRST, remember the position for the page we're showing now. 254 | if {$histPos >= 0} { 255 | lset history $histPos 1 [$self GetPos] 256 | } 257 | 258 | # NEXT, Look forward through the history list for a page that exists, 259 | # and is also different than the current page. 260 | set current [lindex $history $histPos 0] 261 | set pos [expr {$histPos + 1}] 262 | set len [llength $history] 263 | 264 | while {$pos < $len} { 265 | set candidate [lindex $history $pos 0] 266 | 267 | if {[$top pageexists $candidate]} { 268 | 269 | if {![string equal -nocase $candidate $current]} { 270 | break 271 | } 272 | } 273 | 274 | incr pos 275 | } 276 | 277 | if {$pos >= $len} { 278 | bell 279 | return 280 | } 281 | 282 | set histPos $pos 283 | 284 | $self DisplayPage 285 | } 286 | 287 | # Shows the page from the beginning of the Recent Pages list, if it exists. 288 | method cyclerecent {} { 289 | foreach id $recentPages { 290 | if {[$top pageexists $id]} { 291 | $self showpage $id 292 | break 293 | } 294 | } 295 | } 296 | 297 | #------------------------------------------------------------------- 298 | # Private Methods 299 | 300 | # Display the current page 301 | method DisplayPage {} { 302 | # FIRST, Get the current page specs 303 | set name [$top pagename [lindex $history $histPos 0]] 304 | set ypos [lindex $history $histPos 1] 305 | 306 | # NEXT, add the name to the list of recent pages 307 | $self SaveRecentPage $name 308 | 309 | # NEXT, Expand the requested page and display it. 310 | set pagetext [$top pageexpand $name] 311 | 312 | $rp render $name $pagetext 313 | $rp searchfor [$top searchtext] 314 | 315 | # NEXT, give the renderpane the focus so that we don't 316 | # need to click in it. 317 | $self focus -friendly 318 | 319 | $self setpos $ypos 320 | 321 | # NEXT, notify those concerned. 322 | if {$options(-pagecommand) ne {}} { 323 | set command $options(-pagecommand) 324 | lappend command $name 325 | uplevel \#0 $command 326 | } 327 | } 328 | 329 | # Save the specified page to the recent pages list, removing 330 | # any previous entry with the same name. Names are stored 331 | # in lower case. No more than ten names are stored. 332 | method SaveRecentPage {name} { 333 | set name [string tolower $name] 334 | 335 | set ndx [lsearch -exact $recentPages $name] 336 | 337 | if {$ndx != -1} { 338 | set recentPages [lreplace $recentPages $ndx $ndx] 339 | } 340 | 341 | lappend recentPages $name 342 | 343 | # Cut down back to ten, if need be 344 | if {[llength $recentPages] == 11} { 345 | set recentPages [lreplace $recentPages 0 0] 346 | } 347 | } 348 | 349 | 350 | # Get the y-position of the text at the top of the window. 351 | method GetPos {} { 352 | lindex [$rp yview] 0 353 | } 354 | 355 | # Set the y-position of the text at the top of the window. 356 | method setpos {ypos} { 357 | $rp yview moveto $ypos 358 | } 359 | 360 | # Called when the user clicks on the title text. 361 | method TitleHandler {title} { 362 | $top showsearch $title 363 | } 364 | 365 | # Called when the user clicks on a link; shows the linked page. 366 | method LinkHandler {linkName} { 367 | $self showpage $linkName 368 | } 369 | 370 | # Called when the user clicks on a magic button; calls the button's script 371 | method ButtonHandler {script} { 372 | $top evaluser $script 373 | } 374 | 375 | # Finds the macro error text and displays the error message. 376 | method MacroErrorHandler {macro} { 377 | $top evaluser $macro 378 | # TBD: If macros are disabled, then clicking on this won't 379 | # display an error. We need to handle this somehow. 380 | } 381 | 382 | # Called before the Recent Pages context menu is popped up. 383 | method PostRecent {} { 384 | # FIRST, delete any existing items. 385 | $rp.recent delete 2 end 386 | 387 | # NEXT, add items for the recent items. 388 | foreach id $recentPages { 389 | if {[$top pageexists $id]} { 390 | set name [$top pagename $id] 391 | $rp.recent add command \ 392 | -label $name \ 393 | -command [list $self showpage $name] 394 | } 395 | } 396 | } 397 | 398 | # Called before the User Menu context menu is popped up. 399 | method PostUserMenu {} { 400 | # FIRST, delete any existing items. 401 | $rp.user delete 2 end 402 | 403 | # NEXT, retrieve the user's choices 404 | set items [$top evaluser usermenu] 405 | 406 | # NEXT, add items for the user's choices. 407 | foreach {label command} $items { 408 | if {$label eq "separator"} { 409 | $rp.user add separator 410 | } else { 411 | $rp.user add command \ 412 | -label $label \ 413 | -command [list $top evaluser $command] 414 | } 415 | } 416 | } 417 | } 418 | 419 | 420 | 421 | -------------------------------------------------------------------------------- /lib/app_notebook/pkgIndex.tcl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | # TITLE: 3 | # pkgIndex.tcl 4 | # 5 | # PROJECT: 6 | # notebook: Your project description 7 | # 8 | # DESCRIPTION: 9 | # app_notebook(n): pkgIndex file 10 | # 11 | # Generated by Quill 12 | # 13 | #------------------------------------------------------------------------- 14 | 15 | # -quill-ifneeded-begin DO NOT EDIT BY HAND 16 | package ifneeded app_notebook 2.2.0 [list source [file join $dir pkgModules.tcl]] 17 | # -quill-ifneeded-end 18 | -------------------------------------------------------------------------------- /lib/app_notebook/pkgModules.tcl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------- 2 | # TITLE: 3 | # pkgModules.tcl 4 | # 5 | # PROJECT: 6 | # notebook: Your project description 7 | # 8 | # DESCRIPTION: 9 | # app_notebook(n): Package Loader 10 | # 11 | # Generated by Quill 12 | # 13 | #------------------------------------------------------------------------- 14 | 15 | #------------------------------------------------------------------------- 16 | # Provide Package 17 | 18 | # -quill-provide-begin DO NOT EDIT BY HAND 19 | package provide app_notebook 2.2.0 20 | # -quill-provide-end 21 | 22 | #------------------------------------------------------------------------- 23 | # Require Packages 24 | 25 | # -quill-require-begin INSERT PACKAGE REQUIRES HERE 26 | package require snit 2.3 27 | # -quill-require-end 28 | 29 | #------------------------------------------------------------------------- 30 | # Get the library directory 31 | 32 | namespace eval ::app_notebook:: { 33 | variable library [file dirname [info script]] 34 | } 35 | 36 | source [file join $::app_notebook::library main.tcl] 37 | -------------------------------------------------------------------------------- /lib/app_notebook/splash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/lib/app_notebook/splash.gif -------------------------------------------------------------------------------- /lib/app_notebook/userprefs.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # userprefs.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # Notebook application user preferences. 10 | # 11 | # LICENSE: 12 | # Copyright (C) 2002,2003,2004,2005 by William H. Duquette. 13 | # This file may be used subject to the terms in license.txt. 14 | 15 | package require Tk 16 | 17 | #----------------------------------------------------------------------- 18 | # UserPrefs 19 | 20 | # prefs could be implemented as an ensemble: a type with no instances but 21 | # only typemethods. For historical reasons, though, it's been 22 | # implemented as a normal type with just one instance. 23 | snit::type ::app_notebook::prefsmanager { 24 | # Preferences array 25 | variable prefs 26 | 27 | # Observers array. index is observer name, value is command to execute. 28 | variable observers 29 | 30 | # System font lists 31 | variable fonts 32 | 33 | # Pseudo-preferences 34 | # 35 | # Pseudo-preferences aren't saved to the preferences file. 36 | variable pseudoprefs { 37 | defaultscaling 38 | } 39 | 40 | constructor {} { 41 | # Scaling Preference 42 | set prefs(defaultscaling) [tk scaling] 43 | set prefs(scaling) [tk scaling] 44 | 45 | # Font preferences 46 | set prefs(bodytext) {Times 12} 47 | set prefs(monotext) {Courier 10} 48 | set prefs(header1text) {Helvetica 16 bold} 49 | set prefs(header2text) {Helvetica 14 bold} 50 | set prefs(header3text) {Helvetica 12 bold} 51 | set prefs(titletext) {Helvetica 20} 52 | set prefs(smalltext) {Helvetica 8} 53 | 54 | # Color preferences 55 | set prefs(editorfg) black 56 | set prefs(editorbg) white 57 | set prefs(normalfg) black 58 | set prefs(normalbg) white 59 | set prefs(prefg) black 60 | set prefs(prebg) white 61 | set prefs(tclfg) black 62 | set prefs(tclbg) white 63 | set prefs(titlefg) blue 64 | set prefs(titlebg) white 65 | set prefs(linkfg) blue 66 | set prefs(linkbg) white 67 | set prefs(buttonfg) magenta 68 | set prefs(buttonbg) white 69 | set prefs(warningfg) red 70 | set prefs(warningbg) white 71 | set prefs(searchfg) white 72 | set prefs(searchbg) red 73 | 74 | # Editor preferences 75 | set prefs(tabwidth) 4 76 | set prefs(autowrap) 1 77 | set prefs(wrapcolumn) 74 78 | set prefs(autoindent) 1 79 | 80 | # Tcl/Tk Preferences 81 | set prefs(extpath) {} 82 | 83 | # Miscellaneous Options 84 | set prefs(includetour) 1 85 | set prefs(editbottom) 0 86 | set prefs(displaydirectives) 0 87 | set prefs(silentcreation) 0 88 | set prefs(showsidebar) 0 89 | set prefs(incrementalsearch) 1 90 | set prefs(contentsearch) 1 91 | 92 | # Initialize font lists 93 | set fonts(all) {} 94 | set fonts(mono) {} 95 | set fonts(prop) {} 96 | } 97 | 98 | # Register observer. obj is the observing object; command is the 99 | # command to execute when preferences are saved. 100 | method register {obj command} { 101 | set observers($obj) $command 102 | } 103 | 104 | # Unregister observer. obj is the observing object. 105 | method unregister {obj} { 106 | unset observers($obj) 107 | } 108 | 109 | # Set a preference variable's value 110 | method set {var value} { 111 | # HACK: handle translation of "headertext" to "header[123]text" 112 | # This handles a preferences change in V2.1.1 113 | if {$var eq "headertext"} { 114 | $self set header1text $value 115 | $self set header2text $value 116 | $self set header3text $value 117 | return 118 | } 119 | 120 | # Retain and save unknown preferences; this means they can 121 | # switch between this version and a later version with more 122 | # preferences and not have any trouble. 123 | set prefs($var) $value 124 | } 125 | 126 | # Get a preference variable's value 127 | method get {var} { 128 | if {[info exists prefs($var)]} { 129 | return $prefs($var) 130 | } else { 131 | error "Unknown prefs variable: '$var'" 132 | } 133 | } 134 | 135 | # Load the preferences from disk. The file name is system-dependent. 136 | method load {} { 137 | set fname [PrefFileName] 138 | 139 | if {[file exists $fname]} { 140 | try { 141 | source [PrefFileName] 142 | tk scaling $prefs(scaling) 143 | } on error errmsg { 144 | msg "Error in user preferences file '$fname'; preferences might not be set. The error was: $errmsg" 145 | } 146 | } 147 | } 148 | 149 | # Save the preferences and notify observers. 150 | method save {} { 151 | try { 152 | set f [open [PrefFileName] w] 153 | 154 | puts $f "# Notebook [version] Preferences File" 155 | foreach name [array names prefs] { 156 | if {[lsearch -exact $pseudoprefs $name] != -1} { 157 | continue 158 | } 159 | puts $f [list prefs set $name $prefs($name)] 160 | } 161 | puts $f "# End of preferences" 162 | close $f 163 | } on error errmsg { 164 | msg "Could not save user preferences to [PrefFileName]: $errmsg" 165 | } 166 | 167 | $self NotifyObservers 168 | } 169 | 170 | # Pop up the preferences dialog; create it if it hasn't yet been 171 | # seen. 172 | method dialog {} { 173 | # Create the dialog, if one doesn't exist. 174 | if {"" == [info commands .prefsdialog]} { 175 | ::app_notebook::prefsdialog .prefsdialog 176 | } 177 | 178 | # Make sure it's visible. 179 | .prefsdialog show 180 | } 181 | 182 | # Return a list of monospace fonts defined by the system. 183 | method monofonts {} { 184 | $self LoadFonts 185 | return $fonts(mono) 186 | } 187 | 188 | # Return a list of proportional fonts defined by the system. 189 | method propfonts {} { 190 | $self LoadFonts 191 | return $fonts(prop) 192 | } 193 | 194 | # Return a list of *all* fonts defined by the system. 195 | method fonts {} { 196 | $self LoadFonts 197 | return $fonts(all) 198 | } 199 | 200 | #----------------------------------------------------------------------- 201 | # Private methods and procs 202 | 203 | # Determine the preference file name based on the platform 204 | proc PrefFileName {} { 205 | global tcl_platform 206 | global env 207 | 208 | # This is the only way to reference Tcl/Tk Aqua as opposed to 209 | # the X11 version 210 | if {[tk windowingsystem] eq "aqua"} { 211 | return [file join $env(HOME) "Library/Preferences/NotebookPrefs"] 212 | } 213 | 214 | switch $tcl_platform(platform) { 215 | "macintosh" { 216 | # This is classic Mac only 217 | return [file join $env(PREF_FOLDER) "Notebook Preferences"] 218 | } 219 | "windows" { 220 | if {[info exists env(HOME)]} { 221 | return [file join $env(HOME) "notebook.cfg"] 222 | } else { 223 | return [file join "C:/" "notebook.cfg"] 224 | } 225 | } 226 | "unix" - 227 | default { 228 | if {[info exists env(DOTDIR)]} { 229 | return "$env(DOTDIR)/.notebookrc" 230 | } else { 231 | return "$env(HOME)/.notebookrc" 232 | } 233 | } 234 | } 235 | } 236 | 237 | # Notify all registered observers that preferences have changed. 238 | method NotifyObservers {} { 239 | foreach name [array names observers] { 240 | uplevel #0 $observers($name) 241 | } 242 | } 243 | 244 | # Load system fonts, if they've not already been loaded. 245 | method LoadFonts {} { 246 | if {[llength $fonts(all)] != 0} { 247 | return 248 | } 249 | 250 | # Get all fonts. 251 | set fontnames [font families] 252 | 253 | # Tcl defines these names on all platforms, so add them to list 254 | # if they aren't already there. 255 | foreach name {Courier Times Helvetica} { 256 | if {[lsearch $fontnames $name] == -1} { 257 | lappend fontnames $name 258 | } 259 | } 260 | 261 | # On Tcl/Tk Aqua there are some problematic font names; 262 | # they all begin with non-alpha characters. 263 | foreach font $fontnames { 264 | set initial [string index $font 0] 265 | if {![string is alpha $initial]} { 266 | continue 267 | } 268 | 269 | lappend fonts(all) $font 270 | } 271 | 272 | set fonts(all) [lsort $fonts(all)] 273 | 274 | # Create lists of monospace and proportional fonts. 275 | foreach font $fonts(all) { 276 | if {[font metrics [list $font] -fixed]} { 277 | lappend fonts(mono) $font 278 | } else { 279 | lappend fonts(prop) $font 280 | } 281 | } 282 | } 283 | } 284 | 285 | # Create one instance of this object. 286 | ::app_notebook::prefsmanager create ::app_notebook::prefs 287 | 288 | namespace eval ::app_notebook:: { 289 | namespace export prefs 290 | } 291 | 292 | 293 | -------------------------------------------------------------------------------- /lib/app_notebook/welcomer.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # welcomer.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # This component is created if Notebook is invoked 10 | # with no file name. It welcomes the user to Notebook and allows 11 | # them to open or create a notebook file. As such, it's a baby 12 | # version of a notebookbrowser. I'll duplicate the action and 13 | # menu code for now; but ultimately that will need to be fixed. 14 | # 15 | # LICENSE: 16 | # Copyright (C) 2003 by William H. Duquette. This file may 17 | # be used subject to the terms in license.txt. 18 | # 19 | #----------------------------------------------------------------------- 20 | 21 | snit::widget welcomer { 22 | # This is a toplevel window 23 | hulltype toplevel 24 | delegate option * to hull 25 | 26 | #------------------------------------------------------------------- 27 | # Options 28 | 29 | # An error stating why the welcomer was used. 30 | option -errormsg "" 31 | 32 | #------------------------------------------------------------------- 33 | # Instance Variables 34 | 35 | variable greeting { 36 | |Click on the highlighted text to: 37 | | 38 | |* [%Open|open-notebook%] an existing notebook. 39 | |* [%Create|new-notebook%] a new notebook 40 | |* [%Browse|help-introduction%] the on-line help. 41 | | 42 | |If you've never used Notebook before, then you'll want to 43 | |[%create|new-notebook%] a new notebook. It will be created 44 | |with an initial set of pages that will tell you 45 | |the things you need to know to get started. 46 | } 47 | 48 | #------------------------------------------------------------------- 49 | # Components 50 | 51 | variable am ;# The actionmanager 52 | variable status ;# The statusentry 53 | variable render ;# The renderpane 54 | 55 | #------------------------------------------------------------------- 56 | # Constructor 57 | 58 | constructor {args} { 59 | #--------------------------------------------------------------- 60 | # Preliminaries 61 | 62 | # FIRST, withdraw the window, so they don't see it building. 63 | wm withdraw $win 64 | 65 | # NEXT, set the window title 66 | wm title $win "Welcome to Notebook" 67 | 68 | # NEXT, Go ahead and configure the widget options, if any; none are 69 | # delegated to anything but the hull. 70 | $self configurelist $args 71 | 72 | # NEXT, prepare for window closing 73 | windowmanager register $win 74 | 75 | #--------------------------------------------------------------- 76 | # Create Components 77 | 78 | # FIRST, create the actionmanager and define the actions. 79 | install am using nbactionmanager %AUTO% \ 80 | -toplevel $win \ 81 | -windowtype welcomer \ 82 | -errorcommand [mymethod ErrorHandler] 83 | 84 | # NEXT, create the statusentry. It provides a GUI for displaying 85 | # status and entering arguments. 86 | install status using statusentry $win.status \ 87 | -errorcommand [mymethod ErrorHandler] \ 88 | -messagecommand [list messagelog logmessage] 89 | 90 | # NEXT, create the renderpane for the welcome message 91 | install render using renderpane $win.render \ 92 | -buttoncommand [mymethod ButtonHandler] \ 93 | -width 45 \ 94 | -height 20 95 | 96 | # Next, pack the components 97 | pack $status -side bottom -fill x -expand false 98 | pack $render -side top -fill both -expand true 99 | 100 | #--------------------------------------------------------------- 101 | # Render the welcome message 102 | 103 | regsub -all -line {^\s*\|} [string trim $greeting] {} greeting 104 | $render render "Welcome to Notebook!" $greeting 105 | 106 | #--------------------------------------------------------------- 107 | # Final Preparations 108 | 109 | # NEXT, update the state of all actions. 110 | $am updatestate 111 | 112 | # NEXT, The GUI is fully created; display it! 113 | # 114 | # We call "update" to make sure that everything has taken its final 115 | # size before we deiconify. 116 | update 117 | wm deiconify $win 118 | 119 | # NEXT, if there's an error message, display it. 120 | if {$options(-errormsg) ne ""} { 121 | $status msg $options(-errormsg) 122 | } 123 | } 124 | 125 | #------------------------------------------------------------------- 126 | # Private Methods 127 | 128 | method ButtonHandler {action} { 129 | $am invoke $action 130 | } 131 | 132 | # Called when the welcomer's work is done. 133 | method Withdraw {} { 134 | wm withdraw $win 135 | 136 | # TBD: Is the "after 1" really necessary? 137 | after 1 [list windowmanager destroy $win] 138 | } 139 | 140 | # Handles errors from the statusentry and actionmanager components. 141 | method ErrorHandler {msg einfo ecode} { 142 | if {$ecode eq "USER"} { 143 | $status msg $msg 144 | bell 145 | } else { 146 | messagelog logerror $msg $einfo $ecode 147 | 148 | $status msg "$msg -- go to Message Log for more." 149 | } 150 | } 151 | 152 | 153 | #------------------------------------------------------------------- 154 | # Delegate Action Handlers 155 | 156 | delegate method about-notebook to am 157 | delegate method close-window to am 158 | delegate method copy-string to am 159 | delegate method cut-string to am 160 | delegate method edit-preferences to am 161 | delegate method help-on to am 162 | delegate method help-introduction to am 163 | delegate method help-index to am 164 | delegate method help-on-actions to am 165 | delegate method help-on-commands to am 166 | delegate method help-on-markup to am 167 | delegate method notebook-license to am 168 | delegate method notebook-release-notes to am 169 | delegate method paste-string to am 170 | delegate method redo-change to am 171 | delegate method request-action to am 172 | delegate method undo-change to am 173 | delegate method show-version to am 174 | 175 | #------------------------------------------------------------------- 176 | # Action Handlers 177 | # 178 | # These methods are registered as actions with the actionmanager 179 | # component. Actions can be invoked by typing their names into the 180 | # statusentry widget, or programmatically; they can get input 181 | # from the user via the statusentry widget. 182 | # 183 | # When actions are invoked programmatically, none or all of their 184 | # arguments can be specified programmatically; the action will prompt 185 | # for the remaining arguments. 186 | # 187 | # When called programmatically, the action's state is NOT checked. 188 | # Thus, every action must explicitly check the same conditions as 189 | # its -statecommand, giving a good USER error if they are not met. 190 | # 191 | # Every action has a handler with the same name. In some cases, 192 | # this handler implements the entire action. For actions that have 193 | # arguments, it's usual to implement the handler as two methods. 194 | # The first has the usual name, and will do the following: 195 | # 196 | # * Require any conditions for execution of the action that do not 197 | # depend on the argument values. 198 | # 199 | # * Request the arguments, passing control to the second command when 200 | # they've been entered. 201 | # 202 | # * The second method simply takes the arguments and does what's 203 | # necessary. The second method is not intended to be called by 204 | # anyone but the first method; it's called "Do-", 205 | # and should follow immediately after the first method. 206 | 207 | 208 | # Action: new-notebook 209 | # 210 | # Creates a new notebook file and opens it, prompting for the file 211 | # name 212 | method new-notebook {} { 213 | $am new-notebook 214 | 215 | # On success the welcomer isn't needed anymore. 216 | $self Withdraw 217 | } 218 | 219 | # Action: open-notebook 220 | # 221 | # Opens a notebook file in the page browser, replacing the 222 | # existing notebook. 223 | 224 | method open-notebook {} { 225 | $am open-notebook 226 | 227 | # On success the welcomer isn't needed anymore. 228 | $self Withdraw 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /lib/combobox/ANNOUNCE.txt: -------------------------------------------------------------------------------- 1 | Combobox 2.3 release announcement 2 | September 30, 2003 3 | 4 | This is to announce version 2.3 of my pure Tcl combobox widget. This 5 | version includes the following new features from version 2.2 6 | 7 | * added -buttonbackground option 8 | * added -listvar option 9 | * tweaked packing order of internal widgets, which improves the 10 | resize behavior a bit (thanks to Oliver Bienert for the suggestion) 11 | * exposed "subwidget" command (eg: .combobox subwidget entry) 12 | 13 | Where to get it: 14 | 15 | You can download combobox version 2.3 from the following URL: 16 | 17 | http://www.purl.org/net/oakley/tcl/combobox/index.html 18 | 19 | 20 | Basic features: 21 | 22 | * written in pure tcl; no megawidget or OO extensions required 23 | 24 | * platform independent, and works for all versions of tk >= 8.0 25 | 26 | * self-contained and portable; all the code is in one file 27 | 28 | * API is similar to standard Tk widgets, with subcommands 29 | such as configure, cget, insert, etc. 30 | 31 | * keyboard navigation 32 | 33 | * emulates the look and feel of a Windows combobox 34 | 35 | * completely, totally, free. I retain copyright but you are 36 | free to use the code however you see fit. Don't be mean. 37 | 38 | -------------------------------------------------------------------------------- /lib/combobox/CHANGES.txt: -------------------------------------------------------------------------------- 1 | Changes to combobox.tcl and related files: 2 | 3 | ### version 2.3: 4 | 5 | * added -buttonbackground options 6 | * added -listvar option 7 | * tweaked packing order of internal widgets, which improves the 8 | resize behavior a bit (thanks to Oliver Bienert for the suggestion) 9 | * exposed "subwidget" command (eg: .combobox subwidget entry) 10 | * made code which is called when the widget is destroyed a bit more 11 | robust; someone reported that their wish process would sometimes 12 | crash while trying to destroy a combobox widget and this fix made 13 | that crash 14 | 15 | ### version 2.2.2: 16 | 17 | * fixed bug where dropdown list would appear behind the main 18 | window on some window managers. 19 | 20 | ### version 2.2.1: 21 | 22 | * fixed case where mousewheel binding caused problems with tk 8.0 23 | * fixed bug when pressing return with dropdown list visible but 24 | nothing is selected 25 | 26 | ### version 2.2: 27 | 28 | * added support for tk 8.4 (changed use of tk private commands) 29 | * new option -dropdownwidth to control the width of the dropdown menu 30 | * new options -disabledforeground, -disabledbackground 31 | * added support for using the mousewheel in the dropdown list 32 | * finally added documentation for the selection subcommand. For four 33 | years now I've been saying "to be added later". It's later. 34 | 35 | ### version 2.1: 36 | 37 | * added -opencommand 38 | * included documentation for the open and close commands 39 | * fixed problem for tk 8.2 where the dropdown list was always behind 40 | the parent window. 41 | 42 | ### version 2.0: 43 | 44 | * fixed bug when attempting to get the value of the option -value 45 | before a value has been set. 46 | 47 | ### version 2.0b2: 48 | 49 | * fixed bug with non-editable comboboxes. Sometimes (or was it 50 | all the time?), if you selected a value from the dropdown list 51 | of a non-editable combobox the value wasn't being set properly. 52 | 53 | 54 | ### version 2.0b1: 55 | 56 | * option shorthand is now supported (eg: -val instead of -value) 57 | * option synonyms are now supported (eg: -bd instead of -borderwidth) 58 | * added the commands select and curselection 59 | * added option database support 60 | * handling of -textvariable is much more straight-forward. Instead of 61 | lots of internal wrangling, I merely assign the variable to the 62 | -textvariable attribute of the entry widget 63 | * lots of comments, and a wee bit more conformance to Scriptics' 64 | style guidelines 65 | 66 | 67 | ### version 1.07: 68 | 69 | * any pre-existing grab is now restored when the 70 | dropdown listbox is closed. 71 | * it should now be possible to set your own bindings 72 | on the combobox widget and have it work the way 73 | you expect. However, the default bindtags for the 74 | widget probably *aren't* what you expect. 75 | * keyboard traversal / focus management is much better 76 | now. Shift-tab seems to do the right thing without 77 | resorting to any hackery. 78 | * fixed bug where the down arrow wouldn't cause the a 79 | dropdown list to display. 80 | * added support for -highlightcolor 81 | * added the "subwidget" command, to allow you to get at some 82 | of the internal widget paths. I would prefer not to have 83 | to expose the underpinnings of this widget, but I think 84 | some advanced users may need this. 85 | * modified the example to show how one can add a list of items 86 | at once, and to include -highlightthickness 87 | * added support for -xscrollcommand, and added a test case in 88 | test.tcl for this option 89 | * man page has been changed to more accurately describe when 90 | the -command is called. These rules have changed slightly 91 | from previous versions. 92 | * modified how and where the dropdown lists pops up. In 93 | theory it should handle virtual desktops better. Also, 94 | for obscenely large lists (ie: bigger than will fit on 95 | the screen) we try to fit it the best we can. It will 96 | either pop up above or below the entry widget depending 97 | on which side gives us the largest list, and will be 98 | trimmed to start or stop at the edges of the screen. 99 | * added the list command to the man page 100 | 101 | Thanks to John Jackson and Todd Helfter of the Purdue University 102 | Computer Center for the patch to handle virtual window managers 103 | 104 | ### version 1.06: 105 | 106 | * dropdown list scrollbar is now "smart", and will only appear if the 107 | number of elements in the list is larger than the -height (or -maxheight, 108 | if -height is zero) of the widget 109 | * slightly different bitmap for the button, courtesy of Martin M. Hunt 110 | * slight modification to the computation of the height of the dropdown 111 | list. The computation now takes into consideration the borderwidth 112 | of the dropdown list 113 | * added option -commandstate. If "disabled", the value of -command will 114 | *not* be called when the value changes. Useful for setting the value 115 | programatically when you don't want to run the associated command. 116 | NB: I really like this concept (of a -commandstate); I sure wish all 117 | tk widgets had such an option... 118 | * added value checks when setting -state and -commandstate (prior to 119 | 1.06 we would attempt to set the state to whatever you passed in) 120 | * fixed bug in error reporting of some listbox manipulation commands 121 | (like ".foo list insert" and friends) 122 | * the -command is called whenever something is chosen from the list or 123 | the user presses return with focus in the entry, even if the value 124 | hasn't changed. The reasoning is that some people might actually 125 | want this behavior, and for those that don't they can easily do the 126 | check for a changed value themselves. 127 | * went back to putting all procs _outside_ the initial namespace eval, 128 | as per suggestions in the style guide published by scriptics. 129 | * focus is better handled when the dropdown listbox is hidden -- focus 130 | should now be restored to whatever widget had it last. 131 | * added a keyboard widget traversal hack in the README.txt file; this 132 | hack also appears in test.tcl 133 | 134 | ### version 1.05: 135 | 136 | * removed a bunch of the "namespace eval" nonsense, in favor of a more 137 | straight-forward technique using upvar. I still use namespaces, but 138 | I think the implementation is a bit more sensible now. 139 | * put braces around a few expressions that didn't have them. 140 | * put all procs within a single "namespace eval" command, rather 141 | than defining them at the global level, with the namespace used 142 | as part of the proc name. A net effect of zero, but this method 143 | just feels more "right". 144 | 145 | ### version 1.04: 146 | 147 | Thanks for Martin M. Hunt (hunt@cygnus.com) for some of these fixes, or 148 | the inspiration thereof 149 | 150 | * fixed bug when calling the -command proc when the value that was 151 | selected contained spaces 152 | * selecting an item from the listbox when -editable is false no 153 | longer sets the selection in the entry widget 154 | * fixed bug where the entry widget of a non-editable combobox 155 | would become enabled under certain circumstances 156 | * added a bit of code to set the button width to mimic the width 157 | of a standard scrollbar 158 | * added -height option, to set the height of the listbox 159 | * added -maxheight option, to support the combobox autosizing up to 160 | some maximum size 161 | * poping up a list when the list would go past the bottom of the 162 | screen will cause the list to pop above the entry widget instead 163 | of below 164 | * fixed bug in cget routine 165 | 166 | ### version 1.03: 167 | 168 | * scanning the list works. You can click on the button (but not release) 169 | and drag over the list. If you drag above or below the list it should 170 | scroll appropriately 171 | * if you have a textvariable defined, it is no longer set to "" initially 172 | * tab completion has changed slightly..if you type in something that 173 | doesn't match it will try to find something that does (for example, 174 | typing "apx" when "append" is a valid value will highlight append). 175 | * changed the "insert" and "delete" commands to "listinsert" and 176 | "listdelete" 177 | * added the commands "listindex", "listget", "listsize", which are passed 178 | to the listbox as index, get and size, respectively. This gives you what 179 | I think is sufficient access to the listbox (but see more about this 180 | below...) 181 | * added the commands "bbox", "insert", "delete", "icursor", "scan", 182 | "xview", and "selection", which are passed directly to the entry 183 | widget. I haven't tested them all much, but in theory if one works 184 | they all should work... (or conversely, if one is broken...) 185 | * in the entry closes the list if it is open 186 | * added configuration option -selectcommand. It is a command that gets 187 | executed whenever something from the dropdown list is selected. 188 | It *doesn't* get called if you just type in the box. I wasn't sure 189 | when to call it in the latter case -- after each keypress? After a 190 | ? When focus changes? So for the time being it is only called 191 | when you select from the list, and it will append to the command the 192 | name of the widget and the index of the item selected. This works 193 | very well for -editable false. If you want a command to trigger for 194 | -editable true, just put a trace on a -textvariable. 195 | 196 | 197 | ### version 1.02: 198 | 199 | * the namespace for the widget has moved below the ::Combobox namespace 200 | * interaction of pageup/pagedown and up/down has been fixed 201 | * new method for mapping window 202 | * fixed double-click behavior when widget is disabled 203 | * fixed case where you could pop up the list even if the widget was disabled 204 | * tweaked testing routine to use rgb values instead of color names 205 | * clicking Button 1 in the entry widget will pop up the list if the widget 206 | is enabled but not editable 207 | * clicking Button 1 anywhere in the app when the list is showing will 208 | close the list 209 | * closing the listbox will cause the data in the entry widget to be selected 210 | * added options -selectbackground, selectforeground and -selectborderwidth 211 | * added option -cursor 212 | * added check for unknown option in configure, cget 213 | * added package require Tk 8.0 214 | * changed behavior of tab key in the entry widget; it still trys to match 215 | what is there with something in the list, but the way it chooses to 216 | display the data in the entry widget has changed slightly. 217 | -------------------------------------------------------------------------------- /lib/combobox/README.txt: -------------------------------------------------------------------------------- 1 | Combobox version 2.3 2 | Copyright (c) 1998-2003, Bryan Douglas Oakley 3 | All Rights Reserved. 4 | 5 | http://www.purl.org/net/oakley/tcl/combobox/index.html 6 | mailto:oakley@bardo.clearlight.com 7 | 8 | This software is provided AS-IS with no waranty expressed or 9 | implied. This software may be used free of charge, though I would 10 | appreciate it if you give credit where credit is due and mention my 11 | name when you use it. 12 | 13 | To use: place combobox.tcl where your programs can get at it either 14 | via autoloading or from a "source" command. To create a combobox widget 15 | use the command "::combobox::combobox", or import the combobox command 16 | with "import ::combobox::*" to use it as simply "combobox". 17 | 18 | To see a quick example, start up a wish process and cd to the directory 19 | where combobox.tcl is located. Then do "source example.tcl". 20 | 21 | ##################################################################### 22 | -------------------------------------------------------------------------------- /lib/combobox/example.tcl: -------------------------------------------------------------------------------- 1 | # A simple font display utility. 2 | # 3 | # This program uses two variations of the combobox. One, for the font 4 | # family, is non-editable (ie: the user can only pick from the list). 5 | # The other, for font size, is editable. The user can pick from the list 6 | # or enter their own size 7 | 8 | # substitute your favorite method here... 9 | source combobox.tcl 10 | package require combobox 2.3 11 | catch {namespace import combobox::*} 12 | 13 | proc main {} { 14 | # this lets us be reentrant... 15 | destroy .fontpicker .msg 16 | 17 | # default values 18 | set ::size 12 19 | set ::family [lindex [lsort [font families]] 0] 20 | set ::slant roman 21 | set ::weight normal 22 | set ::overstrike 0 23 | set ::underline 0 24 | 25 | wm title . "Combobox Example" 26 | 27 | # the main two areas: a frame to hold the font picker widgets 28 | # and a label to display a sample from the font 29 | set fp [frame .fontpicker] 30 | set msg [label .msg -borderwidth 2 -relief groove -width 30 -height 4] 31 | 32 | pack $fp -side top -fill x 33 | pack $msg -side top -fill both -expand y -pady 2 34 | 35 | $msg configure -text [join [list \ 36 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ 37 | "abcdefghijklmnopqrstuvwxyz" \ 38 | "0123456789~`!@#$%^&*()_-+=" \ 39 | "{}[]:;\"'<>,.?/"] "\n"] 40 | 41 | # this will set the font of the message according to our defaults 42 | changeFont $msg 43 | 44 | # font family... 45 | label $fp.famLabel -text "Font Family:" 46 | combobox $fp.famCombo \ 47 | -borderwidth 1 \ 48 | -textvariable family \ 49 | -editable false \ 50 | -highlightthickness 1 \ 51 | -command [list changeFont $msg] 52 | 53 | 54 | grid $fp.famLabel -row 0 -column 0 -sticky e 55 | grid $fp.famCombo -row 0 -column 1 -sticky ew 56 | grid columnconfigure $fp 1 -weight 1 57 | 58 | # we'll do these one at a time so we can find the widest one and 59 | # set the width of the combobox accordingly (hmmm... wonder if this 60 | # sort of thing should be done by the combobox itself...?) 61 | set widest 0 62 | foreach family [lsort [font families]] { 63 | if {[set length [string length $family]] > $widest} { 64 | set widest $length 65 | } 66 | $fp.famCombo list insert end $family 67 | } 68 | $fp.famCombo configure -width $widest 69 | 70 | # the font size. We know we are puting a fairly small, finite 71 | # number of items in this combobox, so we'll set its maxheight 72 | # to zero so it will grow to fit the number of items 73 | label $fp.sizeLabel -text "Font Size:" 74 | combobox $fp.sizeCombo \ 75 | -borderwidth 1 \ 76 | -highlightthickness 1 \ 77 | -maxheight 0 \ 78 | -width 3 \ 79 | -textvariable size \ 80 | -editable true \ 81 | -command [list changeFont $msg] 82 | 83 | grid $fp.sizeLabel -row 0 -column 2 -sticky e -padx 2 84 | grid $fp.sizeCombo -row 0 -column 3 -sticky new 85 | grid columnconfigure $fp 3 -weight 1 86 | eval $fp.sizeCombo list insert end [list 8 9 10 12 14 16 18 20 24 28 32 36] 87 | 88 | # a dummy frame to give a little spacing... 89 | frame $fp.dummy -width 5 90 | grid $fp.dummy -row 0 -column 4 91 | 92 | # bold 93 | set bold "bold" 94 | checkbutton $fp.bold -variable weight -indicatoron false \ 95 | -onvalue bold -offvalue normal \ 96 | -text "B" -width 2 -height 1 \ 97 | -font {-weight bold -family Times -size 12} \ 98 | -highlightthickness 1 -padx 0 -pady 0 -borderwidth 1 \ 99 | -command [list changeFont $msg] 100 | grid $fp.bold -row 0 -column 5 -sticky nsew 101 | 102 | # underline 103 | checkbutton $fp.underline -variable underline -indicatoron false \ 104 | -onvalue 1 -offvalue 0 \ 105 | -text "U" -width 2 -height 1 \ 106 | -font {-underline 1 -family Times -size 12} \ 107 | -highlightthickness 1 -padx 0 -pady 0 -borderwidth 1 \ 108 | -command [list changeFont $msg] 109 | grid $fp.underline -row 0 -column 6 -sticky nsew 110 | 111 | # italic 112 | checkbutton $fp.italic -variable slant -indicatoron false \ 113 | -onvalue italic -offvalue roman \ 114 | -text "I" -width 2 -height 1 \ 115 | -font {-slant italic -family Times -size 12} \ 116 | -highlightthickness 1 -padx 0 -pady 0 -borderwidth 1 \ 117 | -command [list changeFont $msg] 118 | grid $fp.italic -row 0 -column 7 -sticky nsew 119 | 120 | # overstrike 121 | checkbutton $fp.overstrike -variable overstrike -indicatoron false \ 122 | -onvalue 1 -offvalue 0 \ 123 | -text "O" -width 2 -height 1 \ 124 | -font {-overstrike 1 -family Times -size 12} \ 125 | -highlightthickness 1 -padx 0 -pady 0 -borderwidth 1 \ 126 | -command [list changeFont $msg] 127 | grid $fp.overstrike -row 0 -column 8 -sticky nsew 128 | 129 | # this gives us relatively square buttons that fill the frame 130 | # in the Y direction 131 | bind $fp { 132 | grid columnconfigure %W 5 -minsize %h 133 | grid columnconfigure %W 6 -minsize %h 134 | grid columnconfigure %W 7 -minsize %h 135 | grid columnconfigure %W 8 -minsize %h 136 | } 137 | # put focus on the first widget 138 | catch {focus $fp.famCombo} 139 | 140 | return "" 141 | } 142 | 143 | # this proc changes the font. It is called by various methods, so 144 | # the only parameter we are guaranteed is the first one since 145 | # we supply it ourselves... 146 | proc ::changeFont {w args} { 147 | foreach foo [list family size weight underline slant overstrike] { 148 | if {[set ::$foo] == ""} { 149 | return 150 | } 151 | } 152 | set ::fontspec [list \ 153 | -family $::family \ 154 | -size $::size \ 155 | -weight $::weight \ 156 | -underline $::underline \ 157 | -slant $::slant \ 158 | -overstrike $::overstrike \ 159 | ] 160 | $w configure -font $::fontspec 161 | } 162 | 163 | main 164 | -------------------------------------------------------------------------------- /lib/combobox/pkgIndex.tcl: -------------------------------------------------------------------------------- 1 | 2 | package ifneeded combobox 2.3 \ 3 | [list tclPkgSetup $dir combobox 2.3 {{combobox.tcl source ::combobox::combobox}}] 4 | 5 | -------------------------------------------------------------------------------- /lib/gui/actionmanager.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # actionmanager.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # A manager for user actions. 10 | # 11 | #----------------------------------------------------------------------- 12 | 13 | snit::type ::gui::actionmanager { 14 | #------------------------------------------------------------------- 15 | # Options 16 | 17 | # A command to call if an error is thrown on invocation of an action. 18 | # The command will be passed three arguments, the error message, 19 | # error code, and error info. 20 | option -errorcommand {} 21 | 22 | # A command to call to check the state (normal or disabled) of an 23 | # action. 24 | option -statecommand {} 25 | 26 | #------------------------------------------------------------------- 27 | # Instance Variables 28 | 29 | # A list of action names: 30 | variable actions {} 31 | 32 | # The following variables constitute the action database. Each 33 | # is an array, keyed on the action name. 34 | 35 | variable command ;# The Tcl command to execute when invoked. 36 | variable state ;# 1 or 0; enabled or disabled. 37 | variable label ;# The label, for menus and buttons. 38 | variable requires ;# List of condition tags. 39 | variable options ;# List of options and values 40 | variable keycodes ;# list: key-sequence menu-accelerator 41 | variable widgets ;# list: widget names 42 | 43 | #------------------------------------------------------------------- 44 | # Constructor 45 | 46 | # At present, no constructor is needed. 47 | 48 | #------------------------------------------------------------------- 49 | # Public Methods 50 | 51 | # Creates a new action with the specified name and options. 52 | method add {action args} { 53 | if {![regexp {^\S+$} $action]} { 54 | error "action name '$action' contains whitespace" 55 | } 56 | 57 | if {[info exists command($action)]} { 58 | error "action '$action' is already defined" 59 | } 60 | 61 | # Initialize the database. 62 | lappend actions $action 63 | 64 | set command($action) {} 65 | set label($action) {} 66 | set state($action) normal 67 | set requires($action) {} 68 | set options($action) {} 69 | set keycodes($action) {} 70 | set widgets($action) {} 71 | 72 | # Handle the options 73 | foreach {opt value} $args { 74 | switch -exact -- $opt { 75 | -command { 76 | set command($action) $value 77 | } 78 | -requires { 79 | set requires($action) $value 80 | } 81 | -label { 82 | # Figure out the underline index and update the label 83 | set underline [string first "&" $value] 84 | 85 | if {$underline < 0} { 86 | set underline 0 87 | set lbl $value 88 | } else { 89 | set lbl [string replace $value $underline $underline] 90 | } 91 | 92 | lappend options($action) -underline $underline 93 | set label($action) $lbl 94 | } 95 | -image - 96 | -tooltip { 97 | lappend options($action) $opt $value 98 | } 99 | -state { 100 | $self setstate $action $value 101 | } 102 | -keycodes { 103 | if {([llength $value] % 2) != 0} { 104 | error "invalid -keycodes: '$value'" 105 | } 106 | set gui [tk windowingsystem] 107 | 108 | array set codes $value 109 | 110 | if {[info exists codes($gui)]} { 111 | set keycodes($action) $codes($gui) 112 | } elseif {[info exists codes(any)]} { 113 | set keycodes($action) $codes(any) 114 | } 115 | } 116 | -keytag { 117 | $self bindkey $value $action 118 | } 119 | default { 120 | error "unknown option '$opt'" 121 | } 122 | } 123 | } 124 | } 125 | 126 | # Returns a list of all existing actions. If the state is given, 127 | # (normal | disabled) then it returns a list of all actions 128 | # that have that state. Note that updatestate is *NOT* called. 129 | # Call it explicitly if you want it. 130 | method list {{whichstate ""}} { 131 | if {$whichstate eq ""} { 132 | return $actions 133 | } 134 | 135 | set result {} 136 | foreach action $actions { 137 | if {$state($action) eq $whichstate} { 138 | lappend result $action 139 | } 140 | } 141 | 142 | return $result 143 | } 144 | 145 | # Invokes the action, e.g., calls its -command. This is done whether 146 | # the action is enabled or not, as it allows the command to present 147 | # a suitable error message. 148 | method invoke {action args} { 149 | # FIRST, if no error handler just let nature take its course. 150 | if {$options(-errorcommand) eq {}} { 151 | uplevel \#0 $command($action) $args 152 | return 153 | } 154 | 155 | # OTHERWISE, catch any error and pass it to the error handler. 156 | # If the error handler throws an error, then again let nature 157 | # take its course. 158 | if {[catch {uplevel \#0 $command($action) $args} msg]} { 159 | global errorCode 160 | global errorInfo 161 | 162 | uplevel \#0 $options(-errorcommand) \ 163 | [list $msg $errorInfo $errorCode] 164 | } 165 | 166 | return 167 | } 168 | 169 | # Sets the state of the named action to normal or disabled. 170 | method setstate {action newstate} { 171 | if {$newstate ne "normal" && $newstate ne "disabled"} { 172 | error "Invalid state: '$newstate'" 173 | } 174 | 175 | set state($action) $newstate 176 | 177 | foreach {type widget} $widgets($action) { 178 | switch -exact $type { 179 | button { 180 | $widget configure -state $newstate 181 | } 182 | menu { 183 | set ndx [$widget index $label($action)] 184 | 185 | $widget entryconfigure $ndx -state $newstate 186 | } 187 | default { 188 | error "Invalid widget type: $type" 189 | } 190 | } 191 | } 192 | } 193 | 194 | # Update state: For every action that has a -requires value, 195 | # run the state command, passing it the action's name and its requirements. 196 | # The state command must return the new state. 197 | method updatestate {} { 198 | if {[llength $options(-statecommand)] == 0} { 199 | return 200 | } 201 | 202 | foreach action $actions { 203 | if {$requires($action) ne ""} { 204 | set cmd $options(-statecommand) 205 | lappend cmd $action $requires($action) 206 | 207 | $self setstate $action [uplevel \#0 $cmd] 208 | } 209 | } 210 | 211 | # Without this, sometimes widget states aren't updated promptly 212 | # on Aqua. 213 | update idletasks 214 | } 215 | 216 | # bindkey tag action ?break? 217 | # 218 | # If a keysequence exists for the action, 219 | # binds the action to the tag (usually a window). 220 | # break is included, the binding will "break". 221 | # 222 | # If there's no keysequence defined, then no binding is done. 223 | 224 | method bindkey {tag action {break ""}} { 225 | # Get the binding for this GUI. If none found, we're done. 226 | set seq [lindex $keycodes($action) 0] 227 | 228 | if {$seq eq ""} { 229 | return 230 | } 231 | 232 | # Do the binding, adding "break" as requested. 233 | set cmd [mymethod invoke $action] 234 | 235 | if {$break eq "break"} { 236 | append cmd "; break" 237 | } 238 | 239 | # If the binding is a modified letter key, do both upper and 240 | # lower case. 241 | if {[regexp {^<(.*)-([a-z])>$} $seq dummy root letter]} { 242 | set upper [string toupper $letter] 243 | set lower [string tolower $letter] 244 | 245 | bind $tag <$root-$upper> $cmd 246 | bind $tag <$root-$lower> $cmd 247 | } else { 248 | bind $tag $seq $cmd 249 | } 250 | } 251 | 252 | # Configures a button to invoke this action; also sets the button's 253 | # label or icon, etc. 254 | 255 | method addbutton {button action args} { 256 | array set opt $options($action) 257 | 258 | button $button \ 259 | -command [mymethod invoke $action] \ 260 | -state $state($action) 261 | 262 | if {[info exists opt(-image)]} { 263 | $button configure -image $opt(-image) 264 | } else { 265 | $button configure -text $label($action) -underline $opt(-underline) 266 | } 267 | 268 | eval $button configure $args 269 | 270 | # Enable tool tips--except with aqua on Mac OS X, because it 271 | # causes the focus to go away. 272 | if {[info exists opt(-tooltip)]} { 273 | tooltip register $button $opt(-tooltip) 274 | } 275 | 276 | lappend widgets($action) button $button 277 | 278 | return $button 279 | } 280 | 281 | 282 | # Adds a command item for the action to the named menu, and configures 283 | # additional options if desired. 284 | 285 | method addmenuitem {menu action args} { 286 | array set opt $options($action) 287 | 288 | $menu add command \ 289 | -command [mymethod invoke $action] \ 290 | -label $label($action) \ 291 | -underline $opt(-underline) \ 292 | -state $state($action) \ 293 | -accelerator [lindex $keycodes($action) 1] 294 | 295 | set ndx [$menu index $label($action)] 296 | 297 | foreach {option value} $args { 298 | $menu entryconfigure $ndx $option $value 299 | } 300 | 301 | lappend widgets($action) menu $menu 302 | 303 | # Add a post command to the menu to update the state. 304 | # TBD: Consider making this explicit. 305 | $menu configure -postcommand [mymethod updatestate] 306 | } 307 | } 308 | 309 | -------------------------------------------------------------------------------- /lib/gui/bullet1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/lib/gui/bullet1.gif -------------------------------------------------------------------------------- /lib/gui/bullet2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/lib/gui/bullet2.gif -------------------------------------------------------------------------------- /lib/gui/bullet3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wduquette/notebook/2a9e80cec650ff7899cb9fb1ece68811ebf3fc6a/lib/gui/bullet3.gif -------------------------------------------------------------------------------- /lib/gui/gui.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # gui.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # Main file for the gui package. This package contains 10 | # reusable GUI components written for the Notebook application. 11 | # 12 | #----------------------------------------------------------------------- 13 | 14 | package provide gui 1.0 15 | 16 | package require BWidget 1.9 17 | 18 | namespace eval ::gui:: { 19 | variable library [file dirname [info script]] 20 | variable defaultBackground [. cget -background] 21 | 22 | namespace export actionmanager 23 | namespace export assert 24 | namespace export hasbinding 25 | namespace export require 26 | namespace export rotext 27 | namespace export searchentry 28 | namespace export statusentry 29 | namespace export tooltip 30 | namespace export windowmanager 31 | namespace export windowmenu 32 | } 33 | 34 | #----------------------------------------------------------------------- 35 | # Widget Tweaks for all windowing systems 36 | 37 | # Menu widget 38 | option add *Menu.tearOff no 39 | 40 | #----------------------------------------------------------------------- 41 | # Widget Tweaks for X11 42 | 43 | if {[tk windowingsystem] eq "x11"} { 44 | # On X11, there are quite a few widget option tweaks we'd like to 45 | # do. 46 | 47 | # Text widgets 48 | option add *Text.background white 49 | option add *Text.foreground black 50 | option add *Text.selectBorderWidth 0 51 | 52 | # Scrollbar widgets 53 | option add *Scrollbar.activeBackground $::gui::defaultBackground 54 | 55 | # Button widgets 56 | option add *Button.activeBackground $::gui::defaultBackground 57 | option add *Button.borderWidth 1 58 | 59 | # Menu widgets 60 | option add *Menu.activeBackground "dark blue" 61 | option add *Menu.activeForeground white 62 | option add *Menu.activeBorderWidth 0 63 | option add *Menu.borderWidth 1 64 | 65 | # Entry widgets 66 | option add *Entry.background white 67 | option add *Entry.foreground black 68 | option add *Entry.selectBorderWidth 0 69 | } 70 | 71 | #----------------------------------------------------------------------- 72 | # Widget Tweaks for Mac OS X 73 | 74 | if {[tk windowingsystem] eq "aqua"} { 75 | bind Listbox [bind Listbox ] 76 | } 77 | 78 | 79 | 80 | #----------------------------------------------------------------------- 81 | # Virtual Event Definitions 82 | 83 | # Cut, Copy, Paste, Undo, Redo 84 | 85 | if {[tk windowingsystem] eq "x11"} { 86 | # On X11, we want the standard win32 keybindings for cut, copy, paste, 87 | # undo, and redo to work throughout. So make those keybindings generate 88 | # the relevant virtual events. 89 | # 90 | # 4/28/05: Make sure both upper and lower case work the same. 91 | 92 | event add <> 93 | event add <> 94 | event add <> 95 | event add <> 96 | event add <> 97 | event add <> 98 | event add <> 99 | event add <> 100 | 101 | # Note that is already defined as <>, 102 | # mean ; I need to get rid of that, or 103 | # won't work for <> 104 | event delete <> 105 | 106 | # On X11, <> should be . Provide it in 107 | # both flavors. 108 | event add <> 109 | event add <> 110 | 111 | # In text widgets, the Control-v key can be bound to something else on 112 | # X11. So explicitly unbind it. 113 | bind Text "" 114 | } elseif {[tk windowingsystem] eq "win32"} { 115 | # On Windows, we want the uppercase version of control keys to 116 | # work just like the lower case. So add them all. Include 117 | # the regular ones, just to be explicit. 118 | 119 | event add <> 120 | event add <> 121 | event add <> 122 | event add <> 123 | event add <> 124 | event add <> 125 | event add <> 126 | event add <> 127 | event add <> 128 | event add <> 129 | } elseif {[tk windowingsystem] eq "aqua"} { 130 | # On Mac OS X, we want the uppercase version of control keys to 131 | # work just like the lower case. So add them. 132 | 133 | event add <> 134 | event add <> 135 | event add <> 136 | event add <> 137 | event add <> 138 | event add <> 139 | event add <> 140 | event add <> 141 | 142 | # Tk.tcl erroneously adds Command-y for <>; 143 | # get rid of it before adding the correct keys. 144 | event delete <> 145 | 146 | event add <> 147 | event add <> 148 | } 149 | 150 | 151 | #----------------------------------------------------------------------- 152 | # GUI Component Definitions 153 | 154 | source [file join $::gui::library misc.tcl] 155 | source [file join $::gui::library icon.tcl] 156 | source [file join $::gui::library actionmanager.tcl] 157 | source [file join $::gui::library windowmanager.tcl] 158 | source [file join $::gui::library statusentry.tcl] 159 | source [file join $::gui::library searchentry.tcl] 160 | source [file join $::gui::library rotext.tcl] 161 | source [file join $::gui::library tooltip.tcl] 162 | 163 | -------------------------------------------------------------------------------- /lib/gui/misc.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # misc.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # GUI Helper Functions for notebook's gui package. 10 | # 11 | #----------------------------------------------------------------------- 12 | 13 | # Does window $win have a binding for the event in any of its 14 | # bind tags? 15 | proc ::gui::hasbinding {win event} { 16 | if {$win eq ""} { 17 | return 0 18 | } 19 | 20 | foreach tag [bindtags $win] { 21 | if {[bind $tag $event] ne ""} { 22 | return 1 23 | } 24 | } 25 | 26 | return 0 27 | } 28 | 29 | # require expression message 30 | # 31 | # GUIs often have to do a lot of checking to see whether a given 32 | # user-requested action is valid or not, both in terms of program state 33 | # and in terms of the user's input. Procs that implement these actions 34 | # consequently usually begin with a series of if statements; each statement 35 | # checks a condition, emits some appropriate error message to the user, and 36 | # returns. In many cases, this is the bulk of the command. 37 | # 38 | # require streamlines this kind of code, and makes it work better when nested. 39 | # require evaluates the expression. If it's true, require returns 40 | # silently. But if it's false, require throws an error using the 41 | # given message and an errorcode of "USER". This has a number of 42 | # effects on procs written using require: 43 | # 44 | # * They are useful programmatically. 45 | # * They don't need to know how errors are reported to the user. 46 | # * They allow the caller to distinguish between user input errors 47 | # and unexpected programming errors. 48 | # * They can't be attached directly to menu items, buttons, etc., 49 | # because they need a mediator to catch and present the error. 50 | # 51 | # Thus, using require necessitates some kind of glue mechanism, like 52 | # an action manager of some kind. 53 | 54 | proc ::gui::require {expression message} { 55 | if {[uplevel [list expr $expression]]} { 56 | return 57 | } 58 | 59 | return -code error -errorcode USER $message 60 | } 61 | 62 | # assert expression 63 | # 64 | # Assert is like require, but is used for checking invariants--in 65 | # correctly written code, the expression should invariably be true. 66 | # If the assertion fails an error is thrown indicating 67 | # that an assertion failed and what the condition was. 68 | 69 | proc ::gui::assert {expression} { 70 | if {[uplevel [list expr $expression]]} { 71 | return 72 | } 73 | 74 | return -code error -errorcode ASSERT "Assertion failed: $expression" 75 | 76 | } -------------------------------------------------------------------------------- /lib/gui/pkgIndex.tcl: -------------------------------------------------------------------------------- 1 | # Tcl package index file 2 | 3 | package ifneeded gui 1.0 \ 4 | [list source [file join $dir gui.tcl]] 5 | 6 | -------------------------------------------------------------------------------- /lib/gui/rotext.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # rotext.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # A read-only text widget. A rotext behaves in all ways identical 10 | # to a normal text widget, except that text cannot be inserted or 11 | # deleted by the user. 12 | # 13 | # The widget was made read-only by overriding the "insert" and 14 | # "delete" widget subcommands. They have been replaced by 15 | # "ins" and "del" which do the same thing, but which aren't 16 | # available to the standard mouse and keyboard bindings. 17 | # 18 | # This sneaky trick is due to Donal K. Fellows at the 19 | # Tcler's Wiki; the widget is implemented using snit. 20 | # 21 | # LICENSE: 22 | # Copyright (C) 2003 by William H. Duquette. This file may 23 | # be used subject to the terms in license.txt. 24 | # 25 | #----------------------------------------------------------------------- 26 | 27 | #----------------------------------------------------------------------- 28 | # Public methods 29 | 30 | snit::widgetadaptor ::gui::rotext { 31 | constructor {args} { 32 | # First, create the real text widget. 33 | installhull [text $self -insertwidth 0] 34 | $self configurelist $args 35 | 36 | # Next, copy the default Text bindings to the renderpane, 37 | # and adjust the bindtags so that the Text bindings no longer 38 | # matter. This will allow us to override them. 39 | 40 | foreach sym [bind Text] { 41 | bind $win $sym [bind Text $sym] 42 | } 43 | 44 | set bt [bindtags $win] 45 | set ndx [lsearch -exact $bt Text] 46 | bindtags $win [lreplace $bt $ndx $ndx] 47 | 48 | # Next, remove the <> and <> tags, etc., since renderpanes 49 | # aren't editable. 50 | bind $win <> "" 51 | bind $win <> "" 52 | bind $win <> "" 53 | bind $win <> "" 54 | } 55 | 56 | # Disable the insert and delete methods, to make this readonly. 57 | method insert {args} {} 58 | method delete {args} {} 59 | 60 | # Enable ins and del as synonyms, so the program can insert and 61 | # delete. 62 | delegate method ins to hull as insert 63 | delegate method del to hull as delete 64 | 65 | # Pass all other methods and options to the real text widget, so 66 | # that the remaining behavior is as expected. 67 | delegate method * to hull 68 | delegate option * to hull 69 | } 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /lib/gui/searchentry.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # searchentry.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # A searchentry is an ornamented entry field. The entry is flanked 10 | # on the left by a pulldown that lets the user select one of a number 11 | # search modes, and on the right by a clear button. All three 12 | # components are contained in a single visible border. 13 | # 14 | # The pulldown entries are determined by a widget option. 15 | # The active pulldown selection is also an option. 16 | # The searchentry supports incremental and non-incremental search. 17 | # When the search text is ready, a user-supplied command is called; 18 | # it is passed the search text and the current pulldown option. 19 | # 20 | #----------------------------------------------------------------------- 21 | 22 | snit::widget ::gui::searchentry { 23 | #------------------------------------------------------------------- 24 | # Components 25 | 26 | # The arrow button 27 | variable arrow 28 | 29 | # The entry 30 | delegate option -width to entry 31 | 32 | # The clear button 33 | variable clear 34 | 35 | # The mode menu 36 | variable modemenu 37 | 38 | #------------------------------------------------------------------- 39 | # Variables 40 | 41 | # TBD 42 | 43 | #------------------------------------------------------------------- 44 | # Creation Time Options 45 | # 46 | # These options should only be set at creation time. Note that 47 | # at present there's no error checking to prevent them from being 48 | # set later. 49 | 50 | # A list of mutually exclusive search modes and labels, e.g., 51 | # -modelist {title "Title Only" all "Title and Contents"} 52 | option -modelist {} 53 | 54 | #-------------------------------------------------------------------- 55 | # Other Options 56 | 57 | # The selected search mode: defaults to the first mode in the 58 | # -modelist, or "default" if there is no -modelist. 59 | # 60 | # WARNING: If there's a -modelist, this should only be set to one 61 | # of the modes defined in it. Note that there's no error checking 62 | # on this at present. 63 | option -mode default 64 | 65 | # The user's callback command. It should take two arguments, 66 | # the search mode and the search text. If {}, no command is called. 67 | option -searchcmd {} 68 | 69 | # 1 if we're to do incremental search, and 0 otherwise. 70 | option -incremental 1 71 | 72 | # The search text; maps to the entry's contents. Setting initiates 73 | # a search. 74 | option -searchtext -configuremethod CfgSearchText 75 | 76 | method CfgSearchText {opt value} { 77 | set options($opt) $value 78 | 79 | $self UpdateClear 80 | $self DoSearch complete 81 | } 82 | 83 | #------------------------------------------------------------------- 84 | # Constructor 85 | 86 | constructor {args} { 87 | # FIRST, set the hull's border 88 | $hull configure -relief solid -borderwidth 1 -background white 89 | 90 | # NEXT, get the modelist, since we need it early 91 | set options(-modelist) [from args -modelist] 92 | 93 | # NEXT, identify the search box 94 | label $win.search \ 95 | -relief flat \ 96 | -borderwidth 0 \ 97 | -background white \ 98 | -foreground black \ 99 | -image ::gui::search 100 | 101 | # NEXT, create the pulldown's arrow button 102 | install arrow using label $win.arrow \ 103 | -relief flat \ 104 | -borderwidth 0 \ 105 | -background white \ 106 | -foreground black \ 107 | -image ::gui::downarrow 108 | 109 | # Next, create the popup. 110 | install modemenu using menu $win.modemenu \ 111 | -tearoff no 112 | bind $arrow <1> [mymethod PostModeMenu] 113 | 114 | $modemenu add radiobutton \ 115 | -variable [myvar options(-incremental)] \ 116 | -value 1 \ 117 | -label "Incremental Search" 118 | 119 | $modemenu add radiobutton \ 120 | -variable [myvar options(-incremental)] \ 121 | -value 0 \ 122 | -label "Non-Incremental Search" 123 | 124 | # If we have modes, add them. 125 | if {[llength $options(-modelist)] > 1} { 126 | 127 | set options(-mode) [lindex $options(-modelist) 0] 128 | 129 | $modemenu add separator 130 | 131 | foreach {mode label} $options(-modelist) { 132 | $modemenu add radiobutton \ 133 | -variable [myvar options(-mode)] \ 134 | -value $mode \ 135 | -label $label \ 136 | -command [mymethod DoSearch] 137 | } 138 | } 139 | 140 | # NEXT, create the entry 141 | install entry using entry $win.entry \ 142 | -width 20 \ 143 | -background white \ 144 | -foreground black \ 145 | -borderwidth 0 \ 146 | -relief flat \ 147 | -highlightthickness 0 \ 148 | -textvariable [myvar options(-searchtext)] 149 | 150 | bindtags $entry [concat [bindtags $entry] $win] 151 | 152 | bind $win [mymethod KeyPress] 153 | bind $win [mymethod Return] 154 | bind $win [mymethod Clear] 155 | 156 | # NEXT, if the megawidget gets the focus, it should go to the entry. 157 | bind $win [mymethod FocusIn] 158 | 159 | # NEXT, create the clear button 160 | install clear using label $win.clear \ 161 | -relief flat \ 162 | -borderwidth 0 \ 163 | -background white \ 164 | -foreground black \ 165 | -image ::gui::closex \ 166 | -state disabled 167 | bind $clear <1> [mymethod Clear] 168 | 169 | # Pack all of the components into the hull 170 | pack $win.search -side left -fill y -padx 2 -pady 2 171 | pack $arrow -side left -fill y -padx 2 -pady 2 172 | pack $clear -side right -fill y -padx 2 -pady 2 173 | pack $entry -side left -fill both -pady 2 -expand 1 174 | 175 | # Apply the arguments 176 | $self configurelist $args 177 | } 178 | 179 | #------------------------------------------------------------------- 180 | # Private methods 181 | 182 | method FocusIn {} { 183 | focus $entry 184 | $entry selection range 0 end 185 | } 186 | 187 | method KeyPress {} { 188 | $self UpdateClear 189 | 190 | if {!$options(-incremental)} { 191 | return 192 | } 193 | 194 | $self DoSearch incremental 195 | 196 | # We must have had the focus before, since this call only comes 197 | # because of a KeyPress. So put the focus back in the entry 198 | # widget in case the search callback changed it. 199 | focus $entry 200 | } 201 | 202 | method UpdateClear {} { 203 | if {[string length $options(-searchtext)] > 0} { 204 | $clear configure -state normal 205 | } else { 206 | $clear configure -state disabled 207 | } 208 | } 209 | 210 | method Return {} { 211 | $self DoSearch complete 212 | 213 | # We don't want to call KeyRelease, so break the event. 214 | return -code break 215 | } 216 | 217 | method DoSearch {searchtype} { 218 | # Call the search command 219 | set command $options(-searchcmd) 220 | lappend command $options(-mode) $searchtype $options(-searchtext) 221 | uplevel \#0 $command 222 | } 223 | 224 | method Clear {} { 225 | set options(-searchtext) "" 226 | $clear configure -state disabled 227 | $self DoSearch complete 228 | } 229 | 230 | method PostModeMenu {} { 231 | set x [winfo rootx $arrow] 232 | set y [expr {[winfo rooty $arrow] + [winfo height $arrow]}] 233 | 234 | tk_popup $modemenu $x $y 235 | } 236 | } 237 | 238 | -------------------------------------------------------------------------------- /lib/gui/tooltip.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # tooltip.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # Tool tips for notebook's gui package. 10 | # 11 | #----------------------------------------------------------------------- 12 | 13 | #----------------------------------------------------------------------- 14 | # Tooltip type 15 | # 16 | # The tooltip command is an instance of TooltipType, so that we can 17 | # have options. 18 | 19 | snit::type ::gui::TooltipType { 20 | #------------------------------------------------------------------- 21 | # Options 22 | 23 | option -font {Helvetica 12} 24 | option -background "#FFFFC0" 25 | option -topbackground black 26 | option -foreground black 27 | option -delay 800 28 | 29 | #------------------------------------------------------------------- 30 | # Variables 31 | 32 | # Tool tip text. An array, indexed by window name 33 | variable tiptext 34 | 35 | # Tool tip timeout, or {} 36 | variable timeout {} 37 | 38 | # Tool tip window, or {} 39 | variable tipwin {} 40 | 41 | #------------------------------------------------------------------- 42 | # Constructor 43 | 44 | # Implicit 45 | 46 | #------------------------------------------------------------------- 47 | # Public methods 48 | 49 | method register {window text} { 50 | set tiptext($window) $text 51 | 52 | bind $window [mymethod Enter $window] 53 | bind $window [mymethod Leave $window] 54 | } 55 | 56 | method unregister {window} { 57 | unset tiptext($window) 58 | } 59 | 60 | #------------------------------------------------------------------- 61 | # Private Methods 62 | 63 | # When the mouse pointer enters the window, set the timer. 64 | method Enter {window} { 65 | set timeout [after $options(-delay) [mymethod Popup $window]] 66 | } 67 | 68 | # Pop up the tooltip. 69 | method Popup {window} { 70 | # FIRST, the timeout has fired, so we can forget it. 71 | set timeout {} 72 | 73 | # NEXT, the tooltip will be a child of the window's toplevel. 74 | set top [winfo toplevel $window] 75 | 76 | # NEXT, the tooltip's name depends on which toplevel it is. 77 | set tipwin ".gui_tooltip_window" 78 | 79 | if {$top ne "."} { 80 | set tipwin "$top$tipwin" 81 | } 82 | 83 | # NEXT, create the tooltip window. 84 | frame $tipwin \ 85 | -background $options(-topbackground) 86 | 87 | label $tipwin.label \ 88 | -text $tiptext($window) \ 89 | -foreground $options(-foreground) \ 90 | -background $options(-background) \ 91 | -font $options(-font) 92 | 93 | # Pack the label with a 1 pixel gap, so that there's a box 94 | # around it. 95 | pack $tipwin.label -padx 1 -pady 1 96 | 97 | # NEXT, the tipwin will be placed in the toplevel relative to 98 | # the position of the registered window. We'll figure this out 99 | # by getting the position of both relative to the root window. 100 | 101 | set tx [winfo rootx $top] 102 | set ty [winfo rooty $top] 103 | 104 | set wx [winfo rootx $window] 105 | set wy [winfo rooty $window] 106 | 107 | # We want to the tip to appear below and to the right of the 108 | # registered window. 109 | set offset [expr {[winfo width $window]/2}] 110 | 111 | # Compute the final position. 112 | set x [expr {($wx - $tx) + $offset}] 113 | set y [expr {($wy - $ty) + [winfo height $window] + 2}] 114 | 115 | # Finally, place the tipwin in its position. 116 | place $tipwin -anchor nw -x $x -y $y 117 | 118 | # If the tipwin runs off the right edge, we slide it left; if 119 | # it sticks off the bottom edge, we put it above the button instead. 120 | # If it still can't be seen, that's too bad. 121 | # 122 | # NOTE: I don't know of any way to determine the size of the 123 | # tipwin without letting it pop up, which causes an ugly 124 | # jump when it has to be moved. 125 | update idletasks 126 | 127 | set nx $x 128 | set ny $y 129 | 130 | set rightEdge [expr {$x + [winfo width $tipwin]}] 131 | set topWid [winfo width $top] 132 | 133 | if {$rightEdge >= $topWid} { 134 | set nx [expr {$x - ($rightEdge - $topWid + 2)}] 135 | } 136 | 137 | set bottomEdge [expr {$y + [winfo height $tipwin] + 2}] 138 | set topHt [winfo height $top] 139 | 140 | if {$bottomEdge >= $topHt} { 141 | set ny [expr {($wy - $ty) - [winfo height $tipwin] - 2}] 142 | } 143 | 144 | if {$nx != $x || $ny != $y} { 145 | place $tipwin -anchor nw -x $nx -y $ny 146 | } 147 | } 148 | 149 | # When the mouse pointer leaves the window, cancel the timer or 150 | # popdown the window, as needed. 151 | method Leave {window} { 152 | if {$timeout ne ""} { 153 | after cancel $timeout 154 | set timeout "" 155 | return 156 | } 157 | 158 | if {$tipwin ne ""} { 159 | destroy $tipwin 160 | set tipwin "" 161 | } 162 | } 163 | } 164 | 165 | #----------------------------------------------------------------------- 166 | # The tooltip command 167 | 168 | ::gui::TooltipType ::gui::tooltip 169 | -------------------------------------------------------------------------------- /lib/gui/windowmanager.tcl: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # TITLE: 3 | # windowmanager.tcl 4 | # 5 | # AUTHOR: 6 | # Will Duquette 7 | # 8 | # DESCRIPTION: 9 | # notebook-gui component: manages multiple toplevel windows so that 10 | # the when the last one is destroyed, the application termiantes. 11 | # 12 | # To participate, toplevel windows register with the windowmanager 13 | # on creation. A WM_DELETE_WINDOW handler is created automatically. 14 | # If a registered toplevel window wishes to destroy itself, it must 15 | # call "windowmanager destroy $win", which will in turn destroy the 16 | # window. 17 | # 18 | # windowmanager doesn't withdraw "."; the application must do that 19 | # for itself. 20 | # 21 | #----------------------------------------------------------------------- 22 | 23 | # windowmanager is a snit::type used as a singleton. 24 | 25 | snit::type ::gui::windowmanager { 26 | pragma -hasinstances no 27 | pragma -hastypedestroy no 28 | 29 | #------------------------------------------------------------------- 30 | # Type Variables 31 | 32 | # Tracks the open windows. When all are closed, the 33 | # program is terminated. 34 | typevariable windowList 35 | 36 | #------------------------------------------------------------------- 37 | # typemethods 38 | 39 | # Shadow the default create method 40 | typemethod create {args} { 41 | error "invalid method" 42 | } 43 | 44 | # register a new window 45 | typemethod register {window} { 46 | lappend windowList $window 47 | 48 | wm protocol $window WM_DELETE_WINDOW [list $type destroy $window] 49 | } 50 | 51 | # destroy a window 52 | typemethod destroy {window} { 53 | set ndx [lsearch -exact $windowList $window] 54 | 55 | if {$ndx == -1} { 56 | error "can't destroy '$window': not registered with windowmanager" 57 | } 58 | 59 | set windowList [lreplace $windowList $ndx $ndx] 60 | 61 | destroy $window 62 | 63 | if {[llength $windowList] == 0} { 64 | exit 0 65 | } 66 | } 67 | 68 | # Return the list of windows 69 | typemethod windows {} { 70 | return $windowList 71 | } 72 | 73 | # Raise the named window 74 | typemethod raise {window} { 75 | raise $window 76 | wm deiconify $window 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /lib/markupparser/README.txt: -------------------------------------------------------------------------------- 1 | markupparser 2.0 2 | ----------------------------------------------------------------- 3 | 4 | markupparser parses Notebook markup into an intermediary form, 5 | a flat list of tag/value pairs. The parsing proceeds by first 6 | breaking the input into chunk, where each chunk consists 7 | of one or more full lines of text. There are (at present) 8 | four kinds of chunk: 9 | 10 | Directives 11 | A directive is a line beginning with a hash "#" character; 12 | the specific directive is determined by the text that follows 13 | the hash character. Unless otherwise indicated, any 14 | subsequent text on the line is ignored. Unrecognized 15 | directives are permitted. The existing directives are as 16 | follows: 17 | 18 | #--- 19 | Rendered as a horizontal line, e.g.,
. 20 | 21 | #pre 22 | Begins a chunk of preformatted text. 23 | 24 | #unpre 25 | Ends a chunk of preformatted text. 26 | 27 | #Tcl 28 | Begins a chunk of Tcl Code 29 | 30 | #unTcl 31 | Ends a chunk of Tcl Code 32 | 33 | #meta 34 | Defines a named value associated with the pages. It will be 35 | possible to query a page for its metadata. 36 | 37 | #data 38 | Begins a data chunk. A data chunk is just like a Tcl Code or 39 | preformatted text chunk in terms of how it's handled; however, 40 | it will allow other code to easily extract data of a specific 41 | type from a page. Eventually, it might be possible to add 42 | rendering plugins for specific data types. 43 | 44 | #undata 45 | Ends a data chunk. 46 | 47 | Preformatted 48 | A preformatted chunk is to be rendered verbatim in a 49 | fixed-width font. Performatted chunks are found in two 50 | ways: either a paragraph whose first line begins with 51 | a whitespace character, or a group of lines beginning with 52 | a "#pre" directive and ending with a "#unpre" directive 53 | (or the end of the input). (The directives are not 54 | part of the chunk.) 55 | 56 | Tcl Code 57 | A Tcl code chunk is usually rendered Preformatted; it is 58 | a group of lines bracketed by "#Tcl" and "#unTcl". 59 | 60 | Wrapped Text 61 | A wrapped text paragraph consists of one or more lines of 62 | possibly styled text. Wrapped text paragraphs can be 63 | indented; if indented they can have a bullet. Wrapped 64 | text can also contain links, magic buttons, and embedded 65 | macros. 66 | 67 | Note: embedded macros are usually expanded *before* parsing 68 | the markup, so usually there won't be any in the input. If 69 | there are any, they could be anywhere, in any kind of text. 70 | However, the parser will only recognize them in wrapped text. 71 | 72 | Pass-through Text 73 | Sometimes it's desirable to include text in a page that will be 74 | passed through the parser unchanged, e.g., when using macros 75 | to generate HTML in a page that will be exported as HTML. 76 | There are two ways to specify pass-through text. 77 | 78 | #data thru 79 | Text to be passed through. 80 | #undata 81 | 82 | Text wrapped by #data thru/#undata will go through the parser 83 | unchanged, and will be handled by output processors as a single 84 | paragraph. 85 | 86 | This text has some HTML markup. 87 | 88 | Within a paragraph of wrapped text, the ... 89 | tags can be used to quote a section of text to pass 90 | through unchanged. If the example above were exported 91 | as HTML without the ... tags, the angle 92 | brackets on and would be escaped as 93 | < and >. 94 | 95 | 96 | Intermediate Form 97 | ----------------------------------------------------------------- 98 | 99 | The parser parses the input and produces the intermediate form, 100 | a flat list of tag/value pairs. The tags and their values are 101 | defined as follows: 102 | 103 | 104 | META 105 | The value of META is a dict of the #meta values defined in 106 | this page. If the same keyword appears multiple times, the 107 | final value is retained. If no #meta directives appear in 108 | this page, META appears with an empty list as its value. 109 | 110 | META is always the first tag in the list. 111 | 112 | HASH 113 | One of these pairs is produced for each directive; the value is 114 | the complete line of text, including the newline at the end. 115 | 116 | PRE 117 | This is usually Preformatted text, to be displayed verbatim. The 118 | value is the exact set of lines from the input. If the chunk was 119 | delimited by "#pre" and "#unpre" directives, then this tag/value 120 | will be preceded and followed by the appropriate HASH tag/values. 121 | 122 | TCL 123 | The is a chunk of Tcl code. This tag/value will be preceded 124 | and followed by the "#Tcl" and "#unTcl" HASH tag/values. 125 | 126 | DATA { } 127 | The value of DATA is a list of two elements; the data name from 128 | the #data line and the chunk of data that appeared between the 129 | #data and #undata. 130 | 131 | When the is "thru", the is to be passed through 132 | the parser unchanged. 133 | 134 | P {:|* } 135 | Begins a paragraph of wrapped text. The value is a list of three 136 | items: the paragraph type (":" or "*"), the indent level (0 or 137 | higher) and (for indented and bulleted paragraphs) the leading 138 | string, which is the whitespace between the initial "*" or ":" 139 | and the paragraph text. (It's used to reconstruct a parsed page 140 | just exactly as it was.) 141 | 142 | A normal, non-indented non-bulleted paragraph will begin with 143 | 144 | P {: 0 {}} 145 | 146 | A paragraph with an indent level of 1 will begin with this 147 | (where the length of the leading string depends on the actual 148 | input): 149 | 150 | P {: 1 { }} 151 | 152 | A bulleted paragraph with an indent level of 1 will begin with 153 | this: 154 | 155 | P {* 1 { }} 156 | 157 | For bulleted paragraphs, the indent level must be 1 or more. 158 | Note that a normal paragraph is simply an indented paragraph 159 | with an indent level of zero. 160 | 161 | /P 162 | Terminates a paragraph of wrapped text, of whatever kind. 163 | 164 | The following tags represent components of a paragraph of wrapped 165 | text; they will always appear between P and /P. 166 | 167 | TXT 168 | The value is plain text to be wrapped and rendered. 169 | 170 | THRU 171 | The value is text in the current output format; it 172 | was passed through the parser unchanged. 173 | 174 | STY { } 175 | Notebook markup defines a number of HTML-like style codes, 176 | e.g., ... and .... When one is found in a 177 | wrapped paragraph, this tag is generated. The tag's value is 178 | a list of three elements: the raw style code (), the 179 | style letter (b), and a flag: 1 when the style turns on and 180 | 0 when the style turns off. Note that all styles are 181 | cancelled at the end of the paragraph. 182 | 183 | A renderer is free to render the styles as it prefers. 184 | 185 | LINK 186 | The value is a page link, not including the [ and ]. 187 | 188 | BTN