allout-widgets.el 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422
  1. ;; allout-widgets.el --- Visually highlight allout outline structure.
  2. ;; Copyright (C) 2005-2012 Free Software Foundation, Inc.
  3. ;; Author: Ken Manheimer <ken dot manheimer at gmail...>
  4. ;; Maintainer: Ken Manheimer <ken dot manheimer at gmail...>
  5. ;; Version: 1.0
  6. ;; Created: Dec 2005
  7. ;; Keywords: outlines
  8. ;; Website: http://myriadicity.net/software-and-systems/craft/emacs-allout
  9. ;; This file is part of GNU Emacs.
  10. ;; GNU Emacs is free software: you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation, either version 3 of the License, or
  13. ;; (at your option) any later version.
  14. ;; GNU Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  20. ;;; Commentary:
  21. ;; This is an allout outline-mode add-on that highlights outline structure
  22. ;; with graphical widgets.
  23. ;;
  24. ;; To activate, customize `allout-widgets-auto-activation'. You can also
  25. ;; invoke allout-widgets-mode in a particular allout buffer. When
  26. ;; auto-enabled, you can inhibit widget operation in particular allout
  27. ;; buffers by setting the variable `allout-widgets-mode-inhibit' non-nil in
  28. ;; that file's buffer. Use emacs *file local variables* to generally
  29. ;; inhibit for a file.
  30. ;;
  31. ;; See the `allout-widgets-mode' docstring for more details.
  32. ;;
  33. ;; Info about allout and allout-widgets development are available at
  34. ;; http://myriadicity.net/Sundry/EmacsAllout
  35. ;;
  36. ;; The graphics include:
  37. ;;
  38. ;; - icons for item bullets, varying to distinguish whether the item either
  39. ;; lacks any subitems, the subitems are currently collapsed within the
  40. ;; item, or the item is currently expanded.
  41. ;;
  42. ;; - guide lines connecting item bullet-icons with those of their subitems.
  43. ;;
  44. ;; - cue area between the bullet-icon and the start of the body headline,
  45. ;; for item numbering, encryption indicator, and distinctive bullets.
  46. ;;
  47. ;; The bullet-icon and guide line graphics provide keybindings and mouse
  48. ;; bindings for easy outline navigation and exposure control, extending
  49. ;; outline hot-spot navigation (see `allout-mode' docstring for details).
  50. ;;
  51. ;; Developers note: Our use of emacs widgets is unconventional. We
  52. ;; decorate existing text rather than substituting for it, to
  53. ;; piggy-back on existing allout operation. This employs the C-coded
  54. ;; efficiencies of widget-apply, widget-get, and widget-put, along
  55. ;; with the basic object-oriented organization of widget-create, to
  56. ;; systematically couple overlays, graphics, and other features with
  57. ;; allout-governed text.
  58. ;;; Code:
  59. ;;;_ : General Environment
  60. (require 'allout)
  61. (require 'widget)
  62. (require 'wid-edit)
  63. (eval-when-compile
  64. (progn
  65. (require 'overlay)
  66. (require 'cl)
  67. ))
  68. ;;;_ : internal variables needed before user-customization variables
  69. ;;; In order to enable activation of allout-widgets-mode via customization,
  70. ;;; allout-widgets-auto-activation uses a setting function. That function
  71. ;;; is invoked when the customization variable definition is evaluated,
  72. ;;; during file load, so the involved code must reside above that
  73. ;;; definition in the file.
  74. ;;;_ = allout-widgets-mode
  75. (defvar allout-widgets-mode nil
  76. "Allout mode enhanced with graphical widgets.")
  77. (make-variable-buffer-local 'allout-widgets-mode)
  78. ;;;_ : USER CUSTOMIZATION VARIABLES and incidental functions:
  79. ;;;_ > defgroup allout-widgets
  80. ;;;###autoload
  81. (defgroup allout-widgets nil
  82. "Allout extension that highlights outline structure graphically.
  83. Customize `allout-widgets-auto-activation' to activate allout-widgets
  84. with allout-mode."
  85. :group 'allout)
  86. ;;;_ > defgroup allout-widgets-developer
  87. (defgroup allout-widgets-developer nil
  88. "Settings for development of allout widgets extension."
  89. :group 'allout-widgets)
  90. ;;;_ ; some functions a bit early, for allout-auto-activation dependency:
  91. ;;;_ > allout-widgets-mode-enable
  92. (defun allout-widgets-mode-enable ()
  93. "Enable allout-widgets-mode in allout-mode buffers.
  94. See `allout-widgets-mode-inhibit' for per-file/per-buffer
  95. inhibition of allout-widgets-mode."
  96. (add-hook 'allout-mode-off-hook 'allout-widgets-mode-off)
  97. (add-hook 'allout-mode-on-hook 'allout-widgets-mode-on)
  98. t)
  99. ;;;_ > allout-widgets-mode-disable
  100. (defun allout-widgets-mode-disable ()
  101. "Disable allout-widgets-mode in allout-mode buffers.
  102. See `allout-widgets-mode-inhibit' for per-file/per-buffer
  103. inhibition of allout-widgets-mode."
  104. (remove-hook 'allout-mode-off-hook 'allout-widgets-mode-off)
  105. (remove-hook 'allout-mode-on-hook 'allout-widgets-mode-on)
  106. t)
  107. ;;;_ > allout-widgets-setup (varname value)
  108. ;;;###autoload
  109. (defun allout-widgets-setup (varname value)
  110. "Commission or decommission allout-widgets-mode along with allout-mode.
  111. Meant to be used by customization of `allout-widgets-auto-activation'."
  112. (set-default varname value)
  113. (if allout-widgets-auto-activation
  114. (allout-widgets-mode-enable)
  115. (allout-widgets-mode-disable)))
  116. ;;;_ = allout-widgets-auto-activation
  117. ;;;###autoload
  118. (defcustom allout-widgets-auto-activation nil
  119. "Activate to enable allout icon graphics wherever allout mode is active.
  120. Also enable `allout-auto-activation' for this to take effect upon
  121. visiting an outline.
  122. When this is set you can disable allout widgets in select files
  123. by setting `allout-widgets-mode-inhibit'
  124. Instead of setting `allout-widgets-auto-activation' you can
  125. explicitly invoke `allout-widgets-mode' in allout buffers where
  126. you want allout widgets operation.
  127. See `allout-widgets-mode' for allout widgets mode features."
  128. :version "24.1"
  129. :type 'boolean
  130. :group 'allout-widgets
  131. :set 'allout-widgets-setup
  132. )
  133. ;; ;;;_ = allout-widgets-allow-unruly-edits
  134. ;; (defcustom allout-widgets-allow-unruly-edits nil
  135. ;; "*Control whether manual edits are restricted to maintain outline integrity.
  136. ;; When nil, manual edits must either be within an item's body or encompass
  137. ;; one or more items completely - eg, killing topics as entities, rather than
  138. ;; deleting from the middle of one to the middle of another.
  139. ;; If you only occasionally need to make unrestricted change, you can set this
  140. ;; variable in the specific buffer using set-variable, or just deactivate
  141. ;; `allout-mode' temporarily. You can customize this to always allow unruly
  142. ;; edits, but you will be able to create outlines that are unnavigable in
  143. ;; principle, and not just for allout's navigation and exposure mechanisms."
  144. ;; :type 'boolean
  145. ;; :group allout-widgets)
  146. ;; (make-variable-buffer-local 'allout-widgets-allow-unruly-edits)
  147. ;;;_ = allout-widgets-auto-activation - below, for eval-order dependencies
  148. ;;;_ = allout-widgets-icons-dark-subdir
  149. (defcustom allout-widgets-icons-dark-subdir "icons/allout-widgets/dark-bg/"
  150. "Directory on `image-load-path' holding allout icons for dark backgrounds."
  151. :version "24.1"
  152. :type 'string
  153. :group 'allout-widgets)
  154. ;;;_ = allout-widgets-icons-light-subdir
  155. (defcustom allout-widgets-icons-light-subdir "icons/allout-widgets/light-bg/"
  156. "Directory on `image-load-path' holding allout icons for light backgrounds."
  157. :version "24.1"
  158. :type 'string
  159. :group 'allout-widgets)
  160. ;;;_ = allout-widgets-icon-types
  161. (defcustom allout-widgets-icon-types '(xpm png)
  162. "File extensions for the icon graphic format types, in order of preference."
  163. :version "24.1"
  164. :type '(repeat symbol)
  165. :group 'allout-widgets)
  166. ;;;_ . Decoration format
  167. ;;;_ = allout-widgets-theme-dark-background
  168. (defcustom allout-widgets-theme-dark-background "allout-dark-bg"
  169. "Identify the outline's icon theme to use with a dark background."
  170. :version "24.1"
  171. :type '(string)
  172. :group 'allout-widgets)
  173. ;;;_ = allout-widgets-theme-light-background
  174. (defcustom allout-widgets-theme-light-background "allout-light-bg"
  175. "Identify the outline's icon theme to use with a light background."
  176. :version "24.1"
  177. :type '(string)
  178. :group 'allout-widgets)
  179. ;;;_ = allout-widgets-item-image-properties-emacs
  180. (defcustom allout-widgets-item-image-properties-emacs
  181. '(:ascent center :mask (heuristic t))
  182. "*Default properties item widget images in mainline Emacs."
  183. :version "24.1"
  184. :type 'plist
  185. :group 'allout-widgets)
  186. ;;;_ = allout-widgets-item-image-properties-xemacs
  187. (defcustom allout-widgets-item-image-properties-xemacs
  188. nil
  189. "*Default properties item widget images in XEmacs."
  190. :version "24.1"
  191. :type 'plist
  192. :group 'allout-widgets)
  193. ;;;_ . Developer
  194. ;;;_ = allout-widgets-run-unit-tests-on-load
  195. (defcustom allout-widgets-run-unit-tests-on-load nil
  196. "*When non-nil, unit tests will be run at end of loading allout-widgets.
  197. Generally, allout widgets code developers are the only ones who'll want to
  198. set this.
  199. \(If set, this makes it an even better practice to exercise changes by
  200. doing byte-compilation with a repeat count, so the file is loaded after
  201. compilation.)
  202. See `allout-widgets-run-unit-tests' to see what's run."
  203. :version "24.1"
  204. :type 'boolean
  205. :group 'allout-widgets-developer)
  206. ;;;_ = allout-widgets-time-decoration-activity
  207. (defcustom allout-widgets-time-decoration-activity nil
  208. "*Retain timing info of the last cooperative redecoration.
  209. The details are retained as the value of
  210. `allout-widgets-last-decoration-timing'.
  211. Generally, allout widgets code developers are the only ones who'll want to
  212. set this."
  213. :version "24.1"
  214. :type 'boolean
  215. :group 'allout-widgets-developer)
  216. ;;;_ = allout-widgets-hook-error-post-time 0
  217. (defcustom allout-widgets-hook-error-post-time 0
  218. "*Amount of time to sit showing hook error messages.
  219. 0 is minimal, or nil to not post to the message area.
  220. This is for debugging purposes."
  221. :version "24.1"
  222. :type 'integer
  223. :group 'allout-widgets-developer)
  224. ;;;_ = allout-widgets-maintain-tally nil
  225. (defcustom allout-widgets-maintain-tally nil
  226. "*If non-nil, maintain a collection of widgets, `allout-widgets-tally'.
  227. This is for debugging purposes.
  228. The tally shows the total number of item widgets in the current
  229. buffer, and tracking increases as new widgets are added and
  230. decreases as obsolete widgets are garbage collected."
  231. :version "24.1"
  232. :type 'boolean
  233. :group 'allout-widgets-developer)
  234. (defvar allout-widgets-tally nil
  235. "Hash-table of existing allout widgets, for debugging.
  236. Table is maintained iff `allout-widgets-maintain-tally' is non-nil.
  237. The table contents will be out of sync if any widgets are created
  238. or deleted while this variable is nil.")
  239. (make-variable-buffer-local 'allout-widgets-tally)
  240. (defvar allout-widgets-mode-inhibit) ; defined below
  241. ;;;_ > allout-widgets-tally-string
  242. (defun allout-widgets-tally-string ()
  243. "Return a string giving the number of tracked widgets, or empty string if not tracking.
  244. The string is formed for appending to the allout-mode mode-line lighter.
  245. An empty string is also returned if tracking is inhibited or
  246. widgets are locally inhibited.
  247. The number varies according to the evanescence of objects on a
  248. hash table with weak keys, so tracking of widget erasures is often delayed."
  249. (when (and allout-widgets-maintain-tally
  250. (not allout-widgets-mode-inhibit)
  251. allout-widgets-tally)
  252. (format ":%s" (hash-table-count allout-widgets-tally))))
  253. ;;;_ = allout-widgets-track-decoration nil
  254. (defcustom allout-widgets-track-decoration nil
  255. "*If non-nil, show cursor position of each item decoration.
  256. This is for debugging purposes, and generally set at need in a
  257. buffer rather than as a prevailing configuration \(but it's handy
  258. to publicize it by making it a customization variable\)."
  259. :version "24.1"
  260. :type 'boolean
  261. :group 'allout-widgets-developer)
  262. (make-variable-buffer-local 'allout-widgets-track-decoration)
  263. ;;;_ : Mode context - variables, hookup, and hooks
  264. ;;;_ . internal mode variables
  265. ;;;_ , Mode activation and environment
  266. ;;;_ = allout-widgets-version
  267. (defvar allout-widgets-version "1.0"
  268. "Version of currently loaded allout-widgets extension.")
  269. ;;;_ > allout-widgets-version
  270. (defun allout-widgets-version (&optional here)
  271. "Return string describing the loaded outline version."
  272. (interactive "P")
  273. (let ((msg (concat "Allout Outline Widgets Extension v "
  274. allout-widgets-version)))
  275. (if here (insert msg))
  276. (message "%s" msg)
  277. msg))
  278. ;;;_ = allout-widgets-mode-inhibit
  279. (defvar allout-widgets-mode-inhibit nil
  280. "Inhibit `allout-widgets-mode' from activating widgets.
  281. This also inhibits automatic adjustment of widgets to track allout outline
  282. changes.
  283. You can use this as a file local variable setting to disable
  284. allout widgets enhancements in selected buffers while generally
  285. enabling widgets by customizing `allout-widgets-auto-activation'.
  286. In addition, you can invoked `allout-widgets-mode' allout-mode
  287. buffers where this is set to enable and disable widget
  288. enhancements, directly.")
  289. ;;;###autoload
  290. (put 'allout-widgets-mode-inhibit 'safe-local-variable
  291. (if (fboundp 'booleanp) 'booleanp (lambda (x) (member x '(t nil)))))
  292. (make-variable-buffer-local 'allout-widgets-mode-inhibit)
  293. ;;;_ = allout-inhibit-body-modification-hook
  294. (defvar allout-inhibit-body-modification-hook nil
  295. "Override de-escaping of text-prefixes in item bodies during specific changes.
  296. This is used by `allout-buffer-modification-handler' to signal such changes
  297. to `allout-body-modification-handler', and is always reset by
  298. `allout-post-command-business'.")
  299. (make-variable-buffer-local 'allout-inhibit-body-modification-hook)
  300. ;;;_ = allout-widgets-icons-cache
  301. (defvar allout-widgets-icons-cache nil
  302. "Cache allout icon images, as an association list.
  303. `allout-fetch-icon-image' uses this cache transparently, keying
  304. images with lists containing the name of the icon directory \(as
  305. found on the `load-path') and the icon name.
  306. Set this variable to `nil' to empty the cache, and have it replenish from the
  307. filesystem.")
  308. ;;;_ = allout-widgets-unset-inhibit-read-only
  309. (defvar allout-widgets-unset-inhibit-read-only nil
  310. "Tell `allout-widgets-post-command-business' to unset `inhibit-read-only'.
  311. Used by `allout-graphics-modification-handler'")
  312. ;;;_ = allout-widgets-reenable-before-change-handler
  313. (defvar allout-widgets-reenable-before-change-handler nil
  314. "Tell `allout-widgets-post-command-business' to reequip the handler.
  315. Necessary because the handler sometimes deliberately raises an
  316. error, causing it to be disabled.")
  317. ;;;_ , State for hooks
  318. ;;;_ = allout-unresolved-body-mod-workroster
  319. (defvar allout-unresolved-body-mod-workroster (make-hash-table :size 16)
  320. "List of body-overlays that did before-change business but not after-change.
  321. See `allout-post-command-business' and `allout-body-modification-handler'.")
  322. ;;;_ = allout-structure-unruly-deletion-message
  323. (defvar allout-structure-unruly-deletion-message
  324. "Unruly edit prevented --
  325. To change the bullet character: \\[allout-rebullet-current-heading]
  326. To promote this item: \\[allout-shift-out]
  327. To demote it: \\[allout-shift-in]
  328. To delete it and offspring: \\[allout-kill-topic]
  329. See \\[describe-mode] for many more options."
  330. "Informative message presented on improper editing of outline structure.
  331. The structure includes the guides lines, bullet, and bullet cue.")
  332. ;;;_ = allout-widgets-changes-record
  333. (defvar allout-widgets-changes-record nil
  334. "Record outline changes for processing by post-command hook.
  335. Entries on the list are lists whose first element is a symbol indicating
  336. the change type and subsequent elements are data specific to that change
  337. type. Specifically:
  338. 'exposure `allout-exposure-from' `allout-exposure-to' `allout-exposure-flag'
  339. The changes are recorded in reverse order, with new values pushed
  340. onto the front.")
  341. (make-variable-buffer-local 'allout-widgets-changes-record)
  342. ;;;_ = allout-widgets-undo-exposure-record
  343. (defvar allout-widgets-undo-exposure-record nil
  344. "Record outline undo traces for processing by post-command hook.
  345. The changes are recorded in reverse order, with new values pushed
  346. onto the front.")
  347. (make-variable-buffer-local 'allout-widgets-undo-exposure-record)
  348. ;;;_ = allout-widgets-last-hook-error
  349. (defvar allout-widgets-last-hook-error nil
  350. "String holding last error string, for debugging purposes.")
  351. ;;;_ = allout-widgets-adjust-message-length-threshold 100
  352. (defvar allout-widgets-adjust-message-length-threshold 100
  353. "Display \"Adjusting widgets\" message above this number of pending changes."
  354. )
  355. ;;;_ = allout-widgets-adjust-message-size-threshold 10000
  356. (defvar allout-widgets-adjust-message-size-threshold 10000
  357. "Display \"Adjusting widgets\" message above this size of pending changes."
  358. )
  359. ;;;_ = allout-doing-exposure-undo-processor nil
  360. (defvar allout-undo-exposure-in-progress nil
  361. "Maintained true during `allout-widgets-exposure-undo-processor'")
  362. ;;;_ , Widget-specific outline text format
  363. ;;;_ = allout-escaped-prefix-regexp
  364. (defvar allout-escaped-prefix-regexp ""
  365. "*Regular expression for body text that would look like an item prefix if
  366. not altered with an escape sequence.")
  367. (make-variable-buffer-local 'allout-escaped-prefix-regexp)
  368. ;;;_ , Widget element formatting
  369. ;;;_ = allout-item-icon-keymap
  370. (defvar allout-item-icon-keymap
  371. (let ((km (make-sparse-keymap)))
  372. (dolist (digit '("0" "1" "2" "3"
  373. "4" "5" "6" "7" "8" "9"))
  374. (define-key km digit 'digit-argument))
  375. (define-key km "-" 'negative-argument)
  376. ;; (define-key km [(return)] 'allout-tree-expand-command)
  377. ;; (define-key km [(meta return)] 'allout-toggle-torso-command)
  378. ;; (define-key km [(down-mouse-1)] 'allout-item-button-click)
  379. ;; (define-key km [(down-mouse-2)] 'allout-toggle-torso-event-command)
  380. ;; Override underlying mouse-1 and mouse-2 bindings in icon territory:
  381. (define-key km [(mouse-1)] (lambda () (interactive) nil))
  382. (define-key km [(mouse-2)] (lambda () (interactive) nil))
  383. ;; Catchall, handles actual keybindings, dynamically doing keymap lookups:
  384. (define-key km [t] 'allout-item-icon-key-handler)
  385. km)
  386. "General tree-node key bindings.")
  387. ;;;_ = allout-item-body-keymap
  388. (defvar allout-item-body-keymap
  389. (let ((km (make-sparse-keymap))
  390. (local-map (current-local-map)))
  391. ;; (define-key km [(control return)] 'allout-tree-expand-command)
  392. ;; (define-key km [(meta return)] 'allout-toggle-torso-command)
  393. ;; XXX We need to reset this per buffer's mode; we do so in
  394. ;; allout-widgets-mode.
  395. (if local-map
  396. (set-keymap-parent km local-map))
  397. km)
  398. "General key bindings for the text content of outline items.")
  399. (make-variable-buffer-local 'allout-item-body-keymap)
  400. ;;;_ = allout-body-span-category
  401. (defvar allout-body-span-category nil
  402. "Symbol carrying allout body-text overlay properties.")
  403. ;;;_ = allout-cue-span-keymap
  404. (defvar allout-cue-span-keymap
  405. (let ((km (make-sparse-keymap)))
  406. (set-keymap-parent km allout-item-icon-keymap)
  407. km)
  408. "Keymap used in the item cue area - the space between the icon and headline.")
  409. ;;;_ = allout-escapes-category
  410. (defvar allout-escapes-category nil
  411. "Symbol for category of text property used to hide escapes of prefix-like
  412. text in allout item bodies.")
  413. ;;;_ = allout-guides-category
  414. (defvar allout-guides-category nil
  415. "Symbol carrying allout icon-guides overlay properties.")
  416. ;;;_ = allout-guides-span-category
  417. (defvar allout-guides-span-category nil
  418. "Symbol carrying allout icon and guide lines overlay properties.")
  419. ;;;_ = allout-icon-span-category
  420. (defvar allout-icon-span-category nil
  421. "Symbol carrying allout icon and guide lines overlay properties.")
  422. ;;;_ = allout-cue-span-category
  423. (defvar allout-cue-span-category nil
  424. "Symbol carrying common properties of the space following the outline icon.
  425. \(That space is used to convey selected cues indicating body qualities,
  426. including things like:
  427. - encryption '~'
  428. - numbering '#'
  429. - indirect reference '@'
  430. - distinctive bullets - see `allout-distinctive-bullets-string'.\)")
  431. ;;;_ = allout-span-to-category
  432. (defvar allout-span-to-category
  433. '((:guides-span . allout-guides-span-category)
  434. (:cue-span . allout-cue-span-category)
  435. (:icon-span . allout-icon-span-category)
  436. (:body-span . allout-body-span-category))
  437. "Association list mapping span identifier to category identifier.")
  438. ;;;_ = allout-trailing-category
  439. (defvar allout-trailing-category nil
  440. "Symbol carrying common properties of an overlay's trailing newline.")
  441. ;;;_ , Developer
  442. (defvar allout-widgets-last-decoration-timing nil
  443. "Timing details for the last cooperative decoration action.
  444. This is maintained when `allout-widgets-time-decoration-activity' is set.
  445. The value is a list containing two elements:
  446. - the elapsed time as a number of seconds
  447. - the list of changes processed, a la `allout-widgets-changes-record'.
  448. When active, the value is revised each time automatic decoration activity
  449. happens in the buffer.")
  450. (make-variable-buffer-local 'allout-widgets-last-decoration-timing)
  451. ;;;_ . mode hookup
  452. ;;;_ > define-minor-mode allout-widgets-mode (arg)
  453. ;;;###autoload
  454. (define-minor-mode allout-widgets-mode
  455. "Toggle Allout Widgets mode.
  456. With a prefix argument ARG, enable Allout Widgets mode if ARG is
  457. positive, and disable it otherwise. If called from Lisp, enable
  458. the mode if ARG is omitted or nil.
  459. Allout Widgets mode is an extension of Allout mode that provides
  460. graphical decoration of outline structure. It is meant to
  461. operate along with `allout-mode', via `allout-mode-hook'.
  462. The graphics include:
  463. - guide lines connecting item bullet-icons with those of their subitems.
  464. - icons for item bullets, varying to indicate whether or not the item
  465. has subitems, and if so, whether or not the item is expanded.
  466. - cue area between the bullet-icon and the start of the body headline,
  467. for item numbering, encryption indicator, and distinctive bullets.
  468. The bullet-icon and guide line graphics provide keybindings and mouse
  469. bindings for easy outline navigation and exposure control, extending
  470. outline hot-spot navigation \(see `allout-mode')."
  471. :lighter nil
  472. :keymap nil
  473. ;; define-minor-mode handles any provided argument according to emacs
  474. ;; minor-mode conventions - '(elisp) Minor Mode Conventions' - and sets
  475. ;; allout-widgets-mode accordingly *before* running the body code, so we
  476. ;; cue on that.
  477. (if allout-widgets-mode
  478. ;; Activating:
  479. (progn
  480. (allout-add-resumptions
  481. ;; XXX user may need say in line-truncation/hscrolling - an option
  482. ;; that abstracts mode.
  483. ;; truncate text lines to keep guide lines intact:
  484. '(truncate-lines t)
  485. ;; and enable autoscrolling to ease view of text
  486. '(auto-hscroll-mode t)
  487. '(line-move-ignore-fields t)
  488. '(widget-push-button-prefix "")
  489. '(widget-push-button-suffix "")
  490. ;; allout-escaped-prefix-regexp depends on allout-regexp:
  491. (list 'allout-escaped-prefix-regexp (concat "\\(\\\\\\)"
  492. "\\(" allout-regexp "\\)")))
  493. (allout-add-resumptions
  494. (list 'allout-widgets-tally allout-widgets-tally)
  495. (list 'allout-widgets-escapes-sanitization-regexp-pair
  496. (list (concat "\\(\n\\|\\`\\)"
  497. allout-escaped-prefix-regexp
  498. )
  499. ;; Include everything but the escape symbol.
  500. "\\1\\3"))
  501. )
  502. (add-hook 'after-change-functions 'allout-widgets-after-change-handler
  503. nil t)
  504. (allout-setup-text-properties)
  505. (add-to-invisibility-spec '(allout-torso . t))
  506. (add-to-invisibility-spec 'allout-escapes)
  507. (if (current-local-map)
  508. (set-keymap-parent allout-item-body-keymap (current-local-map)))
  509. (add-hook 'allout-exposure-change-hook
  510. 'allout-widgets-exposure-change-recorder nil 'local)
  511. (add-hook 'allout-structure-added-hook
  512. 'allout-widgets-additions-recorder nil 'local)
  513. (add-hook 'allout-structure-deleted-hook
  514. 'allout-widgets-deletions-recorder nil 'local)
  515. (add-hook 'allout-structure-shifted-hook
  516. 'allout-widgets-shifts-recorder nil 'local)
  517. (add-hook 'allout-after-copy-or-kill-hook
  518. 'allout-widgets-after-copy-or-kill-function nil 'local)
  519. (add-hook 'allout-post-undo-hook
  520. 'allout-widgets-after-undo-function nil 'local)
  521. (add-hook 'before-change-functions 'allout-widgets-before-change-handler
  522. nil 'local)
  523. (add-hook 'post-command-hook 'allout-widgets-post-command-business
  524. nil 'local)
  525. (add-hook 'pre-command-hook 'allout-widgets-pre-command-business
  526. nil 'local)
  527. ;; init the widgets tally for debugging:
  528. (if (not allout-widgets-tally)
  529. (setq allout-widgets-tally (make-hash-table
  530. :test 'eq :weakness 'key)))
  531. ;; add tally count display on minor-mode-alist just after
  532. ;; allout-mode entry.
  533. ;; (we use ternary condition form to keep condition simple for deletion.)
  534. (let* ((mode-line-entry '(allout-widgets-mode-inhibit ""
  535. (:eval (allout-widgets-tally-string))))
  536. (associated (assoc (car mode-line-entry) minor-mode-alist))
  537. ;; need location for it only if not already present:
  538. (after (and (not associated)
  539. (memq (assq 'allout-mode minor-mode-alist) minor-mode-alist))))
  540. (if after
  541. (rplacd after (cons mode-line-entry (cdr after)))))
  542. (allout-widgets-prepopulate-buffer)
  543. t)
  544. ;; Deactivating:
  545. (let ((inhibit-read-only t)
  546. (was-modified (buffer-modified-p)))
  547. (allout-widgets-undecorate-region (point-min)(point-max))
  548. (remove-from-invisibility-spec '(allout-torso . t))
  549. (remove-from-invisibility-spec 'allout-escapes)
  550. (remove-hook 'after-change-functions
  551. 'allout-widgets-after-change-handler 'local)
  552. (remove-hook 'allout-exposure-change-hook
  553. 'allout-widgets-exposure-change-recorder 'local)
  554. (remove-hook 'allout-structure-added-hook
  555. 'allout-widgets-additions-recorder 'local)
  556. (remove-hook 'allout-structure-deleted-hook
  557. 'allout-widgets-deletions-recorder 'local)
  558. (remove-hook 'allout-structure-shifted-hook
  559. 'allout-widgets-shifts-recorder 'local)
  560. (remove-hook 'allout-after-copy-or-kill-hook
  561. 'allout-widgets-after-copy-or-kill-function 'local)
  562. (remove-hook 'before-change-functions
  563. 'allout-widgets-before-change-handler 'local)
  564. (remove-hook 'post-command-hook
  565. 'allout-widgets-post-command-business 'local)
  566. (remove-hook 'pre-command-hook
  567. 'allout-widgets-pre-command-business 'local)
  568. (assq-delete-all 'allout-widgets-mode-inhibit minor-mode-alist)
  569. (set-buffer-modified-p was-modified))))
  570. ;;;_ > allout-widgets-mode-off
  571. (defun allout-widgets-mode-off ()
  572. "Explicitly disable allout-widgets-mode."
  573. (allout-widgets-mode -1))
  574. ;;;_ > allout-widgets-mode-off
  575. (defun allout-widgets-mode-on ()
  576. "Explicitly disable allout-widgets-mode."
  577. (allout-widgets-mode 1))
  578. ;;;_ > allout-setup-text-properties ()
  579. (defun allout-setup-text-properties ()
  580. "Configure category and literal text properties."
  581. ;; XXX body - before-change, entry, keymap
  582. (setplist 'allout-guides-span-category nil)
  583. (put 'allout-guides-span-category
  584. 'modification-hooks '(allout-graphics-modification-handler))
  585. (put 'allout-guides-span-category 'local-map allout-item-icon-keymap)
  586. (put 'allout-guides-span-category 'mouse-face widget-button-face)
  587. (put 'allout-guides-span-category 'field 'structure)
  588. ;; (put 'allout-guides-span-category 'face 'widget-button)
  589. (setplist 'allout-icon-span-category
  590. (allout-widgets-copy-list (symbol-plist
  591. 'allout-guides-span-category)))
  592. (put 'allout-icon-span-category 'field 'structure)
  593. ;; XXX for body text we're instead going to use the buffer-wide
  594. ;; resources, like before/after-change-functions hooks and the
  595. ;; buffer's key map. that way we won't have to do painful provisions
  596. ;; to fixup things after edits, catch outlier interstitial
  597. ;; characters, like newline and empty lines after hidden subitems,
  598. ;; etc.
  599. (setplist 'allout-body-span-category nil)
  600. (put 'allout-body-span-category 'evaporate t)
  601. (put 'allout-body-span-category 'local-map allout-item-body-keymap)
  602. ;;(put 'allout-body-span-category
  603. ;; 'modification-hooks '(allout-body-modification-handler))
  604. ;;(put 'allout-body-span-category 'field 'body)
  605. (setplist 'allout-cue-span-category nil)
  606. (put 'allout-cue-span-category 'evaporate t)
  607. (put 'allout-cue-span-category
  608. 'modification-hooks '(allout-body-modification-handler))
  609. (put 'allout-cue-span-category 'local-map allout-cue-span-keymap)
  610. (put 'allout-cue-span-category 'mouse-face widget-button-face)
  611. (put 'allout-cue-span-category 'pointer 'arrow)
  612. (put 'allout-cue-span-category 'field 'structure)
  613. (setplist 'allout-trailing-category nil)
  614. (put 'allout-trailing-category 'evaporate t)
  615. (put 'allout-trailing-category 'local-map allout-item-body-keymap)
  616. (setplist 'allout-escapes-category nil)
  617. (put 'allout-escapes-category 'invisible 'allout-escapes)
  618. (put 'allout-escapes-category 'evaporate t))
  619. ;;;_ > allout-widgets-prepopulate-buffer ()
  620. (defun allout-widgets-prepopulate-buffer ()
  621. "Step over the current buffers exposed items to do initial widgetizing."
  622. (if (not allout-widgets-mode-inhibit)
  623. (save-excursion
  624. (goto-char (point-min))
  625. (while (allout-next-visible-heading 1)
  626. (when (not (widget-at (point)))
  627. (allout-get-or-create-item-widget))))))
  628. ;;;_ . settings context
  629. ;;;_ = allout-container-item
  630. (defvar allout-container-item-widget nil
  631. "A widget for the current outline's overarching container as an item.
  632. The item has settings \(of the file/connection\) and maybe a body, but no
  633. icon/bullet.")
  634. (make-variable-buffer-local 'allout-container-item-widget)
  635. ;;;_ . Hooks and hook helpers
  636. ;;;_ , major command-loop business:
  637. ;;;_ > allout-widgets-pre-command-business (&optional recursing)
  638. (defun allout-widgets-pre-command-business (&optional recursing)
  639. "Handle actions pending before allout-mode activity."
  640. )
  641. ;;;_ > allout-widgets-post-command-business (&optional recursing)
  642. (defun allout-widgets-post-command-business (&optional recursing)
  643. "Handle actions pending after any allout-mode commands.
  644. Optional RECURSING is for internal use, to limit recursion."
  645. ;; - check changed text for nesting discontinuities and escape anything
  646. ;; that's: (1) asterisks at bol or (2) excessively nested.
  647. (condition-case failure
  648. (when (and (boundp 'allout-mode) allout-mode)
  649. (if allout-widgets-unset-inhibit-read-only
  650. (setq inhibit-read-only nil
  651. allout-widgets-unset-inhibit-read-only nil))
  652. (when allout-widgets-reenable-before-change-handler
  653. (add-hook 'before-change-functions
  654. 'allout-widgets-before-change-handler
  655. nil 'local)
  656. (setq allout-widgets-reenable-before-change-handler nil))
  657. (when (or allout-widgets-undo-exposure-record
  658. allout-widgets-changes-record)
  659. (let* ((debug-on-signal t)
  660. (debug-on-error t)
  661. ;; inhibit recording new undo records when processing
  662. ;; effects of undo-exposure:
  663. (debugger 'allout-widgets-hook-error-handler)
  664. (adjusting-message " Adjusting widgets...")
  665. (replaced-message (allout-widgets-adjusting-message
  666. adjusting-message))
  667. (start-time (current-time)))
  668. (if allout-widgets-undo-exposure-record
  669. ;; inhibit undo recording iff undoing exposure stuff.
  670. ;; XXX we might need to inhibit per respective
  671. ;; change-record, rather than assuming that some undo
  672. ;; activity during a command is all undo activity.
  673. (let ((buffer-undo-list t))
  674. (allout-widgets-exposure-undo-processor)
  675. (allout-widgets-changes-dispatcher))
  676. (allout-widgets-exposure-undo-processor)
  677. (allout-widgets-changes-dispatcher))
  678. (if allout-widgets-time-decoration-activity
  679. (setq allout-widgets-last-decoration-timing
  680. (list (allout-elapsed-time-seconds (current-time)
  681. start-time)
  682. allout-widgets-changes-record)))
  683. (setq allout-widgets-changes-record nil)
  684. (if replaced-message
  685. (if (stringp replaced-message)
  686. (message replaced-message)
  687. (message "")))))
  688. ;; alas, decorated intermediate matches are not easily undecorated
  689. ;; when they're automatically rehidden by isearch, so we're
  690. ;; dropping this nicety.
  691. ;; ;; Detect undecorated items, eg during isearch into previously
  692. ;; ;; unexposed topics, and decorate "economically". Some
  693. ;; ;; undecorated stuff is often exposed, to reduce lag, but the
  694. ;; ;; item containing the cursor is decorated. We constrain
  695. ;; ;; recursion to avoid being trapped by unexpectedly undecoratable
  696. ;; ;; items.
  697. ;; (when (and (not recursing)
  698. ;; (not (allout-current-decorated-p))
  699. ;; (or (not (equal (allout-depth) 0))
  700. ;; (not allout-container-item-widget)))
  701. ;; (let ((buffer-undo-list t))
  702. ;; (allout-widgets-exposure-change-recorder
  703. ;; allout-recent-prefix-beginning allout-recent-prefix-end nil)
  704. ;; (allout-widgets-post-command-business 'recursing)))
  705. ;; Detect and rectify fouled outline structure - decorated item
  706. ;; not at beginning of line.
  707. (let ((this-widget (or (widget-at (point))
  708. ;; XXX we really should be checking across
  709. ;; edited span, not just point and point+1
  710. (and (not (eq (point) (point-max)))
  711. (widget-at (1+ (point))))))
  712. inserted-at)
  713. (save-excursion
  714. (if (and this-widget
  715. (goto-char (widget-get this-widget :from))
  716. (not (bolp)))
  717. (if (not
  718. (condition-case err
  719. (yes-or-no-p
  720. (concat "Misplaced item won't be recognizable "
  721. " as part of outline - rectify? "))
  722. (quit nil)))
  723. (progn
  724. (if (allout-hidden-p (max (1- (point)) 1))
  725. (save-excursion
  726. (goto-char (max (1- (point)) 1))
  727. (allout-show-to-offshoot)))
  728. (allout-widgets-undecorate-item this-widget))
  729. ;; expose any hidden intervening items, so resulting
  730. ;; position is clear:
  731. (setq inserted-at (point))
  732. (allout-unprotected (insert-before-markers "\n"))
  733. (forward-char -1)
  734. ;; ensure the inserted newline is visible:
  735. (allout-flag-region inserted-at (1+ inserted-at) nil)
  736. (allout-widgets-post-command-business 'recursing)
  737. (message (concat "outline structure corrected - item"
  738. " moved to beginning of new line"))
  739. ;; preserve cursor position in some cases:
  740. (if (and inserted-at
  741. (> (point) inserted-at))
  742. (forward-char -1)))))))
  743. (error
  744. ;; zero work list so we don't get stuck futilely retrying.
  745. ;; error recording done by allout-widgets-hook-error-handler.
  746. (setq allout-widgets-changes-record nil))))
  747. ;;;_ , major change handlers:
  748. ;;;_ > allout-widgets-before-change-handler
  749. (defun allout-widgets-before-change-handler (beg end)
  750. "Business to be done before changes in a widgetized allout outline."
  751. ;; protect against unruly edits to structure:
  752. (cond
  753. (undo-in-progress (when (eq (get-text-property beg 'category)
  754. 'allout-icon-span-category)
  755. (save-excursion
  756. (goto-char beg)
  757. (let* ((item-widget (allout-get-item-widget)))
  758. (if item-widget
  759. (allout-widgets-exposure-undo-recorder
  760. item-widget))))))
  761. (inhibit-read-only t)
  762. ((not (and (boundp 'allout-mode) allout-mode)) t)
  763. ((equal this-command 'quoted-insert) t)
  764. ((not (text-property-any beg (if (equal end beg)
  765. (min (1+ beg) (point-max))
  766. end)
  767. 'field 'structure))
  768. t)
  769. ((yes-or-no-p "Unruly edit of outline structure - allow? ")
  770. (setq allout-widgets-unset-inhibit-read-only (not inhibit-read-only)
  771. inhibit-read-only t))
  772. (t
  773. ;; tell the allout-widgets-post-command-business to reestablish the hook:
  774. (setq allout-widgets-reenable-before-change-handler t)
  775. ;; and raise an error to prevent the edit (and disable the hook):
  776. (error
  777. (substitute-command-keys allout-structure-unruly-deletion-message)))))
  778. ;;;_ > allout-widgets-after-change-handler
  779. (defun allout-widgets-after-change-handler (beg end prelength)
  780. "Reconcile what needs to be reconciled for allout widgets after edits."
  781. )
  782. ;;;_ > allout-current-decorated-p ()
  783. (defun allout-current-decorated-p ()
  784. "True if the current item is not decorated"
  785. (save-excursion
  786. (if (allout-back-to-current-heading)
  787. (if (> allout-recent-depth 0)
  788. (and (allout-get-item-widget) t)
  789. allout-container-item-widget))))
  790. ;;;_ > allout-widgets-hook-error-handler
  791. (defun allout-widgets-hook-error-handler (mode args)
  792. "Process errors which occurred in the course of command hook operation.
  793. We store a backtrace of the error information in the variable,
  794. `allout-widgets-last-hook-error', unset the error handlers, and
  795. reraise the error, so that processing continues to the
  796. encompassing condition-case."
  797. ;; first deconstruct special error environment so errors here propagate
  798. ;; to encompassing condition-case:
  799. (setq debugger 'debug
  800. debug-on-error nil
  801. debug-on-signal nil)
  802. (let* ((bt (with-output-to-string (backtrace)))
  803. (this "allout-widgets-hook-error-handler")
  804. (header
  805. (format "allout-widgets-last-hook-error stored, %s/%s %s %s"
  806. this mode args
  807. (format-time-string "%e-%b-%Y %r" (current-time)))))
  808. ;; post to *Messages* then immediately replace with more compact notice:
  809. (message "%s" (setq allout-widgets-last-hook-error
  810. (format "%s:\n%s" header bt)))
  811. (message header) (sit-for allout-widgets-hook-error-post-time)
  812. ;; reraise the error, or one concerning this function if unexpected:
  813. (if (equal mode 'error)
  814. (apply 'signal args)
  815. (error "%s: unexpected mode, %s %s" this mode args))))
  816. ;;;_ > allout-widgets-changes-exceed-threshold-p ()
  817. (defun allout-widgets-adjusting-message (message)
  818. "Post MESSAGE when pending are likely to make a big enough delay.
  819. If posting of the MESSAGE is warranted and there already is a
  820. `current-message' in the minibuffer, the MESSAGE is appended to
  821. the current one, and the previously pending `current-message' is
  822. returned for later posting on completion.
  823. If posting of the MESSAGE is warranted, but no `current-message'
  824. is pending, then t is returned to indicate that case.
  825. If posting of the MESSAGE is not warranted, then nil is returned.
  826. See `allout-widgets-adjust-message-length-threshold',
  827. `allout-widgets-adjust-message-size-threshold' for message
  828. posting threshold criteria."
  829. (if (or (> (length allout-widgets-changes-record)
  830. allout-widgets-adjust-message-length-threshold)
  831. ;; for size, use distance from start of first to end of last:
  832. (let ((min (point-max))
  833. (max 0)
  834. first second)
  835. (mapc (function (lambda (entry)
  836. (if (eq :undone-exposure (car entry))
  837. nil
  838. (setq first (cadr entry)
  839. second (caddr entry))
  840. (if (< (min first second) min)
  841. (setq min (min first second)))
  842. (if (> (max first second) max)
  843. (setq max (max first second))))))
  844. allout-widgets-changes-record)
  845. (> (- max min) allout-widgets-adjust-message-size-threshold)))
  846. (let ((prior (current-message)))
  847. (message (if prior (concat prior " - " message) message))
  848. (or prior t))))
  849. ;;;_ > allout-widgets-changes-dispatcher ()
  850. (defun allout-widgets-changes-dispatcher ()
  851. "Dispatch CHANGES-RECORD items to respective widgets change processors."
  852. (if (not allout-widgets-mode-inhibit)
  853. (let* ((changes-record allout-widgets-changes-record)
  854. (changes-pending (and changes-record t))
  855. entry
  856. exposures
  857. additions
  858. deletions
  859. shifts)
  860. (when changes-pending
  861. (while changes-record
  862. (setq entry (pop changes-record))
  863. (case (car entry)
  864. (:exposed (push entry exposures))
  865. (:added (push entry additions))
  866. (:deleted (push entry deletions))
  867. (:shifted (push entry shifts))))
  868. (if exposures
  869. (allout-widgets-exposure-change-processor exposures))
  870. (if additions
  871. (allout-widgets-additions-processor additions))
  872. (if deletions
  873. (allout-widgets-deletions-processor deletions))
  874. (if shifts
  875. (allout-widgets-shifts-processor shifts))))
  876. (when (not (equal allout-widgets-mode-inhibit 'undecorated))
  877. (allout-widgets-undecorate-region (point-min)(point-max))
  878. (setq allout-widgets-mode-inhibit 'undecorated))))
  879. ;;;_ > allout-widgets-exposure-change-recorder (from to flag)
  880. (defun allout-widgets-exposure-change-recorder (from to flag)
  881. "Record allout exposure changes for tracking during post-command processing.
  882. Records changes in `allout-widgets-changes-record'."
  883. (push (list :exposed from to flag) allout-widgets-changes-record))
  884. ;;;_ > allout-widgets-exposure-change-processor (changes)
  885. (defun allout-widgets-exposure-change-processor (changes)
  886. "Widgetize and adjust item widgets tracking allout outline exposure changes.
  887. Generally invoked via `allout-exposure-change-hook'."
  888. (let ((changes (sort changes (function (lambda (this next)
  889. (< (cadr this) (cadr next))))))
  890. ;; have to distinguish between concealing and exposing so that, eg,
  891. ;; `allout-expose-topic's mix is handled properly.
  892. handled-expose
  893. handled-conceal
  894. covered
  895. deactivate-mark)
  896. (dolist (change changes)
  897. (let (handling
  898. (from (cadr change))
  899. bucket got
  900. (to (caddr change))
  901. (flag (cadddr change))
  902. parent)
  903. ;; swap from and to:
  904. (if (< to from) (setq bucket to
  905. to from
  906. from bucket))
  907. ;; have we already handled exposure changes in this region?
  908. (setq handling (if flag 'handled-conceal 'handled-expose)
  909. got (allout-range-overlaps from to (symbol-value handling))
  910. covered (car got))
  911. (set handling (cadr got))
  912. (when (not covered)
  913. (save-excursion
  914. (goto-char from)
  915. (cond
  916. ;; collapsing:
  917. (flag
  918. (allout-widgets-undecorate-region from to)
  919. (allout-beginning-of-current-line)
  920. (let ((widget (allout-get-item-widget)))
  921. (if (not widget)
  922. (allout-get-or-create-item-widget)
  923. (widget-apply widget :redecorate))))
  924. ;; expanding:
  925. (t
  926. (while (< (point) to)
  927. (allout-beginning-of-current-line)
  928. (setq parent (allout-get-item-widget))
  929. (if (not parent)
  930. (setq parent (allout-get-or-create-item-widget))
  931. (widget-apply parent :redecorate))
  932. (allout-next-visible-heading 1)
  933. (if (widget-get parent :has-subitems)
  934. (allout-redecorate-visible-subtree parent))
  935. (if (> (point) to)
  936. ;; subtree may be well beyond to - incorporate in ranges:
  937. (setq handled-expose
  938. (allout-range-overlaps from (point) handled-expose)
  939. covered (car handled-expose)
  940. handled-expose (cadr handled-expose)))
  941. (allout-next-visible-heading 1))))))))))
  942. ;;;_ > allout-widgets-additions-recorder (from to)
  943. (defun allout-widgets-additions-recorder (from to)
  944. "Record allout item additions for tracking during post-command processing.
  945. Intended for use on `allout-structure-added-hook'.
  946. FROM point at the start of the first new item and TO is point at the start
  947. of the last one.
  948. Records changes in `allout-widgets-changes-record'."
  949. (push (list :added from to) allout-widgets-changes-record))
  950. ;;;_ > allout-widgets-additions-processor (changes)
  951. (defun allout-widgets-additions-processor (changes)
  952. "Widgetize and adjust items tracking allout outline structure additions.
  953. Dispatched by `allout-widgets-post-command-business' in response to
  954. :added entries recorded by `allout-widgets-additions-recorder'."
  955. (save-excursion
  956. (let (handled
  957. covered)
  958. (dolist (change changes)
  959. (let ((from (cadr change))
  960. bucket
  961. (to (caddr change)))
  962. (if (< to from) (setq bucket to to from from bucket))
  963. ;; have we already handled exposure changes in this region?
  964. (setq handled (allout-range-overlaps from to handled)
  965. covered (car handled)
  966. handled (cadr handled))
  967. (when (not covered)
  968. (goto-char from)
  969. ;; Prior sibling and parent can both be affected.
  970. (if (allout-ascend)
  971. (allout-redecorate-visible-subtree
  972. (allout-get-or-create-item-widget 'redecorate)))
  973. (if (< (point) from)
  974. (goto-char from))
  975. (while (and (< (point) to) (not (eobp)))
  976. (allout-beginning-of-current-line)
  977. (allout-redecorate-visible-subtree
  978. (allout-get-or-create-item-widget))
  979. (allout-next-visible-heading 1))
  980. (if (> (point) to)
  981. ;; subtree may be well beyond to - incorporate in ranges:
  982. (setq handled (allout-range-overlaps from (point) handled)
  983. covered (car handled)
  984. handled (cadr handled)))))))))
  985. ;;;_ > allout-widgets-deletions-recorder (depth from)
  986. (defun allout-widgets-deletions-recorder (depth from)
  987. "Record allout item deletions for tracking during post-command processing.
  988. Intended for use on `allout-structure-deleted-hook'.
  989. DEPTH is the depth of the deleted subtree, and FROM is the point from which
  990. the subtree was deleted.
  991. Records changes in `allout-widgets-changes-record'."
  992. (push (list :deleted depth from) allout-widgets-changes-record))
  993. ;;;_ > allout-widgets-deletions-processor (changes)
  994. (defun allout-widgets-deletions-processor (changes)
  995. "Adjust items tracking allout outline structure deletions.
  996. Dispatched by `allout-widgets-post-command-business' in response to
  997. :deleted entries recorded by `allout-widgets-deletions-recorder'."
  998. (save-excursion
  999. (dolist (change changes)
  1000. (let ((depth (cadr change))
  1001. (from (caddr change)))
  1002. (goto-char from)
  1003. (when (allout-previous-visible-heading 1)
  1004. (if (> depth 1)
  1005. (allout-ascend-to-depth (1- depth)))
  1006. (allout-redecorate-visible-subtree
  1007. (allout-get-or-create-item-widget 'redecorate)))))))
  1008. ;;;_ > allout-widgets-shifts-recorder (shifted-amount at)
  1009. (defun allout-widgets-shifts-recorder (shifted-amount at)
  1010. "Record outline subtree shifts for tracking during post-command processing.
  1011. Intended for use on `allout-structure-shifted-hook'.
  1012. SHIFTED-AMOUNT is the depth change and AT is the point at the start of the
  1013. subtree that's been shifted.
  1014. Records changes in `allout-widgets-changes-record'."
  1015. (push (list :shifted shifted-amount at) allout-widgets-changes-record))
  1016. ;;;_ > allout-widgets-shifts-processor (changes)
  1017. (defun allout-widgets-shifts-processor (changes)
  1018. "Widgetize and adjust items tracking allout outline structure additions.
  1019. Dispatched by `allout-widgets-post-command-business' in response to
  1020. :shifted entries recorded by `allout-widgets-shifts-recorder'."
  1021. (save-excursion
  1022. (dolist (change changes)
  1023. (goto-char (caddr change))
  1024. (allout-ascend)
  1025. (allout-redecorate-visible-subtree))))
  1026. ;;;_ > allout-widgets-after-copy-or-kill-function ()
  1027. (defun allout-widgets-after-copy-or-kill-function ()
  1028. "Do allout-widgets processing of text just placed in the kill ring.
  1029. Intended for use on allout-after-copy-or-kill-hook."
  1030. (if (car kill-ring)
  1031. (setcar kill-ring (allout-widgets-undecorate-text (car kill-ring)))))
  1032. ;;;_ > allout-widgets-after-undo-function ()
  1033. (defun allout-widgets-after-undo-function ()
  1034. "Do allout-widgets processing of text after an undo.
  1035. Intended for use on allout-post-undo-hook."
  1036. (save-excursion
  1037. (if (allout-goto-prefix)
  1038. (allout-redecorate-item (allout-get-or-create-item-widget)))))
  1039. ;;;_ > allout-widgets-exposure-undo-recorder (widget from-state)
  1040. (defun allout-widgets-exposure-undo-recorder (widget)
  1041. "Record outline exposure undo for tracking during post-command processing.
  1042. Intended for use by `allout-graphics-modification-handler'.
  1043. WIDGET is the widget being changed.
  1044. Records changes in `allout-widgets-changes-record'."
  1045. ;; disregard the events if we're currently processing them.
  1046. (if (not allout-undo-exposure-in-progress)
  1047. (push widget allout-widgets-undo-exposure-record)))
  1048. ;;;_ > allout-widgets-exposure-undo-processor ()
  1049. (defun allout-widgets-exposure-undo-processor ()
  1050. "Adjust items tracking undo of allout outline structure exposure.
  1051. Dispatched by `allout-widgets-post-command-business' in response to
  1052. :undone-exposure entries recorded by `allout-widgets-exposure-undo-recorder'."
  1053. (let* ((allout-undo-exposure-in-progress t)
  1054. ;; inhibit undo recording while twiddling exposure to track undo:
  1055. (widgets allout-widgets-undo-exposure-record)
  1056. widget widget-start-marker widget-end-marker
  1057. from-state icon-start-point to-state
  1058. handled covered)
  1059. (setq allout-widgets-undo-exposure-record nil)
  1060. (save-excursion
  1061. (dolist (widget widgets)
  1062. (setq widget-start-marker (widget-get widget :from)
  1063. widget-end-marker (widget-get widget :to)
  1064. from-state (widget-get widget :icon-state)
  1065. icon-start-point (widget-apply widget :actual-position
  1066. :icon-start)
  1067. to-state (get-text-property icon-start-point
  1068. :icon-state))
  1069. (setq handled (allout-range-overlaps widget-start-marker
  1070. widget-end-marker
  1071. handled)
  1072. covered (car handled)
  1073. handled (cadr handled))
  1074. (when (not covered)
  1075. (goto-char (widget-get widget :from))
  1076. (when (not (allout-hidden-p))
  1077. ;; adjust actual exposure to that of to-state viz from-state
  1078. (cond ((and (eq to-state 'closed) (eq from-state 'opened))
  1079. (allout-hide-current-subtree)
  1080. (allout-decorate-item-and-context widget))
  1081. ((and (eq to-state 'opened) (eq from-state 'closed))
  1082. (save-excursion
  1083. (dolist
  1084. (expose-to (allout-chart-exposure-contour-by-icon))
  1085. (goto-char expose-to)
  1086. (allout-show-to-offshoot)))))))))))
  1087. ;;;_ > allout-chart-exposure-contour-by-icon (&optional from-depth)
  1088. (defun allout-chart-exposure-contour-by-icon (&optional from-depth)
  1089. "Return points of subtree items to which exposure should be extended.
  1090. The qualifying items are ones with a widget icon that is in the closed or
  1091. empty state, or items with undecorated subitems.
  1092. The resulting list of points is in reverse order.
  1093. Optional FROM-DEPTH is for internal use."
  1094. ;; During internal recursion, we return a pair: (at-end . result)
  1095. ;; Otherwise we just return the result.
  1096. (let ((from-depth from-depth)
  1097. start-point
  1098. at-end level-depth
  1099. this-widget
  1100. got subgot)
  1101. (if from-depth
  1102. (setq level-depth (allout-depth))
  1103. ;; at containing item:
  1104. (setq start-point (point))
  1105. (setq from-depth (allout-depth))
  1106. (setq at-end (not (allout-next-heading))
  1107. level-depth allout-recent-depth))
  1108. ;; traverse the level, recursing on deeper levels:
  1109. (while (and (not at-end)
  1110. (> allout-recent-depth from-depth)
  1111. (setq this-widget (allout-get-item-widget)))
  1112. (if (< level-depth allout-recent-depth)
  1113. ;; recurse:
  1114. (progn
  1115. (setq subgot (allout-chart-exposure-contour-by-icon level-depth)
  1116. at-end (car subgot)
  1117. subgot (cdr subgot))
  1118. (if subgot (setq got (append subgot got))))
  1119. ;; progress at this level:
  1120. (when (memq (widget-get this-widget :icon-state) '(closed empty))
  1121. (push (point) got)
  1122. (allout-end-of-subtree))
  1123. (setq at-end (not (allout-next-heading)))))
  1124. ;; tailor result depending on whether or not we're a recursion:
  1125. (if (not start-point)
  1126. (cons at-end got)
  1127. (goto-char start-point)
  1128. got)))
  1129. ;;;_ > allout-range-overlaps (from to ranges)
  1130. (defun allout-range-overlaps (from to ranges)
  1131. "Return a pair indicating overlap of FROM and TO subtree range in RANGES.
  1132. First element of result indicates whether candidate range FROM, TO
  1133. overlapped any of the existing ranges.
  1134. Second element of result is a new version of RANGES incorporating the
  1135. candidate range with overlaps consolidated.
  1136. FROM and TO must be in increasing order, as must be the pairs in RANGES."
  1137. ;; to append to the end: (rplacd next-to-last-cdr (list 'f))
  1138. (let (new-ranges
  1139. entry
  1140. ;; the start of the range that includes the candidate from:
  1141. included-from
  1142. ;; the end of the range that includes the candidate to:
  1143. included-to
  1144. ;; the candidates were inserted:
  1145. done)
  1146. (while (and ranges (not done))
  1147. (setq entry (car ranges)
  1148. ranges (cdr ranges))
  1149. (cond
  1150. (included-from
  1151. ;; some entry included the candidate from.
  1152. (cond ((> (car entry) to)
  1153. ;; current entry exceeds end of candidate range - done.
  1154. (push (list included-from to) new-ranges)
  1155. (push entry new-ranges)
  1156. (setq included-to to
  1157. done t))
  1158. ((>= (cadr entry) to)
  1159. ;; current entry includes end of candidate range - done.
  1160. (push (list included-from (cadr entry)) new-ranges)
  1161. (setq included-to (cadr entry)
  1162. done t))
  1163. ;; current entry contained in candidate range - ditch, continue:
  1164. (t nil)))
  1165. ((> (car entry) to)
  1166. ;; current entry start exceeds candidate end - done, placed as new entry
  1167. (push (list from to) new-ranges)
  1168. (push entry new-ranges)
  1169. (setq included-to to
  1170. done t))
  1171. ((>= (car entry) from)
  1172. ;; current entry start is above candidate start, but not above
  1173. ;; candidate end (by prior case).
  1174. (setq included-from from)
  1175. ;; now we have to check on whether this entry contains to, or continue:
  1176. (when (>= (cadr entry) to)
  1177. ;; current entry contains only candidate end - done:
  1178. (push (list included-from (cadr entry)) new-ranges)
  1179. (setq included-to (cadr entry)
  1180. done t))
  1181. ;; otherwise, we will continue to look for placement of candidate end.
  1182. )
  1183. ((>= (cadr entry) to)
  1184. ;; current entry properly contains candidate range.
  1185. (push entry new-ranges)
  1186. (setq included-from (car entry)
  1187. included-to (cadr entry)
  1188. done t))
  1189. ((>= (cadr entry) from)
  1190. ;; current entry contains start of candidate range.
  1191. (setq included-from (car entry)))
  1192. (t
  1193. ;; current entry is below the candidate range.
  1194. (push entry new-ranges))))
  1195. (cond ((and included-from included-to)
  1196. ;; candidates placed.
  1197. nil)
  1198. ((not (or included-from included-to))
  1199. ;; candidates found no place, must be at the end:
  1200. (push (list from to) new-ranges))
  1201. (included-from
  1202. ;; candidate start placed but end not:
  1203. (push (list included-from to) new-ranges))
  1204. ;; might be included-to and not included-from, indicating new entry.
  1205. )
  1206. (setq new-ranges (nreverse new-ranges))
  1207. (if ranges (setq new-ranges (append new-ranges ranges)))
  1208. (list (if included-from t) new-ranges)))
  1209. ;;;_ > allout-test-range-overlaps ()
  1210. (defun allout-test-range-overlaps ()
  1211. "allout-range-overlaps unit tests."
  1212. (let* (ranges
  1213. got
  1214. (try (lambda (from to)
  1215. (setq got (allout-range-overlaps from to ranges))
  1216. (setq ranges (cadr got))
  1217. got)))
  1218. ;; ;; biggie:
  1219. ;; (setq ranges nil)
  1220. ;; ;; ~ .02 to .1 seconds for just repeated listing args instead of funcall
  1221. ;; ;; ~ 13 seconds for doing repeated funcall
  1222. ;; (message "time-trial: %s, resulting size %s"
  1223. ;; (time-trial
  1224. ;; '(let ((size 10000)
  1225. ;; doing)
  1226. ;; (random t)
  1227. ;; (dotimes (count size)
  1228. ;; (setq doing (random size))
  1229. ;; (funcall try doing (+ doing (random 5)))
  1230. ;; ;;(list doing (+ doing (random 5)))
  1231. ;; )))
  1232. ;; (length ranges))
  1233. ;; (sit-for 2)
  1234. ;; fresh:
  1235. (setq ranges nil)
  1236. (assert (equal (funcall try 3 5) '(nil ((3 5)))))
  1237. ;; add range at end:
  1238. (assert (equal (funcall try 10 12) '(nil ((3 5) (10 12)))))
  1239. ;; add range at beginning:
  1240. (assert (equal (funcall try 1 2) '(nil ((1 2) (3 5) (10 12)))))
  1241. ;; insert range somewhere in the middle:
  1242. (assert (equal (funcall try 7 9) '(nil ((1 2) (3 5) (7 9) (10 12)))))
  1243. ;; consolidate some:
  1244. (assert (equal (funcall try 5 8) '(t ((1 2) (3 9) (10 12)))))
  1245. ;; add more:
  1246. (assert (equal (funcall try 15 17) '(nil ((1 2) (3 9) (10 12) (15 17)))))
  1247. ;; add more:
  1248. (assert (equal (funcall try 20 22)
  1249. '(nil ((1 2) (3 9) (10 12) (15 17) (20 22)))))
  1250. ;; encompass more:
  1251. (assert (equal (funcall try 4 11) '(t ((1 2) (3 12) (15 17) (20 22)))))
  1252. ;; encompass all:
  1253. (assert (equal (funcall try 2 25) '(t ((1 25)))))
  1254. ;; fresh slate:
  1255. (setq ranges nil)
  1256. (assert (equal (funcall try 20 25) '(nil ((20 25)))))
  1257. (assert (equal (funcall try 30 35) '(nil ((20 25) (30 35)))))
  1258. (assert (equal (funcall try 26 28) '(nil ((20 25) (26 28) (30 35)))))
  1259. (assert (equal (funcall try 15 20) '(t ((15 25) (26 28) (30 35)))))
  1260. (assert (equal (funcall try 10 30) '(t ((10 35)))))
  1261. (assert (equal (funcall try 5 6) '(nil ((5 6) (10 35)))))
  1262. (assert (equal (funcall try 2 100) '(t ((2 100)))))
  1263. (setq ranges nil)
  1264. ))
  1265. ;;;_ > allout-widgetize-buffer (&optional doing)
  1266. (defun allout-widgetize-buffer (&optional doing)
  1267. "EXAMPLE FUNCTION. Widgetize items in buffer using allout-chart-subtree.
  1268. We economize by just focusing on the first of local-maximum depth siblings.
  1269. Optional DOING is for internal use - a chart of the current level, for
  1270. recursive operation."
  1271. (interactive)
  1272. (if (not doing)
  1273. (save-excursion
  1274. (goto-char (point-min))
  1275. ;; Construct the chart by scanning the siblings:
  1276. (dolist (top-level-sibling (allout-chart-siblings))
  1277. (goto-char top-level-sibling)
  1278. (let ((subchart (allout-chart-subtree)))
  1279. (if subchart
  1280. (allout-widgetize-buffer subchart)))))
  1281. ;; save-excursion was done on recursion entry, not necessary here.
  1282. (let (have-sublists)
  1283. (dolist (sibling doing)
  1284. (when (listp sibling)
  1285. (setq have-sublists t)
  1286. (allout-widgetize-buffer sibling)))
  1287. (when (and (not have-sublists) (not (widget-at (car doing))))
  1288. (goto-char (car doing))
  1289. (allout-get-or-create-item-widget)))))
  1290. ;;;_ : Item widget and constructors
  1291. ;;;_ $ allout-item-widget
  1292. (define-widget 'allout-item-widget 'default
  1293. "A widget presenting an allout outline item."
  1294. 'button nil
  1295. ;; widget-field-at respects this to get item if 'field is unused.
  1296. ;; we don't use field to avoid collision with end-of-line, etc, on which
  1297. ;; allout depends.
  1298. 'real-field nil
  1299. ;; data fields:
  1300. ;; tailor the widget for a specific item
  1301. :create 'allout-decorate-item-and-context
  1302. :value-delete 'allout-widgets-undecorate-item
  1303. ;; Not Yet Converted (from original, tree-widget stab)
  1304. :expander 'allout-tree-event-dispatcher ; get children when nil :args
  1305. :expander-p 'identity ; always engage the :expander
  1306. :action 'allout-tree-widget-action
  1307. ;; :notify "when item changes"
  1308. ;; force decoration of item but not context, unless already done this tick:
  1309. :redecorate 'allout-redecorate-item
  1310. :last-decorated-tick nil
  1311. ;; recognize the actual situation of the item's text:
  1312. :parse-item 'allout-parse-item-at-point
  1313. ;; decorate the entirety of the item, sans offspring:
  1314. :decorate-item-span 'allout-decorate-item-span
  1315. ;; decorate the various item elements:
  1316. :decorate-guides 'allout-decorate-item-guides
  1317. :decorate-icon 'allout-decorate-item-icon
  1318. :decorate-cue 'allout-decorate-item-cue
  1319. :decorate-body 'allout-decorate-item-body
  1320. :actual-position 'allout-item-actual-position
  1321. ;; Layout parameters:
  1322. :is-container nil ; is this actually the encompassing file/connection?
  1323. :from nil ; item beginning - marker
  1324. :to nil ; item end - marker
  1325. :span-overlay nil ; overlay by which actual position is determined
  1326. ;; also serves as guide-end:
  1327. :icon-start nil
  1328. :icon-end nil
  1329. :distinctive-start nil
  1330. ;; also serves as cue-start:
  1331. :distinctive-end nil
  1332. ;; also serves as cue-end:
  1333. :body-start nil
  1334. :body-end nil
  1335. :depth nil
  1336. :has-subitems nil
  1337. :was-has-subitems 'init
  1338. :expanded nil
  1339. :was-expanded 'init
  1340. :brief nil
  1341. :was-brief 'init
  1342. :does-encrypt nil ; pending encryption when :is-encrypted false.
  1343. :is-encrypted nil
  1344. ;; the actual location of the item text:
  1345. :location 'allout-item-location
  1346. :button-keymap allout-item-icon-keymap ; XEmacs
  1347. :keymap allout-item-icon-keymap ; Emacs
  1348. ;; Element regions:
  1349. :guides-span nil
  1350. :icon-span nil
  1351. :cue-span nil
  1352. :bullet nil
  1353. :was-bullet nil
  1354. :body-span nil
  1355. :body-brevity-p 'allout-body-brevity-p
  1356. ;; :guide-column-flags indicate (in reverse order) whether or not the
  1357. ;; item's ancestor at the depth corresponding to the column has a
  1358. ;; subsequent sibling - ie, whether or not the corresponding column needs
  1359. ;; a descender line to connect that ancestor with its sibling.
  1360. :guide-column-flags nil
  1361. :was-guide-column-flags 'init
  1362. ;; ie, has subitems:
  1363. :populous-p 'allout-item-populous-p
  1364. :help-echo 'allout-tree-widget-help-echo
  1365. )
  1366. ;;;_ > allout-new-item-widget ()
  1367. (defsubst allout-new-item-widget ()
  1368. "create a new item widget, not yet situated anywhere."
  1369. (if allout-widgets-maintain-tally
  1370. ;; all the extra overhead is incurred only when doing the
  1371. ;; maintenance, except the condition, which can't be avoided.
  1372. (let ((widget (widget-convert 'allout-item-widget)))
  1373. (puthash widget nil allout-widgets-tally)
  1374. widget)
  1375. (widget-convert 'allout-item-widget)))
  1376. ;;;_ : Item decoration
  1377. ;;;_ > allout-decorate-item-and-context (item-widget &optional redecorate
  1378. ;;; blank-container parent)
  1379. (defun allout-decorate-item-and-context (item-widget &optional redecorate
  1380. blank-container parent)
  1381. "Create or adjust widget decorations for ITEM-WIDGET and neighbors at point.
  1382. The neighbors include its siblings and parent.
  1383. ITEM-WIDGET can be a created or converted allout-item-widget.
  1384. If you're only trying to get or create a widget for an item, use
  1385. `allout-get-or-create-item-widget'. If you have the item-widget, applying
  1386. :redecorate will do the right thing.
  1387. Optional BLANK-CONTAINER is for internal use. It is used to fabricate a
  1388. container widget for an empty-bodied container, in the course of decorating
  1389. a proper \(non-container\) item which starts at the beginning of the file.
  1390. Optional REDECORATE causes redecoration of the item-widget and
  1391. its siblings, even if already decorated in this cycle of the command loop.
  1392. Optional PARENT, when provided, bypasses some navigation and computation
  1393. necessary to obtain the parent of the items being processed.
  1394. We return the item-widget corresponding to the item at point."
  1395. (when (or redecorate
  1396. (not (equal (widget-get item-widget :last-decorated-tick)
  1397. allout-command-counter)))
  1398. (let* ((allout-inhibit-body-modification-hook t)
  1399. (was-modified (buffer-modified-p))
  1400. (was-point (point))
  1401. prefix-start
  1402. (is-container (or blank-container
  1403. (not (setq prefix-start (allout-goto-prefix)))
  1404. (< was-point prefix-start)))
  1405. ;; steady-point (set in two steps) is reliable across parent
  1406. ;; widget-creation.
  1407. (steady-point (progn (if is-container (goto-char 1))
  1408. (point-marker)))
  1409. (steady-point (progn (set-marker-insertion-type steady-point t)
  1410. steady-point))
  1411. (parent (and (not is-container)
  1412. (allout-get-or-create-parent-widget)))
  1413. parent-flags parent-depth
  1414. successor-sibling
  1415. body
  1416. doing-item
  1417. sub-item-widget
  1418. depth
  1419. reverse-siblings-chart
  1420. (buffer-undo-list t))
  1421. ;; At this point the parent is decorated and parent-flags indicate
  1422. ;; its guide lines. We will iterate over the siblings according to a
  1423. ;; chart we create at the start, and going from last to first so we
  1424. ;; don't have to worry about text displacement caused by widgetizing.
  1425. (if is-container
  1426. (progn (widget-put item-widget :is-container t)
  1427. (setq reverse-siblings-chart (list 1)))
  1428. (goto-char (widget-apply parent :actual-position :from))
  1429. (if (widget-get parent :is-container)
  1430. ;; `allout-goto-prefix' will go to first non-container item:
  1431. (allout-goto-prefix)
  1432. (allout-next-heading))
  1433. (setq depth (allout-recent-depth))
  1434. (setq reverse-siblings-chart (list allout-recent-prefix-beginning))
  1435. (while (allout-next-sibling)
  1436. (push allout-recent-prefix-beginning reverse-siblings-chart)))
  1437. (dolist (doing-at reverse-siblings-chart)
  1438. (goto-char doing-at)
  1439. (when allout-widgets-track-decoration
  1440. (sit-for 0))
  1441. (setq doing-item (if (= doing-at steady-point)
  1442. item-widget
  1443. (or (allout-get-item-widget)
  1444. (allout-new-item-widget))))
  1445. (when (or redecorate (not (equal (widget-get doing-item
  1446. :last-decorated-tick)
  1447. allout-command-counter)))
  1448. (widget-apply doing-item :parse-item t blank-container)
  1449. (widget-apply doing-item :decorate-item-span)
  1450. (widget-apply doing-item :decorate-guides
  1451. parent (and successor-sibling t))
  1452. (widget-apply doing-item :decorate-icon)
  1453. (widget-apply doing-item :decorate-cue)
  1454. (widget-apply doing-item :decorate-body)
  1455. (widget-put doing-item :last-decorated-tick allout-command-counter))
  1456. (setq successor-sibling doing-at))
  1457. (set-buffer-modified-p was-modified)
  1458. (goto-char steady-point)
  1459. ;; must null the marker or the buffer gets clogged with impedance:
  1460. (set-marker steady-point nil)
  1461. item-widget)))
  1462. ;;;_ > allout-redecorate-item (item)
  1463. (defun allout-redecorate-item (item-widget)
  1464. "Resituate ITEM-WIDGET decorations, disregarding context.
  1465. Use this to redecorate only the item, when you know that its
  1466. situation with respect to siblings, parent, and offspring is
  1467. unchanged from its last decoration. Use
  1468. `allout-decorate-item-and-context' instead to reassess and adjust
  1469. relevant context, when suitable."
  1470. (if (not (equal (widget-get item-widget :last-decorated-tick)
  1471. allout-command-counter))
  1472. (let ((was-modified (buffer-modified-p))
  1473. (buffer-undo-list t))
  1474. (widget-apply item-widget :parse-item)
  1475. (widget-apply item-widget :decorate-guides)
  1476. (widget-apply item-widget :decorate-icon)
  1477. (widget-apply item-widget :decorate-cue)
  1478. (widget-apply item-widget :decorate-body)
  1479. (set-buffer-modified-p was-modified))))
  1480. ;;;_ > allout-redecorate-visible-subtree (&optional parent-widget
  1481. ;;; depth chart)
  1482. (defun allout-redecorate-visible-subtree (&optional parent-widget depth chart)
  1483. "Redecorate all visible items in subtree at point.
  1484. Optional PARENT-WIDGET is for optimization, when the parent
  1485. widget is already available.
  1486. Optional DEPTH restricts the excursion depth of covered.
  1487. Optional CHART is for internal recursion, to carry a chart of the
  1488. target items.
  1489. Point is left at the last sibling in the visible subtree."
  1490. ;; using a treatment that takes care of all the siblings on a level, we
  1491. ;; only need apply it to the first sibling on the level, and we can
  1492. ;; collect and pass the parent of the lower levels to recursive calls as
  1493. ;; we go.
  1494. (let ((parent-widget
  1495. (if (and parent-widget (widget-apply parent-widget
  1496. :actual-position :from))
  1497. (progn (goto-char (widget-apply parent-widget
  1498. :actual-position :from))
  1499. parent-widget)
  1500. (let ((got (allout-get-item-widget)))
  1501. (if got
  1502. (allout-decorate-item-and-context got 'redecorate)
  1503. (allout-get-or-create-item-widget 'redecorate)))))
  1504. (pending-chart (or chart (allout-chart-subtree nil 'visible)))
  1505. item-widget
  1506. previous-sibling-point
  1507. previous-sibling
  1508. recent-sibling-point)
  1509. (setq pending-chart (nreverse pending-chart))
  1510. (dolist (sibling-point pending-chart)
  1511. (cond ((integerp sibling-point)
  1512. (when (not previous-sibling-point)
  1513. (goto-char sibling-point)
  1514. (if (setq item-widget (allout-get-item-widget nil))
  1515. (allout-decorate-item-and-context item-widget 'redecorate
  1516. nil parent-widget)
  1517. (allout-get-or-create-item-widget)))
  1518. (setq previous-sibling-point sibling-point
  1519. recent-sibling-point sibling-point))
  1520. ((listp sibling-point)
  1521. (if (or (not depth)
  1522. (> depth 1))
  1523. (allout-redecorate-visible-subtree
  1524. (if (not previous-sibling-point)
  1525. ;; containment discontinuity - sigh
  1526. parent-widget
  1527. (allout-get-or-create-item-widget 'redecorate))
  1528. (if depth (1- depth))
  1529. sibling-point)))))
  1530. (if (and recent-sibling-point (< (point) recent-sibling-point))
  1531. (goto-char recent-sibling-point))))
  1532. ;;;_ > allout-parse-item-at-point (item-widget &optional at-beginning
  1533. ;;; blank-container)
  1534. (defun allout-parse-item-at-point (item-widget &optional at-beginning
  1535. blank-container)
  1536. "Set widget ITEM-WIDGET layout parameters per item-at-point's actual layout.
  1537. If optional AT-BEGINNING is t, then point is assumed to be at the start of
  1538. the item prefix.
  1539. If optional BLANK-CONTAINER is true, then the parameters of a container
  1540. which has an empty body are set. \(Though the body is blank, the object
  1541. may have subitems.\)"
  1542. ;; Uncomment this sit-for to notice where decoration is happening:
  1543. ;; (sit-for .1)
  1544. (let* ((depth (allout-depth))
  1545. (depth (if blank-container 0 depth))
  1546. (is-container (or blank-container (zerop depth)))
  1547. (does-encrypt (and (not is-container)
  1548. (allout-encrypted-type-prefix)))
  1549. (is-encrypted (and does-encrypt (allout-encrypted-topic-p)))
  1550. (icon-end allout-recent-prefix-end)
  1551. (icon-start (1- icon-end))
  1552. body-start
  1553. body-end
  1554. bullet
  1555. has-subitems
  1556. (contents-depth (1+ depth))
  1557. )
  1558. (widget-put item-widget :depth depth)
  1559. (if is-container
  1560. (progn
  1561. (widget-put item-widget :from (allout-set-boundary-marker
  1562. :from (point-min)
  1563. (widget-get item-widget :from)))
  1564. (widget-put item-widget :icon-end nil)
  1565. (widget-put item-widget :icon-start nil)
  1566. (setq body-start (widget-put item-widget :body-start 1)))
  1567. ;; not container:
  1568. (widget-put item-widget :from (allout-set-boundary-marker
  1569. :from (if at-beginning
  1570. (point)
  1571. allout-recent-prefix-beginning)
  1572. (widget-get item-widget :from)))
  1573. (widget-put item-widget :icon-start icon-start)
  1574. (widget-put item-widget :icon-end icon-end)
  1575. (when does-encrypt
  1576. (widget-put item-widget :does-encrypt t)
  1577. (widget-put item-widget :is-encrypted is-encrypted))
  1578. ;; cue area:
  1579. (setq body-start icon-end)
  1580. (widget-put item-widget :bullet (setq bullet (allout-get-bullet)))
  1581. (if (equal (char-after body-start) ? )
  1582. (setq body-start (1+ body-start)))
  1583. (widget-put item-widget :body-start body-start)
  1584. )
  1585. ;; Both container and regular items:
  1586. ;; :body-end (doesn't include a trailing blank line, if any) -
  1587. (widget-put item-widget :body-end (setq body-end
  1588. (if blank-container
  1589. 1
  1590. (allout-end-of-entry))))
  1591. (widget-put item-widget :to (allout-set-boundary-marker
  1592. :to (if blank-container
  1593. (point-min)
  1594. (or (allout-pre-next-prefix)
  1595. (goto-char (point-max))))
  1596. (widget-get item-widget :to)))
  1597. (widget-put item-widget :has-subitems
  1598. (setq has-subitems
  1599. (and
  1600. ;; has a subsequent item:
  1601. (not (= body-end (point-max)))
  1602. ;; subsequent item is deeper:
  1603. (< depth (setq contents-depth (allout-recent-depth))))))
  1604. ;; note :expanded - true if widget item's content is currently visible?
  1605. (widget-put item-widget :expanded
  1606. (and has-subitems
  1607. ;; subsequent item is or isn't visible:
  1608. (save-excursion
  1609. (goto-char allout-recent-prefix-beginning)
  1610. (not (allout-hidden-p)))))))
  1611. ;;;_ > allout-set-boundary-marker (boundary position &optional current-marker)
  1612. (defun allout-set-boundary-marker (boundary position &optional current-marker)
  1613. "Set or create item widget BOUNDARY type marker at POSITION.
  1614. Optional CURRENT-MARKER is the marker currently being used for
  1615. the boundary, if any.
  1616. BOUNDARY type is either :from or :to, determining the marker insertion type."
  1617. (if (not position) (setq position (point)))
  1618. (if current-marker
  1619. (set-marker current-marker position)
  1620. (let ((marker (make-marker)))
  1621. ;; XXX dang - would like for :from boundary to advance after inserted
  1622. ;; text, but that would omit new header prefixes when allout
  1623. ;; relevels, etc. this competes with ad-hoc edits, which would
  1624. ;; better be omitted
  1625. (set-marker-insertion-type marker nil)
  1626. (set-marker marker position))))
  1627. ;;;_ > allout-decorate-item-span (item-widget)
  1628. (defun allout-decorate-item-span (item-widget)
  1629. "Equip the item with a span, as an entirety.
  1630. This span is implemented so it can be used to detect displacement
  1631. of the widget in absolute terms, and provides an offset bias for
  1632. the various element spans."
  1633. (if (and (widget-get item-widget :is-container)
  1634. ;; the only case where the span could be empty.
  1635. (eq (widget-get item-widget :from)
  1636. (widget-get item-widget :to)))
  1637. nil
  1638. (allout-item-span item-widget
  1639. (widget-get item-widget :from)
  1640. (widget-get item-widget :to))))
  1641. ;;;_ > allout-decorate-item-guides (item-widget
  1642. ;;; &optional parent-widget has-successor)
  1643. (defun allout-decorate-item-guides (item-widget
  1644. &optional parent-widget has-successor)
  1645. "Add ITEM-WIDGET guide icon-prefix descender and connector text properties.
  1646. Optional arguments provide context for deriving the guides. In
  1647. their absence, the current guide column flags are used.
  1648. Optional PARENT-WIDGET is the widget for the item's parent item.
  1649. Optional HAS-SUCCESSOR is true iff the item is followed by a sibling.
  1650. We also hide the header-prefix string.
  1651. Guides are established according to the item-widget's :guide-column-flags,
  1652. when different than :was-guide-column-flags. Changing that property and
  1653. reapplying this method will rectify the glyphs."
  1654. (when (not (widget-get item-widget :is-container))
  1655. (let* ((depth (widget-get item-widget :depth))
  1656. (parent-depth (and parent-widget
  1657. (widget-get parent-widget :depth)))
  1658. (parent-flags (and parent-widget
  1659. (widget-get parent-widget :guide-column-flags)))
  1660. (parent-flags-depth (length parent-flags))
  1661. (extender-length (- depth (+ parent-flags-depth 2)))
  1662. (flags (or (and (> depth 1)
  1663. parent-widget
  1664. (widget-put item-widget :guide-column-flags
  1665. (append (list has-successor)
  1666. (if (< 0 extender-length)
  1667. (make-list extender-length
  1668. '-))
  1669. parent-flags)))
  1670. (widget-get item-widget :guide-column-flags)))
  1671. (was-flags (widget-get item-widget :was-guide-column-flags))
  1672. (guides-start (widget-get item-widget :from))
  1673. (guides-end (widget-get item-widget :icon-start))
  1674. (position guides-start)
  1675. (increment (length allout-header-prefix))
  1676. reverse-flags
  1677. guide-name
  1678. extenders paint-extenders
  1679. (inhibit-read-only t))
  1680. (when (not (equal was-flags flags))
  1681. (setq reverse-flags (reverse flags))
  1682. (while reverse-flags
  1683. (setq guide-name
  1684. (cond ((null (cdr reverse-flags))
  1685. (if (car reverse-flags)
  1686. 'mid-connector
  1687. 'end-connector))
  1688. ((eq (car reverse-flags) '-)
  1689. ;; accumulate extenders tally, to be painted on next
  1690. ;; non-extender flag, according to the flag type.
  1691. (setq extenders (1+ (or extenders 0)))
  1692. nil)
  1693. ((car reverse-flags)
  1694. 'through-descender)
  1695. (t 'skip-descender)))
  1696. (when guide-name
  1697. (put-text-property position (setq position (+ position increment))
  1698. 'display (allout-fetch-icon-image guide-name))
  1699. (if (> increment 1) (setq increment 1))
  1700. (when extenders
  1701. ;; paint extenders after a connector, else leave spaces.
  1702. (dotimes (i extenders)
  1703. (put-text-property
  1704. position (setq position (1+ position))
  1705. 'display (allout-fetch-icon-image
  1706. (if (memq guide-name '(mid-connector end-connector))
  1707. 'extender-connector
  1708. 'skip-descender))))
  1709. (setq extenders nil)))
  1710. (setq reverse-flags (cdr reverse-flags)))
  1711. (widget-put item-widget :was-guide-column-flags flags))
  1712. (allout-item-element-span-is item-widget :guides-span
  1713. guides-start guides-end))))
  1714. ;;;_ > allout-decorate-item-icon (item-widget)
  1715. (defun allout-decorate-item-icon (item-widget)
  1716. "Add item icon glyph and distinctive bullet text properties to ITEM-WIDGET."
  1717. (when (not (widget-get item-widget :is-container))
  1718. (let* ((icon-start (widget-get item-widget :icon-start))
  1719. (icon-end (widget-get item-widget :icon-end))
  1720. (bullet (widget-get item-widget :bullet))
  1721. (use-bullet bullet)
  1722. (was-bullet (widget-get item-widget :was-bullet))
  1723. (distinctive (allout-distinctive-bullet bullet))
  1724. (distinctive-start (widget-get item-widget :distinctive-start))
  1725. (distinctive-end (widget-get item-widget :distinctive-end))
  1726. (does-encrypt (widget-get item-widget :does-encrypt))
  1727. (is-encrypted (and does-encrypt (widget-get item-widget
  1728. :is-encrypted)))
  1729. (expanded (widget-get item-widget :expanded))
  1730. (has-subitems (widget-get item-widget :has-subitems))
  1731. (inhibit-read-only t)
  1732. icon-state)
  1733. (when (not (and (equal (widget-get item-widget :was-expanded) expanded)
  1734. (equal (widget-get item-widget :was-has-subitems)
  1735. has-subitems)
  1736. (equal (widget-get item-widget :was-does-encrypt)
  1737. does-encrypt)
  1738. (equal (widget-get item-widget :was-is-encrypted)
  1739. is-encrypted)))
  1740. (setq icon-state
  1741. (cond (does-encrypt (if is-encrypted
  1742. 'locked-encrypted
  1743. 'unlocked-encrypted))
  1744. (expanded 'opened)
  1745. (has-subitems 'closed)
  1746. (t 'empty)))
  1747. (put-text-property icon-start (1+ icon-start)
  1748. 'display (allout-fetch-icon-image icon-state))
  1749. (widget-put item-widget :was-expanded expanded)
  1750. (widget-put item-widget :was-has-subitems has-subitems)
  1751. (widget-put item-widget :was-does-encrypt does-encrypt)
  1752. (widget-put item-widget :was-is-encrypted is-encrypted)
  1753. ;; preserve as a widget property to track last known:
  1754. (widget-put item-widget :icon-state icon-state)
  1755. ;; preserve as a text property to track undo:
  1756. (put-text-property icon-start icon-end :icon-state icon-state))
  1757. (allout-item-element-span-is item-widget :icon-span
  1758. icon-start icon-end)
  1759. (when (not (string= was-bullet bullet))
  1760. (cond ((not distinctive)
  1761. ;; XXX we strip the prior properties without even checking if
  1762. ;; the prior bullet was distinctive, because the widget
  1763. ;; provisions to convey that info is disappearing, sigh.
  1764. (remove-text-properties icon-end (1+ icon-end) '(display))
  1765. (setq distinctive-start icon-end distinctive-end icon-end)
  1766. (widget-put item-widget :distinctive-start distinctive-start)
  1767. (widget-put item-widget :distinctive-end distinctive-end))
  1768. ((not (string= bullet allout-numbered-bullet))
  1769. (setq distinctive-start icon-end distinctive-end (+ icon-end 1)))
  1770. (does-encrypt
  1771. (setq distinctive-start icon-end distinctive-end (+ icon-end 1)))
  1772. (t
  1773. (goto-char icon-end)
  1774. (looking-at "[0-9]+")
  1775. (setq use-bullet (buffer-substring icon-end (match-end 0)))
  1776. (setq distinctive-start icon-end
  1777. distinctive-end (match-end 0))))
  1778. (put-text-property distinctive-start distinctive-end 'display
  1779. use-bullet)
  1780. (widget-put item-widget :was-bullet bullet)
  1781. (widget-put item-widget :distinctive-start distinctive-start)
  1782. (widget-put item-widget :distinctive-end distinctive-end)))))
  1783. ;;;_ > allout-decorate-item-cue (item-widget)
  1784. (defun allout-decorate-item-cue (item-widget)
  1785. "Incorporate space between bullet icon and body to the ITEM-WIDGET."
  1786. ;; NOTE: most of the cue-area
  1787. (when (not (widget-get item-widget :is-container))
  1788. (let* ((cue-start (or (widget-get item-widget :distinctive-end)
  1789. (widget-get item-widget :icon-end)))
  1790. (body-start (widget-get item-widget :body-start))
  1791. (expanded (widget-get item-widget :expanded))
  1792. (has-subitems (widget-get item-widget :has-subitems))
  1793. (inhibit-read-only t))
  1794. (allout-item-element-span-is item-widget :cue-span cue-start body-start)
  1795. (put-text-property (1- body-start) body-start 'rear-nonsticky t))))
  1796. ;;;_ > allout-decorate-item-body (item-widget &optional force)
  1797. (defun allout-decorate-item-body (item-widget &optional force)
  1798. "Incorporate item body text as part the ITEM-WIDGET.
  1799. Optional FORCE means force reassignment of the region property."
  1800. (let* ((allout-inhibit-body-modification-hook t)
  1801. (body-start (widget-get item-widget :body-start))
  1802. (body-end (widget-get item-widget :body-end))
  1803. (body-text-end body-end)
  1804. (inhibit-read-only t))
  1805. (allout-item-element-span-is item-widget :body-span
  1806. body-start (min (1+ body-end) (point-max))
  1807. force)))
  1808. ;;;_ > allout-item-actual-position (item-widget field)
  1809. (defun allout-item-actual-position (item-widget field)
  1810. "Return ITEM-WIDGET FIELD position taking item displacement into account."
  1811. ;; The item's sub-element positions (:icon-end, :body-start, etc) are
  1812. ;; accurate when the item is parsed, but some offsets from the start
  1813. ;; drift with text added in the body.
  1814. ;;
  1815. ;; Rather than reparse an item with every change (inefficient), or derive
  1816. ;; every position from a distinct field marker/overlay (prohibitive as
  1817. ;; the number of items grows), we use the displacement tracking of the
  1818. ;; :span-overlay's markers, against the registered :from or :body-end
  1819. ;; (depending on whether the requested field value is before or after the
  1820. ;; item body), to bias the registered values.
  1821. ;;
  1822. ;; This is not necessary/useful when the item is being decorated, because
  1823. ;; that always must be preceded by a fresh item parse.
  1824. (if (not (eq field :body-end))
  1825. (widget-get item-widget :from)
  1826. (let* ((span-overlay (widget-get item-widget :span-overlay))
  1827. (body-end-position (widget-get item-widget :body-end))
  1828. (ref-marker-position (and span-overlay
  1829. (overlay-end span-overlay)))
  1830. (offset (and body-end-position span-overlay
  1831. (- (or ref-marker-position 0)
  1832. body-end-position))))
  1833. (+ (widget-get item-widget field) (or offset 0)))))
  1834. ;;;_ : Item undecoration
  1835. ;;;_ > allout-widgets-undecorate-region (start end)
  1836. (defun allout-widgets-undecorate-region (start end)
  1837. "Eliminate widgets and decorations for all items in region from START to END."
  1838. (let ((next start)
  1839. widget)
  1840. (save-excursion
  1841. (goto-char start)
  1842. (while (< (setq next (next-single-char-property-change next
  1843. 'display
  1844. (current-buffer)
  1845. end))
  1846. end)
  1847. (goto-char next)
  1848. (when (setq widget (allout-get-item-widget))
  1849. ;; if the next-property/overly progression got us to a widget:
  1850. (allout-widgets-undecorate-item widget t))))))
  1851. ;;;_ > allout-widgets-undecorate-text (text)
  1852. (defun allout-widgets-undecorate-text (text)
  1853. "Eliminate widgets and decorations for all items in TEXT."
  1854. (remove-text-properties 0 (length text)
  1855. '(display nil :icon-state nil rear-nonsticky nil
  1856. category nil button nil field nil)
  1857. text)
  1858. text)
  1859. ;;;_ > allout-widgets-undecorate-item (item-widget &optional no-expose)
  1860. (defun allout-widgets-undecorate-item (item-widget &optional no-expose)
  1861. "Remove widget decorations from ITEM-WIDGET.
  1862. Any concealed content head lines and item body is exposed, unless
  1863. optional NO-EXPOSE is non-nil."
  1864. (let ((from (widget-get item-widget :from))
  1865. (to (widget-get item-widget :to))
  1866. (text-properties-to-remove '(display nil
  1867. :icon-state nil
  1868. rear-nonsticky nil
  1869. category nil
  1870. button nil
  1871. field nil))
  1872. (span-overlay (widget-get item-widget :span-overlay))
  1873. (button-overlay (widget-get item-widget :button))
  1874. (was-modified (buffer-modified-p))
  1875. (buffer-undo-list t)
  1876. (inhibit-read-only t))
  1877. (if (not no-expose)
  1878. (allout-flag-region from to nil))
  1879. (allout-unprotected
  1880. (remove-text-properties from to text-properties-to-remove))
  1881. (when span-overlay
  1882. (delete-overlay span-overlay) (widget-put item-widget :span-overlay nil))
  1883. (when button-overlay
  1884. (delete-overlay button-overlay) (widget-put item-widget :button nil))
  1885. (set-marker from nil)
  1886. (set-marker to nil)
  1887. (if (not was-modified)
  1888. (set-buffer-modified-p nil))))
  1889. ;;;_ : Item decoration support
  1890. ;;;_ > allout-item-span (item-widget &optional start end)
  1891. (defun allout-item-span (item-widget &optional start end)
  1892. "Return or register the location of an ITEM-WIDGET's actual START and END.
  1893. If START and END are not passed in, return either a dotted pair
  1894. of the current span, if established, or nil if not yet set.
  1895. When the START and END are passed, return the distance that the
  1896. start of the item moved. We return 0 if the span was not
  1897. previously established or is not moved."
  1898. (let ((overlay (widget-get item-widget :span-overlay))
  1899. was-start was-end
  1900. changed)
  1901. (cond ((not overlay) (when start
  1902. (setq overlay (make-overlay start end nil t nil))
  1903. (overlay-put overlay 'button item-widget)
  1904. (overlay-put overlay 'evaporate t)
  1905. (widget-put item-widget :span-overlay overlay)
  1906. t))
  1907. ;; report:
  1908. ((not start) (cons (overlay-start overlay) (overlay-end overlay)))
  1909. ;; move:
  1910. ((or (not (equal (overlay-start overlay) start))
  1911. (not (equal (overlay-end overlay) end)))
  1912. (move-overlay overlay start end)
  1913. t)
  1914. ;; specified span already set:
  1915. (t nil))))
  1916. ;;;_ > allout-item-element-span-is (item-widget element
  1917. ;;; &optional start end force)
  1918. (defun allout-item-element-span-is (item-widget element
  1919. &optional start end force)
  1920. "Return or register the location of the indicated ITEM-WIDGET ELEMENT.
  1921. ELEMENT is one of :guides-span, :icon-span, :cue-span, or :body-span.
  1922. When optional START is specified, optional END must also be.
  1923. START and END are the actual bounds of the region, if provided.
  1924. If START and END are not passed in, we return either a dotted
  1925. pair of the current span, if established, or nil if not yet set.
  1926. When the START and END are passed, we return t if the region
  1927. changed or nil if not.
  1928. Optional FORCE means force assignment of the region's text
  1929. property, even if it's already set."
  1930. (let ((span (widget-get item-widget element)))
  1931. (cond ((or (not span) force)
  1932. (when start
  1933. (widget-put item-widget element (cons start end))
  1934. (put-text-property start end 'category
  1935. (cdr (assoc element
  1936. allout-span-to-category)))
  1937. t))
  1938. ;; report:
  1939. ((not start) span)
  1940. ;; move if necessary:
  1941. ((not (and (eq (car span) start)
  1942. (eq (cdr span) end)))
  1943. (widget-put item-widget element span)
  1944. t)
  1945. ;; specified span already set:
  1946. (t nil))))
  1947. ;;;_ : Item widget retrieval (/ high-level creation):
  1948. ;;;_ > allout-get-item-widget (&optional container)
  1949. (defun allout-get-item-widget (&optional container)
  1950. "Return the widget for the item at point, or nil if no widget yet exists.
  1951. Point must be situated *before* the start of the target item's
  1952. body, so we don't get an existing containing item when we're in
  1953. the process of creating an item in the middle of another.
  1954. Optional CONTAINER is used to obtain the container item."
  1955. (if (or container (zerop (allout-depth)))
  1956. allout-container-item-widget
  1957. ;; allout-recent-* are calibrated by (allout-depth) if we got here.
  1958. (let ((got (widget-at allout-recent-prefix-beginning)))
  1959. (if (and got (listp got))
  1960. (if (marker-position (widget-get got :from))
  1961. (and
  1962. (>= (point) (widget-apply got :actual-position :from))
  1963. (<= (point) (widget-apply got :actual-position :body-start))
  1964. got)
  1965. ;; a wacky residual item - undecorate and disregard:
  1966. (allout-widgets-undecorate-item got)
  1967. nil)))))
  1968. ;;;_ > allout-get-or-create-item-widget (&optional redecorate blank-container)
  1969. (defun allout-get-or-create-item-widget (&optional redecorate blank-container)
  1970. "Return a widget for the item at point, creating the widget if necessary.
  1971. When creating a widget, we assume there has been a context change
  1972. and decorate its siblings and parent, as well.
  1973. Optional BLANK-CONTAINER is for internal use, to fabricate a
  1974. meta-container item with an empty body when the first proper
  1975. \(non-container\) item starts at the beginning of the file.
  1976. Optional REDECORATE, if non-nil, means to redecorate the widget
  1977. if it already exists."
  1978. (let ((widget (allout-get-item-widget blank-container))
  1979. (buffer-undo-list t))
  1980. (cond (widget (if redecorate
  1981. (allout-redecorate-item widget))
  1982. widget)
  1983. ((or blank-container (zerop (allout-depth)))
  1984. (or allout-container-item-widget
  1985. (setq allout-container-item-widget
  1986. (allout-decorate-item-and-context
  1987. (widget-convert 'allout-item-widget)
  1988. nil blank-container))))
  1989. ;; create a widget for a regular/non-container item:
  1990. (t (allout-decorate-item-and-context (widget-convert
  1991. 'allout-item-widget))))))
  1992. ;;;_ > allout-get-or-create-parent-widget (&optional redecorate)
  1993. (defun allout-get-or-create-parent-widget (&optional redecorate)
  1994. "Return widget for parent of item at point, decorating it if necessary.
  1995. We return the container widget if we're above the first proper item in the
  1996. file.
  1997. Optional REDECORATE, if non-nil, means to redecorate the widget if it
  1998. already exists.
  1999. Point will wind up positioned on the beginning of the parent or beginning
  2000. of the buffer."
  2001. ;; use existing widget, if there, else establish it
  2002. (if (or (bobp) (and (not (allout-ascend))
  2003. (looking-at allout-regexp)))
  2004. (allout-get-or-create-item-widget redecorate 'blank-container)
  2005. (allout-get-or-create-item-widget redecorate)))
  2006. ;;;_ : X- Item ancillaries
  2007. ;;;_ >X allout-body-modification-handler (beg end)
  2008. (defun allout-body-modification-handler (beg end)
  2009. "Do routine processing of body text before and after modification.
  2010. Operation is inhibited by `allout-inhibit-body-modification-handler'."
  2011. ;; The primary duties are:
  2012. ;;
  2013. ;; - marking of escaped prefix-like text for delayed cleanup of escapes
  2014. ;; - removal and replacement of the settings
  2015. ;; - maintenance of beginning-of-line guide lines
  2016. ;;
  2017. ;; ?? Escapes removal \(before changes\) is not done when edits span multiple
  2018. ;; items, recognizing that item structure is being preserved, including
  2019. ;; escaping of item-prefix-like text within bodies. See
  2020. ;; `allout-before-modification-handler' and
  2021. ;; `allout-inhibit-body-modification-handler'.
  2022. ;;
  2023. ;; Adds the overlay to the `allout-unresolved-body-mod-workhash' during
  2024. ;; before-change operation, and removes from that list during after-change
  2025. ;; operation.
  2026. (cond (allout-inhibit-body-modification-hook nil)))
  2027. ;;;_ >X allout-graphics-modification-handler (beg end)
  2028. (defun allout-graphics-modification-handler (beg end)
  2029. "Protect against incoherent deletion of decoration graphics.
  2030. Deletes allowed only when inhibit-read-only is t."
  2031. (cond
  2032. (undo-in-progress (when (eq (get-text-property beg 'category)
  2033. 'allout-icon-span-category)
  2034. (save-excursion
  2035. (goto-char beg)
  2036. (let* ((item-widget (allout-get-item-widget)))
  2037. (if item-widget
  2038. (allout-widgets-exposure-undo-recorder
  2039. item-widget))))))
  2040. (inhibit-read-only t)
  2041. ((not (and (boundp 'allout-mode) allout-mode)) t)
  2042. ((equal this-command 'quoted-insert) t)
  2043. ((yes-or-no-p "Unruly edit of outline structure - allow? ")
  2044. (setq allout-widgets-unset-inhibit-read-only (not inhibit-read-only)
  2045. inhibit-read-only t))
  2046. (t (error
  2047. (substitute-command-keys allout-structure-unruly-deletion-message)))))
  2048. ;;;_ > allout-item-icon-key-handler ()
  2049. (defun allout-item-icon-key-handler ()
  2050. "Catchall handling of key bindings in item icon/cue hot-spots.
  2051. Applies `allout-hotspot-key-handler' and calls the result, if any, as an
  2052. interactive command."
  2053. (interactive)
  2054. (let* ((mapped-binding (allout-hotspot-key-handler)))
  2055. (when mapped-binding
  2056. (call-interactively mapped-binding))))
  2057. ;;;_ : Status
  2058. ;;;_ . allout-item-location (item-widget)
  2059. (defun allout-item-location (item-widget)
  2060. "Location of the start of the item's text."
  2061. (overlay-start (widget-get item-widget :span-overlay)))
  2062. ;;;_ : Icon management
  2063. ;;;_ > allout-fetch-icon-image (name)
  2064. (defun allout-fetch-icon-image (name)
  2065. "Fetch allout icon for symbol NAME.
  2066. We use a caching strategy, so the caller doesn't need to do so."
  2067. (let* ((types allout-widgets-icon-types)
  2068. (use-dir (if (equal (allout-frame-property nil 'background-mode)
  2069. 'light)
  2070. allout-widgets-icons-light-subdir
  2071. allout-widgets-icons-dark-subdir))
  2072. (key (list name use-dir))
  2073. (got (assoc key allout-widgets-icons-cache)))
  2074. (if got
  2075. ;; display system shows only first of subsequent adjacent
  2076. ;; `eq'-identical repeats - use copies to avoid this problem.
  2077. (allout-widgets-copy-list (cadr got))
  2078. (while (and types (not got))
  2079. (setq got
  2080. (allout-find-image
  2081. (list (append (list :type (car types)
  2082. :file (concat use-dir
  2083. (symbol-name name)
  2084. "." (symbol-name
  2085. (car types))))
  2086. (if (featurep 'xemacs)
  2087. allout-widgets-item-image-properties-xemacs
  2088. allout-widgets-item-image-properties-emacs)
  2089. ))))
  2090. (setq types (cdr types)))
  2091. (if got
  2092. (push (list key got) allout-widgets-icons-cache))
  2093. got)))
  2094. ;;;_ : Miscellaneous
  2095. ;;;_ > allout-elapsed-time-seconds (triple)
  2096. (defun allout-elapsed-time-seconds (end start)
  2097. "Return seconds between `current-time' style time START/END triples."
  2098. (let ((elapsed (time-subtract end start)))
  2099. (float-time elapsed)))
  2100. ;;;_ > allout-frame-property (frame property)
  2101. (defalias 'allout-frame-property
  2102. (cond ((fboundp 'frame-parameter)
  2103. 'frame-parameter)
  2104. ((fboundp 'frame-property)
  2105. 'frame-property)
  2106. (t nil)))
  2107. ;;;_ > allout-find-image (specs)
  2108. (defalias 'allout-find-image
  2109. (if (fboundp 'find-image)
  2110. 'find-image
  2111. nil) ; aka, not-yet-implemented for xemacs.
  2112. )
  2113. ;;;_ > allout-widgets-copy-list (list)
  2114. (defun allout-widgets-copy-list (list)
  2115. ;; duplicated from cl.el 'copy-list' as of 2008-08-17
  2116. "Return a copy of LIST, which may be a dotted list.
  2117. The elements of LIST are not copied, just the list structure itself."
  2118. (if (consp list)
  2119. (let ((res nil))
  2120. (while (consp list) (push (pop list) res))
  2121. (prog1 (nreverse res) (setcdr res list)))
  2122. (car list)))
  2123. ;;;_ . allout-widgets-count-buttons-in-region (start end)
  2124. (defun allout-widgets-count-buttons-in-region (start end)
  2125. "Debugging/diagnostic tool - count overlays with 'button' property in region."
  2126. (interactive "r")
  2127. (setq start (or start (point-min))
  2128. end (or end (point-max)))
  2129. (if (> start end) (let ((interim start)) (setq start end end interim)))
  2130. (let ((button-overlays (delq nil
  2131. (mapcar (function (lambda (o)
  2132. (if (overlay-get o 'button)
  2133. o)))
  2134. (overlays-in start end)))))
  2135. (length button-overlays)))
  2136. ;;;_ : Run unit tests:
  2137. (defun allout-widgets-run-unit-tests ()
  2138. (message "Running allout-widget tests...")
  2139. (allout-test-range-overlaps)
  2140. (message "Running allout-widget tests... Done.")
  2141. (sit-for .5))
  2142. (when allout-widgets-run-unit-tests-on-load
  2143. (allout-widgets-run-unit-tests))
  2144. ;;;_ : provide
  2145. (provide 'allout-widgets)
  2146. ;;;_. Local emacs vars.
  2147. ;;;_ , Local variables:
  2148. ;;;_ , allout-layout: (-1 : 0)
  2149. ;;;_ , End: