xlwmenu.c 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706
  1. /* Implements a lightweight menubar widget.
  2. Copyright (C) 1992 Lucid, Inc.
  3. Copyright (C) 1994-1995, 1997, 1999-2012 Free Software Foundation, Inc.
  4. This file is part of the Lucid Widget Library.
  5. The Lucid Widget Library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9. The Lucid Widget Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with GNU Emacs; see the file COPYING. If not, write to the
  15. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. Boston, MA 02110-1301, USA. */
  17. /* Created by devin@lucid.com */
  18. #ifdef HAVE_CONFIG_H
  19. #include <config.h>
  20. #endif
  21. #include <setjmp.h>
  22. #include <lisp.h>
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <sys/types.h>
  26. #if (defined __sun) && !(defined SUNOS41)
  27. #define SUNOS41
  28. #include <X11/Xos.h>
  29. #undef SUNOS41
  30. #else
  31. #include <X11/Xos.h>
  32. #endif
  33. #include <X11/IntrinsicP.h>
  34. #include <X11/ObjectP.h>
  35. #include <X11/StringDefs.h>
  36. #include <X11/cursorfont.h>
  37. #include <X11/Shell.h>
  38. #include "xlwmenuP.h"
  39. #ifdef emacs
  40. /* Defined in xfns.c. When config.h defines `static' as empty, we get
  41. redefinition errors when gray_bitmap is included more than once, so
  42. we're referring to the one include in xfns.c here. */
  43. extern int gray_bitmap_width;
  44. extern int gray_bitmap_height;
  45. extern char *gray_bitmap_bits;
  46. #include <xterm.h>
  47. #else /* not emacs */
  48. #include <X11/bitmaps/gray>
  49. #define gray_bitmap_width gray_width
  50. #define gray_bitmap_height gray_height
  51. #define gray_bitmap_bits gray_bits
  52. #endif /* not emacs */
  53. static int pointer_grabbed;
  54. static XEvent menu_post_event;
  55. static char
  56. xlwMenuTranslations [] =
  57. "<BtnDown>: start()\n\
  58. <Motion>: drag()\n\
  59. <BtnUp>: select()\n\
  60. <Key>Shift_L: nothing()\n\
  61. <Key>Shift_R: nothing()\n\
  62. <Key>Meta_L: nothing()\n\
  63. <Key>Meta_R: nothing()\n\
  64. <Key>Control_L: nothing()\n\
  65. <Key>Control_R: nothing()\n\
  66. <Key>Hyper_L: nothing()\n\
  67. <Key>Hyper_R: nothing()\n\
  68. <Key>Super_L: nothing()\n\
  69. <Key>Super_R: nothing()\n\
  70. <Key>Alt_L: nothing()\n\
  71. <Key>Alt_R: nothing()\n\
  72. <Key>Caps_Lock: nothing()\n\
  73. <Key>Shift_Lock: nothing()\n\
  74. <KeyUp>Shift_L: nothing()\n\
  75. <KeyUp>Shift_R: nothing()\n\
  76. <KeyUp>Meta_L: nothing()\n\
  77. <KeyUp>Meta_R: nothing()\n\
  78. <KeyUp>Control_L: nothing()\n\
  79. <KeyUp>Control_R: nothing()\n\
  80. <KeyUp>Hyper_L: nothing()\n\
  81. <KeyUp>Hyper_R: nothing()\n\
  82. <KeyUp>Super_L: nothing()\n\
  83. <KeyUp>Super_R: nothing()\n\
  84. <KeyUp>Alt_L: nothing()\n\
  85. <KeyUp>Alt_R: nothing()\n\
  86. <KeyUp>Caps_Lock: nothing()\n\
  87. <KeyUp>Shift_Lock:nothing()\n\
  88. <Key>Return: select()\n\
  89. <Key>Down: down()\n\
  90. <Key>Up: up()\n\
  91. <Key>Left: left()\n\
  92. <Key>Right: right()\n\
  93. <Key>: key()\n\
  94. <KeyUp>: key()\n\
  95. ";
  96. /* FIXME: Space should toggle togglable menu item but not remove the menu
  97. so you can toggle the next one without entering the menu again. */
  98. /* FIXME: Should ESC close one level of menu structure or the complete menu? */
  99. /* FIXME: F10 should enter the menu, the first one in the menu-bar. */
  100. #define offset(field) XtOffset(XlwMenuWidget, field)
  101. static XtResource
  102. xlwMenuResources[] =
  103. {
  104. #ifdef HAVE_X_I18N
  105. {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
  106. offset(menu.fontSet), XtRFontSet, NULL},
  107. #endif
  108. #ifdef HAVE_XFT
  109. #define DEFAULT_FONTNAME "Sans-10"
  110. #else
  111. #define DEFAULT_FONTNAME "XtDefaultFont"
  112. #endif
  113. {XtNfont, XtCFont, XtRString, sizeof(String),
  114. offset(menu.fontName), XtRString, DEFAULT_FONTNAME },
  115. {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  116. offset(menu.foreground), XtRString, "XtDefaultForeground"},
  117. {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
  118. offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
  119. {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
  120. offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
  121. {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
  122. offset(menu.margin), XtRImmediate, (XtPointer)1},
  123. {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
  124. offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
  125. {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
  126. offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
  127. {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
  128. offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
  129. {XmNshadowThickness, XmCShadowThickness, XtRDimension,
  130. sizeof (Dimension), offset (menu.shadow_thickness),
  131. XtRImmediate, (XtPointer)1},
  132. {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
  133. offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
  134. {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
  135. offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
  136. {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
  137. offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
  138. {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
  139. offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
  140. {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
  141. offset(menu.open), XtRCallback, (XtPointer)NULL},
  142. {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
  143. offset(menu.select), XtRCallback, (XtPointer)NULL},
  144. {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  145. offset(menu.highlight), XtRCallback, (XtPointer)NULL},
  146. {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  147. offset(menu.enter), XtRCallback, (XtPointer)NULL},
  148. {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  149. offset(menu.leave), XtRCallback, (XtPointer)NULL},
  150. {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
  151. offset(menu.contents), XtRImmediate, (XtPointer)NULL},
  152. {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
  153. offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
  154. {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
  155. offset(menu.horizontal), XtRImmediate, (XtPointer)True},
  156. };
  157. #undef offset
  158. static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
  159. ArgList args, Cardinal *num_args);
  160. static void XlwMenuRealize(Widget, Mask *, XSetWindowAttributes *);
  161. static void XlwMenuResize(Widget w);
  162. static void XlwMenuInitialize(Widget, Widget, ArgList, Cardinal *);
  163. static void XlwMenuRedisplay(Widget w, XEvent *ev, Region region);
  164. static void XlwMenuDestroy(Widget w);
  165. static void XlwMenuClassInitialize(void);
  166. static void Start(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  167. static void Drag(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  168. static void Down(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  169. static void Up(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  170. static void Left(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  171. static void Right(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  172. static void Select(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  173. static void Key(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  174. static void Nothing(Widget w, XEvent *ev, String *params, Cardinal *num_params);
  175. static int separator_height (enum menu_separator);
  176. static void pop_up_menu (XlwMenuWidget, XButtonPressedEvent *);
  177. static void abort_gracefully (Widget w) NO_RETURN;
  178. static XtActionsRec
  179. xlwMenuActionsList [] =
  180. {
  181. {"start", Start},
  182. {"drag", Drag},
  183. {"down", Down},
  184. {"up", Up},
  185. {"left", Left},
  186. {"right", Right},
  187. {"select", Select},
  188. {"key", Key},
  189. {"MenuGadgetEscape", Key}, /* Compatibility with Lesstif/Motif. */
  190. {"nothing", Nothing},
  191. };
  192. #define SuperClass ((CoreWidgetClass)&coreClassRec)
  193. XlwMenuClassRec xlwMenuClassRec =
  194. {
  195. { /* CoreClass fields initialization */
  196. (WidgetClass) SuperClass, /* superclass */
  197. "XlwMenu", /* class_name */
  198. sizeof(XlwMenuRec), /* size */
  199. XlwMenuClassInitialize, /* class_initialize */
  200. NULL, /* class_part_initialize */
  201. FALSE, /* class_inited */
  202. XlwMenuInitialize, /* initialize */
  203. NULL, /* initialize_hook */
  204. XlwMenuRealize, /* realize */
  205. xlwMenuActionsList, /* actions */
  206. XtNumber(xlwMenuActionsList), /* num_actions */
  207. xlwMenuResources, /* resources */
  208. XtNumber(xlwMenuResources), /* resource_count */
  209. NULLQUARK, /* xrm_class */
  210. TRUE, /* compress_motion */
  211. XtExposeCompressMaximal, /* compress_exposure */
  212. TRUE, /* compress_enterleave */
  213. FALSE, /* visible_interest */
  214. XlwMenuDestroy, /* destroy */
  215. XlwMenuResize, /* resize */
  216. XlwMenuRedisplay, /* expose */
  217. XlwMenuSetValues, /* set_values */
  218. NULL, /* set_values_hook */
  219. XtInheritSetValuesAlmost, /* set_values_almost */
  220. NULL, /* get_values_hook */
  221. NULL, /* accept_focus */
  222. XtVersion, /* version */
  223. NULL, /* callback_private */
  224. xlwMenuTranslations, /* tm_table */
  225. XtInheritQueryGeometry, /* query_geometry */
  226. XtInheritDisplayAccelerator, /* display_accelerator */
  227. NULL /* extension */
  228. }, /* XlwMenuClass fields initialization */
  229. {
  230. 0 /* dummy */
  231. },
  232. };
  233. WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
  234. int submenu_destroyed;
  235. /* For debug, if installation-directory is non-nil this is not an installed
  236. Emacs. In that case we do not grab the keyboard to make it easier to
  237. debug. */
  238. #define GRAB_KEYBOARD (EQ (Vinstallation_directory, Qnil))
  239. static int next_release_must_exit;
  240. /* Utilities */
  241. /* Ungrab pointer and keyboard */
  242. static void
  243. ungrab_all (Widget w, Time ungrabtime)
  244. {
  245. XtUngrabPointer (w, ungrabtime);
  246. if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
  247. }
  248. /* Like abort, but remove grabs from widget W before. */
  249. static void
  250. abort_gracefully (Widget w)
  251. {
  252. if (XtIsShell (XtParent (w)))
  253. XtRemoveGrab (w);
  254. ungrab_all (w, CurrentTime);
  255. abort ();
  256. }
  257. static void
  258. push_new_stack (XlwMenuWidget mw, widget_value *val)
  259. {
  260. if (!mw->menu.new_stack)
  261. {
  262. mw->menu.new_stack_length = 10;
  263. mw->menu.new_stack =
  264. (widget_value**)XtCalloc (mw->menu.new_stack_length,
  265. sizeof (widget_value*));
  266. }
  267. else if (mw->menu.new_depth == mw->menu.new_stack_length)
  268. {
  269. mw->menu.new_stack_length *= 2;
  270. mw->menu.new_stack =
  271. (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
  272. mw->menu.new_stack_length * sizeof (widget_value*));
  273. }
  274. mw->menu.new_stack [mw->menu.new_depth++] = val;
  275. }
  276. static void
  277. pop_new_stack_if_no_contents (XlwMenuWidget mw)
  278. {
  279. if (mw->menu.new_depth > 1)
  280. {
  281. if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
  282. mw->menu.new_depth -= 1;
  283. }
  284. }
  285. static void
  286. make_old_stack_space (XlwMenuWidget mw, int n)
  287. {
  288. if (!mw->menu.old_stack)
  289. {
  290. mw->menu.old_stack_length = 10;
  291. mw->menu.old_stack =
  292. (widget_value**)XtCalloc (mw->menu.old_stack_length,
  293. sizeof (widget_value*));
  294. }
  295. else if (mw->menu.old_stack_length < n)
  296. {
  297. mw->menu.old_stack_length *= 2;
  298. mw->menu.old_stack =
  299. (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
  300. mw->menu.old_stack_length * sizeof (widget_value*));
  301. }
  302. }
  303. /* Size code */
  304. static int
  305. string_width (XlwMenuWidget mw, char *s)
  306. {
  307. XCharStruct xcs;
  308. int drop;
  309. #ifdef HAVE_XFT
  310. if (mw->menu.xft_font)
  311. {
  312. XGlyphInfo gi;
  313. XftTextExtentsUtf8 (XtDisplay (mw), mw->menu.xft_font,
  314. (FcChar8 *) s,
  315. strlen (s), &gi);
  316. return gi.width;
  317. }
  318. #endif
  319. #ifdef HAVE_X_I18N
  320. if (mw->menu.fontSet)
  321. {
  322. XRectangle ink, logical;
  323. XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
  324. return logical.width;
  325. }
  326. #endif
  327. XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
  328. return xcs.width;
  329. }
  330. #ifdef HAVE_XFT
  331. #define MENU_FONT_HEIGHT(mw) \
  332. ((mw)->menu.xft_font != NULL \
  333. ? (mw)->menu.xft_font->height \
  334. : ((mw)->menu.fontSet != NULL \
  335. ? (mw)->menu.font_extents->max_logical_extent.height \
  336. : (mw)->menu.font->ascent + (mw)->menu.font->descent))
  337. #define MENU_FONT_ASCENT(mw) \
  338. ((mw)->menu.xft_font != NULL \
  339. ? (mw)->menu.xft_font->ascent \
  340. : ((mw)->menu.fontSet != NULL \
  341. ? - (mw)->menu.font_extents->max_logical_extent.y \
  342. : (mw)->menu.font->ascent))
  343. #else
  344. #ifdef HAVE_X_I18N
  345. #define MENU_FONT_HEIGHT(mw) \
  346. ((mw)->menu.fontSet != NULL \
  347. ? (mw)->menu.font_extents->max_logical_extent.height \
  348. : (mw)->menu.font->ascent + (mw)->menu.font->descent)
  349. #define MENU_FONT_ASCENT(mw) \
  350. ((mw)->menu.fontSet != NULL \
  351. ? - (mw)->menu.font_extents->max_logical_extent.y \
  352. : (mw)->menu.font->ascent)
  353. #else
  354. #define MENU_FONT_HEIGHT(mw) \
  355. ((mw)->menu.font->ascent + (mw)->menu.font->descent)
  356. #define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
  357. #endif
  358. #endif
  359. static int
  360. arrow_width (XlwMenuWidget mw)
  361. {
  362. return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
  363. }
  364. /* Return the width of toggle buttons of widget MW. */
  365. static int
  366. toggle_button_width (XlwMenuWidget mw)
  367. {
  368. return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
  369. }
  370. /* Return the width of radio buttons of widget MW. */
  371. static int
  372. radio_button_width (XlwMenuWidget mw)
  373. {
  374. return toggle_button_width (mw) * 1.41;
  375. }
  376. static XtResource
  377. nameResource[] =
  378. {
  379. {"labelString", "LabelString", XtRString, sizeof(String),
  380. 0, XtRImmediate, 0},
  381. };
  382. static char*
  383. resource_widget_value (XlwMenuWidget mw, widget_value *val)
  384. {
  385. if (!val->toolkit_data)
  386. {
  387. char* resourced_name = NULL;
  388. char* complete_name;
  389. XtGetSubresources ((Widget) mw,
  390. (XtPointer) &resourced_name,
  391. val->name, val->name,
  392. nameResource, 1, NULL, 0);
  393. if (!resourced_name)
  394. resourced_name = val->name;
  395. if (!val->value)
  396. {
  397. complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
  398. strcpy (complete_name, resourced_name);
  399. }
  400. else
  401. {
  402. int complete_length =
  403. strlen (resourced_name) + strlen (val->value) + 2;
  404. complete_name = XtMalloc (complete_length);
  405. *complete_name = 0;
  406. strcat (complete_name, resourced_name);
  407. strcat (complete_name, " ");
  408. strcat (complete_name, val->value);
  409. }
  410. val->toolkit_data = complete_name;
  411. val->free_toolkit_data = True;
  412. }
  413. return (char*)val->toolkit_data;
  414. }
  415. /* Returns the sizes of an item */
  416. static void
  417. size_menu_item (XlwMenuWidget mw,
  418. widget_value* val,
  419. int horizontal_p,
  420. int* label_width,
  421. int* rest_width,
  422. int* button_width,
  423. int* height)
  424. {
  425. enum menu_separator separator;
  426. if (lw_separator_p (val->name, &separator, 0))
  427. {
  428. *height = separator_height (separator);
  429. *label_width = 1;
  430. *rest_width = 0;
  431. *button_width = 0;
  432. }
  433. else
  434. {
  435. *height = MENU_FONT_HEIGHT (mw)
  436. + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
  437. *label_width =
  438. string_width (mw, resource_widget_value (mw, val))
  439. + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
  440. *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
  441. if (!horizontal_p)
  442. {
  443. if (val->contents)
  444. /* Add width of the arrow displayed for submenus. */
  445. *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
  446. else if (val->key)
  447. /* Add width of key equivalent string. */
  448. *rest_width += (string_width (mw, val->key)
  449. + mw->menu.arrow_spacing);
  450. if (val->button_type == BUTTON_TYPE_TOGGLE)
  451. *button_width = (toggle_button_width (mw)
  452. + mw->menu.horizontal_spacing);
  453. else if (val->button_type == BUTTON_TYPE_RADIO)
  454. *button_width = (radio_button_width (mw)
  455. + mw->menu.horizontal_spacing);
  456. }
  457. }
  458. }
  459. static void
  460. size_menu (XlwMenuWidget mw, int level)
  461. {
  462. int label_width = 0;
  463. int rest_width = 0;
  464. int button_width = 0;
  465. int max_rest_width = 0;
  466. int max_button_width = 0;
  467. int height = 0;
  468. int horizontal_p = mw->menu.horizontal && (level == 0);
  469. widget_value* val;
  470. window_state* ws;
  471. if (level >= mw->menu.old_depth)
  472. abort_gracefully ((Widget) mw);
  473. ws = &mw->menu.windows [level];
  474. ws->width = 0;
  475. ws->height = 0;
  476. ws->label_width = 0;
  477. ws->button_width = 0;
  478. for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  479. {
  480. size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
  481. &button_width, &height);
  482. if (horizontal_p)
  483. {
  484. ws->width += label_width + rest_width;
  485. if (height > ws->height)
  486. ws->height = height;
  487. }
  488. else
  489. {
  490. if (label_width > ws->label_width)
  491. ws->label_width = label_width;
  492. if (rest_width > max_rest_width)
  493. max_rest_width = rest_width;
  494. if (button_width > max_button_width)
  495. max_button_width = button_width;
  496. ws->height += height;
  497. }
  498. }
  499. if (horizontal_p)
  500. ws->label_width = ws->button_width = 0;
  501. else
  502. {
  503. ws->width = ws->label_width + max_rest_width + max_button_width;
  504. ws->button_width = max_button_width;
  505. }
  506. ws->width += 2 * mw->menu.shadow_thickness;
  507. ws->height += 2 * mw->menu.shadow_thickness;
  508. ws->max_rest_width = max_rest_width;
  509. if (horizontal_p)
  510. {
  511. ws->width += 2 * mw->menu.margin;
  512. ws->height += 2 * mw->menu.margin;
  513. }
  514. }
  515. /* Display code */
  516. static void
  517. draw_arrow (XlwMenuWidget mw,
  518. Window window,
  519. GC gc,
  520. int x,
  521. int y,
  522. int width,
  523. int down_p)
  524. {
  525. Display *dpy = XtDisplay (mw);
  526. GC top_gc = mw->menu.shadow_top_gc;
  527. GC bottom_gc = mw->menu.shadow_bottom_gc;
  528. int thickness = mw->menu.shadow_thickness;
  529. int height = width;
  530. XPoint pt[10];
  531. /* alpha = atan (0.5)
  532. factor = (1 + sin (alpha)) / cos (alpha) */
  533. double factor = 1.62;
  534. int thickness2 = thickness * factor;
  535. y += (MENU_FONT_HEIGHT (mw) - height) / 2;
  536. if (down_p)
  537. {
  538. GC temp;
  539. temp = top_gc;
  540. top_gc = bottom_gc;
  541. bottom_gc = temp;
  542. }
  543. pt[0].x = x;
  544. pt[0].y = y + height;
  545. pt[1].x = x + thickness;
  546. pt[1].y = y + height - thickness2;
  547. pt[2].x = x + thickness2;
  548. pt[2].y = y + thickness2;
  549. pt[3].x = x;
  550. pt[3].y = y;
  551. XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
  552. pt[0].x = x;
  553. pt[0].y = y;
  554. pt[1].x = x + thickness;
  555. pt[1].y = y + thickness2;
  556. pt[2].x = x + width - thickness2;
  557. pt[2].y = y + height / 2;
  558. pt[3].x = x + width;
  559. pt[3].y = y + height / 2;
  560. XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
  561. pt[0].x = x;
  562. pt[0].y = y + height;
  563. pt[1].x = x + thickness;
  564. pt[1].y = y + height - thickness2;
  565. pt[2].x = x + width - thickness2;
  566. pt[2].y = y + height / 2;
  567. pt[3].x = x + width;
  568. pt[3].y = y + height / 2;
  569. XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
  570. }
  571. static void
  572. draw_shadow_rectangle (XlwMenuWidget mw,
  573. Window window,
  574. int x,
  575. int y,
  576. int width,
  577. int height,
  578. int erase_p,
  579. int down_p)
  580. {
  581. Display *dpy = XtDisplay (mw);
  582. GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  583. GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  584. int thickness = mw->menu.shadow_thickness;
  585. XPoint points [4];
  586. if (!erase_p && down_p)
  587. {
  588. GC temp;
  589. temp = top_gc;
  590. top_gc = bottom_gc;
  591. bottom_gc = temp;
  592. }
  593. points [0].x = x;
  594. points [0].y = y;
  595. points [1].x = x + width;
  596. points [1].y = y;
  597. points [2].x = x + width - thickness;
  598. points [2].y = y + thickness;
  599. points [3].x = x;
  600. points [3].y = y + thickness;
  601. XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  602. points [0].x = x;
  603. points [0].y = y + thickness;
  604. points [1].x = x;
  605. points [1].y = y + height;
  606. points [2].x = x + thickness;
  607. points [2].y = y + height - thickness;
  608. points [3].x = x + thickness;
  609. points [3].y = y + thickness;
  610. XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  611. points [0].x = x + width;
  612. points [0].y = y;
  613. points [1].x = x + width - thickness;
  614. points [1].y = y + thickness;
  615. points [2].x = x + width - thickness;
  616. points [2].y = y + height - thickness;
  617. points [3].x = x + width;
  618. points [3].y = y + height - thickness;
  619. XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  620. points [0].x = x;
  621. points [0].y = y + height;
  622. points [1].x = x + width;
  623. points [1].y = y + height;
  624. points [2].x = x + width;
  625. points [2].y = y + height - thickness;
  626. points [3].x = x + thickness;
  627. points [3].y = y + height - thickness;
  628. XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  629. }
  630. static void
  631. draw_shadow_rhombus (XlwMenuWidget mw,
  632. Window window,
  633. int x,
  634. int y,
  635. int width,
  636. int height,
  637. int erase_p,
  638. int down_p)
  639. {
  640. Display *dpy = XtDisplay (mw);
  641. GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  642. GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  643. int thickness = mw->menu.shadow_thickness;
  644. XPoint points [4];
  645. if (!erase_p && down_p)
  646. {
  647. GC temp;
  648. temp = top_gc;
  649. top_gc = bottom_gc;
  650. bottom_gc = temp;
  651. }
  652. points [0].x = x;
  653. points [0].y = y + height / 2;
  654. points [1].x = x + thickness;
  655. points [1].y = y + height / 2;
  656. points [2].x = x + width / 2;
  657. points [2].y = y + thickness;
  658. points [3].x = x + width / 2;
  659. points [3].y = y;
  660. XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  661. points [0].x = x + width / 2;
  662. points [0].y = y;
  663. points [1].x = x + width / 2;
  664. points [1].y = y + thickness;
  665. points [2].x = x + width - thickness;
  666. points [2].y = y + height / 2;
  667. points [3].x = x + width;
  668. points [3].y = y + height / 2;
  669. XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  670. points [0].x = x;
  671. points [0].y = y + height / 2;
  672. points [1].x = x + thickness;
  673. points [1].y = y + height / 2;
  674. points [2].x = x + width / 2;
  675. points [2].y = y + height - thickness;
  676. points [3].x = x + width / 2;
  677. points [3].y = y + height;
  678. XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  679. points [0].x = x + width / 2;
  680. points [0].y = y + height;
  681. points [1].x = x + width / 2;
  682. points [1].y = y + height - thickness;
  683. points [2].x = x + width - thickness;
  684. points [2].y = y + height / 2;
  685. points [3].x = x + width;
  686. points [3].y = y + height / 2;
  687. XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  688. }
  689. /* Draw a toggle button on widget MW, X window WINDOW. X/Y is the
  690. top-left corner of the menu item. SELECTED_P non-zero means the
  691. toggle button is selected. */
  692. static void
  693. draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
  694. {
  695. int width, height;
  696. width = toggle_button_width (mw);
  697. height = width;
  698. x += mw->menu.horizontal_spacing;
  699. y += (MENU_FONT_ASCENT (mw) - height) / 2;
  700. draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
  701. }
  702. /* Draw a radio button on widget MW, X window WINDOW. X/Y is the
  703. top-left corner of the menu item. SELECTED_P non-zero means the
  704. toggle button is selected. */
  705. static void
  706. draw_radio (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
  707. {
  708. int width, height;
  709. width = radio_button_width (mw);
  710. height = width;
  711. x += mw->menu.horizontal_spacing;
  712. y += (MENU_FONT_ASCENT (mw) - height) / 2;
  713. draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
  714. }
  715. /* Draw a menu separator on widget MW, X window WINDOW. X/Y is the
  716. top-left corner of the menu item. WIDTH is the width of the
  717. separator to draw. TYPE is the separator type. */
  718. static void
  719. draw_separator (XlwMenuWidget mw,
  720. Window window,
  721. int x,
  722. int y,
  723. int width,
  724. enum menu_separator type)
  725. {
  726. Display *dpy = XtDisplay (mw);
  727. XGCValues xgcv;
  728. switch (type)
  729. {
  730. case SEPARATOR_NO_LINE:
  731. break;
  732. case SEPARATOR_SINGLE_LINE:
  733. XDrawLine (dpy, window, mw->menu.foreground_gc,
  734. x, y, x + width, y);
  735. break;
  736. case SEPARATOR_DOUBLE_LINE:
  737. draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
  738. draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
  739. break;
  740. case SEPARATOR_SINGLE_DASHED_LINE:
  741. xgcv.line_style = LineOnOffDash;
  742. XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
  743. XDrawLine (dpy, window, mw->menu.foreground_gc,
  744. x, y, x + width, y);
  745. xgcv.line_style = LineSolid;
  746. XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
  747. break;
  748. case SEPARATOR_DOUBLE_DASHED_LINE:
  749. draw_separator (mw, window, x, y, width,
  750. SEPARATOR_SINGLE_DASHED_LINE);
  751. draw_separator (mw, window, x, y + 2, width,
  752. SEPARATOR_SINGLE_DASHED_LINE);
  753. break;
  754. case SEPARATOR_SHADOW_ETCHED_IN:
  755. XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
  756. x, y, x + width, y);
  757. XDrawLine (dpy, window, mw->menu.shadow_top_gc,
  758. x, y + 1, x + width, y + 1);
  759. break;
  760. case SEPARATOR_SHADOW_ETCHED_OUT:
  761. XDrawLine (dpy, window, mw->menu.shadow_top_gc,
  762. x, y, x + width, y);
  763. XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
  764. x, y + 1, x + width, y + 1);
  765. break;
  766. case SEPARATOR_SHADOW_ETCHED_IN_DASH:
  767. xgcv.line_style = LineOnOffDash;
  768. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  769. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  770. draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
  771. xgcv.line_style = LineSolid;
  772. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  773. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  774. break;
  775. case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
  776. xgcv.line_style = LineOnOffDash;
  777. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  778. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  779. draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
  780. xgcv.line_style = LineSolid;
  781. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  782. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  783. break;
  784. case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
  785. draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
  786. draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
  787. break;
  788. case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
  789. draw_separator (mw, window, x, y, width,
  790. SEPARATOR_SHADOW_ETCHED_OUT);
  791. draw_separator (mw, window, x, y + 3, width,
  792. SEPARATOR_SHADOW_ETCHED_OUT);
  793. break;
  794. case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
  795. xgcv.line_style = LineOnOffDash;
  796. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  797. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  798. draw_separator (mw, window, x, y, width,
  799. SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
  800. xgcv.line_style = LineSolid;
  801. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  802. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  803. break;
  804. case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
  805. xgcv.line_style = LineOnOffDash;
  806. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  807. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  808. draw_separator (mw, window, x, y, width,
  809. SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
  810. xgcv.line_style = LineSolid;
  811. XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
  812. XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
  813. break;
  814. default:
  815. abort ();
  816. }
  817. }
  818. /* Return the pixel height of menu separator SEPARATOR. */
  819. static int
  820. separator_height (enum menu_separator separator)
  821. {
  822. switch (separator)
  823. {
  824. case SEPARATOR_NO_LINE:
  825. return 2;
  826. case SEPARATOR_SINGLE_LINE:
  827. case SEPARATOR_SINGLE_DASHED_LINE:
  828. return 1;
  829. case SEPARATOR_DOUBLE_LINE:
  830. case SEPARATOR_DOUBLE_DASHED_LINE:
  831. return 3;
  832. case SEPARATOR_SHADOW_ETCHED_IN:
  833. case SEPARATOR_SHADOW_ETCHED_OUT:
  834. case SEPARATOR_SHADOW_ETCHED_IN_DASH:
  835. case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
  836. return 2;
  837. case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
  838. case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
  839. case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
  840. case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
  841. return 5;
  842. default:
  843. abort ();
  844. }
  845. }
  846. /* Display the menu item and increment where.x and where.y to show how large
  847. the menu item was. */
  848. static void
  849. display_menu_item (XlwMenuWidget mw,
  850. widget_value* val,
  851. window_state* ws,
  852. XPoint* where,
  853. Boolean highlighted_p,
  854. Boolean horizontal_p,
  855. Boolean just_compute_p)
  856. {
  857. GC deco_gc;
  858. GC text_gc;
  859. int font_height = MENU_FONT_HEIGHT (mw);
  860. int font_ascent = MENU_FONT_ASCENT (mw);
  861. int shadow = mw->menu.shadow_thickness;
  862. int margin = mw->menu.margin;
  863. int h_spacing = mw->menu.horizontal_spacing;
  864. int v_spacing = mw->menu.vertical_spacing;
  865. int label_width;
  866. int rest_width;
  867. int button_width;
  868. int height;
  869. int width;
  870. enum menu_separator separator;
  871. int separator_p = lw_separator_p (val->name, &separator, 0);
  872. #ifdef HAVE_XFT
  873. XftColor *xftfg;
  874. #endif
  875. /* compute the sizes of the item */
  876. size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
  877. &button_width, &height);
  878. if (horizontal_p)
  879. width = label_width + rest_width;
  880. else
  881. {
  882. label_width = ws->label_width;
  883. width = ws->width - 2 * shadow;
  884. }
  885. /* Only highlight an enabled item that has a callback. */
  886. if (highlighted_p)
  887. if (!val->enabled || !(val->call_data || val->contents))
  888. highlighted_p = 0;
  889. /* do the drawing. */
  890. if (!just_compute_p)
  891. {
  892. /* Add the shadow border of the containing menu */
  893. int x = where->x + shadow;
  894. int y = where->y + shadow;
  895. if (horizontal_p)
  896. {
  897. x += margin;
  898. y += margin;
  899. }
  900. /* pick the foreground and background GC. */
  901. if (val->enabled)
  902. text_gc = mw->menu.foreground_gc;
  903. else
  904. text_gc = mw->menu.disabled_gc;
  905. deco_gc = mw->menu.foreground_gc;
  906. #ifdef HAVE_XFT
  907. xftfg = val->enabled ? &mw->menu.xft_fg : &mw->menu.xft_disabled_fg;
  908. #endif
  909. if (separator_p)
  910. {
  911. draw_separator (mw, ws->pixmap, x, y, width, separator);
  912. }
  913. else
  914. {
  915. int x_offset = x + h_spacing + shadow;
  916. char* display_string = resource_widget_value (mw, val);
  917. draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, True,
  918. False);
  919. /* Deal with centering a menu title. */
  920. if (!horizontal_p && !val->contents && !val->call_data)
  921. {
  922. int l = string_width (mw, display_string);
  923. if (width > l)
  924. x_offset = (width - l) >> 1;
  925. }
  926. else if (!horizontal_p && ws->button_width)
  927. x_offset += ws->button_width;
  928. #ifdef HAVE_XFT
  929. if (ws->xft_draw)
  930. {
  931. int draw_y = y + v_spacing + shadow;
  932. XftDrawStringUtf8 (ws->xft_draw, xftfg,
  933. mw->menu.xft_font,
  934. x_offset, draw_y + font_ascent,
  935. (unsigned char *) display_string,
  936. strlen (display_string));
  937. }
  938. else
  939. #endif
  940. #ifdef HAVE_X_I18N
  941. if (mw->menu.fontSet)
  942. XmbDrawString (XtDisplay (mw), ws->pixmap, mw->menu.fontSet,
  943. text_gc, x_offset,
  944. y + v_spacing + shadow + font_ascent,
  945. display_string, strlen (display_string));
  946. else
  947. #endif
  948. XDrawString (XtDisplay (mw), ws->pixmap,
  949. text_gc, x_offset,
  950. y + v_spacing + shadow + font_ascent,
  951. display_string, strlen (display_string));
  952. if (!horizontal_p)
  953. {
  954. if (val->button_type == BUTTON_TYPE_TOGGLE)
  955. draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
  956. val->selected);
  957. else if (val->button_type == BUTTON_TYPE_RADIO)
  958. draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
  959. val->selected);
  960. if (val->contents)
  961. {
  962. int a_w = arrow_width (mw);
  963. draw_arrow (mw, ws->pixmap, deco_gc,
  964. x + width - a_w
  965. - mw->menu.horizontal_spacing
  966. - mw->menu.shadow_thickness,
  967. y + v_spacing + shadow, a_w,
  968. highlighted_p);
  969. }
  970. else if (val->key)
  971. {
  972. #ifdef HAVE_XFT
  973. if (ws->xft_draw)
  974. {
  975. int draw_x = ws->width - ws->max_rest_width
  976. + mw->menu.arrow_spacing;
  977. int draw_y = y + v_spacing + shadow + font_ascent;
  978. XftDrawStringUtf8 (ws->xft_draw, xftfg,
  979. mw->menu.xft_font,
  980. draw_x, draw_y,
  981. (unsigned char *) val->key,
  982. strlen (val->key));
  983. }
  984. else
  985. #endif
  986. #ifdef HAVE_X_I18N
  987. if (mw->menu.fontSet)
  988. XmbDrawString (XtDisplay (mw), ws->pixmap,
  989. mw->menu.fontSet,
  990. text_gc,
  991. x + label_width + mw->menu.arrow_spacing,
  992. y + v_spacing + shadow + font_ascent,
  993. val->key, strlen (val->key));
  994. else
  995. #endif
  996. XDrawString (XtDisplay (mw), ws->pixmap,
  997. text_gc,
  998. x + label_width + mw->menu.arrow_spacing,
  999. y + v_spacing + shadow + font_ascent,
  1000. val->key, strlen (val->key));
  1001. }
  1002. }
  1003. else
  1004. {
  1005. XDrawRectangle (XtDisplay (mw), ws->pixmap,
  1006. mw->menu.background_gc,
  1007. x + shadow, y + shadow,
  1008. label_width + h_spacing - 1,
  1009. font_height + 2 * v_spacing - 1);
  1010. draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
  1011. True, False);
  1012. }
  1013. if (highlighted_p)
  1014. draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, False,
  1015. False);
  1016. }
  1017. }
  1018. where->x += width;
  1019. where->y += height;
  1020. }
  1021. static void
  1022. display_menu (XlwMenuWidget mw,
  1023. int level,
  1024. Boolean just_compute_p,
  1025. XPoint *highlighted_pos,
  1026. XPoint *hit,
  1027. widget_value **hit_return)
  1028. {
  1029. widget_value* val;
  1030. widget_value* following_item;
  1031. window_state* ws;
  1032. XPoint where;
  1033. int horizontal_p = mw->menu.horizontal && (level == 0);
  1034. int highlighted_p;
  1035. int no_return = 0;
  1036. enum menu_separator separator;
  1037. if (level >= mw->menu.old_depth)
  1038. abort_gracefully ((Widget) mw);
  1039. if (level < mw->menu.old_depth - 1)
  1040. following_item = mw->menu.old_stack [level + 1];
  1041. else
  1042. following_item = NULL;
  1043. if (hit)
  1044. *hit_return = NULL;
  1045. where.x = 0;
  1046. where.y = 0;
  1047. ws = &mw->menu.windows [level];
  1048. if (!just_compute_p)
  1049. XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
  1050. 0, 0, ws->width, ws->height);
  1051. for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  1052. {
  1053. highlighted_p = val == following_item;
  1054. if (highlighted_p && highlighted_pos)
  1055. {
  1056. if (horizontal_p)
  1057. highlighted_pos->x = where.x;
  1058. else
  1059. highlighted_pos->y = where.y;
  1060. }
  1061. display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
  1062. just_compute_p);
  1063. if (highlighted_p && highlighted_pos)
  1064. {
  1065. if (horizontal_p)
  1066. highlighted_pos->y = where.y;
  1067. else
  1068. highlighted_pos->x = where.x;
  1069. }
  1070. if (hit
  1071. && !*hit_return
  1072. && (horizontal_p ? hit->x < where.x : hit->y < where.y)
  1073. && !lw_separator_p (val->name, &separator, 0)
  1074. && !no_return)
  1075. {
  1076. if (val->enabled)
  1077. *hit_return = val;
  1078. else
  1079. no_return = 1;
  1080. if (mw->menu.inside_entry != val)
  1081. {
  1082. if (mw->menu.inside_entry)
  1083. XtCallCallbackList ((Widget)mw, mw->menu.leave,
  1084. (XtPointer) mw->menu.inside_entry);
  1085. mw->menu.inside_entry = val;
  1086. XtCallCallbackList ((Widget)mw, mw->menu.enter,
  1087. (XtPointer) mw->menu.inside_entry);
  1088. }
  1089. }
  1090. if (horizontal_p)
  1091. where.y = 0;
  1092. else
  1093. where.x = 0;
  1094. }
  1095. if (!just_compute_p)
  1096. {
  1097. draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
  1098. False, False);
  1099. XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
  1100. mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
  1101. }
  1102. }
  1103. /* Motion code */
  1104. static void
  1105. set_new_state (XlwMenuWidget mw, widget_value *val, int level)
  1106. {
  1107. int i;
  1108. mw->menu.new_depth = 0;
  1109. for (i = 0; i < level; i++)
  1110. push_new_stack (mw, mw->menu.old_stack [i]);
  1111. push_new_stack (mw, val);
  1112. }
  1113. static void
  1114. expose_cb (Widget widget,
  1115. XtPointer closure,
  1116. XEvent* event,
  1117. Boolean* continue_to_dispatch)
  1118. {
  1119. XlwMenuWidget mw = (XlwMenuWidget) closure;
  1120. int i;
  1121. *continue_to_dispatch = False;
  1122. for (i = 0; i < mw->menu.windows_length; ++i)
  1123. if (mw->menu.windows [i].w == widget) break;
  1124. if (i < mw->menu.windows_length && i < mw->menu.old_depth)
  1125. display_menu (mw, i, False, NULL, NULL, NULL);
  1126. }
  1127. static void
  1128. set_window_type (Widget w, XlwMenuWidget mw)
  1129. {
  1130. int popup_menu_p = mw->menu.top_depth == 1;
  1131. Atom type = XInternAtom (XtDisplay (w),
  1132. popup_menu_p
  1133. ? "_NET_WM_WINDOW_TYPE_POPUP_MENU"
  1134. : "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
  1135. False);
  1136. XChangeProperty (XtDisplay (w), XtWindow (w),
  1137. XInternAtom (XtDisplay (w), "_NET_WM_WINDOW_TYPE", False),
  1138. XA_ATOM, 32, PropModeReplace,
  1139. (unsigned char *)&type, 1);
  1140. }
  1141. static void
  1142. make_windows_if_needed (XlwMenuWidget mw, int n)
  1143. {
  1144. int i;
  1145. int start_at;
  1146. window_state* windows;
  1147. if (mw->menu.windows_length >= n)
  1148. return;
  1149. if (!mw->menu.windows)
  1150. {
  1151. mw->menu.windows =
  1152. (window_state*)XtMalloc (n * sizeof (window_state));
  1153. start_at = 0;
  1154. }
  1155. else
  1156. {
  1157. mw->menu.windows =
  1158. (window_state*)XtRealloc ((char*)mw->menu.windows,
  1159. n * sizeof (window_state));
  1160. start_at = mw->menu.windows_length;
  1161. }
  1162. mw->menu.windows_length = n;
  1163. windows = mw->menu.windows;
  1164. for (i = start_at; i < n; i++)
  1165. {
  1166. Arg av[10];
  1167. int ac = 0;
  1168. windows [i].x = 0;
  1169. windows [i].y = 0;
  1170. windows [i].width = 1;
  1171. windows [i].height = 1;
  1172. windows [i].max_rest_width = 0;
  1173. XtSetArg (av[ac], XtNwidth, 1); ++ac;
  1174. XtSetArg (av[ac], XtNheight, 1); ++ac;
  1175. XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
  1176. XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
  1177. XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
  1178. XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
  1179. XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
  1180. windows [i].w =
  1181. XtCreatePopupShell ("sub", overrideShellWidgetClass,
  1182. (Widget) mw, av, ac);
  1183. XtRealizeWidget (windows [i].w);
  1184. XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
  1185. windows [i].window = XtWindow (windows [i].w);
  1186. windows [i].pixmap = None;
  1187. #ifdef HAVE_XFT
  1188. windows [i].xft_draw = 0;
  1189. #endif
  1190. set_window_type (windows [i].w, mw);
  1191. }
  1192. XFlush (XtDisplay (mw));
  1193. }
  1194. /* Value is non-zero if WINDOW is part of menu bar widget W. */
  1195. int
  1196. xlwmenu_window_p (Widget w, Window window)
  1197. {
  1198. XlwMenuWidget mw = (XlwMenuWidget) w;
  1199. int i;
  1200. for (i = 0; i < mw->menu.windows_length; ++i)
  1201. if (window == mw->menu.windows[i].window)
  1202. break;
  1203. return i < mw->menu.windows_length;
  1204. }
  1205. /* Make the window fit in the screen */
  1206. static void
  1207. fit_to_screen (XlwMenuWidget mw,
  1208. window_state *ws,
  1209. window_state *previous_ws,
  1210. Boolean horizontal_p)
  1211. {
  1212. unsigned int screen_width = WidthOfScreen (XtScreen (mw));
  1213. unsigned int screen_height = HeightOfScreen (XtScreen (mw));
  1214. /* 1 if we are unable to avoid an overlap between
  1215. this menu and the parent menu in the X dimension. */
  1216. int horizontal_overlap = 0;
  1217. if (ws->x < 0)
  1218. ws->x = 0;
  1219. else if (ws->x + ws->width > screen_width)
  1220. {
  1221. if (!horizontal_p)
  1222. /* The addition of shadow-thickness for a sub-menu's position is
  1223. to reflect a similar adjustment when the menu is displayed to
  1224. the right of the invoking menu-item; it makes the sub-menu
  1225. look more `attached' to the menu-item. */
  1226. ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
  1227. else
  1228. ws->x = screen_width - ws->width;
  1229. if (ws->x < 0)
  1230. {
  1231. ws->x = 0;
  1232. horizontal_overlap = 1;
  1233. }
  1234. }
  1235. /* If we overlap in X, try to avoid overlap in Y. */
  1236. if (horizontal_overlap
  1237. && ws->y < previous_ws->y + previous_ws->height
  1238. && previous_ws->y < ws->y + ws->height)
  1239. {
  1240. /* Put this menu right below or right above PREVIOUS_WS
  1241. if there's room. */
  1242. if (previous_ws->y + previous_ws->height + ws->height < screen_height)
  1243. ws->y = previous_ws->y + previous_ws->height;
  1244. else if (previous_ws->y - ws->height > 0)
  1245. ws->y = previous_ws->y - ws->height;
  1246. }
  1247. if (ws->y < 0)
  1248. ws->y = 0;
  1249. else if (ws->y + ws->height > screen_height)
  1250. {
  1251. if (horizontal_p)
  1252. ws->y = previous_ws->y - ws->height;
  1253. else
  1254. ws->y = screen_height - ws->height;
  1255. if (ws->y < 0)
  1256. ws->y = 0;
  1257. }
  1258. }
  1259. static void
  1260. create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
  1261. {
  1262. if (ws->pixmap != None)
  1263. {
  1264. XFreePixmap (XtDisplay (ws->w), ws->pixmap);
  1265. ws->pixmap = None;
  1266. }
  1267. ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
  1268. ws->width, ws->height,
  1269. DefaultDepthOfScreen (XtScreen (ws->w)));
  1270. #ifdef HAVE_XFT
  1271. if (ws->xft_draw)
  1272. XftDrawDestroy (ws->xft_draw);
  1273. if (mw->menu.xft_font)
  1274. {
  1275. int screen = XScreenNumberOfScreen (mw->core.screen);
  1276. ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
  1277. ws->pixmap,
  1278. DefaultVisual (XtDisplay (ws->w), screen),
  1279. mw->core.colormap);
  1280. }
  1281. else
  1282. ws->xft_draw = 0;
  1283. #endif
  1284. }
  1285. /* Updates old_stack from new_stack and redisplays. */
  1286. static void
  1287. remap_menubar (XlwMenuWidget mw)
  1288. {
  1289. int i;
  1290. int last_same;
  1291. XPoint selection_position;
  1292. int old_depth = mw->menu.old_depth;
  1293. int new_depth = mw->menu.new_depth;
  1294. widget_value** old_stack;
  1295. widget_value** new_stack;
  1296. window_state* windows;
  1297. widget_value* old_selection;
  1298. widget_value* new_selection;
  1299. /* Check that enough windows and old_stack are ready. */
  1300. make_windows_if_needed (mw, new_depth);
  1301. make_old_stack_space (mw, new_depth);
  1302. windows = mw->menu.windows;
  1303. old_stack = mw->menu.old_stack;
  1304. new_stack = mw->menu.new_stack;
  1305. /* compute the last identical different entry */
  1306. for (i = 1; i < old_depth && i < new_depth; i++)
  1307. if (old_stack [i] != new_stack [i])
  1308. break;
  1309. last_same = i - 1;
  1310. /* Memorize the previously selected item to be able to refresh it */
  1311. old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
  1312. if (old_selection && !old_selection->enabled)
  1313. old_selection = NULL;
  1314. new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
  1315. if (new_selection && !new_selection->enabled)
  1316. new_selection = NULL;
  1317. /* Call callback when the highlighted item changes. */
  1318. if (old_selection || new_selection)
  1319. XtCallCallbackList ((Widget)mw, mw->menu.highlight,
  1320. (XtPointer) new_selection);
  1321. /* updates old_state from new_state. It has to be done now because
  1322. display_menu (called below) uses the old_stack to know what to display. */
  1323. for (i = last_same + 1; i < new_depth; i++)
  1324. {
  1325. XtPopdown (mw->menu.windows [i].w);
  1326. old_stack [i] = new_stack [i];
  1327. }
  1328. mw->menu.old_depth = new_depth;
  1329. /* refresh the last selection */
  1330. selection_position.x = 0;
  1331. selection_position.y = 0;
  1332. display_menu (mw, last_same, new_selection == old_selection,
  1333. &selection_position, NULL, NULL);
  1334. /* Now place the new menus. */
  1335. for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
  1336. {
  1337. window_state *previous_ws = &windows[i - 1];
  1338. window_state *ws = &windows[i];
  1339. ws->x = (previous_ws->x + selection_position.x
  1340. + mw->menu.shadow_thickness);
  1341. if (mw->menu.horizontal && i == 1)
  1342. ws->x += mw->menu.margin;
  1343. #if 0
  1344. if (!mw->menu.horizontal || i > 1)
  1345. ws->x += mw->menu.shadow_thickness;
  1346. #endif
  1347. ws->y = (previous_ws->y + selection_position.y
  1348. + mw->menu.shadow_thickness);
  1349. if (mw->menu.horizontal && i == 1)
  1350. ws->y += mw->menu.margin;
  1351. size_menu (mw, i);
  1352. fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
  1353. create_pixmap_for_menu (ws, mw);
  1354. XtMoveWidget (ws->w, ws->x, ws->y);
  1355. XtPopup (ws->w, XtGrabNone);
  1356. XtResizeWidget (ws->w, ws->width, ws->height,
  1357. mw->core.border_width);
  1358. XtResizeWindow (ws->w);
  1359. display_menu (mw, i, False, &selection_position, NULL, NULL);
  1360. }
  1361. /* unmap the menus that popped down */
  1362. for (i = new_depth - 1; i < old_depth; i++)
  1363. if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
  1364. XtPopdown (windows[i].w);
  1365. }
  1366. static Boolean
  1367. motion_event_is_in_menu (XlwMenuWidget mw,
  1368. XMotionEvent *ev,
  1369. int level,
  1370. XPoint *relative_pos)
  1371. {
  1372. window_state* ws = &mw->menu.windows [level];
  1373. int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
  1374. int x = ws->x + shadow;
  1375. int y = ws->y + shadow;
  1376. relative_pos->x = ev->x_root - x;
  1377. relative_pos->y = ev->y_root - y;
  1378. return (x - shadow < ev->x_root && ev->x_root < x + ws->width
  1379. && y - shadow < ev->y_root && ev->y_root < y + ws->height);
  1380. }
  1381. static Boolean
  1382. map_event_to_widget_value (XlwMenuWidget mw,
  1383. XMotionEvent *ev,
  1384. widget_value **val,
  1385. int *level)
  1386. {
  1387. int i;
  1388. XPoint relative_pos;
  1389. window_state* ws;
  1390. int inside = 0;
  1391. *val = NULL;
  1392. /* Find the window */
  1393. for (i = mw->menu.old_depth - 1; i >= 0; i--)
  1394. {
  1395. ws = &mw->menu.windows [i];
  1396. if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
  1397. {
  1398. inside = 1;
  1399. display_menu (mw, i, True, NULL, &relative_pos, val);
  1400. if (*val)
  1401. {
  1402. *level = i + 1;
  1403. return True;
  1404. }
  1405. }
  1406. }
  1407. if (!inside)
  1408. {
  1409. if (mw->menu.inside_entry != NULL)
  1410. XtCallCallbackList ((Widget)mw, mw->menu.leave,
  1411. (XtPointer) mw->menu.inside_entry);
  1412. mw->menu.inside_entry = NULL;
  1413. }
  1414. return False;
  1415. }
  1416. /* Procedures */
  1417. static void
  1418. make_drawing_gcs (XlwMenuWidget mw)
  1419. {
  1420. XGCValues xgcv;
  1421. float scale;
  1422. XtGCMask mask = GCForeground | GCBackground;
  1423. #ifdef HAVE_X_I18N
  1424. if (!mw->menu.fontSet && mw->menu.font)
  1425. {
  1426. xgcv.font = mw->menu.font->fid;
  1427. mask |= GCFont;
  1428. }
  1429. #else
  1430. if (mw->menu.font)
  1431. {
  1432. xgcv.font = mw->menu.font->fid;
  1433. mask |= GCFont;
  1434. }
  1435. #endif
  1436. xgcv.foreground = mw->menu.foreground;
  1437. xgcv.background = mw->core.background_pixel;
  1438. mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1439. xgcv.foreground = mw->menu.button_foreground;
  1440. mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1441. xgcv.background = mw->core.background_pixel;
  1442. #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
  1443. /* Allocate color for disabled menu-items. */
  1444. mw->menu.disabled_foreground = mw->menu.foreground;
  1445. if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
  1446. scale = 2.3;
  1447. else
  1448. scale = 0.55;
  1449. x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
  1450. mw->core.colormap,
  1451. &mw->menu.disabled_foreground,
  1452. scale,
  1453. 0x8000);
  1454. if (mw->menu.foreground == mw->menu.disabled_foreground
  1455. || mw->core.background_pixel == mw->menu.disabled_foreground)
  1456. {
  1457. /* Too few colors, use stipple. */
  1458. xgcv.foreground = mw->menu.foreground;
  1459. xgcv.fill_style = FillStippled;
  1460. xgcv.stipple = mw->menu.gray_pixmap;
  1461. mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
  1462. | GCFillStyle | GCStipple, &xgcv);
  1463. }
  1464. else
  1465. {
  1466. /* Many colors available, use disabled pixel. */
  1467. xgcv.foreground = mw->menu.disabled_foreground;
  1468. mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1469. }
  1470. xgcv.foreground = mw->menu.button_foreground;
  1471. xgcv.background = mw->core.background_pixel;
  1472. xgcv.fill_style = FillStippled;
  1473. xgcv.stipple = mw->menu.gray_pixmap;
  1474. mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
  1475. | GCFillStyle | GCStipple, &xgcv);
  1476. xgcv.foreground = mw->core.background_pixel;
  1477. xgcv.background = mw->menu.foreground;
  1478. mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1479. }
  1480. static void
  1481. release_drawing_gcs (XlwMenuWidget mw)
  1482. {
  1483. XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
  1484. XtReleaseGC ((Widget) mw, mw->menu.button_gc);
  1485. XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
  1486. XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
  1487. XtReleaseGC ((Widget) mw, mw->menu.background_gc);
  1488. /* let's get some segvs if we try to use these... */
  1489. mw->menu.foreground_gc = (GC) -1;
  1490. mw->menu.button_gc = (GC) -1;
  1491. mw->menu.disabled_gc = (GC) -1;
  1492. mw->menu.inactive_button_gc = (GC) -1;
  1493. mw->menu.background_gc = (GC) -1;
  1494. }
  1495. #ifndef emacs
  1496. #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
  1497. ? ((unsigned long) (x)) : ((unsigned long) (y)))
  1498. #endif
  1499. static void
  1500. make_shadow_gcs (XlwMenuWidget mw)
  1501. {
  1502. XGCValues xgcv;
  1503. unsigned long pm = 0;
  1504. Display *dpy = XtDisplay ((Widget) mw);
  1505. Screen *screen = XtScreen ((Widget) mw);
  1506. Colormap cmap = mw->core.colormap;
  1507. XColor topc, botc;
  1508. int top_frobbed = 0, bottom_frobbed = 0;
  1509. mw->menu.free_top_shadow_color_p = 0;
  1510. mw->menu.free_bottom_shadow_color_p = 0;
  1511. if (mw->menu.top_shadow_color == -1)
  1512. mw->menu.top_shadow_color = mw->core.background_pixel;
  1513. else
  1514. mw->menu.top_shadow_color = mw->menu.top_shadow_color;
  1515. if (mw->menu.bottom_shadow_color == -1)
  1516. mw->menu.bottom_shadow_color = mw->menu.foreground;
  1517. else
  1518. mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
  1519. if (mw->menu.top_shadow_color == mw->core.background_pixel ||
  1520. mw->menu.top_shadow_color == mw->menu.foreground)
  1521. {
  1522. topc.pixel = mw->core.background_pixel;
  1523. #ifdef emacs
  1524. if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
  1525. &topc.pixel,
  1526. 1.2, 0x8000))
  1527. #else
  1528. XQueryColor (dpy, cmap, &topc);
  1529. /* don't overflow/wrap! */
  1530. topc.red = MINL (65535, topc.red * 1.2);
  1531. topc.green = MINL (65535, topc.green * 1.2);
  1532. topc.blue = MINL (65535, topc.blue * 1.2);
  1533. if (XAllocColor (dpy, cmap, &topc))
  1534. #endif
  1535. {
  1536. mw->menu.top_shadow_color = topc.pixel;
  1537. mw->menu.free_top_shadow_color_p = 1;
  1538. top_frobbed = 1;
  1539. }
  1540. }
  1541. if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
  1542. mw->menu.bottom_shadow_color == mw->core.background_pixel)
  1543. {
  1544. botc.pixel = mw->core.background_pixel;
  1545. #ifdef emacs
  1546. if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
  1547. &botc.pixel,
  1548. 0.6, 0x4000))
  1549. #else
  1550. XQueryColor (dpy, cmap, &botc);
  1551. botc.red *= 0.6;
  1552. botc.green *= 0.6;
  1553. botc.blue *= 0.6;
  1554. if (XAllocColor (dpy, cmap, &botc))
  1555. #endif
  1556. {
  1557. mw->menu.bottom_shadow_color = botc.pixel;
  1558. mw->menu.free_bottom_shadow_color_p = 1;
  1559. bottom_frobbed = 1;
  1560. }
  1561. }
  1562. if (top_frobbed && bottom_frobbed)
  1563. {
  1564. if (topc.pixel == botc.pixel)
  1565. {
  1566. if (botc.pixel == mw->menu.foreground)
  1567. {
  1568. if (mw->menu.free_top_shadow_color_p)
  1569. {
  1570. x_free_dpy_colors (dpy, screen, cmap,
  1571. &mw->menu.top_shadow_color, 1);
  1572. mw->menu.free_top_shadow_color_p = 0;
  1573. }
  1574. mw->menu.top_shadow_color = mw->core.background_pixel;
  1575. }
  1576. else
  1577. {
  1578. if (mw->menu.free_bottom_shadow_color_p)
  1579. {
  1580. x_free_dpy_colors (dpy, screen, cmap,
  1581. &mw->menu.bottom_shadow_color, 1);
  1582. mw->menu.free_bottom_shadow_color_p = 0;
  1583. }
  1584. mw->menu.bottom_shadow_color = mw->menu.foreground;
  1585. }
  1586. }
  1587. }
  1588. if (!mw->menu.top_shadow_pixmap &&
  1589. mw->menu.top_shadow_color == mw->core.background_pixel)
  1590. {
  1591. mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
  1592. if (mw->menu.free_top_shadow_color_p)
  1593. {
  1594. x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
  1595. mw->menu.free_top_shadow_color_p = 0;
  1596. }
  1597. mw->menu.top_shadow_color = mw->menu.foreground;
  1598. }
  1599. if (!mw->menu.bottom_shadow_pixmap &&
  1600. mw->menu.bottom_shadow_color == mw->core.background_pixel)
  1601. {
  1602. mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
  1603. if (mw->menu.free_bottom_shadow_color_p)
  1604. {
  1605. x_free_dpy_colors (dpy, screen, cmap,
  1606. &mw->menu.bottom_shadow_color, 1);
  1607. mw->menu.free_bottom_shadow_color_p = 0;
  1608. }
  1609. mw->menu.bottom_shadow_color = mw->menu.foreground;
  1610. }
  1611. xgcv.fill_style = FillStippled;
  1612. xgcv.foreground = mw->menu.top_shadow_color;
  1613. xgcv.stipple = mw->menu.top_shadow_pixmap;
  1614. pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  1615. mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
  1616. xgcv.foreground = mw->menu.bottom_shadow_color;
  1617. xgcv.stipple = mw->menu.bottom_shadow_pixmap;
  1618. pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  1619. mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
  1620. }
  1621. static void
  1622. release_shadow_gcs (XlwMenuWidget mw)
  1623. {
  1624. Display *dpy = XtDisplay ((Widget) mw);
  1625. Screen *screen = XtScreen ((Widget) mw);
  1626. Colormap cmap = mw->core.colormap;
  1627. Pixel px[2];
  1628. int i = 0;
  1629. if (mw->menu.free_top_shadow_color_p)
  1630. px[i++] = mw->menu.top_shadow_color;
  1631. if (mw->menu.free_bottom_shadow_color_p)
  1632. px[i++] = mw->menu.bottom_shadow_color;
  1633. if (i > 0)
  1634. x_free_dpy_colors (dpy, screen, cmap, px, i);
  1635. XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
  1636. XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
  1637. }
  1638. #ifdef HAVE_XFT
  1639. static XftFont *
  1640. getDefaultXftFont (XlwMenuWidget mw)
  1641. {
  1642. int screen = XScreenNumberOfScreen (mw->core.screen);
  1643. return XftFontOpenName (XtDisplay (mw), screen, DEFAULT_FONTNAME);
  1644. }
  1645. static int
  1646. openXftFont (XlwMenuWidget mw)
  1647. {
  1648. char *fname = mw->menu.fontName;
  1649. mw->menu.xft_font = 0;
  1650. mw->menu.default_face = fname && strcmp (fname, DEFAULT_FONTNAME) == 0;
  1651. if (fname && strcmp (fname, "none") != 0)
  1652. {
  1653. int screen = XScreenNumberOfScreen (mw->core.screen);
  1654. int len = strlen (fname), i = len-1;
  1655. /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */
  1656. while (i > 0 && isdigit (fname[i]))
  1657. --i;
  1658. if (fname[i] == ' ')
  1659. {
  1660. fname = xstrdup (mw->menu.fontName);
  1661. fname[i] = '-';
  1662. }
  1663. mw->menu.font = XLoadQueryFont (XtDisplay (mw), fname);
  1664. if (!mw->menu.font)
  1665. {
  1666. mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
  1667. if (!mw->menu.xft_font)
  1668. {
  1669. fprintf (stderr, "Can't find font '%s'\n", fname);
  1670. mw->menu.xft_font = getDefaultXftFont (mw);
  1671. }
  1672. }
  1673. }
  1674. if (fname != mw->menu.fontName) xfree (fname);
  1675. return mw->menu.xft_font != 0;
  1676. }
  1677. #endif
  1678. static void
  1679. XlwMenuInitialize (Widget request, Widget w, ArgList args, Cardinal *num_args)
  1680. {
  1681. /* Get the GCs and the widget size */
  1682. XlwMenuWidget mw = (XlwMenuWidget) w;
  1683. Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  1684. Display* display = XtDisplay (mw);
  1685. #if 0
  1686. widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
  1687. /* _XtCreate is freeing the object that was passed to us,
  1688. so make a copy that we will actually keep. */
  1689. memcpy (tem, mw->menu.contents, sizeof (widget_value));
  1690. mw->menu.contents = tem;
  1691. #endif
  1692. /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
  1693. mw->menu.cursor = mw->menu.cursor_shape;
  1694. mw->menu.gray_pixmap
  1695. = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
  1696. gray_bitmap_width, gray_bitmap_height,
  1697. (unsigned long)1, (unsigned long)0, 1);
  1698. #ifdef HAVE_XFT
  1699. if (openXftFont (mw))
  1700. ;
  1701. else
  1702. #endif
  1703. {
  1704. mw->menu.font = XLoadQueryFont (display, mw->menu.fontName);
  1705. if (!mw->menu.font)
  1706. {
  1707. mw->menu.font = XLoadQueryFont (display, "fixed");
  1708. if (!mw->menu.font)
  1709. {
  1710. fprintf (stderr, "Menu font fixed not found, can't continue.\n");
  1711. abort ();
  1712. }
  1713. }
  1714. }
  1715. #ifdef HAVE_X_I18N
  1716. if (mw->menu.fontSet)
  1717. mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
  1718. #endif
  1719. make_drawing_gcs (mw);
  1720. make_shadow_gcs (mw);
  1721. mw->menu.popped_up = False;
  1722. mw->menu.old_depth = 1;
  1723. mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
  1724. mw->menu.old_stack_length = 1;
  1725. mw->menu.old_stack [0] = mw->menu.contents;
  1726. mw->menu.new_depth = 0;
  1727. mw->menu.new_stack = 0;
  1728. mw->menu.new_stack_length = 0;
  1729. push_new_stack (mw, mw->menu.contents);
  1730. mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
  1731. mw->menu.windows_length = 1;
  1732. mw->menu.windows [0].x = 0;
  1733. mw->menu.windows [0].y = 0;
  1734. mw->menu.windows [0].width = 0;
  1735. mw->menu.windows [0].height = 0;
  1736. mw->menu.windows [0].max_rest_width = 0;
  1737. mw->menu.windows [0].pixmap = None;
  1738. #ifdef HAVE_XFT
  1739. mw->menu.windows [0].xft_draw = 0;
  1740. #endif
  1741. size_menu (mw, 0);
  1742. mw->core.width = mw->menu.windows [0].width;
  1743. mw->core.height = mw->menu.windows [0].height;
  1744. }
  1745. static void
  1746. XlwMenuClassInitialize (void)
  1747. {
  1748. }
  1749. static void
  1750. XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
  1751. {
  1752. XlwMenuWidget mw = (XlwMenuWidget)w;
  1753. XSetWindowAttributes xswa;
  1754. int mask;
  1755. (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
  1756. (w, valueMask, attributes);
  1757. xswa.save_under = True;
  1758. xswa.cursor = mw->menu.cursor_shape;
  1759. mask = CWSaveUnder | CWCursor;
  1760. /* I sometimes get random BadCursor errors while creating the first
  1761. frame on a display. I can not find their reason, but they are
  1762. annoying so for now let's ignore any errors here. -- lorentey */
  1763. #ifdef emacs
  1764. x_catch_errors (XtDisplay (w));
  1765. #endif
  1766. XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
  1767. #ifdef emacs
  1768. x_uncatch_errors ();
  1769. #endif
  1770. mw->menu.windows [0].w = w;
  1771. mw->menu.windows [0].window = XtWindow (w);
  1772. mw->menu.windows [0].x = w->core.x;
  1773. mw->menu.windows [0].y = w->core.y;
  1774. mw->menu.windows [0].width = w->core.width;
  1775. mw->menu.windows [0].height = w->core.height;
  1776. set_window_type (mw->menu.windows [0].w, mw);
  1777. create_pixmap_for_menu (&mw->menu.windows [0], mw);
  1778. #ifdef HAVE_XFT
  1779. if (mw->menu.xft_font)
  1780. {
  1781. XColor colors[3];
  1782. colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
  1783. colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
  1784. colors[2].pixel = mw->menu.xft_disabled_fg.pixel
  1785. = mw->menu.disabled_foreground;
  1786. XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 3);
  1787. mw->menu.xft_fg.color.alpha = 0xFFFF;
  1788. mw->menu.xft_fg.color.red = colors[0].red;
  1789. mw->menu.xft_fg.color.green = colors[0].green;
  1790. mw->menu.xft_fg.color.blue = colors[0].blue;
  1791. mw->menu.xft_bg.color.alpha = 0xFFFF;
  1792. mw->menu.xft_bg.color.red = colors[1].red;
  1793. mw->menu.xft_bg.color.green = colors[1].green;
  1794. mw->menu.xft_bg.color.blue = colors[1].blue;
  1795. mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
  1796. mw->menu.xft_disabled_fg.color.red = colors[2].red;
  1797. mw->menu.xft_disabled_fg.color.green = colors[2].green;
  1798. mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
  1799. }
  1800. #endif
  1801. }
  1802. /* Only the toplevel menubar/popup is a widget so it's the only one that
  1803. receives expose events through Xt. So we repaint all the other panes
  1804. when receiving an Expose event. */
  1805. static void
  1806. XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
  1807. {
  1808. XlwMenuWidget mw = (XlwMenuWidget)w;
  1809. /* If we have a depth beyond 1, it's because a submenu was displayed.
  1810. If the submenu has been destroyed, set the depth back to 1. */
  1811. if (submenu_destroyed)
  1812. {
  1813. mw->menu.old_depth = 1;
  1814. submenu_destroyed = 0;
  1815. }
  1816. display_menu (mw, 0, False, NULL, NULL, NULL);
  1817. }
  1818. /* Part of a hack to make the menu redisplay when a tooltip frame
  1819. over a menu item is unmapped. */
  1820. void
  1821. xlwmenu_redisplay (Widget w)
  1822. {
  1823. XlwMenuRedisplay (w, NULL, None);
  1824. }
  1825. static void
  1826. XlwMenuDestroy (Widget w)
  1827. {
  1828. int i;
  1829. XlwMenuWidget mw = (XlwMenuWidget) w;
  1830. if (pointer_grabbed)
  1831. ungrab_all ((Widget)w, CurrentTime);
  1832. pointer_grabbed = 0;
  1833. submenu_destroyed = 1;
  1834. release_drawing_gcs (mw);
  1835. release_shadow_gcs (mw);
  1836. /* this doesn't come from the resource db but is created explicitly
  1837. so we must free it ourselves. */
  1838. XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
  1839. mw->menu.gray_pixmap = (Pixmap) -1;
  1840. #if 0
  1841. /* Do free mw->menu.contents because nowadays we copy it
  1842. during initialization. */
  1843. XtFree (mw->menu.contents);
  1844. #endif
  1845. /* Don't free mw->menu.contents because that comes from our creator.
  1846. The `*_stack' elements are just pointers into `contents' so leave
  1847. that alone too. But free the stacks themselves. */
  1848. if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
  1849. if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
  1850. /* Remember, you can't free anything that came from the resource
  1851. database. This includes:
  1852. mw->menu.cursor
  1853. mw->menu.top_shadow_pixmap
  1854. mw->menu.bottom_shadow_pixmap
  1855. mw->menu.font
  1856. Also the color cells of top_shadow_color, bottom_shadow_color,
  1857. foreground, and button_foreground will never be freed until this
  1858. client exits. Nice, eh?
  1859. */
  1860. #ifdef HAVE_XFT
  1861. if (mw->menu.windows [0].xft_draw)
  1862. XftDrawDestroy (mw->menu.windows [0].xft_draw);
  1863. if (mw->menu.xft_font)
  1864. XftFontClose (XtDisplay (mw), mw->menu.xft_font);
  1865. #endif
  1866. if (mw->menu.windows [0].pixmap != None)
  1867. XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
  1868. /* start from 1 because the one in slot 0 is w->core.window */
  1869. for (i = 1; i < mw->menu.windows_length; i++)
  1870. {
  1871. if (mw->menu.windows [i].pixmap != None)
  1872. XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
  1873. #ifdef HAVE_XFT
  1874. if (mw->menu.windows [i].xft_draw)
  1875. XftDrawDestroy (mw->menu.windows [i].xft_draw);
  1876. #endif
  1877. }
  1878. if (mw->menu.windows)
  1879. XtFree ((char *) mw->menu.windows);
  1880. }
  1881. #ifdef HAVE_XFT
  1882. static int
  1883. fontname_changed (XlwMenuWidget newmw,
  1884. XlwMenuWidget oldmw)
  1885. {
  1886. /* This will force a new XftFont even if the same string is set.
  1887. This is good, as rendering parameters may have changed and
  1888. we just want to do a redisplay. */
  1889. return newmw->menu.fontName != oldmw->menu.fontName;
  1890. }
  1891. #endif
  1892. static Boolean
  1893. XlwMenuSetValues (Widget current, Widget request, Widget new,
  1894. ArgList args, Cardinal *num_args)
  1895. {
  1896. XlwMenuWidget oldmw = (XlwMenuWidget)current;
  1897. XlwMenuWidget newmw = (XlwMenuWidget)new;
  1898. Boolean do_redisplay = False;
  1899. if (newmw->menu.contents
  1900. && newmw->menu.contents->contents
  1901. && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
  1902. do_redisplay = True;
  1903. /* Do redisplay if the contents are entirely eliminated. */
  1904. if (newmw->menu.contents
  1905. && newmw->menu.contents->contents == 0
  1906. && newmw->menu.contents->change >= VISIBLE_CHANGE)
  1907. do_redisplay = True;
  1908. if (newmw->core.background_pixel != oldmw->core.background_pixel
  1909. || newmw->menu.foreground != oldmw->menu.foreground
  1910. #ifdef HAVE_XFT
  1911. || fontname_changed (newmw, oldmw)
  1912. #endif
  1913. #ifdef HAVE_X_I18N
  1914. || newmw->menu.fontSet != oldmw->menu.fontSet
  1915. || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
  1916. #else
  1917. || newmw->menu.font != oldmw->menu.font
  1918. #endif
  1919. )
  1920. {
  1921. int i;
  1922. release_drawing_gcs (newmw);
  1923. make_drawing_gcs (newmw);
  1924. release_shadow_gcs (newmw);
  1925. /* Cause the shadow colors to be recalculated. */
  1926. newmw->menu.top_shadow_color = -1;
  1927. newmw->menu.bottom_shadow_color = -1;
  1928. make_shadow_gcs (newmw);
  1929. do_redisplay = True;
  1930. if (XtIsRealized (current))
  1931. /* If the menu is currently displayed, change the display. */
  1932. for (i = 0; i < oldmw->menu.windows_length; i++)
  1933. {
  1934. XSetWindowBackground (XtDisplay (oldmw),
  1935. oldmw->menu.windows [i].window,
  1936. newmw->core.background_pixel);
  1937. /* clear windows and generate expose events */
  1938. XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
  1939. 0, 0, 0, 0, True);
  1940. }
  1941. }
  1942. #ifdef HAVE_XFT
  1943. if (fontname_changed (newmw, oldmw))
  1944. {
  1945. int i;
  1946. int screen = XScreenNumberOfScreen (newmw->core.screen);
  1947. if (newmw->menu.xft_font)
  1948. XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
  1949. openXftFont (newmw);
  1950. for (i = 0; i < newmw->menu.windows_length; i++)
  1951. {
  1952. if (newmw->menu.windows [i].xft_draw)
  1953. XftDrawDestroy (newmw->menu.windows [i].xft_draw);
  1954. newmw->menu.windows [i].xft_draw = 0;
  1955. }
  1956. if (newmw->menu.xft_font)
  1957. for (i = 0; i < newmw->menu.windows_length; i++)
  1958. newmw->menu.windows [i].xft_draw
  1959. = XftDrawCreate (XtDisplay (newmw),
  1960. newmw->menu.windows [i].window,
  1961. DefaultVisual (XtDisplay (newmw), screen),
  1962. newmw->core.colormap);
  1963. }
  1964. #endif
  1965. #ifdef HAVE_X_I18N
  1966. if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
  1967. {
  1968. do_redisplay = True;
  1969. newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
  1970. }
  1971. #endif
  1972. return do_redisplay;
  1973. }
  1974. static void
  1975. XlwMenuResize (Widget w)
  1976. {
  1977. XlwMenuWidget mw = (XlwMenuWidget)w;
  1978. if (mw->menu.popped_up)
  1979. {
  1980. /* Don't allow the popup menu to resize itself. */
  1981. mw->core.width = mw->menu.windows [0].width;
  1982. mw->core.height = mw->menu.windows [0].height;
  1983. mw->core.parent->core.width = mw->core.width;
  1984. mw->core.parent->core.height = mw->core.height;
  1985. }
  1986. else
  1987. {
  1988. mw->menu.windows [0].width = mw->core.width;
  1989. mw->menu.windows [0].height = mw->core.height;
  1990. create_pixmap_for_menu (&mw->menu.windows [0], mw);
  1991. }
  1992. }
  1993. /* Action procedures */
  1994. static void
  1995. handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
  1996. {
  1997. widget_value* val;
  1998. int level;
  1999. if (!map_event_to_widget_value (mw, ev, &val, &level))
  2000. pop_new_stack_if_no_contents (mw);
  2001. else
  2002. set_new_state (mw, val, level);
  2003. remap_menubar (mw);
  2004. /* Sync with the display. Makes it feel better on X terms. */
  2005. XSync (XtDisplay (mw), False);
  2006. }
  2007. static void
  2008. handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
  2009. {
  2010. int x = ev->x_root;
  2011. int y = ev->y_root;
  2012. int state = ev->state;
  2013. XMotionEvent oldev = *ev;
  2014. /* allow motion events to be generated again */
  2015. if (ev->is_hint
  2016. && XQueryPointer (XtDisplay (mw), ev->window,
  2017. &ev->root, &ev->subwindow,
  2018. &ev->x_root, &ev->y_root,
  2019. &ev->x, &ev->y,
  2020. &ev->state)
  2021. && ev->state == state
  2022. && (ev->x_root != x || ev->y_root != y))
  2023. handle_single_motion_event (mw, ev);
  2024. else
  2025. handle_single_motion_event (mw, &oldev);
  2026. }
  2027. static void
  2028. Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2029. {
  2030. XlwMenuWidget mw = (XlwMenuWidget)w;
  2031. if (!mw->menu.popped_up)
  2032. {
  2033. menu_post_event = *ev;
  2034. /* If event is set to CurrentTime, get the last known time stamp.
  2035. This is for calculating if (popup) menus should stay up after
  2036. a fast click. */
  2037. if (menu_post_event.xbutton.time == CurrentTime)
  2038. menu_post_event.xbutton.time
  2039. = XtLastTimestampProcessed (XtDisplay (w));
  2040. pop_up_menu (mw, (XButtonPressedEvent*) ev);
  2041. }
  2042. else
  2043. {
  2044. /* If we push a button while the menu is posted semipermanently,
  2045. releasing the button should always pop the menu down. */
  2046. next_release_must_exit = 1;
  2047. /* notes the absolute position of the menubar window */
  2048. mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
  2049. mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
  2050. /* handles the down like a move, slots are compatible */
  2051. ev->xmotion.is_hint = 0;
  2052. handle_motion_event (mw, &ev->xmotion);
  2053. }
  2054. }
  2055. static void
  2056. Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2057. {
  2058. XlwMenuWidget mw = (XlwMenuWidget)w;
  2059. if (mw->menu.popped_up)
  2060. handle_motion_event (mw, &ev->xmotion);
  2061. }
  2062. /* Do nothing.
  2063. This is how we handle presses and releases of modifier keys. */
  2064. static void
  2065. Nothing (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2066. {
  2067. }
  2068. static widget_value *
  2069. find_first_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
  2070. {
  2071. widget_value *current = item;
  2072. enum menu_separator separator;
  2073. while (lw_separator_p (current->name, &separator, 0) || !current->enabled
  2074. || (skip_titles && !current->call_data && !current->contents))
  2075. if (current->next)
  2076. current=current->next;
  2077. else
  2078. return NULL;
  2079. return current;
  2080. }
  2081. static widget_value *
  2082. find_next_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
  2083. {
  2084. widget_value *current = item;
  2085. enum menu_separator separator;
  2086. while (current->next && (current=current->next) &&
  2087. (lw_separator_p (current->name, &separator, 0) || !current->enabled
  2088. || (skip_titles && !current->call_data && !current->contents)))
  2089. ;
  2090. if (current == item)
  2091. {
  2092. if (mw->menu.old_depth < 2)
  2093. return current;
  2094. current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
  2095. while (lw_separator_p (current->name, &separator, 0)
  2096. || !current->enabled
  2097. || (skip_titles && !current->call_data
  2098. && !current->contents))
  2099. {
  2100. if (current->next)
  2101. current=current->next;
  2102. if (current == item)
  2103. break;
  2104. }
  2105. }
  2106. return current;
  2107. }
  2108. static widget_value *
  2109. find_prev_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
  2110. {
  2111. widget_value *current = item;
  2112. widget_value *prev = item;
  2113. while ((current=find_next_selectable (mw, current, skip_titles))
  2114. != item)
  2115. {
  2116. if (prev == current)
  2117. break;
  2118. prev=current;
  2119. }
  2120. return prev;
  2121. }
  2122. static void
  2123. Down (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2124. {
  2125. XlwMenuWidget mw = (XlwMenuWidget) w;
  2126. widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2127. int popup_menu_p = mw->menu.top_depth == 1;
  2128. /* Inside top-level menu-bar? */
  2129. if (mw->menu.old_depth == mw->menu.top_depth)
  2130. /* When <down> in the menu-bar is pressed, display the corresponding
  2131. sub-menu and select the first selectable menu item there.
  2132. If this is a popup menu, skip title item of the popup. */
  2133. set_new_state (mw,
  2134. find_first_selectable (mw,
  2135. selected_item->contents,
  2136. popup_menu_p),
  2137. mw->menu.old_depth);
  2138. else
  2139. /* Highlight next possible (enabled and not separator) menu item. */
  2140. set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
  2141. mw->menu.old_depth - 1);
  2142. remap_menubar (mw);
  2143. }
  2144. static void
  2145. Up (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2146. {
  2147. XlwMenuWidget mw = (XlwMenuWidget) w;
  2148. widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2149. int popup_menu_p = mw->menu.top_depth == 1;
  2150. /* Inside top-level menu-bar? */
  2151. if (mw->menu.old_depth == mw->menu.top_depth)
  2152. {
  2153. /* FIXME: this is tricky. <up> in the menu-bar should select the
  2154. last selectable item in the list. So we select the first
  2155. selectable one and find the previous selectable item. Is there
  2156. a better way? */
  2157. /* If this is a popup menu, skip title item of the popup. */
  2158. set_new_state (mw,
  2159. find_first_selectable (mw,
  2160. selected_item->contents,
  2161. popup_menu_p),
  2162. mw->menu.old_depth);
  2163. remap_menubar (mw);
  2164. selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2165. set_new_state (mw,
  2166. find_prev_selectable (mw,
  2167. selected_item,
  2168. popup_menu_p),
  2169. mw->menu.old_depth - 1);
  2170. }
  2171. else
  2172. /* Highlight previous (enabled and not separator) menu item. */
  2173. set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
  2174. mw->menu.old_depth - 1);
  2175. remap_menubar (mw);
  2176. }
  2177. void
  2178. Left (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2179. {
  2180. XlwMenuWidget mw = (XlwMenuWidget) w;
  2181. widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2182. /* Inside top-level menu-bar? */
  2183. if (mw->menu.old_depth == mw->menu.top_depth)
  2184. /* When <left> in the menu-bar is pressed, display the previous item on
  2185. the menu-bar. If the current item is the first one, highlight the
  2186. last item in the menubar (probably Help). */
  2187. set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
  2188. mw->menu.old_depth - 1);
  2189. else if (mw->menu.old_depth == 1
  2190. && selected_item->contents) /* Is this menu item expandable? */
  2191. {
  2192. set_new_state (mw, selected_item->contents, mw->menu.old_depth);
  2193. remap_menubar (mw);
  2194. selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2195. if (!selected_item->enabled && find_first_selectable (mw,
  2196. selected_item,
  2197. 0))
  2198. set_new_state (mw, find_first_selectable (mw, selected_item, 0),
  2199. mw->menu.old_depth - 1);
  2200. }
  2201. else
  2202. {
  2203. pop_new_stack_if_no_contents (mw);
  2204. set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
  2205. mw->menu.old_depth - 2);
  2206. }
  2207. remap_menubar (mw);
  2208. }
  2209. void
  2210. Right (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2211. {
  2212. XlwMenuWidget mw = (XlwMenuWidget) w;
  2213. widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2214. /* Inside top-level menu-bar? */
  2215. if (mw->menu.old_depth == mw->menu.top_depth)
  2216. /* When <right> in the menu-bar is pressed, display the next item on
  2217. the menu-bar. If the current item is the last one, highlight the
  2218. first item (probably File). */
  2219. set_new_state (mw, find_next_selectable (mw, selected_item, 0),
  2220. mw->menu.old_depth - 1);
  2221. else if (selected_item->contents) /* Is this menu item expandable? */
  2222. {
  2223. set_new_state (mw, selected_item->contents, mw->menu.old_depth);
  2224. remap_menubar (mw);
  2225. selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2226. if (!selected_item->enabled && find_first_selectable (mw,
  2227. selected_item,
  2228. 0))
  2229. set_new_state (mw, find_first_selectable (mw, selected_item, 0),
  2230. mw->menu.old_depth - 1);
  2231. }
  2232. else
  2233. {
  2234. pop_new_stack_if_no_contents (mw);
  2235. set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
  2236. mw->menu.old_depth - 2);
  2237. }
  2238. remap_menubar (mw);
  2239. }
  2240. /* Handle key press and release events while menu is popped up.
  2241. Our action is to get rid of the menu. */
  2242. static void
  2243. Key (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2244. {
  2245. XlwMenuWidget mw = (XlwMenuWidget)w;
  2246. /* Pop down everything. */
  2247. mw->menu.new_depth = 1;
  2248. remap_menubar (mw);
  2249. if (mw->menu.popped_up)
  2250. {
  2251. mw->menu.popped_up = False;
  2252. ungrab_all ((Widget)mw, ev->xmotion.time);
  2253. if (XtIsShell (XtParent ((Widget) mw)))
  2254. XtPopdown (XtParent ((Widget) mw));
  2255. else
  2256. {
  2257. XtRemoveGrab ((Widget) mw);
  2258. display_menu (mw, 0, False, NULL, NULL, NULL);
  2259. }
  2260. }
  2261. /* callback */
  2262. XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
  2263. }
  2264. static void
  2265. Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2266. {
  2267. XlwMenuWidget mw = (XlwMenuWidget)w;
  2268. widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2269. /* If user releases the button quickly, without selecting anything,
  2270. after the initial down-click that brought the menu up,
  2271. do nothing. */
  2272. if ((selected_item == 0
  2273. || ((widget_value *) selected_item)->call_data == 0)
  2274. && !next_release_must_exit
  2275. && (ev->xbutton.time - menu_post_event.xbutton.time
  2276. < XtGetMultiClickTime (XtDisplay (w))))
  2277. return;
  2278. /* pop down everything. */
  2279. mw->menu.new_depth = 1;
  2280. remap_menubar (mw);
  2281. if (mw->menu.popped_up)
  2282. {
  2283. mw->menu.popped_up = False;
  2284. ungrab_all ((Widget)mw, ev->xmotion.time);
  2285. if (XtIsShell (XtParent ((Widget) mw)))
  2286. XtPopdown (XtParent ((Widget) mw));
  2287. else
  2288. {
  2289. XtRemoveGrab ((Widget) mw);
  2290. display_menu (mw, 0, False, NULL, NULL, NULL);
  2291. }
  2292. }
  2293. /* callback */
  2294. XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
  2295. }
  2296. /* Special code to pop-up a menu */
  2297. static void
  2298. pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
  2299. {
  2300. int x = event->x_root;
  2301. int y = event->y_root;
  2302. int w;
  2303. int h;
  2304. int borderwidth = mw->menu.shadow_thickness;
  2305. Screen* screen = XtScreen (mw);
  2306. Display *display = XtDisplay (mw);
  2307. next_release_must_exit = 0;
  2308. mw->menu.inside_entry = NULL;
  2309. XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
  2310. if (XtIsShell (XtParent ((Widget)mw)))
  2311. size_menu (mw, 0);
  2312. w = mw->menu.windows [0].width;
  2313. h = mw->menu.windows [0].height;
  2314. x -= borderwidth;
  2315. y -= borderwidth;
  2316. if (x < borderwidth)
  2317. x = borderwidth;
  2318. if (x + w + 2 * borderwidth > WidthOfScreen (screen))
  2319. x = WidthOfScreen (screen) - w - 2 * borderwidth;
  2320. if (y < borderwidth)
  2321. y = borderwidth;
  2322. if (y + h + 2 * borderwidth> HeightOfScreen (screen))
  2323. y = HeightOfScreen (screen) - h - 2 * borderwidth;
  2324. mw->menu.popped_up = True;
  2325. if (XtIsShell (XtParent ((Widget)mw)))
  2326. {
  2327. XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
  2328. XtParent ((Widget)mw)->core.border_width);
  2329. XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
  2330. display_menu (mw, 0, False, NULL, NULL, NULL);
  2331. mw->menu.windows [0].x = x + borderwidth;
  2332. mw->menu.windows [0].y = y + borderwidth;
  2333. mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */
  2334. }
  2335. else
  2336. {
  2337. XEvent *ev = (XEvent *) event;
  2338. XtAddGrab ((Widget) mw, True, True);
  2339. /* notes the absolute position of the menubar window */
  2340. mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
  2341. mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
  2342. mw->menu.top_depth = 2;
  2343. }
  2344. #ifdef emacs
  2345. x_catch_errors (display);
  2346. #endif
  2347. if (XtGrabPointer ((Widget)mw, False,
  2348. (PointerMotionMask
  2349. | PointerMotionHintMask
  2350. | ButtonReleaseMask
  2351. | ButtonPressMask),
  2352. GrabModeAsync, GrabModeAsync, None,
  2353. mw->menu.cursor_shape,
  2354. event->time) == Success)
  2355. {
  2356. if (! GRAB_KEYBOARD
  2357. || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
  2358. GrabModeAsync, event->time) == Success)
  2359. {
  2360. XtSetKeyboardFocus((Widget)mw, None);
  2361. pointer_grabbed = 1;
  2362. }
  2363. else
  2364. XtUngrabPointer ((Widget)mw, event->time);
  2365. }
  2366. #ifdef emacs
  2367. if (x_had_errors_p (display))
  2368. {
  2369. pointer_grabbed = 0;
  2370. XtUngrabPointer ((Widget)mw, event->time);
  2371. }
  2372. x_uncatch_errors ();
  2373. #endif
  2374. ((XMotionEvent*)event)->is_hint = 0;
  2375. handle_motion_event (mw, (XMotionEvent*)event);
  2376. }