1"=
2" cream-addon.vim
3"
4" Cream -- An easy-to-use configuration of the famous Vim text  editor
5" [ http://cream.sourceforge.net ] Copyright (C) 2001-2011 Steve Hall
6"
7" License:
8" This program is free software; you can redistribute it and/or modify
9" it under the terms of the GNU General Public License as published by
10" the Free Software Foundation; either version 2 of  the  License,  or
11" (at your option) any later version.
12" [ http://www.gnu.org/licenses/gpl.html ]
13"
14" This program is distributed in the hope that it will be useful,  but
15" WITHOUT  ANY  WARRANTY;  without  even  the  implied   warranty   of
16" MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE.  See  the  GNU
17" General Public License for more details.
18"
19" You should have received a copy of the GNU  General  Public  License
20" along with  this  program;  if  not,  write  to  the  Free  Software
21" Foundation,  Inc.,  59  Temple  Place  -  Suite  330,   Boston,   MA
22" 02111-1307, USA.
23"
24
25" README {{{1
26" Description:
27"
28" A Cream add-on is simply a Vim script written to add functionality
29" not provided in the standard Cream distribution. Through these
30" add-ons, a wide range of functionality can be made available to the
31" user through both menus and key mappings.
32"
33" o See Cream_addon_list() for how to register an add-on.
34"
35" o Automatically loaded from the cream/addons/ subdirectory.
36"
37" o Placed into a submenu in the Tools menu.
38"
39" o Each add-on must provide a single functionality in the form of
40"   a function call made through either the menu or the optional key
41"   mapping if the user opts to map it.
42"
43" o 6 variations of F12 key are available for the user to map: F12,
44"   Shift+F12, Ctl+F12, Alt+F12, Ctrl+Shift+F12 and Alt+Shift+F12.
45"   (Ctrl+Alt+F12 unused due to typical GNU/Linux window manager
46"   restrictions).
47"
48" Notes:
49"
50" o Should we check to ensure there are no duplicate functions?
51"
52" o This is a pretty simple scheme. Someone with even moderate
53"   knowledge of the Vim scripting language can theoretically do most
54"   anything with it since we automatically load the script, put it in
55"   the menu and execute the call statement. Much the same way Vim
56"   plugins are flexible and powerful, so are add-ons.
57"
58" o Functionality may depend on the available Cream environment. But
59"   we suggest you supply your own functions unless you keep us up to
60"   speed on what you're depending on.
61"
62" o Obviously, each function should act as if called from a standing
63"   normal mode. You should never use <Esc> within Vim script because
64"   it has different implications depending on whether insertmode has
65"   been set or not. *Always select your intended mode, never assume
66"   a user is in a particular one.*
67"
68" o It would be trivial to devise a massively intertwined set of
69"   libraries with multiple files to create a whole suite of
70"   functions. Talk to us if you're so inclined, that's crazy. ;)
71"
72" o All functions should be script-scoped (s:) except for the one
73"   called.
74"
75" o Developers should use keys intuitively
76"
77" o Developers can provide extensively more flexibility through dialog
78"   choices, not key mappings.
79"
80" o Some existing Cream tools should probably be here:
81"   * Capitalize?
82"   * Whitespace handling?
83"   * Cream_debug()
84"   * Backup
85"
86
87" Cream_addon_loadall() {{{1
88function! Cream_addon_loadall()
89
90	" Default:
91	call s:Cream_addon_loader($CREAM . 'addons/*.vim')
92	" User: (only if exist)
93	if exists("g:cream_user")
94		call s:Cream_addon_loader(g:cream_user . 'addons/*.vim')
95	endif
96
97endfunction
98function! s:Cream_addon_loader(path)
99" load add-ons
100
101	" get file list
102	let myaddons = glob(a:path)
103	if strlen(myaddons) > 0
104		let myaddons = myaddons . "\n"
105	else
106		" none found, quit
107		return
108	endif
109
110	" source each item (destroys array in process)
111	let i = 0
112	let max = MvNumberOfElements(myaddons, "\n")
113	while i < max
114		let idx = stridx(myaddons, "\n")
115		let myaddon = strpart(myaddons, 0, idx)
116		call Cream_source(myaddon)
117		let myaddons = strpart(myaddons, idx + 1)
118		" *** we're not establishing g:cream_addons here! Each add-on
119		" loads itself!! ***
120		let i = i + 1
121	endwhile
122
123endfunction
124
125" Cream_addon_register() {{{1
126function! Cream_addon_register(name, tag, summary, menu, icall, ...)
127" called by each add-on to register:
128"
129"   call Cream_addon_list("{name}","{tag}","{summary}","{menu}","{icall}","{vcall}")
130"
131"   o Name
132"   o Tag is a brief one-line, 40-character descripter
133"   o Summary should briefly describe usage.
134"   o Menu name as it should apear in the menu (no accelerator)
135"   o Insert mode function call or execute statement that Cream will
136"     run on menu select or key press.
137"     * Optional: Use '<Nil>' to eliminate insertmode call/map.
138"       Requires valid visual mode call then.
139"   o Visual mode (optional) functon call if the function behaves
140"     differently from Insert and Visual modes. If not provided,
141"     no visual call is made available. '<Nil>' also excepted.
142"
143"   This call loads the array into a global structure of add-ons, and
144"   also manages and retains the user-defined key mappings.
145"
146
147	" visual call specified
148	if a:0 == 1
149		let myvcall = a:1
150	else
151		let myvcall = '<Nil>'
152	endif
153
154	" validate
155	" cannot have empty values
156	if     a:name == ""
157		call confirm(
158			\ "Error: Required \"name\" missing to list add-on--not loaded.\n" .
159			\ "\n", "&Ok", 1, "Info")
160		return -1
161	elseif a:tag     == ""
162		call confirm(
163			\ "Error: Required parameter \"tag\" missing to list add-on \"" . a:name . "\"--not loaded.\n" .
164			\ "\n", "&Ok", 1, "Info")
165		return -1
166	elseif a:summary == ""
167		call confirm(
168			\ "Error: Required parameter \"summary\" missing to list add-on \"" . a:name . "\"--not loaded.\n" .
169			\ "\n", "&Ok", 1, "Info")
170		return -1
171	elseif a:menu    == ""
172		call confirm(
173			\ "Error: Required parameter \"menu\" missing to list add-on \"" . a:name . "\"--not loaded.\n" .
174			\ "\n", "&Ok", 1, "Info")
175		return -1
176	elseif a:icall   == ""
177		call confirm(
178			\ "Error: Required parameter \"icall\" missing to list add-on \"" . a:name . "\"--not loaded.\n" .
179			\ "\n", "&Ok", 1, "Info")
180		return -1
181	endif
182
183	"----------------------------------------------------------------------
184	" escape dis-allowed characters
185	" (nah, let them eat cake for now)
186
187	"" validate
188	"let myname = substitute(a:name, "[\n\t\r]", '', 'g')
189	"let mytag  = substitute(a:tag,  "[\n\t\r]", '', 'g')
190
191	"let mysummary = a:summary
192	""let mysummary = substitute(mysummary,  "[\r]", "\n", 'g')
193	""let mysummary = substitute(mysummary,  "\\[!nt]", "\\\\", 'g')
194
195	"" escape
196	"let mysummary = substitute(mysummary, '\\', '\\\\', 'g')
197
198	"" un-escape desired escapes
199	"" * Anything not recovered here is escaped forever ;)
200	"let mysummary = substitute(mysummary, '\\\\n', '\\n', 'g')
201	"let mysummary = substitute(mysummary, '\\\\r', '\\n', 'g')
202	"let mysummary = substitute(mysummary, '\\\\t', '\\t', 'g')
203	"
204	"" escape slashes (thwarts substitute!)
205	"let mysummary = substitute(mysummary, '/', '\\/', 'g')
206	"----------------------------------------------------------------------
207
208	" compose this add-on's sub-array
209	"let myitem = a:name . "\t" . a:tag . "\t" . a:summary . "\t" . a:menu . "\t" . a:icall . "\t" . myvcall . "\t"
210	if !exists("g:cream_addons{0}")
211		let g:cream_addons{0} = 1
212	else
213		let g:cream_addons{0} = g:cream_addons{0} + 1
214	endif
215	let g:cream_addons{g:cream_addons{0}}_{1} = a:name
216	let g:cream_addons{g:cream_addons{0}}_{2} = a:tag
217	let g:cream_addons{g:cream_addons{0}}_{3} = a:summary
218	let g:cream_addons{g:cream_addons{0}}_{4} = a:menu
219	let g:cream_addons{g:cream_addons{0}}_{5} = a:icall
220	let g:cream_addons{g:cream_addons{0}}_{6} = myvcall
221
222endfunction
223
224function! Cream_addon_sort()
225	"*** BROKEN: too much memory, crash
226	"let g:cream_addons = MvQSortElements(g:cream_addons, '\n', 'CmpAddonMenu', 1)
227	"***
228endfunction
229function! CmpAddonMenu(item1, item2, direction)
230" compares menu components of two addon registries
231
232	" get menu item (4th, index 3) of each
233	let mymenu1 = MvElementAt(a:item1, "\t", 3) + 0
234	let mymenu2 = MvElementAt(a:item2, "\t", 3) + 0
235
236	""*** DEBUG:
237	"let n = confirm(
238	"    \ "DEBUG:\n" .
239	"    \ "  mymenu1   = \"" . mymenu1 . "\"\n" .
240	"    \ "  mymenu2   = \"" . mymenu2 . "\"\n" .
241	"    \ "\n", "&Ok\n&Cancel", 1, "Info")
242	"if n != 1
243	"    return
244	"endif
245	""***
246
247	if     mymenu1 < mymenu2
248		return -a:direction
249	elseif mymenu1 > mymenu2
250		return a:direction
251	else
252		return 0
253	endif
254endfunction
255
256function! Cream_addon_menu()
257
258	let i = 0
259	while i < g:cream_addons{0}
260		let i = i + 1
261
262		let mymenu  = g:cream_addons{i}_{4}
263		let myicall = g:cream_addons{i}_{5}
264		let myvcall = g:cream_addons{i}_{6}
265
266		"...................................................................
267		" menu priority number according to it's name, first two chars:
268		"   decimal value of char 1 * 26^3
269		" + decimal value of char 2 * 26^2
270		" Notes:
271		" o This is base26 (26^2 * [1-26] is the largest value we can
272		"   fit in Vim's self-imposed 32000 (16-bit) priority menu
273		"   limit)
274		" o No sorting happens below submenus
275		"
276		let charcnt = 2
277		let menupriority = 1
278		" we might destroy the name in prioritizing (e.g., removing
279		" ampersands)
280		let mymenumunge = mymenu
281		let j = 1
282		let max = charcnt
283		if strlen(mymenumunge) < charcnt
284			let max = strlen(mymenumunge)
285		endif
286		" get each char
287		while j <= max
288
289			" get 26 * j
290			let power = 1
291			let h = charcnt
292			let charval = 0
293			while h > j
294				let power = power * 26
295				let h = h - 1
296			endwhile
297
298			" validate char (keep removing them till one falls in
299			" range)
300			let valid = 0
301			while valid == 0
302				let mycharval = char2nr(mymenumunge[j-1])
303
304				" A-Z
305				if     mycharval >= 65
306				\ &&   mycharval <= 90
307					break
308				" period
309				elseif mycharval == 46
310					break
311				" a-z
312				elseif mycharval >= 97
313				\ &&   mycharval <= 122
314					break
315				else
316					" remove the offending characters
317					let mymenumunge = strpart(mymenumunge, 0, j-1) . strpart(mymenumunge, j)
318				endif
319
320			endwhile
321
322			" get value
323			" Note: we're converting A-Z to 1-26
324			if     char2nr(mymenumunge[j-1]) == 32
325				" space (highest, makes broken word first)
326				let charval = 27
327			elseif char2nr(mymenumunge[j-1]) == 46
328				" period (quit)
329				break
330			else
331				" else (whatever passed through filter above)
332				let charval = (toupper(char2nr(mymenumunge[j-1]))-64)
333			endif
334
335			let value = charval * power
336			let menupriority = menupriority + value
337
338""*** DEBUG:
339"let n = confirm(
340"    \ "DEBUG:\n" .
341"    \ "  mymenu         = \"" . mymenu . "\"\n" .
342"    \ "  mymenumunge    = \"" . mymenumunge . "\"\n" .
343"    \ "  i              = \"" . i . "\"\n" .
344"    \ "  j              = \"" . j . "\"\n" .
345"    \ "  h              = \"" . h . "\"\n" .
346"    \ "  mymenumunge[j-1]        = \"" . mymenumunge[j - 1] . "\"\n" .
347"    \ "  char2nr(mymenumunge[j - 1])   = \"" . char2nr(mymenumunge[j - 1]) . "\"\n" .
348"    \ "  toupper(char2nr(mymenumunge[j -1]))    = \"" . toupper(char2nr(mymenumunge[j - 1])) . "\"\n" .
349"    \ "  (toupper(char2nr(mymenumunge[j-1]))-64) = \"" . (toupper(char2nr(mymenumunge[j-1]))-64) . "\"\n" .
350"    \ "  charval        = \"" . charval . "\"\n" .
351"    \ "  power          = \"" . power . "\"\n" .
352"    \ "  value          = \"" . value . "\"\n" .
353"    \ "  menupriority   = \"" . menupriority . "\"\n" .
354"    \ "\n", "&Ok\n&Cancel", 1, "Info")
355"if n != 1
356"    return
357"endif
358""***
359			let j = j + 1
360		endwhile
361		"...................................................................
362
363		" make sure spaces and slashes in name are escaped
364		let mymenu = escape(mymenu, ' \')
365
366		" add menus
367		" (a:icall cannot be empty, validated above)
368		" both '<Nil>', skip
369		if     myicall ==? '<Nil>' && myvcall ==? '<Nil>'
370			return
371		" icall live (vcall dead menu)
372		elseif myvcall ==? '<Nil>'
373			execute 'imenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' <C-b>:' . myicall . '<CR>'
374			execute 'vmenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . " :\<C-u>call confirm(\"Option not available with selection.\", \"&Ok\", 1, \"Info\")\<CR>"
375		" vcall live (icall dead menu) (could show inactive without selection?)
376		elseif myicall ==? '<Nil>'
377			execute 'imenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . " \<C-b>:call confirm(\"Option not available without selection.\", \"&Ok\", 1, \"Info\")\<CR>"
378			execute 'vmenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' :' . myvcall . '<CR>'
379		" both live
380		else
381			execute 'imenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' <C-b>:' . myicall . '<CR>'
382			" Note: myvcall must be complete mapping. Remember that
383			"       a <C-u> should be passed if range is not desired.
384			execute 'vmenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' :' . myvcall . '<CR>'
385		endif
386	endwhile
387
388endfunction
389
390" Cream_addon_select() {{{1
391function! Cream_addon_select()
392" * Compose elements to be posted in s:Cream_addon_map_dialog()
393
394	" condition maximum displayed
395	let max = 7
396	let addoncount = g:cream_addons{0}
397	if addoncount < max
398		" if less than max
399		let max = addoncount
400	endif
401
402	" current item begins in the middle of the height
403	let current = max / 2
404
405	" set maximum line length
406	let maxline = 70
407
408	" display dialog
409	let quit = 0
410	while quit == 0
411
412		"----------------------------------------------------------------------
413		" compose dialog
414
415		" compose selection and description areas
416		let selection = ""
417		let description = ""
418
419		" wrap at beginning
420		if     current < 0
421			let current = addoncount - 1
422		" wrap at end
423		elseif current > addoncount - 1
424			let current = 0
425		endif
426
427		"" First Item Displayed Is Half The Diplay Height Before Current
428		"let Addon = Current - ( Max / 2 )
429		"" Catch Error When Current <= 1/4 Max To List Start
430		"if Current <= ( ( Max / 2 ) / 2 )
431		"    Let Addon = Addoncount - ( Max / 2 ) + Current
432		"endif
433
434		" first item displayed is half the diplay height before current
435		let addon = current - ( max / 2 )
436		" catch error when current <= 1/4 max to list start
437		if addon < 0
438			let addon = addon + addoncount
439		endif
440
441		" compose each add-on
442		let i = 0
443		while i < max
444
445			" wrap starting item
446			if addon < 0
447				let addon = addoncount - 1
448			elseif addon > addoncount - 1
449				let addon = 0
450			endif
451
452			" get specific elements of item
453			let myaddonname    = g:cream_addons{addon+1}_{1}
454			let myaddontag     = g:cream_addons{addon+1}_{2}
455			let myaddonsummary = g:cream_addons{addon+1}_{3}
456			"let myaddonmenu    = g:cream_addons{addon+1}_{4}
457			"let myaddonicall   = g:cream_addons{addon+1}_{5}
458			"let myaddonvcall   = g:cream_addons{addon+1}_{6}
459
460			" truncate name and tag so we fit consistently
461			let myname = myaddonname
462			if strlen(myname) > 25
463				let myname = strpart(myname, 0, 22) . "..."
464			endif
465			let mytag = myaddontag
466			if strlen(myname . mytag) > maxline - 3
467				let mytag = strpart(mytag, 0, maxline - strlen(myname)) . "..."
468			endif
469			" beef each up slightly for nicer margins
470			while strlen(myname . mytag) < maxline + 10
471				let mytag = mytag . " "
472			endwhile
473
474			" truncate summary so we fit in the box consistently
475			let mysummary = myaddonsummary
476			if strlen(mysummary) > maxline * 9
477				let mysummary = strpart(mysummary, 0, maxline * 9 - 3) . "..."
478			endif
479			" insert newlines to paragraph summary
480			let remaining = mysummary
481			let mynewstr = ""
482			while strlen(remaining) > maxline
483				" truncate at a space
484				let j = 0
485				" find space
486				while strpart(remaining, maxline - j, 1) != " "
487					let j = j + 1
488					" hmm... didn't find a space, just slash at maxline
489					if j == maxline
490						let j = 0
491						break
492					endif
493				endwhile
494				" now section there and add newline (add 1 to keep the space outboard)
495				let mynewstr = mynewstr . strpart(remaining, 0, maxline - j + 1) . "\n"
496				" remaining is from there beyond
497				let remaining = strpart(remaining, maxline - j + 1)
498			endwhile
499			" get remainder
500			let mysummary = mynewstr . strpart(remaining, 0) . "\n"
501			" make sure each summary has the same number of lines for good looks ;)
502			" (this is an abuse of this function, but it works!)
503			while MvNumberOfElements(mysummary, "\n") < 4
504				let mysummary = mysummary . "\n"
505			endwhile
506
507			" add selection indicators for current
508			if addon == current
509				let selection = selection . ">>  " . myname . " -- " . mytag . "\n"
510				" title with current add-on's name
511				let description = "Description:  " . myaddonname . "\n" . mysummary
512			else
513				let selection = selection . "       " . myname . " -- " . mytag . "\n"
514			endif
515
516			let addon = addon + 1
517			let i = i + 1
518		endwhile
519
520		" remove iterations above
521		let addon = addon - max
522
523		"----------------------------------------------------------------------
524		" post dialog accounting
525		let n = s:Cream_addon_map_dialog(selection, description)
526		if     n == "prev"
527			let current = current - 1
528			let addon = addon - 1
529			continue
530		elseif n == "next"
531			let current = current + 1
532			let addon = addon + 1
533			continue
534		elseif n == "map"
535
536			" get map key
537			let mykey = s:Cream_addon_getkey()
538
539			" if none selected, drop back to select dialog again
540			if mykey == -1
541				continue
542			endif
543
544			" get mapping sub-elements
545			let icall = g:cream_addons{current+1}_{5}
546			let vcall = g:cream_addons{current+1}_{6}
547
548			" map
549			let n = s:Cream_addon_maps(mykey, icall, vcall)
550
551			" retain across sessions if valid
552			if n == 1
553				" if key matches, stick into right array position so
554				" we never have to sort
555				if     mykey == '<F12>'
556					let mykeyno = 1
557				elseif mykey == '<S-F12>'
558					let mykeyno = 2
559				elseif mykey == '<C-F12>'
560					let mykeyno = 3
561				elseif mykey == '<M-F12>'
562					let mykeyno = 4
563				elseif mykey == '<C-S-F12>'
564					let mykeyno = 5
565				elseif mykey == '<M-S-F12>'
566					let mykeyno = 6
567				elseif mykey == '<C-M-F12>'
568					let mykeyno = 7
569				elseif mykey == '<C-M-S-F12>'
570					let mykeyno = 8
571				endif
572				if exists("mykeyno")
573					let g:CREAM_ADDON_MAPS{mykeyno}_{1} = mykey
574					let g:CREAM_ADDON_MAPS{mykeyno}_{2} = icall
575					let g:CREAM_ADDON_MAPS{mykeyno}_{3} = vcall
576					unlet mykeyno
577				endif
578			endif
579
580		elseif n == "unmap"
581			" go to unmap routine
582			let quit = Cream_addon_unmap_select()
583
584		elseif n == -1
585			" quit
586			let quit = 1
587		endif
588
589	endwhile
590
591	return 1
592
593endfunction
594
595" Cream_addon_map_dialog() {{{1
596function! s:Cream_addon_map_dialog(selections, description)
597" * Prompt user to select an addon through dialog
598" * Return key
599"
600"   +------------------------------------------------------+
601"   |       Item 1 -- Encrypt.                             |
602"   |    >  Item 2 -- Color everything green.     <        |
603"   |       Item 3 -- Color everything blue.               |
604"   |   -----------------------------------------------    |
605"   |                                                      |
606"   |  Simply select whatever you wish to highlight        |
607"   |  green, and press Ok!                                |
608"   |                                                      |
609"   |                                                      |
610"   | +--------+ +--------+ +-------+ +-------+ +--------+ |
611"   | |   Up   | |  Down  | |  Map  | | UnMap | | Cancel | |
612"   | +--------+ +--------+ +-------+ +-------+ +--------+ |
613"   +------------------------------------------------------+
614
615	let sav_go = &guioptions
616	set guioptions+=v
617
618	" remember focus
619	if !exists("s:focus_map")
620		let s:focus_map = 2
621	endif
622
623	let key = ""
624	let n = confirm(
625		\ a:selections . "\n" .
626		\ "\----------------------------------------------------------------------------------------------------------------------------          \n" .
627		\ a:description . "\n" .
628		\ "\n", "&Up\n&Down\n&Map\nU&nMap...\n&Quit", s:focus_map, "Info")
629	if     n == 1
630		let myreturn = "prev"
631		let s:focus_map = 1
632	elseif n == 2
633		let myreturn = "next"
634		let s:focus_map = 2
635	elseif n == 3
636		let myreturn = "map"
637		let s:focus_map = 2
638	elseif n == 4
639		let myreturn = "unmap"
640		let s:focus_map = 2
641	else
642		let myreturn = -1
643		let s:focus_map = 2
644	endif
645
646	" restore and return
647	let &guioptions = sav_go
648	return myreturn
649
650endfunction
651
652" Cream_addon_getkey() {{{1
653function! s:Cream_addon_getkey()
654" Return requested mapping
655"
656"   +---------------------------------------------------------------------------------+
657"   |                                                                                 |
658"   |                 Select key (or combination) to map add-on to...                 |
659"   |                                                                                 |
660"   |                                                                                 |
661"   | ......... ......... ......... ......... .............. ............. .......... |
662"   | :  F12  : : +Shft : : +Ctrl : : +Alt  : : +Shft+Ctrl : : +Shft+Alt : : Cancel : |
663"   | :.......: :.......: :.......: :.......: :............: :...........: :........: |
664"   +---------------------------------------------------------------------------------+
665"
666
667	let sav_go = &guioptions
668	set guioptions+=v
669
670	let n = confirm(
671		\ "Select key (or combination) to map add-on to...\n" .
672		\ "\n", "&F12\nShift+F12\nCtrl+F12\nAlt+F12\nCtrl+Shift+F12\nAlt+Shift+F12\nCtrl+Alt+F12\nCtrl+Alt+Shift+F12\n&Cancel", 1, "Info")
673	if     n == 1
674		let myreturn = '<F12>'
675	elseif n == 2
676		let myreturn = '<S-F12>'
677	elseif n == 3
678		let myreturn = '<C-F12>'
679	elseif n == 4
680		let myreturn = '<M-F12>'
681	elseif n == 5
682		let myreturn = '<C-S-F12>'
683	elseif n == 6
684		let myreturn = '<M-S-F12>'
685	elseif n == 7
686		let myreturn = '<C-M-F12>'
687	elseif n == 8
688		let myreturn = '<C-M-S-F12>'
689	else
690		let myreturn = -1
691	endif
692
693	" restore and return
694	let &guioptions = sav_go
695	return myreturn
696
697endfunction
698
699" Cream_addon_unmap_select() {{{1
700function! Cream_addon_unmap_select()
701" * Compose elements to be posted in s:Cream_addon_unmap_dialog()
702
703	" initialize to first item (0-based)
704	let current = 0
705
706	" list of mappings to display
707	let mapdisplay = ""
708
709	" main event loop
710	let quit = 0
711	while quit == 0
712
713		" wrap at beginning
714		if     current < 0
715			let current = 7
716		" wrap at end
717		elseif current > 7
718			let current = 0
719		endif
720
721		" compose
722		let mapdisplay = "UNMAP:\n\n\n"
723		" Note: keys are 1-based!
724		let i = 0
725		while i < 8
726			"if exists("g:CREAM_ADDON_MAPS{i + 1}_{1}")
727			"    let mykey   = g:CREAM_ADDON_MAPS{i + 1}_{1}
728			"endif
729			if exists("g:CREAM_ADDON_MAPS{i + 1}_{2}")
730				let myicall = g:CREAM_ADDON_MAPS{i + 1}_{2}
731			endif
732			if exists("g:CREAM_ADDON_MAPS{i + 1}_{3}")
733				let myvcall = g:CREAM_ADDON_MAPS{i + 1}_{3}
734			endif
735			" display each key, even if unmapped
736			if     i == 0
737				let tmpkey = '<F12>:                                     '
738			elseif i == 1
739				let tmpkey = '<S-F12>:                        '
740			elseif i == 2
741				let tmpkey = '<C-F12>:                          '
742			elseif i == 3
743				let tmpkey = '<M-F12>:                           '
744			elseif i == 4
745				let tmpkey = '<C-S-F12>:             '
746			elseif i == 5
747				let tmpkey = '<M-S-F12>:              '
748			elseif i == 6
749				let tmpkey = '<C-M-F12>:                '
750			elseif i == 7
751				let tmpkey = '<C-M-S-F12>:   '
752			endif
753			" convert key mapping to human-readable form just for
754			" dialog
755			let tmpkey = substitute(tmpkey, '[<>]', '', 'g')
756			let tmpkey = substitute(tmpkey, 'C-', 'Ctrl + ', 'g')
757			let tmpkey = substitute(tmpkey, 'M-', 'Alt + ', 'g')
758			let tmpkey = substitute(tmpkey, 'S-', 'Shift + ', 'g')
759			" highlight first line
760			if i == current
761				let mapdisplay = mapdisplay . "                    >>  " . tmpkey
762			else
763				let mapdisplay = mapdisplay . "                           " . tmpkey
764			endif
765			" info lines for mapping explaining current calls
766			if myicall ==? '<Nil>'
767				let mapdisplay = mapdisplay . myvcall . "\n"
768			else
769				let mapdisplay = mapdisplay . myicall . "\n"
770			endif
771
772			let i = i + 1
773		endwhile
774		" pad bottom of text to even map dialog
775		while MvNumberOfElements(mapdisplay, "\n") < 11
776			let mapdisplay = mapdisplay . "\n"
777		endwhile
778
779		" post dialog accounting
780		let n = s:Cream_addon_unmap_dialog(mapdisplay)
781		if     n == "prev"
782			let current = current - 1
783			continue
784		elseif n == "next"
785			let current = current + 1
786			continue
787		elseif n == "unmap"
788
789			" get element's key
790			let mykey = g:CREAM_ADDON_MAPS{current + 1}_{1}
791
792			" if already empty, just go back to select dialog
793			if mykey == -1
794				continue
795			endif
796
797			" unmap both insert and visual modes
798			execute "silent! iunmap " . mykey . '<CR>'
799			execute "silent! vunmap " . mykey . '<CR>'
800
801			" drop from global
802			let g:CREAM_ADDON_MAPS{current + 1}_{1} = ""
803			let g:CREAM_ADDON_MAPS{current + 1}_{2} = ""
804			let g:CREAM_ADDON_MAPS{current + 1}_{3} = ""
805
806		elseif n == "map"
807			" go to unmap routine
808			let quit = Cream_addon_select()
809
810		elseif n == -1
811			" quit
812			let quit = 1
813		endif
814
815	endwhile
816
817	return 1
818
819endfunction
820
821" Cream_addon_unmap_dialog() {{{1
822function! s:Cream_addon_unmap_dialog(selections)
823" * Prompt user to select an addon through dialog
824" * Return key
825
826	let sav_go = &guioptions
827	set guioptions+=v
828
829	" remember focus
830	if !exists("s:focus_unmap")
831		let s:focus_unmap = 2
832	endif
833
834	let key = ""
835	let n = confirm(
836		\ a:selections .
837		\ "\n\n\n\n\----------------------------------------------------------------------------------------------------------------------------          " .
838		\ "\n", "&Up\n&Down\nU&nMap\n&Map...\n&Quit", s:focus_unmap, "Info")
839	if     n == 1
840		let myreturn = "prev"
841		let s:focus_unmap = 1
842	elseif n == 2
843		let myreturn = "next"
844		let s:focus_unmap = 2
845	elseif n == 3
846		let myreturn = "unmap"
847		let s:focus_unmap = 2
848	elseif n == 4
849		let myreturn = "map"
850		let s:focus_unmap = 2
851	else
852		let myreturn = -1
853		let s:focus_unmap = 2
854	endif
855
856	" restore and return
857	let &guioptions = sav_go
858	return myreturn
859
860endfunction
861
862" Cream_addon_maps() {{{1
863function! s:Cream_addon_maps(key, icall, vcall)
864" map keys for add-ons
865" * return 1 if successful, -1 if either are bad
866
867	" both "<Nil>"
868	if     a:icall ==? '<Nil>' && a:vcall ==? '<Nil>'
869		" do nothing
870		return 1
871	endif
872
873	" icall
874	if a:icall ==? '<Nil>'
875		" dead key
876		execute 'imap <silent> ' . a:key . ' <Nop>'
877	else
878		" validate prior to mapping
879		if s:Cream_addon_function_check(a:icall) == 1
880			execute 'imap <silent> ' . a:key . ' <C-b>:' . a:icall . '<CR>'
881		else
882			return -1
883		endif
884	endif
885	" vcall
886	if a:vcall ==? '<Nil>'
887		" dead key
888		execute 'vmap <silent> ' . a:key . ' <Nop>'
889	else
890		" validate function existance
891		if s:Cream_addon_function_check(a:vcall) == 1
892			" map
893			execute 'vmap <silent> ' . a:key . ' :' . a:vcall . '<CR>'
894		else
895			" unmap icall
896			execute 'iunmap <silent> ' . a:key . '<CR>'
897			return -1
898		endif
899	endif
900
901	" valid functions
902	return 1
903
904
905	"" if vcall passed
906	"if a:0 == 1
907	"    let vcall = a:1
908	"endif
909	"
910	"" if visual call distinguished
911	"if exists("vcall")
912	"    let vcall = a:1
913	"    " if icall live
914	"    if a:icall !=? '<Nil>'
915	"        if s:Cream_addon_function_check(a:icall) == 1
916	"            execute 'imap <silent> ' . a:key . ' <C-b>:' . a:icall . '<CR>'
917	"        else
918	"            " invalid
919	"            return -1
920	"        endif
921	"    else
922	"        " map key dead in insert mode
923	"        execute 'imap <silent> ' . a:key . ' <Nop>'
924	"    endif
925	"    if s:Cream_addon_function_check(vcall) == 1
926	"        execute 'vmap <silent> ' . a:key . ' :' . a:1 . '<CR>'
927	"    else
928	"        execute 'iunmap <silent> ' . a:key . '<CR>'
929	"        " invalid (vcall)
930	"        return -1
931	"    endif
932	"else
933	"    " map key dead in visual mode
934	"    execute 'vmap <silent> ' . a:key . ' <Nop>'
935	"    if s:Cream_addon_function_check(a:icall) == 1
936	"        execute 'imap <silent> ' . a:key . ' <C-b>:' . a:icall . '<CR>'
937	"    else
938	"        " unmap vcall
939	"        execute 'vunmap <silent> ' . a:key . '<CR>'
940	"        " invalid
941	"        return -1
942	"    endif
943	"endif
944	"
945	"" valid functions
946	"return 1
947
948endfunction
949
950" Cream_addon_function_check() {{{1
951function! s:Cream_addon_function_check(call)
952" * verifies specified add-on call belongs to an existing function
953" * returns 1 on success, -1 on error
954" * warns that a particular function is not available and that mapping
955"   will be skipped.
956
957	" empty returns success
958	if a:call == ""
959		return 1
960	endif
961
962	" strip preceding "*call  " off function ("*" includes "<C-u>", et,al.)
963	let myfunction = substitute(a:call, '.*call\s*', '', 'g')
964
965	" test exists
966	if !exists("*" . myfunction)
967		call confirm(
968			\ "Warning: Referenced function \"" . myfunction . "\" not available to map. Skipping...\n" .
969			\ "\n", "&Ok", 1, "Info")
970		return -1
971	endif
972	return 1
973
974endfunction
975
976" Cream_addon_maps_init() {{{1
977function! Cream_addon_maps_init()
978" initialize a previous add-on map at session beginning
979" * add-on maps stored in global g:CREAM_ADDON_MAPS as array
980" * called by autocmd (thus, not s: script-local scope)
981
982	"if !exists("g:CREAM_ADDON_MAPS{0}")
983	"    let g:CREAM_ADDON_MAPS{0} = 0
984	"    return
985	"endif
986
987	" iterate through array, assigning maps
988	let i = 1
989	while i <= 8
990		" test exists and not empty
991		if !exists("g:CREAM_ADDON_MAPS{i}_{1}")
992		\ || g:CREAM_ADDON_MAPS{i}_{1} == ""
993			let i = i + 1
994			continue
995		endif
996		" map (validated here, too)
997		let n = s:Cream_addon_maps(g:CREAM_ADDON_MAPS{i}_{1}, g:CREAM_ADDON_MAPS{i}_{2}, g:CREAM_ADDON_MAPS{i}_{3})
998		" if failed, remove from string
999		if n == -1
1000			unlet g:CREAM_ADDON_MAPS{i}_{1}
1001			unlet g:CREAM_ADDON_MAPS{i}_{2}
1002			unlet g:CREAM_ADDON_MAPS{i}_{3}
1003			" don't increment
1004			continue
1005		endif
1006		let i = i + 1
1007	endwhile
1008
1009endfunction
1010
1011" 1}}}
1012" vim:foldmethod=marker
1013