widget.c 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062
  1. /* Widgets for the Midnight Commander
  2. Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
  3. 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
  4. Authors: 1994, 1995 Radek Doulik
  5. 1994, 1995 Miguel de Icaza
  6. 1995 Jakub Jelinek
  7. 1996 Andrej Borsenkow
  8. 1997 Norbert Warmuth
  9. 2009, 2010 Andrew Borodin
  10. This program 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 2 of the License, or
  13. (at your option) any later version.
  14. This program 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 this program; if not, write to the Free Software
  20. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. */
  22. /** \file widget.c
  23. * \brief Source: widgets
  24. */
  25. #include <config.h>
  26. #include <assert.h>
  27. #include <ctype.h>
  28. #include <errno.h>
  29. #include <stdlib.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <fcntl.h>
  33. #include <sys/types.h>
  34. #include "lib/global.h"
  35. #include "lib/tty/tty.h"
  36. #include "lib/tty/mouse.h"
  37. #include "lib/tty/key.h" /* XCTRL and ALT macros */
  38. #include "lib/skin.h"
  39. #include "lib/mcconfig.h" /* for history loading and saving */
  40. #include "lib/vfs/mc-vfs/vfs.h"
  41. #include "lib/fileloc.h"
  42. #include "lib/strutil.h"
  43. #include "dialog.h"
  44. #include "widget.h"
  45. #include "wtools.h"
  46. #include "cmddef.h" /* CK_ cmd name const */
  47. #include "keybind.h" /* global_keymap_t */
  48. #include "panel.h" /* current_panel */
  49. #include "main.h" /* confirm_history_cleanup */
  50. const global_keymap_t *input_map;
  51. static void
  52. widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
  53. {
  54. Dlg_head *h = w->parent;
  55. tty_setcolor (hotkey
  56. ? (focused
  57. ? DLG_HOT_FOCUSC (h)
  58. : DLG_HOT_NORMALC (h))
  59. : (focused
  60. ? DLG_FOCUSC (h)
  61. : DLG_NORMALC (h)));
  62. }
  63. struct hotkey_t
  64. parse_hotkey (const char *text)
  65. {
  66. struct hotkey_t result;
  67. const char *cp, *p;
  68. /* search for '&', that is not on the of text */
  69. cp = strchr (text, '&');
  70. if (cp != NULL && cp[1] != '\0')
  71. {
  72. result.start = g_strndup (text, cp - text);
  73. /* skip '&' */
  74. cp++;
  75. p = str_cget_next_char (cp);
  76. result.hotkey = g_strndup (cp, p - cp);
  77. cp = p;
  78. result.end = g_strdup (cp);
  79. }
  80. else
  81. {
  82. result.start = g_strdup (text);
  83. result.hotkey = NULL;
  84. result.end = NULL;
  85. }
  86. return result;
  87. }
  88. void
  89. release_hotkey (const struct hotkey_t hotkey)
  90. {
  91. g_free (hotkey.start);
  92. g_free (hotkey.hotkey);
  93. g_free (hotkey.end);
  94. }
  95. int
  96. hotkey_width (const struct hotkey_t hotkey)
  97. {
  98. int result;
  99. result = str_term_width1 (hotkey.start);
  100. result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
  101. result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
  102. return result;
  103. }
  104. static void
  105. draw_hotkey (Widget * w, const struct hotkey_t hotkey, gboolean focused)
  106. {
  107. widget_selectcolor (w, focused, FALSE);
  108. tty_print_string (hotkey.start);
  109. if (hotkey.hotkey != NULL)
  110. {
  111. widget_selectcolor (w, focused, TRUE);
  112. tty_print_string (hotkey.hotkey);
  113. widget_selectcolor (w, focused, FALSE);
  114. }
  115. if (hotkey.end != NULL)
  116. tty_print_string (hotkey.end);
  117. }
  118. /* Default callback for widgets */
  119. cb_ret_t
  120. default_proc (widget_msg_t msg, int parm)
  121. {
  122. (void) parm;
  123. switch (msg)
  124. {
  125. case WIDGET_INIT:
  126. case WIDGET_FOCUS:
  127. case WIDGET_UNFOCUS:
  128. case WIDGET_DRAW:
  129. case WIDGET_DESTROY:
  130. case WIDGET_CURSOR:
  131. case WIDGET_IDLE:
  132. return MSG_HANDLED;
  133. default:
  134. return MSG_NOT_HANDLED;
  135. }
  136. }
  137. static int button_event (Gpm_Event * event, void *);
  138. int quote = 0;
  139. static cb_ret_t
  140. button_callback (Widget * w, widget_msg_t msg, int parm)
  141. {
  142. WButton *b = (WButton *) w;
  143. int stop = 0;
  144. int off = 0;
  145. Dlg_head *h = b->widget.parent;
  146. switch (msg)
  147. {
  148. case WIDGET_HOTKEY:
  149. /*
  150. * Don't let the default button steal Enter from the current
  151. * button. This is a workaround for the flawed event model
  152. * when hotkeys are sent to all widgets before the key is
  153. * handled by the current widget.
  154. */
  155. if (parm == '\n' && h->current == &b->widget)
  156. {
  157. button_callback (w, WIDGET_KEY, ' ');
  158. return MSG_HANDLED;
  159. }
  160. if (parm == '\n' && b->flags == DEFPUSH_BUTTON)
  161. {
  162. button_callback (w, WIDGET_KEY, ' ');
  163. return MSG_HANDLED;
  164. }
  165. if (b->text.hotkey != NULL)
  166. {
  167. if (g_ascii_tolower ((gchar) b->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
  168. {
  169. button_callback (w, WIDGET_KEY, ' ');
  170. return MSG_HANDLED;
  171. }
  172. }
  173. return MSG_NOT_HANDLED;
  174. case WIDGET_KEY:
  175. if (parm != ' ' && parm != '\n')
  176. return MSG_NOT_HANDLED;
  177. if (b->callback)
  178. stop = (*b->callback) (b->action);
  179. if (!b->callback || stop)
  180. {
  181. h->ret_value = b->action;
  182. dlg_stop (h);
  183. }
  184. return MSG_HANDLED;
  185. case WIDGET_CURSOR:
  186. switch (b->flags)
  187. {
  188. case DEFPUSH_BUTTON:
  189. off = 3;
  190. break;
  191. case NORMAL_BUTTON:
  192. off = 2;
  193. break;
  194. case NARROW_BUTTON:
  195. off = 1;
  196. break;
  197. case HIDDEN_BUTTON:
  198. default:
  199. off = 0;
  200. break;
  201. }
  202. widget_move (&b->widget, 0, b->hotpos + off);
  203. return MSG_HANDLED;
  204. case WIDGET_UNFOCUS:
  205. case WIDGET_FOCUS:
  206. case WIDGET_DRAW:
  207. if (msg == WIDGET_UNFOCUS)
  208. b->selected = 0;
  209. else if (msg == WIDGET_FOCUS)
  210. b->selected = 1;
  211. widget_selectcolor (w, b->selected, FALSE);
  212. widget_move (w, 0, 0);
  213. switch (b->flags)
  214. {
  215. case DEFPUSH_BUTTON:
  216. tty_print_string ("[< ");
  217. break;
  218. case NORMAL_BUTTON:
  219. tty_print_string ("[ ");
  220. break;
  221. case NARROW_BUTTON:
  222. tty_print_string ("[");
  223. break;
  224. case HIDDEN_BUTTON:
  225. default:
  226. return MSG_HANDLED;
  227. }
  228. draw_hotkey (w, b->text, b->selected);
  229. switch (b->flags)
  230. {
  231. case DEFPUSH_BUTTON:
  232. tty_print_string (" >]");
  233. break;
  234. case NORMAL_BUTTON:
  235. tty_print_string (" ]");
  236. break;
  237. case NARROW_BUTTON:
  238. tty_print_string ("]");
  239. break;
  240. }
  241. return MSG_HANDLED;
  242. case WIDGET_DESTROY:
  243. release_hotkey (b->text);
  244. return MSG_HANDLED;
  245. default:
  246. return default_proc (msg, parm);
  247. }
  248. }
  249. static int
  250. button_event (Gpm_Event * event, void *data)
  251. {
  252. WButton *b = data;
  253. if (event->type & (GPM_DOWN | GPM_UP))
  254. {
  255. Dlg_head *h = b->widget.parent;
  256. dlg_select_widget (b);
  257. if (event->type & GPM_UP)
  258. {
  259. button_callback ((Widget *) data, WIDGET_KEY, ' ');
  260. h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
  261. return MOU_NORMAL;
  262. }
  263. }
  264. return MOU_NORMAL;
  265. }
  266. int
  267. button_get_len (const WButton * b)
  268. {
  269. int ret = hotkey_width (b->text);
  270. switch (b->flags)
  271. {
  272. case DEFPUSH_BUTTON:
  273. ret += 6;
  274. break;
  275. case NORMAL_BUTTON:
  276. ret += 4;
  277. break;
  278. case NARROW_BUTTON:
  279. ret += 2;
  280. break;
  281. case HIDDEN_BUTTON:
  282. default:
  283. return 0;
  284. }
  285. return ret;
  286. }
  287. WButton *
  288. button_new (int y, int x, int action, int flags, const char *text, bcback callback)
  289. {
  290. WButton *b = g_new (WButton, 1);
  291. b->action = action;
  292. b->flags = flags;
  293. b->text = parse_hotkey (text);
  294. init_widget (&b->widget, y, x, 1, button_get_len (b), button_callback, button_event);
  295. b->selected = 0;
  296. b->callback = callback;
  297. widget_want_hotkey (b->widget, 1);
  298. b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
  299. return b;
  300. }
  301. const char *
  302. button_get_text (const WButton * b)
  303. {
  304. if (b->text.hotkey != NULL)
  305. return g_strconcat (b->text.start, "&", b->text.hotkey, b->text.end, (char *) NULL);
  306. else
  307. return g_strdup (b->text.start);
  308. }
  309. void
  310. button_set_text (WButton * b, const char *text)
  311. {
  312. release_hotkey (b->text);
  313. b->text = parse_hotkey (text);
  314. b->widget.cols = button_get_len (b);
  315. dlg_redraw (b->widget.parent);
  316. }
  317. /* Radio button widget */
  318. static int radio_event (Gpm_Event * event, void *);
  319. static cb_ret_t
  320. radio_callback (Widget * w, widget_msg_t msg, int parm)
  321. {
  322. WRadio *r = (WRadio *) w;
  323. int i;
  324. Dlg_head *h = r->widget.parent;
  325. switch (msg)
  326. {
  327. case WIDGET_HOTKEY:
  328. {
  329. int lp = g_ascii_tolower ((gchar) parm);
  330. for (i = 0; i < r->count; i++)
  331. {
  332. if (r->texts[i].hotkey != NULL)
  333. {
  334. int c = g_ascii_tolower ((gchar) r->texts[i].hotkey[0]);
  335. if (c != lp)
  336. continue;
  337. r->pos = i;
  338. /* Take action */
  339. radio_callback (w, WIDGET_KEY, ' ');
  340. return MSG_HANDLED;
  341. }
  342. }
  343. }
  344. return MSG_NOT_HANDLED;
  345. case WIDGET_KEY:
  346. switch (parm)
  347. {
  348. case ' ':
  349. r->sel = r->pos;
  350. h->callback (h, w, DLG_ACTION, 0, NULL);
  351. radio_callback (w, WIDGET_FOCUS, ' ');
  352. return MSG_HANDLED;
  353. case KEY_UP:
  354. case KEY_LEFT:
  355. if (r->pos > 0)
  356. {
  357. r->pos--;
  358. return MSG_HANDLED;
  359. }
  360. return MSG_NOT_HANDLED;
  361. case KEY_DOWN:
  362. case KEY_RIGHT:
  363. if (r->count - 1 > r->pos)
  364. {
  365. r->pos++;
  366. return MSG_HANDLED;
  367. }
  368. }
  369. return MSG_NOT_HANDLED;
  370. case WIDGET_CURSOR:
  371. h->callback (h, w, DLG_ACTION, 0, NULL);
  372. radio_callback (w, WIDGET_FOCUS, ' ');
  373. widget_move (&r->widget, r->pos, 1);
  374. return MSG_HANDLED;
  375. case WIDGET_UNFOCUS:
  376. case WIDGET_FOCUS:
  377. case WIDGET_DRAW:
  378. for (i = 0; i < r->count; i++)
  379. {
  380. const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
  381. widget_selectcolor (w, focused, FALSE);
  382. widget_move (&r->widget, i, 0);
  383. tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
  384. draw_hotkey (w, r->texts[i], focused);
  385. }
  386. return MSG_HANDLED;
  387. case WIDGET_DESTROY:
  388. for (i = 0; i < r->count; i++)
  389. {
  390. release_hotkey (r->texts[i]);
  391. }
  392. g_free (r->texts);
  393. return MSG_HANDLED;
  394. default:
  395. return default_proc (msg, parm);
  396. }
  397. }
  398. static int
  399. radio_event (Gpm_Event * event, void *data)
  400. {
  401. WRadio *r = data;
  402. Widget *w = data;
  403. if (event->type & (GPM_DOWN | GPM_UP))
  404. {
  405. Dlg_head *h = r->widget.parent;
  406. r->pos = event->y - 1;
  407. dlg_select_widget (r);
  408. if (event->type & GPM_UP)
  409. {
  410. radio_callback (w, WIDGET_KEY, ' ');
  411. radio_callback (w, WIDGET_FOCUS, 0);
  412. h->callback (h, w, DLG_POST_KEY, ' ', NULL);
  413. return MOU_NORMAL;
  414. }
  415. }
  416. return MOU_NORMAL;
  417. }
  418. WRadio *
  419. radio_new (int y, int x, int count, const char **texts)
  420. {
  421. WRadio *result = g_new (WRadio, 1);
  422. int i, max, m;
  423. /* Compute the longest string */
  424. result->texts = g_new (struct hotkey_t, count);
  425. max = 0;
  426. for (i = 0; i < count; i++)
  427. {
  428. result->texts[i] = parse_hotkey (texts[i]);
  429. m = hotkey_width (result->texts[i]);
  430. if (m > max)
  431. max = m;
  432. }
  433. init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
  434. result->state = 1;
  435. result->pos = 0;
  436. result->sel = 0;
  437. result->count = count;
  438. widget_want_hotkey (result->widget, 1);
  439. return result;
  440. }
  441. /* Checkbutton widget */
  442. static int check_event (Gpm_Event * event, void *);
  443. static cb_ret_t
  444. check_callback (Widget * w, widget_msg_t msg, int parm)
  445. {
  446. WCheck *c = (WCheck *) w;
  447. Dlg_head *h = c->widget.parent;
  448. switch (msg)
  449. {
  450. case WIDGET_HOTKEY:
  451. if (c->text.hotkey != NULL)
  452. {
  453. if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
  454. {
  455. check_callback (w, WIDGET_KEY, ' '); /* make action */
  456. return MSG_HANDLED;
  457. }
  458. }
  459. return MSG_NOT_HANDLED;
  460. case WIDGET_KEY:
  461. if (parm != ' ')
  462. return MSG_NOT_HANDLED;
  463. c->state ^= C_BOOL;
  464. c->state ^= C_CHANGE;
  465. h->callback (h, w, DLG_ACTION, 0, NULL);
  466. check_callback (w, WIDGET_FOCUS, ' ');
  467. return MSG_HANDLED;
  468. case WIDGET_CURSOR:
  469. widget_move (&c->widget, 0, 1);
  470. return MSG_HANDLED;
  471. case WIDGET_FOCUS:
  472. case WIDGET_UNFOCUS:
  473. case WIDGET_DRAW:
  474. widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
  475. widget_move (&c->widget, 0, 0);
  476. tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
  477. draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
  478. return MSG_HANDLED;
  479. case WIDGET_DESTROY:
  480. release_hotkey (c->text);
  481. return MSG_HANDLED;
  482. default:
  483. return default_proc (msg, parm);
  484. }
  485. }
  486. static int
  487. check_event (Gpm_Event * event, void *data)
  488. {
  489. WCheck *c = data;
  490. Widget *w = data;
  491. if (event->type & (GPM_DOWN | GPM_UP))
  492. {
  493. Dlg_head *h = c->widget.parent;
  494. dlg_select_widget (c);
  495. if (event->type & GPM_UP)
  496. {
  497. check_callback (w, WIDGET_KEY, ' ');
  498. check_callback (w, WIDGET_FOCUS, 0);
  499. h->callback (h, w, DLG_POST_KEY, ' ', NULL);
  500. return MOU_NORMAL;
  501. }
  502. }
  503. return MOU_NORMAL;
  504. }
  505. WCheck *
  506. check_new (int y, int x, int state, const char *text)
  507. {
  508. WCheck *c = g_new (WCheck, 1);
  509. c->text = parse_hotkey (text);
  510. init_widget (&c->widget, y, x, 1, hotkey_width (c->text), check_callback, check_event);
  511. c->state = state ? C_BOOL : 0;
  512. widget_want_hotkey (c->widget, 1);
  513. return c;
  514. }
  515. static gboolean
  516. save_text_to_clip_file (const char *text)
  517. {
  518. int file;
  519. char *fname = NULL;
  520. fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
  521. file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
  522. S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
  523. g_free (fname);
  524. if (file == -1)
  525. return FALSE;
  526. mc_write (file, (char *) text, strlen (text));
  527. mc_close (file);
  528. return TRUE;
  529. }
  530. static gboolean
  531. load_text_from_clip_file (char **text)
  532. {
  533. char buf[BUF_LARGE];
  534. FILE *f;
  535. char *fname = NULL;
  536. gboolean first = TRUE;
  537. fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
  538. f = fopen (fname, "r");
  539. g_free (fname);
  540. if (f == NULL)
  541. return FALSE;
  542. *text = NULL;
  543. while (fgets (buf, sizeof (buf), f))
  544. {
  545. size_t len;
  546. len = strlen (buf);
  547. if (len > 0)
  548. {
  549. if (buf[len - 1] == '\n')
  550. buf[len - 1] = '\0';
  551. if (first)
  552. {
  553. first = FALSE;
  554. *text = g_strdup (buf);
  555. }
  556. else
  557. {
  558. /* remove \n on EOL */
  559. char *tmp;
  560. tmp = g_strconcat (*text, " ", buf, (char *) NULL);
  561. g_free (*text);
  562. *text = tmp;
  563. }
  564. }
  565. }
  566. fclose (f);
  567. return (*text != NULL);
  568. }
  569. static gboolean
  570. panel_save_curent_file_to_clip_file (void)
  571. {
  572. gboolean res;
  573. if (current_panel->marked == 0)
  574. res = save_text_to_clip_file (selection (current_panel)->fname);
  575. else
  576. {
  577. int i;
  578. gboolean first = TRUE;
  579. char *flist = NULL;
  580. for (i = 0; i < current_panel->count; i++)
  581. if (current_panel->dir.list[i].f.marked != 0)
  582. { /* Skip the unmarked ones */
  583. if (first)
  584. {
  585. flist = g_strdup (current_panel->dir.list[i].fname);
  586. first = FALSE;
  587. }
  588. else
  589. {
  590. /* Add empty lines after the file */
  591. char *tmp;
  592. tmp =
  593. g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
  594. g_free (flist);
  595. flist = tmp;
  596. }
  597. }
  598. if (flist != NULL)
  599. {
  600. res = save_text_to_clip_file (flist);
  601. g_free (flist);
  602. }
  603. }
  604. return res;
  605. }
  606. /* Label widget */
  607. static cb_ret_t
  608. label_callback (Widget * w, widget_msg_t msg, int parm)
  609. {
  610. WLabel *l = (WLabel *) w;
  611. Dlg_head *h = l->widget.parent;
  612. switch (msg)
  613. {
  614. case WIDGET_INIT:
  615. return MSG_HANDLED;
  616. /* We don't want to get the focus */
  617. case WIDGET_FOCUS:
  618. return MSG_NOT_HANDLED;
  619. case WIDGET_DRAW:
  620. {
  621. char *p = l->text, *q, c = 0;
  622. int y = 0;
  623. if (!l->text)
  624. return MSG_HANDLED;
  625. if (l->transparent)
  626. tty_setcolor (DEFAULT_COLOR);
  627. else
  628. tty_setcolor (DLG_NORMALC (h));
  629. for (;;)
  630. {
  631. q = strchr (p, '\n');
  632. if (q != NULL)
  633. {
  634. c = q[0];
  635. q[0] = '\0';
  636. }
  637. widget_move (&l->widget, y, 0);
  638. tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
  639. if (q == NULL)
  640. break;
  641. q[0] = c;
  642. p = q + 1;
  643. y++;
  644. }
  645. return MSG_HANDLED;
  646. }
  647. case WIDGET_DESTROY:
  648. g_free (l->text);
  649. return MSG_HANDLED;
  650. default:
  651. return default_proc (msg, parm);
  652. }
  653. }
  654. void
  655. label_set_text (WLabel * label, const char *text)
  656. {
  657. int newcols = label->widget.cols;
  658. int newlines;
  659. if (label->text && text && !strcmp (label->text, text))
  660. return; /* Flickering is not nice */
  661. g_free (label->text);
  662. if (text != NULL)
  663. {
  664. label->text = g_strdup (text);
  665. if (label->auto_adjust_cols)
  666. {
  667. str_msg_term_size (text, &newlines, &newcols);
  668. if (newcols > label->widget.cols)
  669. label->widget.cols = newcols;
  670. if (newlines > label->widget.lines)
  671. label->widget.lines = newlines;
  672. }
  673. }
  674. else
  675. label->text = NULL;
  676. if (label->widget.parent)
  677. label_callback ((Widget *) label, WIDGET_DRAW, 0);
  678. if (newcols < label->widget.cols)
  679. label->widget.cols = newcols;
  680. }
  681. WLabel *
  682. label_new (int y, int x, const char *text)
  683. {
  684. WLabel *l;
  685. int cols = 1;
  686. int lines = 1;
  687. if (text != NULL)
  688. str_msg_term_size (text, &lines, &cols);
  689. l = g_new (WLabel, 1);
  690. init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
  691. l->text = (text != NULL) ? g_strdup (text) : NULL;
  692. l->auto_adjust_cols = 1;
  693. l->transparent = 0;
  694. widget_want_cursor (l->widget, 0);
  695. return l;
  696. }
  697. static cb_ret_t
  698. hline_callback (Widget * w, widget_msg_t msg, int parm)
  699. {
  700. WHLine *l = (WHLine *) w;
  701. Dlg_head *h = l->widget.parent;
  702. switch (msg)
  703. {
  704. case WIDGET_INIT:
  705. case WIDGET_RESIZED:
  706. if (l->auto_adjust_cols)
  707. {
  708. if (((w->parent->flags & DLG_COMPACT) != 0))
  709. {
  710. w->x = w->parent->x;
  711. w->cols = w->parent->cols;
  712. }
  713. else
  714. {
  715. w->x = w->parent->x + 1;
  716. w->cols = w->parent->cols - 2;
  717. }
  718. }
  719. case WIDGET_FOCUS:
  720. /* We don't want to get the focus */
  721. return MSG_NOT_HANDLED;
  722. case WIDGET_DRAW:
  723. if (l->transparent)
  724. tty_setcolor (DEFAULT_COLOR);
  725. else
  726. tty_setcolor (DLG_NORMALC (h));
  727. tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
  728. if (l->auto_adjust_cols)
  729. {
  730. widget_move (w, 0, 0);
  731. tty_print_alt_char (ACS_LTEE, FALSE);
  732. widget_move (w, 0, w->cols - 1);
  733. tty_print_alt_char (ACS_RTEE, FALSE);
  734. }
  735. return MSG_HANDLED;
  736. default:
  737. return default_proc (msg, parm);
  738. }
  739. }
  740. WHLine *
  741. hline_new (int y, int x, int width)
  742. {
  743. WHLine *l;
  744. int cols = width;
  745. int lines = 1;
  746. l = g_new (WHLine, 1);
  747. init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
  748. l->auto_adjust_cols = (width < 0);
  749. l->transparent = FALSE;
  750. widget_want_cursor (l->widget, 0);
  751. return l;
  752. }
  753. /* Gauge widget (progress indicator) */
  754. /* Currently width is hardcoded here for text mode */
  755. #define gauge_len 47
  756. static cb_ret_t
  757. gauge_callback (Widget * w, widget_msg_t msg, int parm)
  758. {
  759. WGauge *g = (WGauge *) w;
  760. Dlg_head *h = g->widget.parent;
  761. if (msg == WIDGET_INIT)
  762. return MSG_HANDLED;
  763. /* We don't want to get the focus */
  764. if (msg == WIDGET_FOCUS)
  765. return MSG_NOT_HANDLED;
  766. if (msg == WIDGET_DRAW)
  767. {
  768. widget_move (&g->widget, 0, 0);
  769. tty_setcolor (DLG_NORMALC (h));
  770. if (!g->shown)
  771. tty_printf ("%*s", gauge_len, "");
  772. else
  773. {
  774. int percentage, columns;
  775. long total = g->max, done = g->current;
  776. if (total <= 0 || done < 0)
  777. {
  778. done = 0;
  779. total = 100;
  780. }
  781. if (done > total)
  782. done = total;
  783. while (total > 65535)
  784. {
  785. total /= 256;
  786. done /= 256;
  787. }
  788. percentage = (200 * done / total + 1) / 2;
  789. columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
  790. tty_print_char ('[');
  791. if (g->from_left_to_right)
  792. {
  793. tty_setcolor (GAUGE_COLOR);
  794. tty_printf ("%*s", (int) columns, "");
  795. tty_setcolor (DLG_NORMALC (h));
  796. tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
  797. }
  798. else
  799. {
  800. tty_setcolor (DLG_NORMALC (h));
  801. tty_printf ("%*s", gauge_len - columns - 7, "");
  802. tty_setcolor (GAUGE_COLOR);
  803. tty_printf ("%*s", columns, "");
  804. tty_setcolor (DLG_NORMALC (h));
  805. tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
  806. }
  807. }
  808. return MSG_HANDLED;
  809. }
  810. return default_proc (msg, parm);
  811. }
  812. void
  813. gauge_set_value (WGauge * g, int max, int current)
  814. {
  815. if (g->current == current && g->max == max)
  816. return; /* Do not flicker */
  817. if (max == 0)
  818. max = 1; /* I do not like division by zero :) */
  819. g->current = current;
  820. g->max = max;
  821. gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
  822. }
  823. void
  824. gauge_show (WGauge * g, int shown)
  825. {
  826. if (g->shown == shown)
  827. return;
  828. g->shown = shown;
  829. gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
  830. }
  831. WGauge *
  832. gauge_new (int y, int x, int shown, int max, int current)
  833. {
  834. WGauge *g = g_new (WGauge, 1);
  835. init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
  836. g->shown = shown;
  837. if (max == 0)
  838. max = 1; /* I do not like division by zero :) */
  839. g->max = max;
  840. g->current = current;
  841. g->from_left_to_right = TRUE;
  842. widget_want_cursor (g->widget, 0);
  843. return g;
  844. }
  845. /* Input widget */
  846. /* {{{ history button */
  847. #define LARGE_HISTORY_BUTTON 1
  848. #ifdef LARGE_HISTORY_BUTTON
  849. # define HISTORY_BUTTON_WIDTH 3
  850. #else
  851. # define HISTORY_BUTTON_WIDTH 1
  852. #endif
  853. #define should_show_history_button(in) \
  854. (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
  855. static void
  856. draw_history_button (WInput * in)
  857. {
  858. char c;
  859. c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
  860. widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
  861. #ifdef LARGE_HISTORY_BUTTON
  862. {
  863. Dlg_head *h;
  864. h = in->widget.parent;
  865. tty_setcolor (NORMAL_COLOR);
  866. tty_print_string ("[ ]");
  867. /* Too distracting: tty_setcolor (MARKED_COLOR); */
  868. widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
  869. tty_print_char (c);
  870. }
  871. #else
  872. tty_setcolor (MARKED_COLOR);
  873. tty_print_char (c);
  874. #endif
  875. }
  876. /* }}} history button */
  877. /* Input widgets now have a global kill ring */
  878. /* Pointer to killed data */
  879. static char *kill_buffer = NULL;
  880. void
  881. update_input (WInput * in, int clear_first)
  882. {
  883. int has_history = 0;
  884. int i;
  885. int buf_len = str_length (in->buffer);
  886. const char *cp;
  887. int pw;
  888. if (should_show_history_button (in))
  889. has_history = HISTORY_BUTTON_WIDTH;
  890. if (in->disable_update)
  891. return;
  892. pw = str_term_width2 (in->buffer, in->point);
  893. /* Make the point visible */
  894. if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
  895. {
  896. in->term_first_shown = pw - (in->field_width / 3);
  897. if (in->term_first_shown < 0)
  898. in->term_first_shown = 0;
  899. }
  900. /* Adjust the mark */
  901. if (in->mark > buf_len)
  902. in->mark = buf_len;
  903. if (has_history)
  904. draw_history_button (in);
  905. tty_setcolor (in->color);
  906. widget_move (&in->widget, 0, 0);
  907. if (!in->is_password)
  908. {
  909. tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
  910. in->field_width - has_history));
  911. }
  912. else
  913. {
  914. cp = in->buffer;
  915. for (i = -in->term_first_shown; i < in->field_width - has_history; i++)
  916. {
  917. if (i >= 0)
  918. {
  919. tty_print_char ((cp[0] != '\0') ? '*' : ' ');
  920. }
  921. if (cp[0] != '\0')
  922. str_cnext_char (&cp);
  923. }
  924. }
  925. if (clear_first)
  926. in->first = 0;
  927. }
  928. void
  929. winput_set_origin (WInput * in, int x, int field_width)
  930. {
  931. in->widget.x = x;
  932. in->field_width = in->widget.cols = field_width;
  933. update_input (in, 0);
  934. }
  935. /* {{{ history saving and loading */
  936. int num_history_items_recorded = 60;
  937. /*
  938. This loads and saves the history of an input line to and from the
  939. widget. It is called with the widgets history name on creation of the
  940. widget, and returns the GList list. It stores histories in the file
  941. ~/.mc/history in using the profile code.
  942. If def_text is passed as INPUT_LAST_TEXT (to the input_new()
  943. function) then input_new assigns the default text to be the last text
  944. entered, or "" if not found.
  945. */
  946. GList *
  947. history_get (const char *input_name)
  948. {
  949. size_t i;
  950. GList *hist = NULL;
  951. char *profile;
  952. mc_config_t *cfg;
  953. char **keys;
  954. size_t keys_num = 0;
  955. char *this_entry;
  956. if (num_history_items_recorded == 0) /* this is how to disable */
  957. return NULL;
  958. if ((input_name == NULL) || (*input_name == '\0'))
  959. return NULL;
  960. profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
  961. cfg = mc_config_init (profile);
  962. /* get number of keys */
  963. keys = mc_config_get_keys (cfg, input_name, &keys_num);
  964. g_strfreev (keys);
  965. for (i = 0; i < keys_num; i++)
  966. {
  967. char key[BUF_TINY];
  968. g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
  969. this_entry = mc_config_get_string (cfg, input_name, key, "");
  970. if (this_entry != NULL)
  971. hist = list_append_unique (hist, this_entry);
  972. }
  973. mc_config_deinit (cfg);
  974. g_free (profile);
  975. /* return pointer to the last entry in the list */
  976. return g_list_last (hist);
  977. }
  978. void
  979. history_put (const char *input_name, GList * h)
  980. {
  981. int i;
  982. char *profile;
  983. mc_config_t *cfg;
  984. if (num_history_items_recorded == 0) /* this is how to disable */
  985. return;
  986. if ((input_name == NULL) || (*input_name == '\0'))
  987. return;
  988. if (h == NULL)
  989. return;
  990. profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
  991. i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  992. if (i != -1)
  993. close (i);
  994. /* Make sure the history is only readable by the user */
  995. if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
  996. {
  997. g_free (profile);
  998. return;
  999. }
  1000. /* go to end of list */
  1001. h = g_list_last (h);
  1002. /* go back 60 places */
  1003. for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
  1004. h = g_list_previous (h);
  1005. cfg = mc_config_init (profile);
  1006. if (input_name != NULL)
  1007. mc_config_del_group (cfg, input_name);
  1008. /* dump history into profile */
  1009. for (i = 0; h != NULL; h = g_list_next (h))
  1010. {
  1011. char *text = (char *) h->data;
  1012. /* We shouldn't have null entries, but let's be sure */
  1013. if (text != NULL)
  1014. {
  1015. char key[BUF_TINY];
  1016. g_snprintf (key, sizeof (key), "%d", i++);
  1017. mc_config_set_string (cfg, input_name, key, text);
  1018. }
  1019. }
  1020. mc_config_save_file (cfg, NULL);
  1021. mc_config_deinit (cfg);
  1022. g_free (profile);
  1023. }
  1024. /* }}} history saving and loading */
  1025. /* {{{ history display */
  1026. static const char *
  1027. i18n_htitle (void)
  1028. {
  1029. return _(" History ");
  1030. }
  1031. typedef struct
  1032. {
  1033. Widget *widget;
  1034. size_t count;
  1035. size_t maxlen;
  1036. } dlg_hist_data;
  1037. static cb_ret_t
  1038. dlg_hist_reposition (Dlg_head * dlg_head)
  1039. {
  1040. dlg_hist_data *data;
  1041. int x = 0, y, he, wi;
  1042. /* guard checks */
  1043. if ((dlg_head == NULL) || (dlg_head->data == NULL))
  1044. return MSG_NOT_HANDLED;
  1045. data = (dlg_hist_data *) dlg_head->data;
  1046. y = data->widget->y;
  1047. he = data->count + 2;
  1048. if (he <= y || y > (LINES - 6))
  1049. {
  1050. he = min (he, y - 1);
  1051. y -= he;
  1052. }
  1053. else
  1054. {
  1055. y++;
  1056. he = min (he, LINES - y);
  1057. }
  1058. if (data->widget->x > 2)
  1059. x = data->widget->x - 2;
  1060. wi = data->maxlen + 4;
  1061. if ((wi + x) > COLS)
  1062. {
  1063. wi = min (wi, COLS);
  1064. x = COLS - wi;
  1065. }
  1066. dlg_set_position (dlg_head, y, x, y + he, x + wi);
  1067. return MSG_HANDLED;
  1068. }
  1069. static cb_ret_t
  1070. dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  1071. {
  1072. switch (msg)
  1073. {
  1074. case DLG_RESIZE:
  1075. return dlg_hist_reposition (h);
  1076. default:
  1077. return default_dlg_callback (h, sender, msg, parm, data);
  1078. }
  1079. }
  1080. char *
  1081. show_hist (GList ** history, Widget * widget)
  1082. {
  1083. GList *z, *hlist = NULL, *hi;
  1084. size_t maxlen, i, count = 0;
  1085. char *r = NULL;
  1086. Dlg_head *query_dlg;
  1087. WListbox *query_list;
  1088. dlg_hist_data hist_data;
  1089. if (*history == NULL)
  1090. return NULL;
  1091. maxlen = str_term_width1 (i18n_htitle ()) + 2;
  1092. for (z = *history; z != NULL; z = g_list_previous (z))
  1093. {
  1094. WLEntry *entry;
  1095. i = str_term_width1 ((char *) z->data);
  1096. maxlen = max (maxlen, i);
  1097. count++;
  1098. entry = g_new0 (WLEntry, 1);
  1099. /* history is being reverted here */
  1100. entry->text = g_strdup ((char *) z->data);
  1101. hlist = g_list_prepend (hlist, entry);
  1102. }
  1103. hist_data.widget = widget;
  1104. hist_data.count = count;
  1105. hist_data.maxlen = maxlen;
  1106. query_dlg =
  1107. create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
  1108. "[History-query]", i18n_htitle (), DLG_COMPACT);
  1109. query_dlg->data = &hist_data;
  1110. query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
  1111. /* this call makes list stick to all sides of dialog, effectively make
  1112. it be resized with dialog */
  1113. add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
  1114. /* to avoid diplicating of (calculating sizes in two places)
  1115. code, call dlg_hist_callback function here, to set dialog and
  1116. controls positions.
  1117. The main idea - create 4x4 dialog and add 2x2 list in
  1118. center of it, and let dialog function resize it to needed
  1119. size. */
  1120. dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
  1121. if (query_dlg->y < widget->y)
  1122. {
  1123. /* draw list entries from bottom upto top */
  1124. listbox_set_list (query_list, hlist);
  1125. listbox_select_last (query_list);
  1126. }
  1127. else
  1128. {
  1129. /* draw list entries from top downto bottom */
  1130. /* revert history direction */
  1131. hlist = g_list_reverse (hlist);
  1132. listbox_set_list (query_list, hlist);
  1133. }
  1134. if (run_dlg (query_dlg) != B_CANCEL)
  1135. {
  1136. char *q;
  1137. listbox_get_current (query_list, &q, NULL);
  1138. if (q != NULL)
  1139. r = g_strdup (q);
  1140. }
  1141. /* get modified history from dialog */
  1142. z = NULL;
  1143. for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
  1144. {
  1145. WLEntry *entry;
  1146. entry = (WLEntry *) hi->data;
  1147. /* history is being reverted here again */
  1148. z = g_list_prepend (z, entry->text);
  1149. entry->text = NULL;
  1150. }
  1151. destroy_dlg (query_dlg);
  1152. /* restore history direction */
  1153. if (query_dlg->y < widget->y)
  1154. z = g_list_reverse (z);
  1155. g_list_foreach (*history, (GFunc) g_free, NULL);
  1156. g_list_free (*history);
  1157. *history = g_list_last (z);
  1158. return r;
  1159. }
  1160. static void
  1161. do_show_hist (WInput * in)
  1162. {
  1163. char *r;
  1164. r = show_hist (&in->history, &in->widget);
  1165. if (r != NULL)
  1166. {
  1167. assign_text (in, r);
  1168. g_free (r);
  1169. }
  1170. }
  1171. /* }}} history display */
  1172. static void
  1173. input_destroy (WInput * in)
  1174. {
  1175. if (in == NULL)
  1176. {
  1177. fprintf (stderr, "Internal error: null Input *\n");
  1178. exit (1);
  1179. }
  1180. new_input (in);
  1181. if (in->history != NULL)
  1182. {
  1183. if (!in->is_password && (((Widget *) in)->parent->ret_value != B_CANCEL))
  1184. history_put (in->history_name, in->history);
  1185. in->history = g_list_first (in->history);
  1186. g_list_foreach (in->history, (GFunc) g_free, NULL);
  1187. g_list_free (in->history);
  1188. }
  1189. g_free (in->buffer);
  1190. free_completions (in);
  1191. g_free (in->history_name);
  1192. g_free (kill_buffer);
  1193. kill_buffer = NULL;
  1194. }
  1195. void
  1196. input_disable_update (WInput * in)
  1197. {
  1198. in->disable_update++;
  1199. }
  1200. void
  1201. input_enable_update (WInput * in)
  1202. {
  1203. in->disable_update--;
  1204. update_input (in, 0);
  1205. }
  1206. static void
  1207. push_history (WInput * in, const char *text)
  1208. {
  1209. /* input widget where urls with passwords are entered without any
  1210. vfs prefix */
  1211. const char *password_input_fields[] = {
  1212. N_(" Link to a remote machine "),
  1213. N_(" FTP to machine "),
  1214. N_(" SMB link to machine ")
  1215. };
  1216. const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
  1217. char *t;
  1218. size_t i;
  1219. gboolean empty;
  1220. if (text == NULL)
  1221. return;
  1222. #ifdef ENABLE_NLS
  1223. for (i = 0; i < ELEMENTS; i++)
  1224. password_input_fields[i] = _(password_input_fields[i]);
  1225. #endif
  1226. t = g_strstrip (g_strdup (text));
  1227. empty = *t == '\0';
  1228. g_free (t);
  1229. t = g_strdup (empty ? "" : text);
  1230. if (in->history_name != NULL)
  1231. {
  1232. const char *p = in->history_name + 3;
  1233. for (i = 0; i < ELEMENTS; i++)
  1234. if (strcmp (p, password_input_fields[i]) == 0)
  1235. break;
  1236. strip_password (t, i >= ELEMENTS);
  1237. }
  1238. in->history = list_append_unique (in->history, t);
  1239. in->need_push = 0;
  1240. }
  1241. /* Cleans the input line and adds the current text to the history */
  1242. void
  1243. new_input (WInput * in)
  1244. {
  1245. push_history (in, in->buffer);
  1246. in->need_push = 1;
  1247. in->buffer[0] = '\0';
  1248. in->point = 0;
  1249. in->charpoint = 0;
  1250. in->mark = 0;
  1251. free_completions (in);
  1252. update_input (in, 0);
  1253. }
  1254. static void
  1255. move_buffer_backward (WInput * in, int start, int end)
  1256. {
  1257. int i, pos, len;
  1258. int str_len = str_length (in->buffer);
  1259. if (start >= str_len || end > str_len + 1)
  1260. return;
  1261. pos = str_offset_to_pos (in->buffer, start);
  1262. len = str_offset_to_pos (in->buffer, end) - pos;
  1263. for (i = pos; in->buffer[i + len - 1]; i++)
  1264. in->buffer[i] = in->buffer[i + len];
  1265. }
  1266. static cb_ret_t
  1267. insert_char (WInput * in, int c_code)
  1268. {
  1269. size_t i;
  1270. int res;
  1271. if (c_code == -1)
  1272. return MSG_NOT_HANDLED;
  1273. if (in->charpoint >= MB_LEN_MAX)
  1274. return MSG_HANDLED;
  1275. in->charbuf[in->charpoint] = c_code;
  1276. in->charpoint++;
  1277. res = str_is_valid_char (in->charbuf, in->charpoint);
  1278. if (res < 0)
  1279. {
  1280. if (res != -2)
  1281. in->charpoint = 0; /* broken multibyte char, skip */
  1282. return MSG_HANDLED;
  1283. }
  1284. in->need_push = 1;
  1285. if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
  1286. {
  1287. /* Expand the buffer */
  1288. size_t new_length = in->current_max_size + in->field_width + in->charpoint;
  1289. char *narea = g_try_renew (char, in->buffer, new_length);
  1290. if (narea)
  1291. {
  1292. in->buffer = narea;
  1293. in->current_max_size = new_length;
  1294. }
  1295. }
  1296. if (strlen (in->buffer) + in->charpoint < in->current_max_size)
  1297. {
  1298. /* bytes from begin */
  1299. size_t ins_point = str_offset_to_pos (in->buffer, in->point);
  1300. /* move chars */
  1301. size_t rest_bytes = strlen (in->buffer + ins_point);
  1302. for (i = rest_bytes + 1; i > 0; i--)
  1303. in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
  1304. memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
  1305. in->point++;
  1306. }
  1307. in->charpoint = 0;
  1308. return MSG_HANDLED;
  1309. }
  1310. static void
  1311. beginning_of_line (WInput * in)
  1312. {
  1313. in->point = 0;
  1314. in->charpoint = 0;
  1315. }
  1316. static void
  1317. end_of_line (WInput * in)
  1318. {
  1319. in->point = str_length (in->buffer);
  1320. in->charpoint = 0;
  1321. }
  1322. static void
  1323. backward_char (WInput * in)
  1324. {
  1325. const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
  1326. if (in->point > 0)
  1327. {
  1328. in->point -= str_cprev_noncomb_char (&act, in->buffer);
  1329. }
  1330. in->charpoint = 0;
  1331. }
  1332. static void
  1333. forward_char (WInput * in)
  1334. {
  1335. const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
  1336. if (act[0] != '\0')
  1337. {
  1338. in->point += str_cnext_noncomb_char (&act);
  1339. }
  1340. in->charpoint = 0;
  1341. }
  1342. static void
  1343. forward_word (WInput * in)
  1344. {
  1345. const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
  1346. while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
  1347. {
  1348. str_cnext_char (&p);
  1349. in->point++;
  1350. }
  1351. while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
  1352. {
  1353. str_cnext_char (&p);
  1354. in->point++;
  1355. }
  1356. }
  1357. static void
  1358. backward_word (WInput * in)
  1359. {
  1360. const char *p;
  1361. const char *p_tmp;
  1362. for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
  1363. (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
  1364. while (p != in->buffer)
  1365. {
  1366. p_tmp = p;
  1367. str_cprev_char (&p);
  1368. if (!str_isspace (p) && !str_ispunct (p))
  1369. {
  1370. p = p_tmp;
  1371. break;
  1372. }
  1373. in->point--;
  1374. }
  1375. while (p != in->buffer)
  1376. {
  1377. str_cprev_char (&p);
  1378. if (str_isspace (p) || str_ispunct (p))
  1379. break;
  1380. in->point--;
  1381. }
  1382. }
  1383. static void
  1384. key_left (WInput * in)
  1385. {
  1386. backward_char (in);
  1387. }
  1388. static void
  1389. key_ctrl_left (WInput * in)
  1390. {
  1391. backward_word (in);
  1392. }
  1393. static void
  1394. key_right (WInput * in)
  1395. {
  1396. forward_char (in);
  1397. }
  1398. static void
  1399. key_ctrl_right (WInput * in)
  1400. {
  1401. forward_word (in);
  1402. }
  1403. static void
  1404. backward_delete (WInput * in)
  1405. {
  1406. const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
  1407. int start;
  1408. if (in->point == 0)
  1409. return;
  1410. start = in->point - str_cprev_noncomb_char (&act, in->buffer);
  1411. move_buffer_backward (in, start, in->point);
  1412. in->charpoint = 0;
  1413. in->need_push = 1;
  1414. in->point = start;
  1415. }
  1416. static void
  1417. delete_char (WInput * in)
  1418. {
  1419. const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
  1420. int end = in->point;
  1421. end += str_cnext_noncomb_char (&act);
  1422. move_buffer_backward (in, in->point, end);
  1423. in->charpoint = 0;
  1424. in->need_push = 1;
  1425. }
  1426. static void
  1427. copy_region (WInput * in, int x_first, int x_last)
  1428. {
  1429. int first = min (x_first, x_last);
  1430. int last = max (x_first, x_last);
  1431. if (last == first)
  1432. {
  1433. /* Copy selected files to clipboard */
  1434. panel_save_curent_file_to_clip_file ();
  1435. return;
  1436. }
  1437. g_free (kill_buffer);
  1438. first = str_offset_to_pos (in->buffer, first);
  1439. last = str_offset_to_pos (in->buffer, last);
  1440. kill_buffer = g_strndup (in->buffer + first, last - first);
  1441. save_text_to_clip_file (kill_buffer);
  1442. }
  1443. static void
  1444. delete_region (WInput * in, int x_first, int x_last)
  1445. {
  1446. int first = min (x_first, x_last);
  1447. int last = max (x_first, x_last);
  1448. size_t len;
  1449. in->point = first;
  1450. if (in->mark > first)
  1451. in->mark = first;
  1452. last = str_offset_to_pos (in->buffer, last);
  1453. first = str_offset_to_pos (in->buffer, first);
  1454. len = strlen (&in->buffer[last]) + 1;
  1455. memmove (&in->buffer[first], &in->buffer[last], len);
  1456. in->charpoint = 0;
  1457. in->need_push = 1;
  1458. }
  1459. static void
  1460. kill_word (WInput * in)
  1461. {
  1462. int old_point = in->point;
  1463. int new_point;
  1464. forward_word (in);
  1465. new_point = in->point;
  1466. in->point = old_point;
  1467. copy_region (in, old_point, new_point);
  1468. delete_region (in, old_point, new_point);
  1469. in->need_push = 1;
  1470. in->charpoint = 0;
  1471. in->charpoint = 0;
  1472. }
  1473. static void
  1474. back_kill_word (WInput * in)
  1475. {
  1476. int old_point = in->point;
  1477. int new_point;
  1478. backward_word (in);
  1479. new_point = in->point;
  1480. in->point = old_point;
  1481. copy_region (in, old_point, new_point);
  1482. delete_region (in, old_point, new_point);
  1483. in->need_push = 1;
  1484. }
  1485. static void
  1486. set_mark (WInput * in)
  1487. {
  1488. in->mark = in->point;
  1489. }
  1490. static void
  1491. kill_save (WInput * in)
  1492. {
  1493. copy_region (in, in->mark, in->point);
  1494. }
  1495. static void
  1496. kill_region (WInput * in)
  1497. {
  1498. kill_save (in);
  1499. delete_region (in, in->point, in->mark);
  1500. }
  1501. static void
  1502. clear_region (WInput * in)
  1503. {
  1504. delete_region (in, in->point, in->mark);
  1505. }
  1506. static void
  1507. yank (WInput * in)
  1508. {
  1509. if (kill_buffer != NULL)
  1510. {
  1511. char *p;
  1512. in->charpoint = 0;
  1513. for (p = kill_buffer; *p != '\0'; p++)
  1514. insert_char (in, *p);
  1515. in->charpoint = 0;
  1516. }
  1517. }
  1518. static void
  1519. kill_line (WInput * in)
  1520. {
  1521. int chp = str_offset_to_pos (in->buffer, in->point);
  1522. g_free (kill_buffer);
  1523. kill_buffer = g_strdup (&in->buffer[chp]);
  1524. in->buffer[chp] = '\0';
  1525. in->charpoint = 0;
  1526. }
  1527. static void
  1528. ins_from_clip (WInput * in)
  1529. {
  1530. char *p = NULL;
  1531. if (load_text_from_clip_file (&p))
  1532. {
  1533. char *pp;
  1534. for (pp = p; *pp != '\0'; pp++)
  1535. insert_char (in, *pp);
  1536. g_free (p);
  1537. }
  1538. }
  1539. void
  1540. assign_text (WInput * in, const char *text)
  1541. {
  1542. free_completions (in);
  1543. g_free (in->buffer);
  1544. in->buffer = g_strdup (text); /* was in->buffer->text */
  1545. in->current_max_size = strlen (in->buffer) + 1;
  1546. in->point = str_length (in->buffer);
  1547. in->mark = 0;
  1548. in->need_push = 1;
  1549. in->charpoint = 0;
  1550. }
  1551. static void
  1552. hist_prev (WInput * in)
  1553. {
  1554. GList *prev;
  1555. if (!in->history)
  1556. return;
  1557. if (in->need_push)
  1558. push_history (in, in->buffer);
  1559. prev = g_list_previous (in->history);
  1560. if (prev != NULL)
  1561. {
  1562. in->history = prev;
  1563. assign_text (in, (char *) prev->data);
  1564. in->need_push = 0;
  1565. }
  1566. }
  1567. static void
  1568. hist_next (WInput * in)
  1569. {
  1570. if (in->need_push)
  1571. {
  1572. push_history (in, in->buffer);
  1573. assign_text (in, "");
  1574. return;
  1575. }
  1576. if (!in->history)
  1577. return;
  1578. if (!in->history->next)
  1579. {
  1580. assign_text (in, "");
  1581. return;
  1582. }
  1583. in->history = g_list_next (in->history);
  1584. assign_text (in, (char *) in->history->data);
  1585. in->need_push = 0;
  1586. }
  1587. static void
  1588. port_region_marked_for_delete (WInput * in)
  1589. {
  1590. in->buffer[0] = '\0';
  1591. in->point = 0;
  1592. in->first = 0;
  1593. in->charpoint = 0;
  1594. }
  1595. static cb_ret_t
  1596. input_execute_cmd (WInput * in, unsigned long command)
  1597. {
  1598. cb_ret_t res = MSG_HANDLED;
  1599. switch (command)
  1600. {
  1601. case CK_InputBol:
  1602. beginning_of_line (in);
  1603. break;
  1604. case CK_InputEol:
  1605. end_of_line (in);
  1606. break;
  1607. case CK_InputMoveLeft:
  1608. key_left (in);
  1609. break;
  1610. case CK_InputWordLeft:
  1611. key_ctrl_left (in);
  1612. break;
  1613. case CK_InputMoveRight:
  1614. key_right (in);
  1615. break;
  1616. case CK_InputWordRight:
  1617. key_ctrl_right (in);
  1618. break;
  1619. case CK_InputBackwardChar:
  1620. backward_char (in);
  1621. break;
  1622. case CK_InputBackwardWord:
  1623. backward_word (in);
  1624. break;
  1625. case CK_InputForwardChar:
  1626. forward_char (in);
  1627. break;
  1628. case CK_InputForwardWord:
  1629. forward_word (in);
  1630. break;
  1631. case CK_InputBackwardDelete:
  1632. backward_delete (in);
  1633. break;
  1634. case CK_InputDeleteChar:
  1635. delete_char (in);
  1636. break;
  1637. case CK_InputKillWord:
  1638. kill_word (in);
  1639. break;
  1640. case CK_InputBackwardKillWord:
  1641. back_kill_word (in);
  1642. break;
  1643. case CK_InputSetMark:
  1644. set_mark (in);
  1645. break;
  1646. case CK_InputKillRegion:
  1647. kill_region (in);
  1648. break;
  1649. case CK_InputClearLine:
  1650. clear_region (in);
  1651. break;
  1652. case CK_InputKillSave:
  1653. kill_save (in);
  1654. break;
  1655. case CK_InputYank:
  1656. yank (in);
  1657. break;
  1658. case CK_InputPaste:
  1659. ins_from_clip (in);
  1660. break;
  1661. case CK_InputKillLine:
  1662. kill_line (in);
  1663. break;
  1664. case CK_InputHistoryPrev:
  1665. hist_prev (in);
  1666. break;
  1667. case CK_InputHistoryNext:
  1668. hist_next (in);
  1669. break;
  1670. case CK_InputHistoryShow:
  1671. do_show_hist (in);
  1672. break;
  1673. case CK_InputComplete:
  1674. complete (in);
  1675. break;
  1676. default:
  1677. res = MSG_NOT_HANDLED;
  1678. }
  1679. return res;
  1680. }
  1681. /* This function is a test for a special input key used in complete.c */
  1682. /* Returns 0 if it is not a special key, 1 if it is a non-complete key
  1683. and 2 if it is a complete key */
  1684. int
  1685. is_in_input_map (WInput * in, int key)
  1686. {
  1687. size_t i;
  1688. for (i = 0; input_map[i].key != 0; i++)
  1689. if (key == input_map[i].key)
  1690. {
  1691. input_execute_cmd (in, input_map[i].command);
  1692. return (input_map[i].command == CK_InputComplete) ? 2 : 1;
  1693. }
  1694. return 0;
  1695. }
  1696. cb_ret_t
  1697. handle_char (WInput * in, int key)
  1698. {
  1699. cb_ret_t v;
  1700. int i;
  1701. v = MSG_NOT_HANDLED;
  1702. if (quote)
  1703. {
  1704. free_completions (in);
  1705. v = insert_char (in, key);
  1706. update_input (in, 1);
  1707. quote = 0;
  1708. return v;
  1709. }
  1710. for (i = 0; input_map[i].key; i++)
  1711. {
  1712. if (key == input_map[i].key)
  1713. {
  1714. if (input_map[i].command != CK_InputComplete)
  1715. free_completions (in);
  1716. input_execute_cmd (in, input_map[i].command);
  1717. update_input (in, 1);
  1718. v = MSG_HANDLED;
  1719. break;
  1720. }
  1721. }
  1722. if (input_map[i].command == 0)
  1723. {
  1724. if (key > 255)
  1725. return MSG_NOT_HANDLED;
  1726. if (in->first)
  1727. port_region_marked_for_delete (in);
  1728. free_completions (in);
  1729. v = insert_char (in, key);
  1730. }
  1731. update_input (in, 1);
  1732. return v;
  1733. }
  1734. /* Inserts text in input line */
  1735. void
  1736. stuff (WInput * in, const char *text, int insert_extra_space)
  1737. {
  1738. input_disable_update (in);
  1739. while (*text != '\0')
  1740. handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
  1741. if (insert_extra_space)
  1742. handle_char (in, ' ');
  1743. input_enable_update (in);
  1744. update_input (in, 1);
  1745. }
  1746. void
  1747. input_set_point (WInput * in, int pos)
  1748. {
  1749. int max_pos = str_length (in->buffer);
  1750. if (pos > max_pos)
  1751. pos = max_pos;
  1752. if (pos != in->point)
  1753. free_completions (in);
  1754. in->point = pos;
  1755. in->charpoint = 0;
  1756. update_input (in, 1);
  1757. }
  1758. cb_ret_t
  1759. input_callback (Widget * w, widget_msg_t msg, int parm)
  1760. {
  1761. WInput *in = (WInput *) w;
  1762. cb_ret_t v;
  1763. switch (msg)
  1764. {
  1765. case WIDGET_KEY:
  1766. if (parm == XCTRL ('q'))
  1767. {
  1768. quote = 1;
  1769. v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
  1770. quote = 0;
  1771. return v;
  1772. }
  1773. /* Keys we want others to handle */
  1774. if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
  1775. || parm == KEY_F (10) || parm == '\n')
  1776. return MSG_NOT_HANDLED;
  1777. /* When pasting multiline text, insert literal Enter */
  1778. if ((parm & ~KEY_M_MASK) == '\n')
  1779. {
  1780. quote = 1;
  1781. v = handle_char (in, '\n');
  1782. quote = 0;
  1783. return v;
  1784. }
  1785. return handle_char (in, parm);
  1786. case WIDGET_COMMAND:
  1787. return input_execute_cmd (in, parm);
  1788. case WIDGET_FOCUS:
  1789. case WIDGET_UNFOCUS:
  1790. case WIDGET_DRAW:
  1791. update_input (in, 0);
  1792. return MSG_HANDLED;
  1793. case WIDGET_CURSOR:
  1794. widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
  1795. - in->term_first_shown);
  1796. return MSG_HANDLED;
  1797. case WIDGET_DESTROY:
  1798. input_destroy (in);
  1799. return MSG_HANDLED;
  1800. default:
  1801. return default_proc (msg, parm);
  1802. }
  1803. }
  1804. static int
  1805. input_event (Gpm_Event * event, void *data)
  1806. {
  1807. WInput *in = data;
  1808. if (event->type & (GPM_DOWN | GPM_DRAG))
  1809. {
  1810. dlg_select_widget (in);
  1811. if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
  1812. && should_show_history_button (in))
  1813. {
  1814. do_show_hist (in);
  1815. }
  1816. else
  1817. {
  1818. in->point = str_length (in->buffer);
  1819. if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
  1820. in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
  1821. }
  1822. update_input (in, 1);
  1823. }
  1824. return MOU_NORMAL;
  1825. }
  1826. WInput *
  1827. input_new (int y, int x, int color, int width, const char *def_text,
  1828. const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
  1829. {
  1830. WInput *in = g_new (WInput, 1);
  1831. size_t initial_buffer_len;
  1832. init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
  1833. /* history setup */
  1834. in->history_name = NULL;
  1835. in->history = NULL;
  1836. if ((histname != NULL) && (*histname != '\0'))
  1837. {
  1838. in->history_name = g_strdup (histname);
  1839. in->history = history_get (histname);
  1840. }
  1841. if (def_text == NULL)
  1842. def_text = "";
  1843. else if (def_text == INPUT_LAST_TEXT)
  1844. {
  1845. if ((in->history != NULL) && (in->history->data != NULL))
  1846. def_text = (char *) in->history->data;
  1847. else
  1848. def_text = "";
  1849. }
  1850. initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
  1851. in->widget.options |= W_IS_INPUT;
  1852. in->completions = NULL;
  1853. in->completion_flags = completion_flags;
  1854. in->current_max_size = initial_buffer_len;
  1855. in->buffer = g_new (char, initial_buffer_len);
  1856. in->color = color;
  1857. in->field_width = width;
  1858. in->first = 1;
  1859. in->term_first_shown = 0;
  1860. in->disable_update = 0;
  1861. in->mark = 0;
  1862. in->need_push = 1;
  1863. in->is_password = 0;
  1864. strcpy (in->buffer, def_text);
  1865. in->point = str_length (in->buffer);
  1866. in->charpoint = 0;
  1867. return in;
  1868. }
  1869. /* Listbox widget */
  1870. /* Should draw the scrollbar, but currently draws only
  1871. * indications that there is more information
  1872. */
  1873. static void
  1874. listbox_entry_free (void *data)
  1875. {
  1876. WLEntry *e = data;
  1877. g_free (e->text);
  1878. g_free (e);
  1879. }
  1880. static void
  1881. listbox_drawscroll (WListbox * l)
  1882. {
  1883. const int max_line = l->widget.lines - 1;
  1884. int line = 0;
  1885. int i;
  1886. /* Are we at the top? */
  1887. widget_move (&l->widget, 0, l->widget.cols);
  1888. if (l->top == 0)
  1889. tty_print_one_vline (TRUE);
  1890. else
  1891. tty_print_char ('^');
  1892. /* Are we at the bottom? */
  1893. widget_move (&l->widget, max_line, l->widget.cols);
  1894. if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
  1895. tty_print_one_vline (TRUE);
  1896. else
  1897. tty_print_char ('v');
  1898. /* Now draw the nice relative pointer */
  1899. if (l->count != 0)
  1900. line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
  1901. for (i = 1; i < max_line; i++)
  1902. {
  1903. widget_move (&l->widget, i, l->widget.cols);
  1904. if (i != line)
  1905. tty_print_one_vline (TRUE);
  1906. else
  1907. tty_print_char ('*');
  1908. }
  1909. }
  1910. static void
  1911. listbox_draw (WListbox * l, gboolean focused)
  1912. {
  1913. const Dlg_head *h = l->widget.parent;
  1914. const int normalc = DLG_NORMALC (h);
  1915. int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
  1916. GList *le;
  1917. int pos;
  1918. int i;
  1919. int sel_line = -1;
  1920. le = g_list_nth (l->list, l->top);
  1921. /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
  1922. pos = (le == NULL) ? 0 : l->top;
  1923. for (i = 0; i < l->widget.lines; i++)
  1924. {
  1925. const char *text;
  1926. /* Display the entry */
  1927. if (pos == l->pos && sel_line == -1)
  1928. {
  1929. sel_line = i;
  1930. tty_setcolor (selc);
  1931. }
  1932. else
  1933. tty_setcolor (normalc);
  1934. widget_move (&l->widget, i, 1);
  1935. if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
  1936. text = "";
  1937. else
  1938. {
  1939. WLEntry *e = (WLEntry *) le->data;
  1940. text = e->text;
  1941. le = g_list_next (le);
  1942. pos++;
  1943. }
  1944. tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
  1945. }
  1946. l->cursor_y = sel_line;
  1947. if (l->scrollbar && (l->count > l->widget.lines))
  1948. {
  1949. tty_setcolor (normalc);
  1950. listbox_drawscroll (l);
  1951. }
  1952. }
  1953. static int
  1954. listbox_check_hotkey (WListbox * l, int key)
  1955. {
  1956. int i;
  1957. GList *le;
  1958. for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
  1959. {
  1960. WLEntry *e = (WLEntry *) le->data;
  1961. if (e->hotkey == key)
  1962. return i;
  1963. }
  1964. return (-1);
  1965. }
  1966. /* Selects the last entry and scrolls the list to the bottom */
  1967. void
  1968. listbox_select_last (WListbox * l)
  1969. {
  1970. l->pos = l->count - 1;
  1971. l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
  1972. }
  1973. /* Selects the first entry and scrolls the list to the top */
  1974. void
  1975. listbox_select_first (WListbox * l)
  1976. {
  1977. l->pos = l->top = 0;
  1978. }
  1979. void
  1980. listbox_set_list (WListbox * l, GList * list)
  1981. {
  1982. listbox_remove_list (l);
  1983. if (l != NULL)
  1984. {
  1985. l->list = list;
  1986. l->top = l->pos = 0;
  1987. l->count = g_list_length (list);
  1988. }
  1989. }
  1990. void
  1991. listbox_remove_list (WListbox * l)
  1992. {
  1993. if ((l != NULL) && (l->count != 0))
  1994. {
  1995. g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
  1996. g_list_free (l->list);
  1997. l->list = NULL;
  1998. l->count = l->pos = l->top = 0;
  1999. }
  2000. }
  2001. void
  2002. listbox_remove_current (WListbox * l)
  2003. {
  2004. if ((l != NULL) && (l->count != 0))
  2005. {
  2006. GList *current;
  2007. current = g_list_nth (l->list, l->pos);
  2008. l->list = g_list_remove_link (l->list, current);
  2009. listbox_entry_free ((WLEntry *) current->data);
  2010. g_list_free_1 (current);
  2011. l->count--;
  2012. if (l->count == 0)
  2013. l->top = l->pos = 0;
  2014. else if (l->pos >= l->count)
  2015. l->pos = l->count - 1;
  2016. }
  2017. }
  2018. void
  2019. listbox_select_entry (WListbox * l, int dest)
  2020. {
  2021. GList *le;
  2022. int pos;
  2023. gboolean top_seen = FALSE;
  2024. if (dest < 0)
  2025. return;
  2026. /* Special case */
  2027. for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
  2028. {
  2029. if (pos == l->top)
  2030. top_seen = TRUE;
  2031. if (pos == dest)
  2032. {
  2033. l->pos = dest;
  2034. if (!top_seen)
  2035. l->top = l->pos;
  2036. else if (l->pos - l->top >= l->widget.lines)
  2037. l->top = l->pos - l->widget.lines + 1;
  2038. return;
  2039. }
  2040. }
  2041. /* If we are unable to find it, set decent values */
  2042. l->pos = l->top = 0;
  2043. }
  2044. /* Selects from base the pos element */
  2045. static int
  2046. listbox_select_pos (WListbox * l, int base, int pos)
  2047. {
  2048. int last = l->count - 1;
  2049. base += pos;
  2050. if (base >= last)
  2051. base = last;
  2052. return base;
  2053. }
  2054. static void
  2055. listbox_fwd (WListbox * l)
  2056. {
  2057. if (l->pos + 1 >= l->count)
  2058. listbox_select_first (l);
  2059. else
  2060. listbox_select_entry (l, l->pos + 1);
  2061. }
  2062. static void
  2063. listbox_back (WListbox * l)
  2064. {
  2065. if (l->pos <= 0)
  2066. listbox_select_last (l);
  2067. else
  2068. listbox_select_entry (l, l->pos - 1);
  2069. }
  2070. /* Return MSG_HANDLED if we want a redraw */
  2071. static cb_ret_t
  2072. listbox_key (WListbox * l, int key)
  2073. {
  2074. int i;
  2075. cb_ret_t j = MSG_NOT_HANDLED;
  2076. if (l->list == NULL)
  2077. return MSG_NOT_HANDLED;
  2078. /* focus on listbox item N by '0'..'9' keys */
  2079. if (key >= '0' && key <= '9')
  2080. {
  2081. int oldpos = l->pos;
  2082. listbox_select_entry (l, key - '0');
  2083. /* need scroll to item? */
  2084. if (abs (oldpos - l->pos) > l->widget.lines)
  2085. l->top = l->pos;
  2086. return MSG_HANDLED;
  2087. }
  2088. switch (key)
  2089. {
  2090. case KEY_HOME:
  2091. case KEY_A1:
  2092. case ALT ('<'):
  2093. listbox_select_first (l);
  2094. return MSG_HANDLED;
  2095. case KEY_END:
  2096. case KEY_C1:
  2097. case ALT ('>'):
  2098. listbox_select_last (l);
  2099. return MSG_HANDLED;
  2100. case XCTRL ('p'):
  2101. case KEY_UP:
  2102. listbox_back (l);
  2103. return MSG_HANDLED;
  2104. case XCTRL ('n'):
  2105. case KEY_DOWN:
  2106. listbox_fwd (l);
  2107. return MSG_HANDLED;
  2108. case KEY_NPAGE:
  2109. case XCTRL ('v'):
  2110. for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
  2111. {
  2112. listbox_fwd (l);
  2113. j = MSG_HANDLED;
  2114. }
  2115. break;
  2116. case KEY_PPAGE:
  2117. case ALT ('v'):
  2118. for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
  2119. {
  2120. listbox_back (l);
  2121. j = MSG_HANDLED;
  2122. }
  2123. break;
  2124. case KEY_DC:
  2125. case 'd':
  2126. if (l->deletable)
  2127. {
  2128. gboolean is_last = (l->pos + 1 >= l->count);
  2129. gboolean is_more = (l->top + l->widget.lines >= l->count);
  2130. listbox_remove_current (l);
  2131. if ((l->top > 0) && (is_last || is_more))
  2132. l->top--;
  2133. }
  2134. return MSG_HANDLED;
  2135. case (KEY_M_SHIFT | KEY_DC):
  2136. case 'D':
  2137. if (l->deletable && confirm_history_cleanup
  2138. /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
  2139. && (query_dialog (Q_ ("DialogTitle|History cleanup"),
  2140. _("Do you want clean this history?"),
  2141. D_ERROR, 2, _("&Yes"), _("&No")) == 0))
  2142. {
  2143. listbox_remove_list (l);
  2144. j = MSG_HANDLED;
  2145. }
  2146. break;
  2147. default:
  2148. break;
  2149. }
  2150. return j;
  2151. }
  2152. static inline void
  2153. listbox_destroy (WListbox * l)
  2154. {
  2155. /* don't delete list in modifable listbox */
  2156. if (!l->deletable)
  2157. listbox_remove_list (l);
  2158. }
  2159. static cb_ret_t
  2160. listbox_callback (Widget * w, widget_msg_t msg, int parm)
  2161. {
  2162. WListbox *l = (WListbox *) w;
  2163. Dlg_head *h = l->widget.parent;
  2164. cb_ret_t ret_code;
  2165. switch (msg)
  2166. {
  2167. case WIDGET_INIT:
  2168. return MSG_HANDLED;
  2169. case WIDGET_HOTKEY:
  2170. {
  2171. int pos, action;
  2172. pos = listbox_check_hotkey (l, parm);
  2173. if (pos < 0)
  2174. return MSG_NOT_HANDLED;
  2175. listbox_select_entry (l, pos);
  2176. h->callback (h, w, DLG_ACTION, l->pos, NULL);
  2177. if (l->cback != NULL)
  2178. action = l->cback (l);
  2179. else
  2180. action = LISTBOX_DONE;
  2181. if (action == LISTBOX_DONE)
  2182. {
  2183. h->ret_value = B_ENTER;
  2184. dlg_stop (h);
  2185. }
  2186. return MSG_HANDLED;
  2187. }
  2188. case WIDGET_KEY:
  2189. ret_code = listbox_key (l, parm);
  2190. if (ret_code != MSG_NOT_HANDLED)
  2191. {
  2192. listbox_draw (l, TRUE);
  2193. h->callback (h, w, DLG_ACTION, l->pos, NULL);
  2194. }
  2195. return ret_code;
  2196. case WIDGET_CURSOR:
  2197. widget_move (&l->widget, l->cursor_y, 0);
  2198. h->callback (h, w, DLG_ACTION, l->pos, NULL);
  2199. return MSG_HANDLED;
  2200. case WIDGET_FOCUS:
  2201. case WIDGET_UNFOCUS:
  2202. case WIDGET_DRAW:
  2203. listbox_draw (l, msg != WIDGET_UNFOCUS);
  2204. return MSG_HANDLED;
  2205. case WIDGET_DESTROY:
  2206. listbox_destroy (l);
  2207. return MSG_HANDLED;
  2208. case WIDGET_RESIZED:
  2209. return MSG_HANDLED;
  2210. default:
  2211. return default_proc (msg, parm);
  2212. }
  2213. }
  2214. static int
  2215. listbox_event (Gpm_Event * event, void *data)
  2216. {
  2217. WListbox *l = data;
  2218. int i;
  2219. Dlg_head *h = l->widget.parent;
  2220. /* Single click */
  2221. if (event->type & GPM_DOWN)
  2222. dlg_select_widget (l);
  2223. if (l->list == NULL)
  2224. return MOU_NORMAL;
  2225. if (event->type & (GPM_DOWN | GPM_DRAG))
  2226. {
  2227. int ret = MOU_REPEAT;
  2228. if (event->x < 0 || event->x > l->widget.cols)
  2229. return ret;
  2230. if (event->y < 1)
  2231. for (i = -event->y; i >= 0; i--)
  2232. listbox_back (l);
  2233. else if (event->y > l->widget.lines)
  2234. for (i = event->y - l->widget.lines; i > 0; i--)
  2235. listbox_fwd (l);
  2236. else if (event->buttons & GPM_B_UP)
  2237. {
  2238. listbox_back (l);
  2239. ret = MOU_NORMAL;
  2240. }
  2241. else if (event->buttons & GPM_B_DOWN)
  2242. {
  2243. listbox_fwd (l);
  2244. ret = MOU_NORMAL;
  2245. }
  2246. else
  2247. listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
  2248. /* We need to refresh ourselves since the dialog manager doesn't */
  2249. /* know about this event */
  2250. listbox_draw (l, TRUE);
  2251. return ret;
  2252. }
  2253. /* Double click */
  2254. if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
  2255. {
  2256. int action;
  2257. if (event->x < 0 || event->x >= l->widget.cols
  2258. || event->y < 1 || event->y > l->widget.lines)
  2259. return MOU_NORMAL;
  2260. dlg_select_widget (l);
  2261. listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
  2262. if (l->cback != NULL)
  2263. action = l->cback (l);
  2264. else
  2265. action = LISTBOX_DONE;
  2266. if (action == LISTBOX_DONE)
  2267. {
  2268. h->ret_value = B_ENTER;
  2269. dlg_stop (h);
  2270. return MOU_NORMAL;
  2271. }
  2272. }
  2273. return MOU_NORMAL;
  2274. }
  2275. WListbox *
  2276. listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
  2277. {
  2278. WListbox *l = g_new (WListbox, 1);
  2279. if (height <= 0)
  2280. height = 1;
  2281. init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
  2282. l->list = NULL;
  2283. l->top = l->pos = 0;
  2284. l->count = 0;
  2285. l->deletable = deletable;
  2286. l->cback = callback;
  2287. l->allow_duplicates = TRUE;
  2288. l->scrollbar = !tty_is_slow ();
  2289. widget_want_hotkey (l->widget, 1);
  2290. return l;
  2291. }
  2292. static int
  2293. listbox_entry_cmp (const void *a, const void *b)
  2294. {
  2295. const WLEntry *ea = (const WLEntry *) a;
  2296. const WLEntry *eb = (const WLEntry *) b;
  2297. return strcmp (ea->text, eb->text);
  2298. }
  2299. /* Listbox item adding function */
  2300. static inline void
  2301. listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
  2302. {
  2303. switch (pos)
  2304. {
  2305. case LISTBOX_APPEND_AT_END:
  2306. l->list = g_list_append (l->list, e);
  2307. break;
  2308. case LISTBOX_APPEND_BEFORE:
  2309. l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
  2310. if (l->pos > 0)
  2311. l->pos--;
  2312. break;
  2313. case LISTBOX_APPEND_AFTER:
  2314. l->list = g_list_insert (l->list, e, l->pos + 1);
  2315. break;
  2316. case LISTBOX_APPEND_SORTED:
  2317. l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
  2318. break;
  2319. default:
  2320. return;
  2321. }
  2322. l->count++;
  2323. }
  2324. char *
  2325. listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
  2326. {
  2327. WLEntry *entry;
  2328. if (l == NULL)
  2329. return NULL;
  2330. if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
  2331. return NULL;
  2332. entry = g_new (WLEntry, 1);
  2333. entry->text = g_strdup (text);
  2334. entry->data = data;
  2335. entry->hotkey = hotkey;
  2336. listbox_append_item (l, entry, pos);
  2337. return entry->text;
  2338. }
  2339. int
  2340. listbox_search_text (WListbox * l, const char *text)
  2341. {
  2342. if (l != NULL)
  2343. {
  2344. int i;
  2345. GList *le;
  2346. for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
  2347. {
  2348. WLEntry *e = (WLEntry *) le->data;
  2349. if (strcmp (e->text, text) == 0)
  2350. return i;
  2351. }
  2352. }
  2353. return (-1);
  2354. }
  2355. /* Returns the current string text as well as the associated extra data */
  2356. void
  2357. listbox_get_current (WListbox * l, char **string, void **extra)
  2358. {
  2359. WLEntry *e = NULL;
  2360. gboolean ok;
  2361. if (l != NULL)
  2362. e = (WLEntry *) g_list_nth_data (l->list, l->pos);
  2363. ok = (e != NULL);
  2364. if (string != NULL)
  2365. *string = ok ? e->text : NULL;
  2366. if (extra != NULL)
  2367. *extra = ok ? e->data : NULL;
  2368. }
  2369. /* ButtonBar widget */
  2370. /* returns TRUE if a function has been called, FALSE otherwise. */
  2371. static gboolean
  2372. buttonbar_call (WButtonBar * bb, int i)
  2373. {
  2374. cb_ret_t ret = MSG_NOT_HANDLED;
  2375. if (bb != NULL)
  2376. ret = bb->widget.parent->callback (bb->widget.parent,
  2377. (Widget *) bb, DLG_ACTION,
  2378. bb->labels[i].command, bb->labels[i].receiver);
  2379. return ret;
  2380. }
  2381. /* calculate width of one button, width is never lesser than 7 */
  2382. static int
  2383. buttonbat_get_button_width (void)
  2384. {
  2385. int result = COLS / BUTTONBAR_LABELS_NUM;
  2386. return (result >= 7) ? result : 7;
  2387. }
  2388. static cb_ret_t
  2389. buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
  2390. {
  2391. WButtonBar *bb = (WButtonBar *) w;
  2392. int i;
  2393. const char *text;
  2394. switch (msg)
  2395. {
  2396. case WIDGET_FOCUS:
  2397. return MSG_NOT_HANDLED;
  2398. case WIDGET_HOTKEY:
  2399. for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
  2400. if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
  2401. return MSG_HANDLED;
  2402. return MSG_NOT_HANDLED;
  2403. case WIDGET_DRAW:
  2404. if (bb->visible)
  2405. {
  2406. int offset = 0;
  2407. int count_free_positions;
  2408. widget_move (&bb->widget, 0, 0);
  2409. tty_setcolor (DEFAULT_COLOR);
  2410. bb->btn_width = buttonbat_get_button_width ();
  2411. tty_printf ("%-*s", bb->widget.cols, "");
  2412. count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
  2413. for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++)
  2414. {
  2415. widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
  2416. tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
  2417. tty_printf ("%2d", i + 1);
  2418. tty_setcolor (BUTTONBAR_BUTTON_COLOR);
  2419. text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
  2420. tty_print_string (str_fit_to_term (text,
  2421. bb->btn_width - 2 + (int) (offset <
  2422. count_free_positions),
  2423. J_LEFT_FIT));
  2424. if (count_free_positions != 0 && offset < count_free_positions)
  2425. offset++;
  2426. }
  2427. }
  2428. return MSG_HANDLED;
  2429. case WIDGET_DESTROY:
  2430. for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
  2431. g_free (bb->labels[i].text);
  2432. return MSG_HANDLED;
  2433. default:
  2434. return default_proc (msg, parm);
  2435. }
  2436. }
  2437. static int
  2438. buttonbar_event (Gpm_Event * event, void *data)
  2439. {
  2440. WButtonBar *bb = data;
  2441. int button;
  2442. if (!(event->type & GPM_UP))
  2443. return MOU_NORMAL;
  2444. if (event->y == 2)
  2445. return MOU_NORMAL;
  2446. button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
  2447. if (button < BUTTONBAR_LABELS_NUM)
  2448. buttonbar_call (bb, button);
  2449. return MOU_NORMAL;
  2450. }
  2451. WButtonBar *
  2452. buttonbar_new (gboolean visible)
  2453. {
  2454. WButtonBar *bb;
  2455. bb = g_new0 (WButtonBar, 1);
  2456. init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
  2457. bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
  2458. bb->visible = visible;
  2459. widget_want_hotkey (bb->widget, 1);
  2460. widget_want_cursor (bb->widget, 0);
  2461. bb->btn_width = buttonbat_get_button_width ();
  2462. return bb;
  2463. }
  2464. static void
  2465. set_label_text (WButtonBar * bb, int lc_index, const char *text)
  2466. {
  2467. g_free (bb->labels[lc_index - 1].text);
  2468. bb->labels[lc_index - 1].text = g_strdup (text);
  2469. }
  2470. /* Find ButtonBar widget in the dialog */
  2471. WButtonBar *
  2472. find_buttonbar (const Dlg_head * h)
  2473. {
  2474. return (WButtonBar *) find_widget_type (h, buttonbar_callback);
  2475. }
  2476. void
  2477. buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
  2478. const struct global_keymap_t *keymap, const Widget * receiver)
  2479. {
  2480. if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
  2481. {
  2482. unsigned long command = CK_Ignore_Key;
  2483. if (keymap != NULL)
  2484. command = lookup_keymap_command (keymap, KEY_F (idx));
  2485. if ((text == NULL) || (text[0] == '\0'))
  2486. set_label_text (bb, idx, "");
  2487. else
  2488. set_label_text (bb, idx, text);
  2489. bb->labels[idx - 1].command = command;
  2490. bb->labels[idx - 1].receiver = (Widget *) receiver;
  2491. }
  2492. }
  2493. void
  2494. buttonbar_set_visible (WButtonBar * bb, gboolean visible)
  2495. {
  2496. bb->visible = visible;
  2497. }
  2498. void
  2499. buttonbar_redraw (WButtonBar * bb)
  2500. {
  2501. if (bb != NULL)
  2502. send_message ((Widget *) bb, WIDGET_DRAW, 0);
  2503. }
  2504. static cb_ret_t
  2505. groupbox_callback (Widget * w, widget_msg_t msg, int parm)
  2506. {
  2507. WGroupbox *g = (WGroupbox *) w;
  2508. switch (msg)
  2509. {
  2510. case WIDGET_INIT:
  2511. return MSG_HANDLED;
  2512. case WIDGET_FOCUS:
  2513. return MSG_NOT_HANDLED;
  2514. case WIDGET_DRAW:
  2515. tty_setcolor (COLOR_NORMAL);
  2516. draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
  2517. g->widget.x - g->widget.parent->x, g->widget.lines, g->widget.cols, TRUE);
  2518. tty_setcolor (COLOR_HOT_NORMAL);
  2519. dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
  2520. g->widget.x - g->widget.parent->x + 1);
  2521. tty_print_string (g->title);
  2522. return MSG_HANDLED;
  2523. case WIDGET_DESTROY:
  2524. g_free (g->title);
  2525. return MSG_HANDLED;
  2526. default:
  2527. return default_proc (msg, parm);
  2528. }
  2529. }
  2530. WGroupbox *
  2531. groupbox_new (int y, int x, int height, int width, const char *title)
  2532. {
  2533. WGroupbox *g = g_new (WGroupbox, 1);
  2534. init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
  2535. g->widget.options &= ~W_WANT_CURSOR;
  2536. widget_want_hotkey (g->widget, 0);
  2537. /* Strip existing spaces, add one space before and after the title */
  2538. if (title)
  2539. {
  2540. char *t;
  2541. t = g_strstrip (g_strdup (title));
  2542. g->title = g_strconcat (" ", t, " ", (char *) NULL);
  2543. g_free (t);
  2544. }
  2545. return g;
  2546. }