aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Love2000-09-15 17:22:25 +0000
committerDave Love2000-09-15 17:22:25 +0000
commit0d0db51e66b8b3cde84853e26389aeb4e63fd801 (patch)
tree5576ccfd8f0c6d621266ce6e9e98fb99a261a2c1
parentebb8f1165d53d93b99552ea8246abaab82170e86 (diff)
downloademacs-0d0db51e66b8b3cde84853e26389aeb4e63fd801.tar.gz
emacs-0d0db51e66b8b3cde84853e26389aeb4e63fd801.zip
Sync with maintainer's current version with changes
for Emacs, but avoid runtime cl and levents. (toplevel): Change autoloads and compilation requires. (strokes-version, strokes-bug-address, strokes-lift): Values changed. (strokes-xpm-header, strokes-insinuated): New variable. (strokes): Add :link. (strokes-mode): Customized. (strokes-while-inhibiting-garbage-collector): New macro. (strokes-remassoc): Avoid remove-if. (strokes-fix-button2-command): Don't use ad-do-it. (strokes-insinuate): New function. (strokes-button-press-event-p, strokes-button-release-event-p): New functions, used instead of non-`strokes-' versions.. (strokes-mouse-event-p): Rewritten. (strokes-event-closest-point): Avoid event-point. (strokes-get-grid-position): Avoid cdadr, caadr (strokes-read-stroke, strokes-read-complex-stroke): Avoid levents functions. (strokes-help): Use with-output-to-temp-buffer. (strokes-window-configuration-changed-p): New function. (strokes-update-window-configuration): Use buffer-live-p, strokes-window-configuration-changed-p. (strokes-mode): Use strokes-insinuate. Alter mouse bindings. (strokes-char-face): New face. (strokes-char-table, strokes-base64-chars): New variable. (strokes-xpm-for-stroke, strokes-list-strokes) (strokes-xpm-char-on-p, strokes-xpm-char-bit-p) (strokes-xpm-encode-length-as-string, strokes-xpm-decode-char) (strokes-xpm-to-compressed-string, strokes-decode-buffer) (strokes-encode-buffer, strokes-xpm-for-compressed-string) (strokes-compose-complex-stroke, strokes-alphabetic-lessp): New functions.
-rw-r--r--lisp/strokes.el929
1 files changed, 772 insertions, 157 deletions
diff --git a/lisp/strokes.el b/lisp/strokes.el
index d45d9c19732..7c9423b3c8f 100644
--- a/lisp/strokes.el
+++ b/lisp/strokes.el
@@ -1,6 +1,6 @@
1;;; strokes.el --- control Emacs through mouse strokes 1;;; strokes.el --- control Emacs through mouse strokes
2 2
3;; Copyright (C) 1997 Free Software Foundation, Inc. 3;; Copyright (C) 1997, 2000 Free Software Foundation, Inc.
4 4
5;; Author: David Bakhash <cadet@alum.mit.edu> 5;; Author: David Bakhash <cadet@alum.mit.edu>
6;; Maintainer: David Bakhash <cadet@alum.mit.edu> 6;; Maintainer: David Bakhash <cadet@alum.mit.edu>
@@ -93,7 +93,7 @@
93;; documentation on the variables there. 93;; documentation on the variables there.
94 94
95;; `strokes-minimum-match-score' (determines the threshold of error that 95;; `strokes-minimum-match-score' (determines the threshold of error that
96;; makes a stroke acceptable or unacceptable. If your strokes arn't 96;; makes a stroke acceptable or unacceptable. If your strokes aren't
97;; matching, then you should raise this variable. 97;; matching, then you should raise this variable.
98 98
99;; `strokes-grid-resolution' (determines the grid dimensions that you use 99;; `strokes-grid-resolution' (determines the grid dimensions that you use
@@ -136,8 +136,7 @@
136;; and then add the following to your .emacs file (or wherever 136;; and then add the following to your .emacs file (or wherever
137;; you put Emacs-specific startup preferences): 137;; you put Emacs-specific startup preferences):
138 138
139;;(and (fboundp 'device-on-window-system-p) 139;;(and window-system
140;; (device-on-window-system-p)
141;; (require 'strokes)) 140;; (require 'strokes))
142 141
143;; Once loaded, you can start stroking. You can also toggle between 142;; Once loaded, you can start stroking. You can also toggle between
@@ -163,27 +162,24 @@
163;; which is nothing but a file with some helper commands for inserting 162;; which is nothing but a file with some helper commands for inserting
164;; alphanumerics and punctuation. 163;; alphanumerics and punctuation.
165 164
166;; Great thanks to Rob Ristroph for his generosity in letting me use his 165;; Great thanks to Rob Ristroph for his generosity in letting me use
167;; PC to develop this, Jason Johnson for his help in algorithms, Euna 166;; his PC to develop this, Jason Johnson for his help in algorithms,
168;; Kim for her help in Korean, and massive thanks to the helpful guys 167;; Euna Kim for her help in Korean, and massive thanks to the helpful
169;; on the help instance on athena (zeno, jered, amu, gsstark, ghudson, etc) 168;; guys on the help instance on athena (zeno, jered, amu, gsstark,
170;; Special thanks to Steve Baur and Hrvoje Niksic for all their help. 169;; ghudson, etc) Special thanks to Steve Baur, Kyle Jones, and Hrvoje
171;; And even more thanks to Dave Gillespie for all the elisp help--he 170;; Niksic for all their help. And special thanks to Dave Gillespie
172;; is responsible for helping me use the cl macros at (near) max speed. 171;; for all the elisp help--he is responsible for helping me use the cl
172;; macros at (near) max speed.
173 173
174;; Tasks: (what I'm getting ready for future version)... 174;; Tasks: (what I'm getting ready for future version)...
175;; 2) use 'strokes-read-complex-stroke for korean, etc. 175;; 2) use 'strokes-read-complex-stroke for korean, etc.
176;; 4) buffer-local 'strokes-local-map, and mode-stroke-maps would be nice 176;; 4) buffer-local 'strokes-local-map, and mode-stroke-maps would be nice
177;; 5) 'list-strokes (kinda important). What do people want?
178;; How about an optional docstring for each stroke so that a person
179;; can examine the strokes-file and actually make sense of it?
180;; (e.g. "This stroke is a pentagram")
181;; 6) add some hooks, like `strokes-read-stroke-hook' 177;; 6) add some hooks, like `strokes-read-stroke-hook'
182;; 7) See what people think of the factory settings. Should I change 178;; 7) See what people think of the factory settings. Should I change
183;; them? They're all pretty arbitrary in a way. I guess they 179;; them? They're all pretty arbitrary in a way. I guess they
184;; should be minimal, but computers are getting lots faster, and 180;; should be minimal, but computers are getting lots faster, and
185;; if I choose the defaults too conservatively, then strokes will 181;; if I choose the defaults too conservatively, then strokes will
186;; surely dissapoint some people on decent machines (until they 182;; surely disappoint some people on decent machines (until they
187;; figure out M-x customize). I need feedback. 183;; figure out M-x customize). I need feedback.
188;; Other: I always have the most beta version of strokes, so if you 184;; Other: I always have the most beta version of strokes, so if you
189;; want it just let me know. 185;; want it just let me know.
@@ -192,51 +188,57 @@
192 188
193;;; Requirements and provisions... 189;;; Requirements and provisions...
194 190
195(autoload 'reporter-submit-bug-report "reporter")
196(autoload 'mail-position-on-field "sendmail") 191(autoload 'mail-position-on-field "sendmail")
197(eval-and-compile 192(eval-when-compile (require 'cl))
198 (mapcar 'require '(pp reporter advice custom cl))
199 (mapcar 'load '("cl-macs" "cl-seq" "levents")))
200 193
201;;; Constants... 194;;; Constants...
202 195
203(defconst strokes-version "0.0-beta") 196(defconst strokes-version "2.4-Emacs")
204 197
205(defconst strokes-bug-address "cadet@mit.edu") 198(defconst strokes-bug-address "cadet@alum.mit.edu")
206 199
207(defconst strokes-lift 'strokes-lift 200(defconst strokes-lift :strokes-lift
208 "Symbol representing a stroke lift event for complex strokes. 201 "Symbol representing a stroke lift event for complex strokes.
209Complex strokes are those which contain two or more simple strokes. 202Complex strokes are those which contain two or more simple strokes.")
210This will be useful for when Emacs understands Chinese.") 203
204(defconst strokes-xpm-header "/* XPM */
205static char * stroke_xpm[] = {
206/* width height ncolors cpp [x_hot y_hot] */
207\"33 33 9 1 26 23\",
208/* colors */
209\" c none s none\",
210\"* c #000000 s foreground\",
211\"R c #FFFF00000000\",
212\"O c #FFFF80000000\",
213\"Y c #FFFFFFFF0000\",
214\"G c #0000FFFF0000\",
215\"B c #00000000FFFF\",
216\"P c #FFFF0000FFFF\",
217\". c #45458B8B0000\",
218/* pixels */\n"
219 "The header to all xpm buffers created by strokes")
211 220
212;;; user variables... 221;;; user variables...
213 222
214;; suggested Custom hack, so strokes is compatible with emacs19...
215
216(eval-and-compile
217 (if (fboundp 'defgroup) nil
218 (defmacro defgroup (&rest forms) nil)
219 (defmacro defcustom (name init doc &rest forms)
220 (list 'defvar name init doc))))
221
222(defgroup strokes nil 223(defgroup strokes nil
223 "Control Emacs through mouse strokes" 224 "Control Emacs through mouse strokes"
225 :link '(url-link "http://www.mit.edu/people/cadet/strokes-help.html")
224 :group 'mouse) 226 :group 'mouse)
225 227
226(defcustom strokes-modeline-string " Strokes" 228(defcustom strokes-modeline-string " Strokes"
227 "*Modeline identification when strokes are on \(default is \" Strokes\"\)." 229 "*Modeline identification when strokes-mode is on \(default is \" Strokes\"\)."
228 :type 'string 230 :type 'string
229 :group 'strokes) 231 :group 'strokes)
230 232
231(defcustom strokes-character ?@ 233(defcustom strokes-character ?@
232 "*Character used when drawing strokes in the strokes buffer. 234 "*Character used when drawing strokes in the strokes buffer.
233\(The default is lower-case `@', which works okay\)." 235\(The default is `@', which works well.\)"
234 :type 'character 236 :type 'character
235 :group 'strokes) 237 :group 'strokes)
236 238
237(defcustom strokes-minimum-match-score 1000 239(defcustom strokes-minimum-match-score 1000
238 "*Minimum score for a stroke to be considered a possible match. 240 "*Minimum score for a stroke to be considered a possible match.
239Requiring a perfect match would set this variable to 0. 241Setting this variable to 0 would require a perfectly precise match.
240The default value is 1000, but it's mostly dependent on how precisely 242The default value is 1000, but it's mostly dependent on how precisely
241you manage to replicate your user-defined strokes. It also depends on 243you manage to replicate your user-defined strokes. It also depends on
242the value of `strokes-grid-resolution', since a higher grid resolution 244the value of `strokes-grid-resolution', since a higher grid resolution
@@ -294,9 +296,20 @@ This is set to `mouse-yank-at-click' by default."
294 296
295;;; internal variables... 297;;; internal variables...
296 298
299;; This is an internal variable, but we defcustom it so Customize can
300;; use it.
297;;;###autoload 301;;;###autoload
298(defvar strokes-mode nil 302(defcustom strokes-mode nil
299 "Non-nil when `strokes' is globally enabled") 303 "Non-nil when `strokes' is globally enabled.
304Setting this variable directly does not take effect. Use either Customize
305or M-x strokes-mode."
306 :type 'boolean
307 :set (lambda (symbol value)
308 (strokes-mode (or value 0)))
309 :initialize 'custom-initialize-default
310 :require 'strokes
311 :version "21.1"
312 :group 'strokes)
300 313
301(defvar strokes-window-configuration nil 314(defvar strokes-window-configuration nil
302 "The special window configuration used when entering strokes. 315 "The special window configuration used when entering strokes.
@@ -318,8 +331,25 @@ corresponding interactive function")
318(defvar strokes-load-hook nil 331(defvar strokes-load-hook nil
319 "Function or functions to be called when `strokes' is loaded.") 332 "Function or functions to be called when `strokes' is loaded.")
320 333
334;;; ### NOT IMPLEMENTED YET ###
335;;(defvar edit-strokes-menu
336;; '("Edit-Strokes"
337;; ["Add stroke..." strokes-global-set-stroke t]
338;; ["Delete stroke..." strokes-edit-delete-stroke t]
339;; ["Change stroke" strokes-smaller t]
340;; ["Change definition" strokes-larger t]
341;; ["[Re]List Strokes chronologically" strokes-list-strokes t]
342;; ["[Re]List Strokes alphabetically" strokes-list-strokes t]
343;; ["Quit" strokes-edit-quit t]
344;; ))
345
321;;; Macros... 346;;; Macros...
322 347
348(defmacro strokes-while-inhibiting-garbage-collector (&rest forms)
349 "Execute FORMS without interference from the garbage collector."
350 `(let ((gc-cons-threshold 134217727))
351 ,@forms))
352
323(defsubst strokes-click-p (stroke) 353(defsubst strokes-click-p (stroke)
324 "Non-nil if STROKE is really click." 354 "Non-nil if STROKE is really click."
325 (< (length stroke) 2)) 355 (< (length stroke) 2))
@@ -334,10 +364,10 @@ corresponding interactive function")
334;; (list 'remassoc stroke stroke-map))))) 364;; (list 'remassoc stroke stroke-map)))))
335 365
336(defsubst strokes-remassoc (key list) 366(defsubst strokes-remassoc (key list)
337 (remove-if 367 (let (elt)
338 (lambda (element) 368 (while (setq elt (assoc key list))
339 (equal key (car element))) 369 (setq list (delete elt list))))
340 list)) 370 list)
341 371
342(defmacro strokes-define-stroke (stroke-map stroke def) 372(defmacro strokes-define-stroke (stroke-map stroke def)
343 "Add STROKE to STROKE-MAP alist with given command DEF." 373 "Add STROKE to STROKE-MAP alist with given command DEF."
@@ -409,39 +439,41 @@ and which is an interactive funcion of one event argument:
409 `(progn 439 `(progn
410 (defadvice ,command (around strokes-fix-button2 compile preactivate) 440 (defadvice ,command (around strokes-fix-button2 compile preactivate)
411 ,(format "Fix %s to work with strokes." command) 441 ,(format "Fix %s to work with strokes." command)
412 (if strokes-use-strokes-buffer 442 (let ((strokes-click-command
413 ;; then strokes is no good and we'll have to use the original
414 ad-do-it
415 ;; otherwise, we can make strokes work too...
416 (let ((strokes-click-command
417 ',(intern (format "ad-Orig-%s" command)))) 443 ',(intern (format "ad-Orig-%s" command))))
418 (strokes-do-stroke (ad-get-arg 0)))))))) 444 (strokes-do-stroke (ad-get-arg 0)))))))
419 445
420(strokes-fix-button2-command 'vm-mouse-button-2) 446(defvar strokes-insinuated nil)
421(strokes-fix-button2-command 'rmail-summary-mouse-goto-msg) 447
422(strokes-fix-button2-command 'Buffer-menu-mouse-select) 448(defun strokes-insinuate ()
423(strokes-fix-button2-command 'w3-widget-button-click) 449 "Insinuate Emacs with strokes advices."
424(strokes-fix-button2-command 'widget-image-button-press) 450 (unless strokes-insinuated
425(strokes-fix-button2-command 'Info-follow-clicked-node) 451 (strokes-fix-button2-command 'vm-mouse-button-2)
426(strokes-fix-button2-command 'compile-mouse-goto-error) 452 (strokes-fix-button2-command 'rmail-summary-mouse-goto-msg)
427(strokes-fix-button2-command 'gdbsrc-select-or-yank) 453 (strokes-fix-button2-command 'Buffer-menu-mouse-select)
428(strokes-fix-button2-command 'hypropos-mouse-get-doc) 454 (strokes-fix-button2-command 'w3-widget-button-click)
429(strokes-fix-button2-command 'gnus-mouse-pick-group) 455 (strokes-fix-button2-command 'widget-image-button-press)
430(strokes-fix-button2-command 'gnus-mouse-pick-article) 456 (strokes-fix-button2-command 'Info-follow-clicked-node)
431(strokes-fix-button2-command 'gnus-article-push-button) 457 (strokes-fix-button2-command 'compile-mouse-goto-error)
432(strokes-fix-button2-command 'dired-mouse-find-file) 458 (strokes-fix-button2-command 'gdbsrc-select-or-yank)
433(strokes-fix-button2-command 'url-dired-find-file-mouse) 459 (strokes-fix-button2-command 'hypropos-mouse-get-doc)
434(strokes-fix-button2-command 'dired-u-r-mouse-toggle) 460 (strokes-fix-button2-command 'gnus-mouse-pick-group)
435(strokes-fix-button2-command 'dired-u-w-mouse-toggle) 461 (strokes-fix-button2-command 'gnus-mouse-pick-article)
436(strokes-fix-button2-command 'dired-u-x-mouse-toggle) 462 (strokes-fix-button2-command 'gnus-article-push-button)
437(strokes-fix-button2-command 'dired-g-r-mouse-toggle) 463 (strokes-fix-button2-command 'dired-mouse-find-file)
438(strokes-fix-button2-command 'dired-g-w-mouse-toggle) 464 (strokes-fix-button2-command 'url-dired-find-file-mouse)
439(strokes-fix-button2-command 'dired-g-x-mouse-toggle) 465 (strokes-fix-button2-command 'dired-u-r-mouse-toggle)
440(strokes-fix-button2-command 'dired-o-r-mouse-toggle) 466 (strokes-fix-button2-command 'dired-u-w-mouse-toggle)
441(strokes-fix-button2-command 'dired-o-w-mouse-toggle) 467 (strokes-fix-button2-command 'dired-u-x-mouse-toggle)
442(strokes-fix-button2-command 'isearch-yank-x-selection) 468 (strokes-fix-button2-command 'dired-g-r-mouse-toggle)
443(strokes-fix-button2-command 'occur-mode-mouse-goto) 469 (strokes-fix-button2-command 'dired-g-w-mouse-toggle)
444(strokes-fix-button2-command 'cvs-mouse-find-file) 470 (strokes-fix-button2-command 'dired-g-x-mouse-toggle)
471 (strokes-fix-button2-command 'dired-o-r-mouse-toggle)
472 (strokes-fix-button2-command 'dired-o-w-mouse-toggle)
473 (strokes-fix-button2-command 'isearch-yank-x-selection)
474 (strokes-fix-button2-command 'occur-mode-mouse-goto)
475 (strokes-fix-button2-command 'cvs-mouse-find-file)
476 (setq strokes-insinuated t)))
445 477
446;;; I can fix the customize widget button click, but then 478;;; I can fix the customize widget button click, but then
447;;; people will get confused when they try to customize 479;;; people will get confused when they try to customize
@@ -462,9 +494,20 @@ and which is an interactive funcion of one event argument:
462;;; Functions... 494;;; Functions...
463 495
464(defsubst strokes-mouse-event-p (event) 496(defsubst strokes-mouse-event-p (event)
465 (or (motion-event-p event) 497 (and (consp event) (symbolp (car event))
466 (button-press-event-p event) 498 (or (eq (car event) 'mouse-movement)
467 (button-release-event-p event))) 499 (memq 'click (get (car event) 'event-symbol-elements))
500 (memq 'down (get (car event) 'event-symbol-elements))
501 (memq 'drag (get (car event) 'event-symbol-elements)))))
502
503(defsubst strokes-button-press-event-p (event)
504 (and (consp event) (symbolp (car event))
505 (memq 'down (get (car event) 'event-symbol-elements))))
506
507(defsubst strokes-button-release-event-p (event)
508 (and (consp event) (symbolp (car event))
509 (or (memq 'click (get (car event) 'event-symbol-elements))
510 (memq 'drag (get (car event) 'event-symbol-elements)))))
468 511
469(defun strokes-event-closest-point-1 (window &optional line) 512(defun strokes-event-closest-point-1 (window &optional line)
470 "Return position of start of line LINE in WINDOW. 513 "Return position of start of line LINE in WINDOW.
@@ -486,12 +529,12 @@ This is computed for the window where EVENT's motion started,
486or for window WINDOW if that is specified." 529or for window WINDOW if that is specified."
487 (or start-window (setq start-window (posn-window (event-start event)))) 530 (or start-window (setq start-window (posn-window (event-start event))))
488 (if (eq start-window (posn-window (event-end event))) 531 (if (eq start-window (posn-window (event-end event)))
489 (if (eq (event-point event) 'vertical-line) 532 (if (eq (posn-point (event-end event)) 'vertical-line)
490 (strokes-event-closest-point-1 start-window 533 (strokes-event-closest-point-1 start-window
491 (cdr (posn-col-row (event-end event)))) 534 (cdr (posn-col-row (event-end event))))
492 (if (eq (event-point event) 'mode-line) 535 (if (eq (posn-point (event-end event)) 'mode-line)
493 (strokes-event-closest-point-1 start-window) 536 (strokes-event-closest-point-1 start-window)
494 (event-point event))) 537 (posn-point (event-end event))))
495 ;; EVENT ended in some other window. 538 ;; EVENT ended in some other window.
496 (let* ((end-w (posn-window (event-end event))) 539 (let* ((end-w (posn-window (event-end event)))
497 (end-w-top) 540 (end-w-top)
@@ -506,7 +549,7 @@ or for window WINDOW if that is specified."
506 (window-start start-window))))) 549 (window-start start-window)))))
507 550
508(defun strokes-lift-p (object) 551(defun strokes-lift-p (object)
509 "Return non-nil if object is a stroke-lift." 552 "Return non-nil if OBJECT is a stroke-lift."
510 (eq object strokes-lift)) 553 (eq object strokes-lift))
511 554
512(defun strokes-unset-last-stroke () 555(defun strokes-unset-last-stroke ()
@@ -562,8 +605,8 @@ The grid is a square whose dimesion is [0,GRID-RESOLUTION)."
562 (ymin (cdar stroke-extent)) 605 (ymin (cdar stroke-extent))
563 ;; the `1+' is there to insure that the 606 ;; the `1+' is there to insure that the
564 ;; formula evaluates correctly at the boundaries 607 ;; formula evaluates correctly at the boundaries
565 (xmax (1+ (caadr stroke-extent))) 608 (xmax (1+ (car (cadr stroke-extent))))
566 (ymax (1+ (cdadr stroke-extent)))) 609 (ymax (1+ (cdr (cadr stroke-extent)))))
567 (cons (floor (* grid-resolution 610 (cons (floor (* grid-resolution
568 (/ (float (- x xmin)) 611 (/ (float (- x xmin))
569 (- xmax xmin)))) 612 (- xmax xmin))))
@@ -573,29 +616,6 @@ The grid is a square whose dimesion is [0,GRID-RESOLUTION)."
573 ((strokes-lift-p position) ; stroke lift 616 ((strokes-lift-p position) ; stroke lift
574 strokes-lift))) 617 strokes-lift)))
575 618
576;;(defun strokes-get-grid-position (stroke-extent pix-pos)
577;; "Return the stroke-grid position for PIX-POS given the total STROKE-EXTENT.
578;;STROKE-EXTENT as a list \(\(xmin . ymin\) \(xmax . ymax\)\) and a particular
579;;pixel position or `strokes-lift', find the corresponding grid position
580;;\(based on `strokes-grid-resolution'\) for the PIX-POS."
581;; (cond ((consp pix-pos) ; actual pixel location
582;; (let ((x (car pix-pos))
583;; (y (cdr pix-pos))
584;; (xmin (caar stroke-extent))
585;; (ymin (cdar stroke-extent))
586;; ;; the `1+' is there to insure that the
587;; ;; formula evaluates correctly at the boundaries
588;; (xmax (1+ (caadr stroke-extent)))
589;; (ymax (1+ (cdadr stroke-extent))))
590;; (cons (floor (* strokes-grid-resolution
591;; (/ (float (- x xmin))
592;; (- xmax xmin))))
593;; (floor (* strokes-grid-resolution
594;; (/ (float (- y ymin))
595;; (- ymax ymin)))))))
596;; ((strokes-lift-p pix-pos) ; stroke lift
597;; strokes-lift)))
598
599(defun strokes-get-stroke-extent (pixel-positions) 619(defun strokes-get-stroke-extent (pixel-positions)
600 "From a list of absolute PIXEL-POSITIONS, returns absolute spatial extent. 620 "From a list of absolute PIXEL-POSITIONS, returns absolute spatial extent.
601The return value is a list ((XMIN . YMIN) (XMAX . YMAX))." 621The return value is a list ((XMIN . YMIN) (XMAX . YMAX))."
@@ -674,16 +694,6 @@ The grid is a square whose dimesion is [0,GRID-RESOLUTION)."
674 (strokes-get-grid-position stroke-extent pos grid-resolution))) 694 (strokes-get-grid-position stroke-extent pos grid-resolution)))
675 positions))) 695 positions)))
676 696
677;;(defun strokes-normalize-pixels-to-grid (pixel-positions)
678;; "Map PIXEL-POSITIONS to the stroke grid.
679;;PIXEL-POSITIONS is a list of pixel-positions and stroke-lifts. The
680;;normalized stroke grid is defined by the variable STROKES-GRID-RESOLUTION"
681;; (let ((stroke-extent (strokes-get-stroke-extent pixel-positions)))
682;; (mapcar (function
683;; (lambda (pix-pos)
684;; (strokes-get-grid-position stroke-extent pix-pos)))
685;; pixel-positions)))
686
687(defun strokes-fill-stroke (unfilled-stroke &optional force) 697(defun strokes-fill-stroke (unfilled-stroke &optional force)
688 "Fill in missing grid locations in the list of UNFILLED-STROKE. 698 "Fill in missing grid locations in the list of UNFILLED-STROKE.
689If FORCE is non-nil, then fill the stroke even if it's `stroke-click'. 699If FORCE is non-nil, then fill the stroke even if it's `stroke-click'.
@@ -848,13 +858,13 @@ Optional EVENT is acceptable as the starting event of the stroke"
848 (when prompt 858 (when prompt
849 (message prompt) 859 (message prompt)
850 (setq event (read-event)) 860 (setq event (read-event))
851 (or (button-press-event-p event) 861 (or (strokes-button-press-event-p event)
852 (error "You must draw with the mouse"))) 862 (error "You must draw with the mouse")))
853 (unwind-protect 863 (unwind-protect
854 (track-mouse 864 (track-mouse
855 (or event (setq event (read-event) 865 (or event (setq event (read-event)
856 safe-to-draw-p t)) 866 safe-to-draw-p t))
857 (while (not (button-release-event-p event)) 867 (while (not (strokes-button-release-event-p event))
858 (if (strokes-mouse-event-p event) 868 (if (strokes-mouse-event-p event)
859 (let ((point (strokes-event-closest-point event))) 869 (let ((point (strokes-event-closest-point event)))
860 (if (and point safe-to-draw-p) 870 (if (and point safe-to-draw-p)
@@ -864,8 +874,7 @@ Optional EVENT is acceptable as the starting event of the stroke"
864 (subst-char-in-region point (1+ point) ?\ strokes-character)) 874 (subst-char-in-region point (1+ point) ?\ strokes-character))
865 ;; otherwise, we can start drawing the next time... 875 ;; otherwise, we can start drawing the next time...
866 (setq safe-to-draw-p t)) 876 (setq safe-to-draw-p t))
867 (push (cons (event-x-pixel event) 877 (push (cdr (mouse-pixel-position))
868 (event-y-pixel event))
869 pix-locs))) 878 pix-locs)))
870 (setq event (read-event))))) 879 (setq event (read-event)))))
871 ;; protected 880 ;; protected
@@ -878,14 +887,13 @@ Optional EVENT is acceptable as the starting event of the stroke"
878 (when prompt 887 (when prompt
879 (message prompt) 888 (message prompt)
880 (setq event (read-event)) 889 (setq event (read-event))
881 (or (button-press-event-p event) 890 (or (strokes-button-press-event-p event)
882 (error "You must draw with the mouse"))) 891 (error "You must draw with the mouse")))
883 (track-mouse 892 (track-mouse
884 (or event (setq event (read-event))) 893 (or event (setq event (read-event)))
885 (while (not (button-release-event-p event)) 894 (while (not (strokes-button-release-event-p event))
886 (if (strokes-mouse-event-p event) 895 (if (strokes-mouse-event-p event)
887 (push (cons (event-x-pixel event) 896 (push (cdr (mouse-pixel-position))
888 (event-y-pixel event))
889 pix-locs)) 897 pix-locs))
890 (setq event (read-event)))) 898 (setq event (read-event))))
891 (setq grid-locs (strokes-renormalize-to-grid (nreverse pix-locs))) 899 (setq grid-locs (strokes-renormalize-to-grid (nreverse pix-locs)))
@@ -905,31 +913,32 @@ Optional EVENT is acceptable as the starting event of the stroke"
905 (let ((pix-locs nil) 913 (let ((pix-locs nil)
906 (grid-locs nil)) 914 (grid-locs nil))
907 (if prompt 915 (if prompt
908 (while (not (button-press-event-p event)) 916 (while (not (strokes-button-press-event-p event))
909 (message prompt) 917 (message prompt)
910 (setq event (read-event)))) 918 (setq event (read-event))))
911 (unwind-protect 919 (unwind-protect
912 (track-mouse 920 (track-mouse
913 (or event (setq event (read-event))) 921 (or event (setq event (read-event)))
914 (while (not (and (button-press-event-p event) 922 (while (not (and (strokes-button-press-event-p event)
915 (eq (event-button event) 3))) 923 (eq 'mouse-3
916 (while (not (button-release-event-p event)) 924 (car (get (car event)
925 'event-symbol-elements)))))
926 (while (not (strokes-button-release-event-p event))
917 (if (strokes-mouse-event-p event) 927 (if (strokes-mouse-event-p event)
918 (let ((point (strokes-event-closest-point event))) 928 (let ((point (strokes-event-closest-point event)))
919 (when point 929 (when point
920 (goto-char point) 930 (goto-char point)
921 (subst-char-in-region point (1+ point) ?\ strokes-character)) 931 (subst-char-in-region point (1+ point) ?\ strokes-character))
922 (push (cons (event-x-pixel event) 932 (push (cdr (mouse-pixel-position))
923 (event-y-pixel event))
924 pix-locs))) 933 pix-locs)))
925 (setq event (read-event))) 934 (setq event (read-event)))
926 (push strokes-lift pix-locs) 935 (push strokes-lift pix-locs)
927 (while (not (button-press-event-p event)) 936 (while (not (strokes-button-press-event-p event))
928 (setq event (read-event)))) 937 (setq event (read-event))))
929 ;; ### KLUDGE! ### sit and wait 938 ;; ### KLUDGE! ### sit and wait
930 ;; for some useless event to 939 ;; for some useless event to
931 ;; happen to fix the minibuffer bug. 940 ;; happen to fix the minibuffer bug.
932 (while (not (button-release-event-p (read-event)))) 941 (while (not (strokes-button-release-event-p (read-event))))
933 (setq pix-locs (nreverse (cdr pix-locs)) 942 (setq pix-locs (nreverse (cdr pix-locs))
934 grid-locs (strokes-renormalize-to-grid pix-locs)) 943 grid-locs (strokes-renormalize-to-grid pix-locs))
935 (strokes-fill-stroke 944 (strokes-fill-stroke
@@ -949,7 +958,16 @@ If no stroke matches, nothing is done and return value is nil."
949 (command (car match)) 958 (command (car match))
950 (score (cdr match))) 959 (score (cdr match)))
951 (cond ((strokes-click-p stroke) 960 (cond ((strokes-click-p stroke)
952 ;; This is the case of a `click' type event 961 ;; This is the case of a `click' type event.
962 ;; The `sit-for' is a minor frob that has to do with timing
963 ;; problems. Without the `sit-for', mouse-yank will not
964 ;; yank at the proper location if the user opted for
965 ;; mouse-yank-at-point to be nil (i.e. mouse-yank takes
966 ;; place at pointer position). The sit-for tells redisplay
967 ;; to be sure to wait for the `*strokes*' buffer to vanish
968 ;; from consideration when deciding on a point to be used
969 ;; for mouse-yank.
970 (sit-for 0)
953 (command-execute strokes-click-command)) 971 (command-execute strokes-click-command))
954 ((and match (<= score strokes-minimum-match-score)) 972 ((and match (<= score strokes-minimum-match-score))
955 (message "%s" command) 973 (message "%s" command)
@@ -968,7 +986,7 @@ If no stroke matches, nothing is done and return value is nil."
968 986
969;;;###autoload 987;;;###autoload
970(defun strokes-do-stroke (event) 988(defun strokes-do-stroke (event)
971 "Read a simple stroke from the user and then exectute its comand. 989 "Read a simple stroke from the user and then exectute its command.
972This must be bound to a mouse event." 990This must be bound to a mouse event."
973 (interactive "e") 991 (interactive "e")
974 (or strokes-mode (strokes-mode t)) 992 (or strokes-mode (strokes-mode t))
@@ -1007,7 +1025,7 @@ This must be bound to a mouse event."
1007 1025
1008;;;###autoload 1026;;;###autoload
1009(defun strokes-help () 1027(defun strokes-help ()
1010 "Get instructional help on using the the `strokes' package." 1028 "Get instructional help on using the `strokes' package."
1011 (interactive) 1029 (interactive)
1012 (with-output-to-temp-buffer "*Help with Strokes*" 1030 (with-output-to-temp-buffer "*Help with Strokes*"
1013 (let ((helpdoc 1031 (let ((helpdoc
@@ -1032,6 +1050,14 @@ corresponding to commands, and then executes the commands. It does
1032character recognition, so you don't have to worry about getting it 1050character recognition, so you don't have to worry about getting it
1033right every time. 1051right every time.
1034 1052
1053Strokes also allows you to compose documents graphically. You can
1054fully edit documents in Chinese, Japanese, etc. based on XEmacs
1055strokes. Once you've done so, you can ascii compress-and-encode them
1056and then safely save them for later use, send letters to friends
1057\(using Emacs, of course). Strokes will later decode these documents,
1058extracting the strokes for editing use once again, so the editing
1059cycle can continue.
1060
1035Strokes are easy to program and fun to use. To start strokes going, 1061Strokes are easy to program and fun to use. To start strokes going,
1036you'll want to put the following line in your .emacs file as mentioned 1062you'll want to put the following line in your .emacs file as mentioned
1037in the commentary to strokes.el. 1063in the commentary to strokes.el.
@@ -1043,7 +1069,7 @@ To toggle strokes-mode, you just do
1043 1069
1044> M-x strokes-mode 1070> M-x strokes-mode
1045 1071
1046** Strokes for controling the behavior of Emacs... 1072** Strokes for controlling the behavior of Emacs...
1047 1073
1048When you're ready to start defining strokes, just use the command 1074When you're ready to start defining strokes, just use the command
1049 1075
@@ -1058,7 +1084,7 @@ edit command, so type
1058 1084
1059> M-x global-set-stroke 1085> M-x global-set-stroke
1060 1086
1061Then, in the ` *strokes*' buffer, draw the letter `C' (for `copy'\) 1087Then, in the ` *strokes*' buffer, draw the letter `C' (for `copy')
1062and then, when it asks you to enter the command to map that to, type 1088and then, when it asks you to enter the command to map that to, type
1063 1089
1064> copy-region-as-kill 1090> copy-region-as-kill
@@ -1071,10 +1097,10 @@ If ever you want to know what a certain strokes maps to, then do
1071> M-x describe-stroke 1097> M-x describe-stroke
1072 1098
1073and you can enter in any arbitrary stroke. Remember: The strokes 1099and you can enter in any arbitrary stroke. Remember: The strokes
1074package lets you program in simple and complex, or multi-lift, strokes. 1100package lets you program in simple and complex (multi-lift) strokes.
1075The only difference is how you *invoke* the two. You will most likely 1101The only difference is how you *invoke* the two. You will most likely
1076use simple strokes, as complex strokes were developed for 1102use simple strokes, as complex strokes were developed for
1077Chinese/Japanese/Korean. So the middle mouse button, button2, will 1103Chinese/Japanese/Korean. So the middle mouse button (mouse-2) will
1078invoke the command `strokes-do-stroke' in buffers where button2 doesn't 1104invoke the command `strokes-do-stroke' in buffers where button2 doesn't
1079already have a meaning other than its original, which is `mouse-yank'. 1105already have a meaning other than its original, which is `mouse-yank'.
1080But don't worry: `mouse-yank' will still work with strokes. See the 1106But don't worry: `mouse-yank' will still work with strokes. See the
@@ -1085,6 +1111,20 @@ it with the command
1085 1111
1086> M-x strokes-unset-last-stroke 1112> M-x strokes-unset-last-stroke
1087 1113
1114You can always get an idea of what your current strokes look like with
1115the command
1116
1117> M-x strokes-list-strokes
1118
1119Your strokes will be displayed in alphabetical order (based on command
1120names) and the beginning of each simple stroke will be marked by a
1121color dot. Since you may have several simple strokes in a complex
1122stroke, the dot colors are arranged in the rainbow color sequence,
1123`ROYGBIV'. If you want a listing of your strokes from most recent
1124down, then use a prefix argument:
1125
1126> C-u M-x strokes-list-strokes
1127
1088Your strokes are stored as you enter them. They get saved in a file 1128Your strokes are stored as you enter them. They get saved in a file
1089called ~/.strokes, along with other strokes configuration variables. 1129called ~/.strokes, along with other strokes configuration variables.
1090You can change this location by setting the variable `strokes-file'. 1130You can change this location by setting the variable `strokes-file'.
@@ -1098,11 +1138,29 @@ You can also load in your user-defined strokes with
1098 1138
1099> M-x load-user-strokes 1139> M-x load-user-strokes
1100 1140
1141** Strokes for pictographic editing...
1142
1143If you'd like to create graphical files with strokes, you'll have to
1144be running a version of Emacs with XPM support. You use the
1145binding C-mouse-2 to start drawing your strokes. These are just
1146complex strokes, and thus you continue drawing with mouse-1 or mouse-2 and
1147end with mouse-3-3. Then the stroke image gets inserted into the
1148buffer. You treat it like any other character, which you can copy,
1149paste, delete, move, etc. The command which is bound to C-mouse-2 is
1150called `strokes-compose-complex-stroke'. When all is done, you may
1151want to send the file, or save it. This is done with
1152
1153> M-x strokes-encode-buffer
1154
1155Likewise, to decode the strokes from a strokes-encoded buffer you do
1156
1157> M-x strokes-decode-buffer
1158
1101** A few more important things... 1159** A few more important things...
1102 1160
1103o The command `strokes-do-stroke' is also invoked with M-button2, so that you 1161o The command `strokes-do-complex-stroke' is invoked with M-mouse-2,
1104 can still enter a stroke in modes which use button2 for other things, 1162 so that you can execute complex strokes (i.e. with more than one lift)
1105 such as cross-referencing. 1163 if preferred.
1106 1164
1107o Strokes are a bit computer-dependent in that they depend somewhat on 1165o Strokes are a bit computer-dependent in that they depend somewhat on
1108 the speed of the computer you're working on. This means that you 1166 the speed of the computer you're working on. This means that you
@@ -1115,19 +1173,16 @@ o Strokes are a bit computer-dependent in that they depend somewhat on
1115 by customizing the group named `strokes' via the customization package: 1173 by customizing the group named `strokes' via the customization package:
1116 1174
1117 > M-x customize")) 1175 > M-x customize"))
1118 (save-excursion 1176 (with-output-to-temp-buffer "*Help"
1119 (princ helpdoc) 1177 (princ helpdoc)
1120 (set-buffer standard-output) 1178 (set-buffer standard-output)
1121 (help-mode)) 1179 (help-mode))
1122 (print-help-return-message)))) 1180 (print-help-return-message))))
1123 1181
1124(defun strokes-report-bug () 1182(defun strokes-report-bug ()
1125 "Submit a bug report for strokes." 1183 "Submit a bug report for strokes."
1126 (interactive) 1184 (interactive)
1127 (let ((reporter-prompt-for-summary-p t)) 1185 (let ((reporter-prompt-for-summary-p t))
1128 (or (boundp 'reporter-version)
1129 (setq reporter-version
1130 "Your version of reporter is obsolete. Please upgrade."))
1131 (reporter-submit-bug-report 1186 (reporter-submit-bug-report
1132 strokes-bug-address "Strokes" 1187 strokes-bug-address "Strokes"
1133 (cons 1188 (cons
@@ -1153,15 +1208,21 @@ o Strokes are a bit computer-dependent in that they depend somewhat on
1153 (insert " " strokes-version " bug:"))))))))) 1208 (insert " " strokes-version " bug:")))))))))
1154 1209
1155(defsubst strokes-fill-current-buffer-with-whitespace () 1210(defsubst strokes-fill-current-buffer-with-whitespace ()
1156 "Erase the contents of the current buffer and fill it with whitespace" 1211 "Erase the contents of the current buffer and fill it with whitespace."
1157 (erase-buffer) 1212 (erase-buffer)
1158 (loop repeat (frame-height) do 1213 (loop repeat (frame-height) do
1159 (insert-char ?\ (1- (frame-width))) 1214 (insert-char ?\ (1- (frame-width)))
1160 (newline)) 1215 (newline))
1161 (goto-char (point-min))) 1216 (goto-char (point-min)))
1162 1217
1218(defun strokes-window-configuration-changed-p ()
1219 "Non-nil if the `strokes-window-configuration' frame properties changed.
1220This is based on the last time the `strokes-window-configuration was updated."
1221 (compare-window-configurations (current-window-configuration)
1222 strokes-window-configuration))
1223
1163(defun strokes-update-window-configuration () 1224(defun strokes-update-window-configuration ()
1164 "Insure that `strokes-window-configuration' is up-to-date." 1225 "Ensure that `strokes-window-configuration' is up-to-date."
1165 (interactive) 1226 (interactive)
1166 (let ((current-window (selected-window))) 1227 (let ((current-window (selected-window)))
1167 (cond ((or (window-minibuffer-p current-window) 1228 (cond ((or (window-minibuffer-p current-window)
@@ -1170,7 +1231,7 @@ o Strokes are a bit computer-dependent in that they depend somewhat on
1170 ;; if window is dedicated or a minibuffer 1231 ;; if window is dedicated or a minibuffer
1171 nil) 1232 nil)
1172 ((or (interactive-p) 1233 ((or (interactive-p)
1173 (not (bufferp (get-buffer strokes-buffer-name))) 1234 (not (buffer-live-p (get-buffer strokes-buffer-name)))
1174 (null strokes-window-configuration)) 1235 (null strokes-window-configuration))
1175 ;; create `strokes-window-configuration' from scratch... 1236 ;; create `strokes-window-configuration' from scratch...
1176 (save-excursion 1237 (save-excursion
@@ -1188,8 +1249,9 @@ o Strokes are a bit computer-dependent in that they depend somewhat on
1188 (strokes-fill-current-buffer-with-whitespace) 1249 (strokes-fill-current-buffer-with-whitespace)
1189 (setq strokes-window-configuration (current-window-configuration)) 1250 (setq strokes-window-configuration (current-window-configuration))
1190 (bury-buffer)))) 1251 (bury-buffer))))
1191 (t ; `strokes buffer' still exists... 1252 ((strokes-window-configuration-changed-p) ; simple update
1192 ;; update the strokes-window-configuration for this specific frame... 1253 ;; update the strokes-window-configuration for this
1254 ;; specific frame...
1193 (save-excursion 1255 (save-excursion
1194 (save-window-excursion 1256 (save-window-excursion
1195 (set-window-buffer current-window strokes-buffer-name) 1257 (set-window-buffer current-window strokes-buffer-name)
@@ -1264,6 +1326,226 @@ Returns value of `strokes-use-strokes-buffer'."
1264 (if arg (> (prefix-numeric-value arg) 0) 1326 (if arg (> (prefix-numeric-value arg) 0)
1265 (not strokes-use-strokes-buffer)))) 1327 (not strokes-use-strokes-buffer))))
1266 1328
1329(defun strokes-xpm-for-stroke (&optional stroke bufname b/w-only)
1330 "Create an xpm pixmap for the given STROKE in buffer ` *strokes-xpm*'.
1331If STROKE is not supplied, then `strokes-last-stroke' will be used.
1332Optional BUFNAME to name something else.
1333The pixmap will contain time information via rainbow dot colors
1334where each individual strokes begins.
1335Optional B/W-ONLY non-nil will create a mono pixmap, not intended
1336for trying to figure out the order of strokes, but rather for reading
1337the stroke as a character in some language."
1338 (interactive)
1339 (save-excursion
1340 (let ((buf (get-buffer-create (or bufname " *strokes-xpm*")))
1341 (stroke (strokes-eliminate-consecutive-redundancies
1342 (strokes-fill-stroke
1343 (strokes-renormalize-to-grid (or stroke
1344 strokes-last-stroke)
1345 31))))
1346 (lift-flag t)
1347 (rainbow-chars (list ?R ?O ?Y ?G ?B ?P))) ; ROYGBIV w/o indigo
1348 (set-buffer buf)
1349 (erase-buffer)
1350 (insert strokes-xpm-header)
1351 (loop repeat 33 do
1352 (insert ?\")
1353 (insert-char ?\ 33)
1354 (insert "\",")
1355 (newline)
1356 finally
1357 (forward-line -1)
1358 (end-of-line)
1359 (insert "}\n"))
1360 (loop for point in stroke
1361 for x = (car-safe point)
1362 for y = (cdr-safe point) do
1363 (cond ((consp point)
1364 ;; draw a point, and possibly a starting-point
1365 (if (and lift-flag (not b/w-only))
1366 ;; mark starting point with the appropriate color
1367 (let ((char (or (car rainbow-chars) ?\.)))
1368 (loop for i from 0 to 2 do
1369 (loop for j from 0 to 2 do
1370 (goto-line (+ 16 i y))
1371 (forward-char (+ 1 j x))
1372 (delete-char 1)
1373 (insert char)))
1374 (setq rainbow-chars (cdr rainbow-chars)
1375 lift-flag nil))
1376 ;; Otherwise, just plot the point...
1377 (goto-line (+ 17 y))
1378 (forward-char (+ 2 x))
1379 (subst-char-in-region (point) (1+ (point)) ?\ ?\*)))
1380 ((strokes-lift-p point)
1381 ;; a lift--tell the loop to X out the next point...
1382 (setq lift-flag t))))
1383 (when (interactive-p)
1384 (pop-to-buffer " *strokes-xpm*")
1385 ;; (xpm-mode 1)
1386 (goto-char (point-min))
1387 (put-image (create-image (buffer-string) 'xpm t :ascent 100)
1388 (line-end-position))))))
1389
1390;;; Strokes Edit stuff... ### NOT IMLEMENTED YET ###
1391
1392;;(defun strokes-edit-quit ()
1393;; (interactive)
1394;; (or (one-window-p t 0)
1395;; (delete-window))
1396;; (kill-buffer "*Strokes List*"))
1397
1398;;(define-derived-mode edit-strokes-mode list-mode
1399;; "Edit-Strokes"
1400;; "Major mode for `edit-strokes' and `list-strokes' buffers.
1401
1402;;Editing commands:
1403
1404;;\\{edit-strokes-mode-map}"
1405;; (setq truncate-lines nil
1406;; auto-show-mode nil ; don't want problems here either
1407;; mode-popup-menu edit-strokes-menu) ; what about extent-specific stuff?
1408;; (and (featurep 'menubar)
1409;; current-menubar
1410;; (set (make-local-variable 'current-menubar)
1411;; (copy-sequence current-menubar))
1412;; (add-submenu nil edit-strokes-menu)))
1413
1414;;(let ((map edit-strokes-mode-map))
1415;; (define-key map "<" 'beginning-of-buffer)
1416;; (define-key map ">" 'end-of-buffer)
1417;; ;; (define-key map "c" 'strokes-copy-other-face)
1418;; ;; (define-key map "C" 'strokes-copy-this-face)
1419;; ;; (define-key map "s" 'strokes-smaller)
1420;; ;; (define-key map "l" 'strokes-larger)
1421;; ;; (define-key map "b" 'strokes-bold)
1422;; ;; (define-key map "i" 'strokes-italic)
1423;; (define-key map "e" 'strokes-list-edit)
1424;; ;; (define-key map "f" 'strokes-font)
1425;; ;; (define-key map "u" 'strokes-underline)
1426;; ;; (define-key map "t" 'strokes-truefont)
1427;; ;; (define-key map "F" 'strokes-foreground)
1428;; ;; (define-key map "B" 'strokes-background)
1429;; ;; (define-key map "D" 'strokes-doc-string)
1430;; (define-key map "a" 'strokes-global-set-stroke)
1431;; (define-key map "d" 'strokes-list-delete-stroke)
1432;; ;; (define-key map "n" 'strokes-list-next)
1433;; ;; (define-key map "p" 'strokes-list-prev)
1434;; ;; (define-key map " " 'strokes-list-next)
1435;; ;; (define-key map "\C-?" 'strokes-list-prev)
1436;; (define-key map "g" 'strokes-list-strokes) ; refresh display
1437;; (define-key map "q" 'strokes-edit-quit)
1438;; (define-key map [(control c) (control c)] 'bury-buffer))
1439
1440;;;;;###autoload
1441;;(defun strokes-edit-strokes (&optional chronological strokes-map)
1442;; ;; ### DEAL WITH THE 2nd ARGUMENT ISSUE! ###
1443;; "Edit strokes in a pop-up buffer containing strokes and their definitions.
1444;;If STROKES-MAP is not given, `strokes-global-map' will be used instead.
1445
1446;;Editing commands:
1447
1448;;\\{edit-faces-mode-map}"
1449;; (interactive "P")
1450;; (pop-to-buffer (get-buffer-create "*Strokes List*"))
1451;; (reset-buffer (current-buffer)) ; handy function from minibuf.el
1452;; (setq strokes-map (or strokes-map
1453;; strokes-global-map
1454;; (progn
1455;; (strokes-load-user-strokes)
1456;; strokes-global-map)))
1457;; (or chronological
1458;; (setq strokes-map (sort (copy-sequence strokes-map)
1459;; 'strokes-alphabetic-lessp)))
1460;; ;; (push-window-configuration)
1461;; (insert
1462;; "Command Stroke\n"
1463;; "------- ------")
1464;; (loop for def in strokes-map
1465;; for i from 0 to (1- (length strokes-map)) do
1466;; (let ((stroke (car def))
1467;; (command-name (symbol-name (cdr def))))
1468;; (strokes-xpm-for-stroke stroke " *strokes-xpm*")
1469;; (newline 2)
1470;; (insert-char ?\ 45)
1471;; (beginning-of-line)
1472;; (insert command-name)
1473;; (beginning-of-line)
1474;; (forward-char 45)
1475;; (set (intern (format "strokes-list-annotation-%d" i))
1476;; (make-annotation (make-glyph
1477;; (list
1478;; (vector 'xpm
1479;; :data (buffer-substring
1480;; (point-min " *strokes-xpm*")
1481;; (point-max " *strokes-xpm*")
1482;; " *strokes-xpm*"))
1483;; [string :data "[Stroke]"]))
1484;; (point) 'text))
1485;; (set-annotation-data (symbol-value (intern (format "strokes-list-annotation-%d" i)))
1486;; def))
1487;; finally do (kill-region (1+ (point)) (point-max)))
1488;; (edit-strokes-mode)
1489;; (goto-char (point-min)))
1490
1491;;;;;###autoload
1492;;(defalias 'edit-strokes 'strokes-edit-strokes)
1493
1494(eval-when-compile (defvar view-mode-map))
1495
1496;;;###autoload
1497(defun strokes-list-strokes (&optional chronological strokes-map)
1498 "Pop up a buffer containing an alphabetical listing of strokes in STROKES-MAP.
1499With CHRONOLOGICAL prefix arg \(\\[universal-argument]\) list strokes
1500chronologically by command name.
1501If STROKES-MAP is not given, `strokes-global-map' will be used instead."
1502 (interactive "P")
1503 (setq strokes-map (or strokes-map
1504 strokes-global-map
1505 (progn
1506 (strokes-load-user-strokes)
1507 strokes-global-map)))
1508 (if (not chronological)
1509 ;; then alphabetize the strokes based on command names...
1510 (setq strokes-map (sort (copy-sequence strokes-map)
1511 (function strokes-alphabetic-lessp))))
1512 (let ((config (current-window-configuration)))
1513 (set-buffer (get-buffer-create "*Strokes List*"))
1514 (setq buffer-read-only nil)
1515 (erase-buffer)
1516 (insert
1517 "Command Stroke\n"
1518 "------- ------")
1519 (loop for def in strokes-map do
1520 (let ((stroke (car def))
1521 (command-name (symbol-name (cdr def))))
1522 (strokes-xpm-for-stroke stroke " *strokes-xpm*")
1523 (newline 2)
1524 (insert-char ?\ 45)
1525 (beginning-of-line)
1526 (insert command-name)
1527 (beginning-of-line)
1528 (forward-char 45)
1529 (insert-image (create-image (with-current-buffer " *strokes-xpm*"
1530 (buffer-string))
1531 'xpm t)))
1532 finally do (kill-region (1+ (point)) (point-max)))
1533 (view-buffer "*Strokes List*" t)
1534 (set (make-local-variable 'view-mode-map)
1535 (let ((map (copy-keymap view-mode-map)))
1536 (define-key map "q" `(lambda ()
1537 (interactive)
1538 (View-quit)
1539 (set-window-configuration ,config)))
1540 map))
1541 (goto-char (point-min))))
1542
1543(defun strokes-alphabetic-lessp (stroke1 stroke2)
1544 "T iff command name for STROKE1 is less than STROKE2's in lexicographic order."
1545 (let ((command-name-1 (symbol-name (cdr stroke1)))
1546 (command-name-2 (symbol-name (cdr stroke2))))
1547 (string-lessp command-name-1 command-name-2)))
1548
1267;;;###autoload 1549;;;###autoload
1268(defun strokes-mode (&optional arg) 1550(defun strokes-mode (&optional arg)
1269 "Toggle strokes being enabled. 1551 "Toggle strokes being enabled.
@@ -1276,7 +1558,7 @@ new strokes with
1276> M-x global-set-stroke 1558> M-x global-set-stroke
1277 1559
1278To use strokes for pictographic editing, such as Chinese/Japanese, use 1560To use strokes for pictographic editing, such as Chinese/Japanese, use
1279Sh-button-2, which draws strokes and inserts them. Encode/decode your 1561S-mouse-2, which draws strokes and inserts them. Encode/decode your
1280strokes with 1562strokes with
1281 1563
1282> M-x strokes-encode-buffer 1564> M-x strokes-encode-buffer
@@ -1288,6 +1570,7 @@ strokes with
1288 (cond ((not (display-mouse-p)) 1570 (cond ((not (display-mouse-p))
1289 (error "Can't use strokes without a mouse")) 1571 (error "Can't use strokes without a mouse"))
1290 (on-p ; turn on strokes 1572 (on-p ; turn on strokes
1573 (strokes-insinuate) ; make sure defadvices are set
1291 (and (file-exists-p strokes-file) 1574 (and (file-exists-p strokes-file)
1292 (null strokes-global-map) 1575 (null strokes-global-map)
1293 (strokes-load-user-strokes)) 1576 (strokes-load-user-strokes))
@@ -1297,7 +1580,8 @@ strokes with
1297 'strokes-update-window-configuration) 1580 'strokes-update-window-configuration)
1298 (strokes-update-window-configuration) 1581 (strokes-update-window-configuration)
1299 (define-key global-map [(down-mouse-2)] 'strokes-do-stroke) 1582 (define-key global-map [(down-mouse-2)] 'strokes-do-stroke)
1300 (define-key global-map [(meta down-mouse-2)] 'strokes-do-stroke) 1583 (define-key global-map [(meta down-mouse-2)]
1584 'strokes-do-complex-stroke)
1301 ;; (define-key global-map [(control down-mouse-2)] 'strokes-do-complex-stroke) 1585 ;; (define-key global-map [(control down-mouse-2)] 'strokes-do-complex-stroke)
1302 (ad-activate-regexp "^strokes-") ; advise button2 commands 1586 (ad-activate-regexp "^strokes-") ; advise button2 commands
1303 (setq strokes-mode t)) 1587 (setq strokes-mode t))
@@ -1309,13 +1593,344 @@ strokes with
1309 (if (string-match "^strokes-" (symbol-name (key-binding [(down-mouse-2)]))) 1593 (if (string-match "^strokes-" (symbol-name (key-binding [(down-mouse-2)])))
1310 (define-key global-map [(down-mouse-2)] strokes-click-command)) 1594 (define-key global-map [(down-mouse-2)] strokes-click-command))
1311 (if (string-match "^strokes-" (symbol-name (key-binding [(meta down-mouse-2)]))) 1595 (if (string-match "^strokes-" (symbol-name (key-binding [(meta down-mouse-2)])))
1312 (global-unset-key [(meta button2)])) 1596 (global-unset-key [(meta down-mouse-2)]))
1597 (if (string-match "^strokes-" (symbol-name (key-binding [(control down-mouse-2)])))
1598 (global-unset-key [(control down-mouse-2)]))
1313 ;; (if (string-match "^strokes-" (symbol-name (key-binding [(shift button2)]))) 1599 ;; (if (string-match "^strokes-" (symbol-name (key-binding [(shift button2)])))
1314 ;; (global-unset-key [(shift button2)])) 1600 ;; (global-unset-key [(shift button2)]))
1315 (ad-deactivate-regexp "^strokes-") ; unadvise strokes-button2 commands 1601 (ad-deactivate-regexp "^strokes-") ; unadvise strokes-button2 commands
1316 (setq strokes-mode nil)))) 1602 (setq strokes-mode nil))))
1317 (force-mode-line-update)) 1603 (force-mode-line-update))
1318 1604
1605;;;; strokes-xpm stuff (later may be separate)...
1606
1607;; This is the stuff that will eventuall be used for composing letters in
1608;; any language, compression, decompression, graphics, editing, etc.
1609
1610(defface strokes-char-face '((t (:background "lightgray")))
1611 "Face for strokes characters."
1612 :version "21.1"
1613 :group 'strokes)
1614
1615(put 'strokes 'char-table-extra-slots 0)
1616(defconst strokes-char-table (make-char-table 'strokes) ;
1617 "The table which stores values for the character keys.")
1618(aset strokes-char-table ?0 0)
1619(aset strokes-char-table ?1 1)
1620(aset strokes-char-table ?2 2)
1621(aset strokes-char-table ?3 3)
1622(aset strokes-char-table ?4 4)
1623(aset strokes-char-table ?5 5)
1624(aset strokes-char-table ?6 6)
1625(aset strokes-char-table ?7 7)
1626(aset strokes-char-table ?8 8)
1627(aset strokes-char-table ?9 9)
1628(aset strokes-char-table ?a 10)
1629(aset strokes-char-table ?b 11)
1630(aset strokes-char-table ?c 12)
1631(aset strokes-char-table ?d 13)
1632(aset strokes-char-table ?e 14)
1633(aset strokes-char-table ?f 15)
1634(aset strokes-char-table ?g 16)
1635(aset strokes-char-table ?h 17)
1636(aset strokes-char-table ?i 18)
1637(aset strokes-char-table ?j 19)
1638(aset strokes-char-table ?k 20)
1639(aset strokes-char-table ?l 21)
1640(aset strokes-char-table ?m 22)
1641(aset strokes-char-table ?n 23)
1642(aset strokes-char-table ?o 24)
1643(aset strokes-char-table ?p 25)
1644(aset strokes-char-table ?q 26)
1645(aset strokes-char-table ?r 27)
1646(aset strokes-char-table ?s 28)
1647(aset strokes-char-table ?t 29)
1648(aset strokes-char-table ?u 30)
1649(aset strokes-char-table ?v 31)
1650(aset strokes-char-table ?w 32)
1651(aset strokes-char-table ?x 33)
1652(aset strokes-char-table ?y 34)
1653(aset strokes-char-table ?z 35)
1654(aset strokes-char-table ?A 36)
1655(aset strokes-char-table ?B 37)
1656(aset strokes-char-table ?C 38)
1657(aset strokes-char-table ?D 39)
1658(aset strokes-char-table ?E 40)
1659(aset strokes-char-table ?F 41)
1660(aset strokes-char-table ?G 42)
1661(aset strokes-char-table ?H 43)
1662(aset strokes-char-table ?I 44)
1663(aset strokes-char-table ?J 45)
1664(aset strokes-char-table ?K 46)
1665(aset strokes-char-table ?L 47)
1666(aset strokes-char-table ?M 48)
1667(aset strokes-char-table ?N 49)
1668(aset strokes-char-table ?O 50)
1669(aset strokes-char-table ?P 51)
1670(aset strokes-char-table ?Q 52)
1671(aset strokes-char-table ?R 53)
1672(aset strokes-char-table ?S 54)
1673(aset strokes-char-table ?T 55)
1674(aset strokes-char-table ?U 56)
1675(aset strokes-char-table ?V 57)
1676(aset strokes-char-table ?W 58)
1677(aset strokes-char-table ?X 59)
1678(aset strokes-char-table ?Y 60)
1679(aset strokes-char-table ?Z 61)
1680
1681(defconst strokes-base64-chars
1682 ;; I wanted to make this a vector of individual like (vector ?0
1683 ;; ?1 ?2 ...), but `concat' in XEmacs-20.* refuses to accept single
1684 ;; characters.
1685 (vector "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
1686 "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o"
1687 "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "A" "B" "C" "D"
1688 "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
1689 "T" "U" "V" "W" "X" "Y" "Z")
1690;; (vector [?0] [?1] [?2] [?3] [?4] [?5] [?6] [?7] [?8] [?9]
1691;; [?a] [?b] [?c] [?d] [?e] [?f] [?g] [?h] [?i] [?j]
1692;; [?k] [?l] [?m] [?n] [?o] [?p] [?q] [?r] [?s] [?t]
1693;; [?u] [?v] [?w] [?x] [?y] [?z]
1694;; [?A] [?B] [?C] [?D] [?E] [?F] [?G] [?H] [?I] [?J]
1695;; [?K] [?L] [?M] [?N] [?O] [?P] [?Q] [?R] [?S] [?T]
1696;; [?U] [?V] [?W] [?X] [?Y] [?Z])
1697 "Character vector for fast lookup of base-64 encoding of numbers in [0,61].")
1698
1699(defsubst strokes-xpm-char-on-p (char)
1700 "Non-nil if CHAR represents an `on' bit in the xpm."
1701 (eq char ?*))
1702
1703(defsubst strokes-xpm-char-bit-p (char)
1704 "Non-nil if CHAR represents an `on' or `off' bit in the xpm."
1705 (or (eq char ?\ )
1706 (eq char ?*)))
1707
1708;;(defsubst strokes-xor (a b) ### Should I make this an inline function? ###
1709;; "T iff one and only one of A and B is non-nil; otherwise, returns nil.
1710;;NOTE: Don't use this as a numeric xor since it treats all non-nil
1711;; values as t including `0' (zero)."
1712;; (eq (null a) (not (null b))))
1713
1714(defsubst strokes-xpm-encode-length-as-string (length)
1715 "Given some LENGTH in [0,62) do a fast lookup of it's encoding."
1716 (aref strokes-base64-chars length))
1717
1718(defsubst strokes-xpm-decode-char (character)
1719 "Given a CHARACTER, do a fast lookup to find its corresponding integer value."
1720 (aref strokes-char-table character))
1721
1722(defun strokes-xpm-to-compressed-string (&optional xpm-buffer)
1723 "Convert the xpm in XPM-BUFFER into a compressed string representing the stroke.
1724XPM-BUFFER is an optional argument, and defaults to `*strokes-xpm*'."
1725 (save-excursion
1726 (set-buffer (setq xpm-buffer (or xpm-buffer "*strokes-xpm*")))
1727 (goto-char (point-min))
1728 (search-forward "/* pixels */") ; skip past header junk
1729 (forward-char 2)
1730 ;; a note for below:
1731 ;; the `current-char' is the char being counted -- NOT the char at (point)
1732 ;; which happens to be called `char-at-point'
1733 (let ((compressed-string "+/") ; initialize the output
1734 (count 0) ; keep a current count of
1735 ; `current-char'
1736 (last-char-was-on-p t) ; last entered stream
1737 ; represented `on' bits
1738 (current-char-is-on-p nil) ; current stream represents `on' bits
1739 (char-at-point (char-after))) ; read the first char
1740 (while (not (eq char-at-point ?})) ; a `}' denotes the
1741 ; end of the pixmap
1742 (cond ((zerop count) ; must restart counting
1743 ;; check to see if the `char-at-point' is an actual pixmap bit
1744 (when (strokes-xpm-char-bit-p char-at-point)
1745 (setq count 1
1746 current-char-is-on-p (strokes-xpm-char-on-p char-at-point)))
1747 (forward-char 1))
1748 ((= count 61) ; maximum single char's
1749 ; encoding length
1750 (setq compressed-string (concat compressed-string
1751 ;; add a zero-length
1752 ;; encoding when
1753 ;; necessary
1754 (when (eq last-char-was-on-p
1755 current-char-is-on-p)
1756 ;; "0"
1757 (strokes-xpm-encode-length-as-string 0))
1758 (strokes-xpm-encode-length-as-string 61))
1759 last-char-was-on-p current-char-is-on-p
1760 count 0)) ; note that we just set
1761 ; count=0 and *don't* advance
1762 ; (point)
1763 ((strokes-xpm-char-bit-p char-at-point) ; an actual xpm bit
1764 (if (eq current-char-is-on-p
1765 (strokes-xpm-char-on-p char-at-point))
1766 ;; yet another of the same bit-type, so we continue
1767 ;; counting...
1768 (progn
1769 (incf count)
1770 (forward-char 1))
1771 ;; otherwise, it's the opposite bit-type, so we do a
1772 ;; write and then restart count ### NOTE (for myself
1773 ;; to be aware of) ### I really should advance
1774 ;; (point) in this case instead of letting another
1775 ;; iteration go through and letting the case: count=0
1776 ;; take care of this stuff for me. That's why
1777 ;; there's no (forward-char 1) below.
1778 (setq compressed-string (concat compressed-string
1779 ;; add a zero-length
1780 ;; encoding when
1781 ;; necessary
1782 (when (eq last-char-was-on-p
1783 current-char-is-on-p)
1784 ;; "0"
1785 (strokes-xpm-encode-length-as-string 0))
1786 (strokes-xpm-encode-length-as-string count))
1787 count 0
1788 last-char-was-on-p current-char-is-on-p)))
1789 (t ; ELSE it's some other useless
1790 ; char, like `"' or `,'
1791 (forward-char 1)))
1792 (setq char-at-point (char-after)))
1793 (concat compressed-string
1794 (when (> count 0)
1795 (concat (when (eq last-char-was-on-p
1796 current-char-is-on-p)
1797 ;; "0"
1798 (strokes-xpm-encode-length-as-string 0))
1799 (strokes-xpm-encode-length-as-string count)))
1800 "/"))))
1801
1802;;;###autoload
1803(defun strokes-decode-buffer (&optional buffer force)
1804 "Decode stroke strings in BUFFER and display their corresponding glyphs.
1805Optional BUFFER defaults to the current buffer.
1806Optional FORCE non-nil will ignore the buffer's read-only status."
1807 (interactive)
1808 ;; (interactive "*bStrokify buffer: ")
1809 (save-excursion
1810 (set-buffer (setq buffer (get-buffer (or buffer (current-buffer)))))
1811 (when (or (not buffer-read-only)
1812 force
1813 inhibit-read-only
1814 (y-or-n-p
1815 (format "Buffer %s is read-only. Strokify anyway? " buffer)))
1816 (let ((inhibit-read-only t))
1817 (message "Strokifying %s..." buffer)
1818 (goto-char (point-min))
1819 (let (ext string image)
1820 ;; The comment below is what i'd have to do if I wanted to
1821 ;; deal with random newlines in the midst of the compressed
1822 ;; strings. If I do this, I'll also have to change
1823 ;; `strokes-xpm-to-compress-string' to deal with the newline,
1824 ;; and possibly other whitespace stuff. YUCK!
1825 ;; (while (re-search-forward "\\+/\\(\\w\\|\\)+/" nil t nil (get-buffer buffer))
1826 (while (with-current-buffer buffer
1827 (when (re-search-forward "\\+/\\(\\w+\\)/" nil t nil)
1828 (setq string (match-string 1))
1829 (goto-char (match-end 0))
1830 (replace-match " ")
1831 t))
1832 (strokes-xpm-for-compressed-string string " *strokes-xpm*")
1833 (setq image (create-image (with-current-buffer " *strokes-xpm*"
1834 (buffer-string))
1835 'xpm t))
1836 (insert-image image
1837 (propertize " "
1838 'type 'stroke-glyph
1839 'stroke-glyph image
1840 'data string))))
1841 (message "Strokifying %s...done" buffer)))))
1842
1843(defun strokes-encode-buffer (&optional buffer force)
1844 "Convert the glyphs in BUFFER to thier base-64 ASCII representations.
1845Optional BUFFER defaults to the current buffer.
1846Optional FORCE non-nil will ignore the buffer's read-only status."
1847 ;; ### NOTE !!! ### (for me)
1848 ;; For later on, you can/should make the inserted strings atomic
1849 ;; extents, so that the users have a clue that they shouldn't be
1850 ;; editing inside them. Plus, if you make them extents, you can
1851 ;; very easily just hide the glyphs, so if you unstrokify, and the
1852 ;; restrokify, then those that already are glyphed don't need to be
1853 ;; re-calculated, etc. It's just nicer that way. The only things
1854 ;; to worry about is cleanup (i.e. do the glyphs get gc'd when the
1855 ;; buffer is killed?
1856 ;; (interactive "*bUnstrokify buffer: ")
1857 (interactive)
1858 (save-excursion
1859 (set-buffer (setq buffer (or buffer (current-buffer))))
1860 (when (or (not buffer-read-only)
1861 force
1862 inhibit-read-only
1863 (y-or-n-p
1864 (format "Buffer %s is read-only. Encode anyway? " buffer)))
1865 (message "Encoding strokes in %s..." buffer)
1866 ;; (map-extents
1867 ;; (lambda (ext buf)
1868 ;; (when (eq (extent-property ext 'type) 'stroke-glyph)
1869 ;; (goto-char (extent-start-position ext))
1870 ;; (delete-char 1) ; ### What the hell do I do here? ###
1871 ;; (insert "+/" (extent-property ext 'data) "/")
1872 ;; (delete-extent ext))))))
1873 (let ((inhibit-read-only t)
1874 (start nil)
1875 glyph)
1876 (while (or (and (bobp)
1877 (get-text-property (point) 'type))
1878 (setq start (next-single-property-change (point) 'type)))
1879 (when (eq 'stroke-glyph (get-text-property (point) 'type))
1880 (goto-char start)
1881 (setq start (point-marker)
1882 glyph (get-text-property start 'display))
1883 (insert "+/" (get-text-property (point) 'data) ?/)
1884 (delete-char 1)
1885 (add-text-properties start (point)
1886 (list 'type 'stroke-string
1887 'face 'strokes-char-face
1888 'stroke-glyph glyph
1889 'display nil))))
1890 (message "Encoding strokes in %s...done" buffer)))))
1891
1892(defun strokes-xpm-for-compressed-string (compressed-string &optional bufname)
1893 "Convert the stroke represented by COMPRESSED-STRING into an xpm.
1894Store xpm in buffer BUFNAME if supplied \(default is `*strokes-xpm*'\)"
1895 (save-excursion
1896 (or bufname (setq bufname "*strokes-xpm*"))
1897 (set-buffer (get-buffer-create bufname))
1898 (erase-buffer)
1899 (insert compressed-string)
1900 (goto-char (point-min))
1901 (let ((current-char-is-on-p nil))
1902 (while (not (eobp))
1903 (insert-char
1904 (if current-char-is-on-p
1905 ?*
1906 ?\ )
1907 (strokes-xpm-decode-char (char-after)))
1908 (delete-char 1)
1909 (setq current-char-is-on-p (not current-char-is-on-p)))
1910 (goto-char (point-min))
1911 (loop repeat 33 do
1912 (insert ?\")
1913 (forward-char 33)
1914 (insert "\",\n"))
1915 (goto-char (point-min))
1916 (insert strokes-xpm-header))))
1917
1918;;;###autoload
1919(defun strokes-compose-complex-stroke ()
1920 ;; ### NOTE !!! ###
1921 ;; Even though we have lexical scoping, it's somewhat ugly how I
1922 ;; pass around variables in the global name space. I can/should
1923 ;; change this.
1924 "Read a complex stroke and insert its glyph into the current buffer."
1925 (interactive "*")
1926 (let ((strokes-grid-resolution 33))
1927 (strokes-read-complex-stroke)
1928 (strokes-xpm-for-stroke nil " *strokes-xpm*" t)
1929 (insert (strokes-xpm-to-compressed-string " *strokes-xpm*"))
1930 (strokes-decode-buffer)
1931 ;; strokes-decode-buffer does a save-excursion.
1932 (forward-char)))
1933
1319(or (assq 'strokes-mode minor-mode-alist) 1934(or (assq 'strokes-mode minor-mode-alist)
1320 (setq minor-mode-alist (cons (list 'strokes-mode strokes-modeline-string) 1935 (setq minor-mode-alist (cons (list 'strokes-mode strokes-modeline-string)
1321 minor-mode-alist))) 1936 minor-mode-alist)))