JWM Source Documentation
menu.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 #include "menu.h"
12 #include "font.h"
13 #include "client.h"
14 #include "icon.h"
15 #include "cursor.h"
16 #include "key.h"
17 #include "button.h"
18 #include "event.h"
19 #include "root.h"
20 #include "settings.h"
21 #include "desktop.h"
22 #include "parse.h"
23 #include "winmenu.h"
24 #include "screen.h"
25 #include "hint.h"
26 #include "misc.h"
27 #include "popup.h"
28 
29 #define BASE_ICON_OFFSET 3
30 #define MENU_BORDER_SIZE 1
31 
32 typedef unsigned char MenuSelectionType;
33 #define MENU_NOSELECTION 0
34 #define MENU_LEAVE 1
35 #define MENU_SUBSELECT 2
36 
37 static char ShowSubmenu(Menu *menu, Menu *parent,
38  RunMenuCommandType runner,
39  int x, int y, char keyboard);
40 
41 static void PatchMenu(Menu *menu);
42 static void UnpatchMenu(Menu *menu);
43 static void MapMenu(Menu *menu, int x, int y, char keyboard);
44 static void HideMenu(Menu *menu);
45 static void DrawMenu(Menu *menu);
46 
47 static char MenuLoop(Menu *menu, RunMenuCommandType runner);
48 static void MenuCallback(const TimeType *now, int x, int y,
49  Window w, void *data);
51  RunMenuCommandType runner,
52  XEvent *event);
53 
54 static void UpdateMenu(Menu *menu);
55 static void DrawMenuItem(Menu *menu, MenuItem *item, int index);
56 static MenuItem *GetMenuItem(Menu *menu, int index);
57 static int GetNextMenuIndex(Menu *menu);
58 static int GetPreviousMenuIndex(Menu *menu);
59 static int GetMenuIndex(Menu *menu, int index);
60 static void SetPosition(Menu *tp, int index);
61 static char IsMenuValid(const Menu *menu);
62 
63 int menuShown = 0;
64 
67 {
68  Menu *menu = Allocate(sizeof(Menu));
69  menu->itemHeight = 0;
70  menu->items = NULL;
71  menu->label = NULL;
72  menu->dynamic = NULL;
73  return menu;
74 }
75 
78 {
79  MenuItem *item = Allocate(sizeof(MenuItem));
80  memset(item, 0, sizeof(MenuItem));
81  item->type = type;
82  return item;
83 }
84 
86 void InitializeMenu(Menu *menu)
87 {
88 
89  MenuItem *np;
90  int index, temp;
91  int userHeight;
92  int hasSubmenu;
93  char hasIcon;
94 
95  menu->textOffset = 0;
96  menu->itemCount = 0;
97 
98  /* Compute the max size needed */
99  hasIcon = 0;
100  userHeight = menu->itemHeight;
101  if(userHeight < 0) {
102  userHeight = 0;
103  }
105  for(np = menu->items; np; np = np->next) {
106  if(np->iconName) {
107  np->icon = LoadNamedIcon(np->iconName, 1, 1);
108  if(np->icon) {
109  hasIcon = 1;
110  }
111  } else if(np->icon) {
112  hasIcon = 1;
113  }
114  menu->itemCount += 1;
115  }
116  menu->itemHeight += BASE_ICON_OFFSET * 2;
117 
118  if(userHeight) {
119  menu->itemHeight = userHeight + BASE_ICON_OFFSET * 2;
120  }
121  if(hasIcon) {
122  menu->textOffset = menu->itemHeight + BASE_ICON_OFFSET * 2;
123  }
124 
125  menu->width = 5;
126  menu->parent = NULL;
127  menu->parentOffset = 0;
128 
129  /* Make sure the menu is wide enough for a label if it is labeled. */
130  if(menu->label) {
131  temp = GetStringWidth(FONT_MENU, menu->label);
132  if(temp > menu->width) {
133  menu->width = temp;
134  }
135  }
136 
137  menu->height = MENU_BORDER_SIZE;
138  if(menu->label) {
139  menu->height += menu->itemHeight;
140  }
141 
142  /* Nothing else to do if there is nothing in the menu. */
143  if(JUNLIKELY(menu->itemCount == 0)) {
144  return;
145  }
146 
147  menu->offsets = Allocate(sizeof(int) * menu->itemCount);
148 
149  hasSubmenu = 0;
150  index = 0;
151  for(np = menu->items; np; np = np->next) {
152  menu->offsets[index++] = menu->height;
153  if(np->type == MENU_ITEM_SEPARATOR) {
154  menu->height += 6;
155  } else {
156  menu->height += menu->itemHeight;
157  }
158  if(np->name) {
159  temp = GetStringWidth(FONT_MENU, np->name);
160  if(temp > menu->width) {
161  menu->width = temp;
162  }
163  }
164  if(hasIcon && !np->icon) {
165  np->icon = &emptyIcon;
166  }
167  if(np->submenu) {
168  hasSubmenu = (menu->itemHeight + 3) / 4;
169  InitializeMenu(np->submenu);
170  }
171  }
172  menu->width += hasSubmenu + menu->textOffset;
173  menu->width += 7 + 2 * MENU_BORDER_SIZE;
174  menu->height += MENU_BORDER_SIZE;
175  menu->mousex = -1;
176  menu->mousey = -1;
177 
178 }
179 
181 char ShowMenu(Menu *menu, RunMenuCommandType runner,
182  int x, int y, char keyboard)
183 {
184  /* Don't show the menu if there isn't anything to show. */
185  if(JUNLIKELY(!IsMenuValid(menu))) {
186  return 0;
187  }
188  if(JUNLIKELY(shouldExit)) {
189  return 0;
190  }
191 
192  if(x < 0 && y < 0) {
193  Window w;
194  GetMousePosition(&x, &y, &w);
195  x -= menu->itemHeight / 2;
196  y -= menu->itemHeight / 2 + menu->offsets[0];
197  }
198 
199  if(!GrabMouse(rootWindow)) {
200  return 0;
201  }
202  if(JXGrabKeyboard(display, rootWindow, False, GrabModeAsync,
203  GrabModeAsync, CurrentTime) != GrabSuccess) {
204  JXUngrabPointer(display, CurrentTime);
205  return 0;
206  }
207 
208  RegisterCallback(100, MenuCallback, menu);
209  ShowSubmenu(menu, NULL, runner, x, y, keyboard);
211  UnpatchMenu(menu);
212 
213  JXUngrabKeyboard(display, CurrentTime);
214  JXUngrabPointer(display, CurrentTime);
215  RefocusClient();
216 
217  if(shouldReload) {
218  ReloadMenu();
219  }
220 
221  return 1;
222 }
223 
225 void HideMenu(Menu *menu)
226 {
227  Menu *mp;
228  for(mp = menu; mp; mp = mp->parent) {
230  }
231 }
232 
234 void DestroyMenu(Menu *menu)
235 {
236  MenuItem *np;
237  if(menu) {
238  while(menu->items) {
239  np = menu->items->next;
240  if(menu->items->name) {
241  Release(menu->items->name);
242  }
243  if(menu->items->tooltip) {
244  Release(menu->items->tooltip);
245  }
246  switch(menu->items->action.type & MA_ACTION_MASK) {
247  case MA_EXECUTE:
248  case MA_EXIT:
249  case MA_DYNAMIC:
250  if(menu->items->action.str) {
251  Release(menu->items->action.str);
252  }
253  break;
254  default:
255  break;
256  }
257  if(menu->items->iconName) {
258  Release(menu->items->iconName);
259  }
260  if(menu->items->submenu) {
261  DestroyMenu(menu->items->submenu);
262  }
263  Release(menu->items);
264  menu->items = np;
265  }
266  if(menu->label) {
267  Release(menu->label);
268  }
269  if(menu->dynamic) {
270  Release(menu->dynamic);
271  }
272  if(menu->offsets) {
273  Release(menu->offsets);
274  }
275  Release(menu);
276  }
277 }
278 
280 char ShowSubmenu(Menu *menu, Menu *parent,
281  RunMenuCommandType runner,
282  int x, int y, char keyboard)
283 {
284 
285  char status;
286 
287  PatchMenu(menu);
288  menu->parent = parent;
289  MapMenu(menu, x, y, keyboard);
290 
291  menuShown += 1;
292  status = MenuLoop(menu, runner);
293  menuShown -= 1;
294 
296  JXFreePixmap(display, menu->pixmap);
297 
298  return status;
299 
300 }
301 
303 void PatchMenu(Menu *menu)
304 {
305  MenuItem *item;
306  for(item = menu->items; item; item = item->next) {
307  Menu *submenu = NULL;
308  switch(item->action.type & MA_ACTION_MASK) {
309  case MA_DESKTOP_MENU:
310  submenu = CreateDesktopMenu(1 << currentDesktop,
311  item->action.context);
312  break;
313  case MA_SENDTO_MENU:
314  submenu = CreateSendtoMenu(
315  item->action.type & ~MA_ACTION_MASK,
316  item->action.context);
317  break;
318  case MA_WINDOW_MENU:
319  submenu = CreateWindowMenu(item->action.context);
320  break;
321  case MA_DYNAMIC:
322  if(!item->submenu) {
323  submenu = ParseDynamicMenu(item->action.str);
324  submenu->itemHeight = item->action.value;
325  }
326  break;
327  default:
328  break;
329  }
330  if(submenu) {
331  InitializeMenu(submenu);
332  item->submenu = submenu;
333  }
334  }
335 }
336 
338 void UnpatchMenu(Menu *menu)
339 {
340  MenuItem *item;
341  for(item = menu->items; item; item = item->next) {
342  if(item->submenu) {
343  UnpatchMenu(item->submenu);
344  switch(item->action.type & MA_ACTION_MASK) {
345  case MA_DESKTOP_MENU:
346  case MA_SENDTO_MENU:
347  case MA_WINDOW_MENU:
348  case MA_DYNAMIC:
349  DestroyMenu(item->submenu);
350  item->submenu = NULL;
351  break;
352  default:
353  break;
354  }
355  }
356  }
357 }
358 
362 char MenuLoop(Menu *menu, RunMenuCommandType runner)
363 {
364 
365  XEvent event;
366  MenuItem *ip;
367  Window pressw;
368  int pressx, pressy;
369  char hadMotion;
370 
371  hadMotion = 0;
372 
373  GetMousePosition(&pressx, &pressy, &pressw);
374 
375  for(;;) {
376 
377  WaitForEvent(&event);
378 
379  switch(event.type) {
380  case Expose:
381  if(event.xexpose.count == 0) {
382  Menu *mp = menu;
383  while(mp) {
384  if(mp->window == event.xexpose.window) {
385  DrawMenu(mp);
386  break;
387  }
388  mp = mp->parent;
389  }
390  }
391  break;
392 
393  case ButtonPress:
394 
395  pressx = -100;
396  pressy = -100;
397 
398  case KeyPress:
399  case MotionNotify:
400  hadMotion = 1;
401  switch(UpdateMotion(menu, runner, &event)) {
402  case MENU_NOSELECTION: /* no selection */
403  break;
404  case MENU_LEAVE: /* mouse left the menu */
405  JXPutBackEvent(display, &event);
406  return 0;
407  case MENU_SUBSELECT: /* selection made */
408  return 1;
409  }
410  break;
411 
412  case ButtonRelease:
413 
414  if(event.xbutton.button == Button4) {
415  break;
416  }
417  if(event.xbutton.button == Button5) {
418  break;
419  }
420  if(!hadMotion) {
421  break;
422  }
423  if(abs(event.xbutton.x_root - pressx) < settings.doubleClickDelta) {
424  if(abs(event.xbutton.y_root - pressy) < settings.doubleClickDelta) {
425  break;
426  }
427  }
428 
429  if(menu->parent && menu->currentIndex < 0) {
430  ip = GetMenuItem(menu->parent, menu->parent->currentIndex);
431  } else {
432  ip = GetMenuItem(menu, menu->currentIndex);
433  }
434  if(ip != NULL) {
435  if(ip->type == MENU_ITEM_NORMAL) {
436  HideMenu(menu);
437  (runner)(&ip->action, event.xbutton.button);
438  } else if(ip->type == MENU_ITEM_SUBMENU) {
439  const Menu *parent = menu->parent;
440  if(event.xbutton.x >= menu->x &&
441  event.xbutton.x < menu->x + menu->width &&
442  event.xbutton.y >= menu->y &&
443  event.xbutton.y < menu->y + menu->height) {
444  break;
445  } else if(parent &&
446  event.xbutton.x >= parent->x &&
447  event.xbutton.x < parent->x + parent->width &&
448  event.xbutton.y >= parent->y &&
449  event.xbutton.y < parent->y + parent->height) {
450  break;
451  }
452  }
453  }
454  return 1;
455  default:
456  break;
457  }
458 
459  }
460 }
461 
463 void MenuCallback(const TimeType *now, int x, int y, Window w, void *data)
464 {
465  Menu *menu = data;
466  MenuItem *item;
467  int i;
468 
469  /* Check if the mouse moved (and reset if it did). */
470  if( abs(menu->mousex - x) > settings.doubleClickDelta
471  || abs(menu->mousey - y) > settings.doubleClickDelta) {
472  menu->mousex = x;
473  menu->mousey = y;
474  menu->lastTime = *now;
475  return;
476  }
477 
478  /* See if enough time has passed. */
479  const unsigned long diff = GetTimeDifference(now, &menu->lastTime);
480  if(diff < settings.popupDelay) {
481  return;
482  }
483 
484  /* Locate the active menu item. */
485  while(menu) {
486  if(x > menu->x && x < menu->x + menu->width) {
487  if(y > menu->y && y < menu->y + menu->height) {
488  break;
489  }
490  }
491  if(menu->currentIndex < 0) {
492  return;
493  }
494  item = menu->items;
495  for(i = 0; i < menu->currentIndex; i++) {
496  item = item->next;
497  }
498  if(item->type != MENU_ITEM_SUBMENU) {
499  return;
500  }
501  menu = item->submenu;
502  }
503  if(menu->currentIndex < 0) {
504  return;
505  }
506  item = menu->items;
507  for(i = 0; i < menu->currentIndex; i++) {
508  item = item->next;
509  }
510  if(item->tooltip) {
511  ShowPopup(x, y, item->tooltip, POPUP_MENU);
512  }
513 
514 }
515 
517 void MapMenu(Menu *menu, int x, int y, char keyboard)
518 {
519  XSetWindowAttributes attr;
520  unsigned long attrMask;
521  int temp;
522 
523  if(menu->parent) {
524  menu->screen = menu->parent->screen;
525  } else {
526  menu->screen = GetCurrentScreen(x + menu->width / 2,
527  y + menu->height / 2);
528  }
529  if(x + menu->width > menu->screen->x + menu->screen->width) {
530  if(menu->parent) {
531  x = menu->parent->x - menu->width;
532  } else {
533  x = menu->screen->x + menu->screen->width - menu->width;
534  }
535  }
536  temp = y;
537  if(y + menu->height > menu->screen->y + menu->screen->height) {
538  y = menu->screen->y + menu->screen->height - menu->height;
539  }
540  if(y < 0) {
541  y = 0;
542  }
543 
544  menu->x = x;
545  menu->y = y;
546  menu->parentOffset = temp - y;
547 
548  attrMask = 0;
549 
550  attrMask |= CWEventMask;
551  attr.event_mask = ExposureMask;
552 
553  attrMask |= CWBackPixel;
554  attr.background_pixel = colors[COLOR_MENU_BG];
555 
556  attrMask |= CWSaveUnder;
557  attr.save_under = True;
558 
559  menu->window = JXCreateWindow(display, rootWindow, x, y,
560  menu->width, menu->height, 0,
561  CopyFromParent, InputOutput,
562  CopyFromParent, attrMask, &attr);
565  menu->pixmap = JXCreatePixmap(display, menu->window,
566  menu->width, menu->height, rootDepth);
567 
568  if(settings.menuOpacity < UINT_MAX) {
571  }
572 
573  JXMapRaised(display, menu->window);
574 
575  if(keyboard && menu->itemCount != 0) {
576  const int y = menu->offsets[0] + menu->itemHeight / 2;
577  menu->lastIndex = 0;
578  menu->currentIndex = 0;
579  MoveMouse(menu->window, menu->itemHeight / 2, y);
580  } else {
581  menu->lastIndex = -1;
582  menu->currentIndex = -1;
583  }
584 
585 }
586 
588 void DrawMenu(Menu *menu)
589 {
590 
591  MenuItem *np;
592  int x;
593 
595  JXFillRectangle(display, menu->pixmap, rootGC, 0, 0,
596  menu->width, menu->height);
597 
601  0, 0, menu->width, 0);
603  0, 0, 0, menu->height);
604 
607  0, menu->height - 1, menu->width, menu->height - 1);
609  menu->width - 1, 0, menu->width - 1, menu->height);
610  } else {
613  0, 0, menu->width - 1, menu->height - 1);
614  }
615 
616  if(menu->label) {
617  DrawMenuItem(menu, NULL, -1);
618  }
619 
620  x = 0;
621  for(np = menu->items; np; np = np->next) {
622  DrawMenuItem(menu, np, x);
623  ++x;
624  }
625  JXCopyArea(display, menu->pixmap, menu->window, rootGC,
626  0, 0, menu->width, menu->height, 0, 0);
627 
628 }
629 
632  RunMenuCommandType runner,
633  XEvent *event)
634 {
635  MenuItem *ip;
636  Menu *tp;
637  Window subwindow;
638  int x, y;
639 
640  if(event->type == MotionNotify) {
641 
642  SetMousePosition(event->xmotion.x_root, event->xmotion.y_root,
643  event->xmotion.window);
644  DiscardMotionEvents(event, menu->window);
645 
646  x = event->xmotion.x_root - menu->x;
647  y = event->xmotion.y_root - menu->y;
648  subwindow = event->xmotion.subwindow;
649 
650  } else if(event->type == ButtonPress) {
651 
652  if(menu->currentIndex >= 0 || !menu->parent) {
653  tp = menu;
654  } else {
655  tp = menu->parent;
656  }
657 
658  y = -1;
659  if(event->xbutton.button == Button4) {
660  y = GetPreviousMenuIndex(tp);
661  } else if(event->xbutton.button == Button5) {
662  y = GetNextMenuIndex(tp);
663  }
664 
665  if(y >= 0) {
666  SetPosition(tp, y);
667  }
668 
669  return MENU_NOSELECTION;
670 
671  } else if(event->type == KeyPress) {
672 
673  if(menu->currentIndex >= 0 || !menu->parent) {
674  tp = menu;
675  } else {
676  tp = menu->parent;
677  }
678 
679  y = -1;
680  switch(GetKey(&event->xkey) & 0xFF) {
681  case KEY_UP:
682  y = GetPreviousMenuIndex(tp);
683  break;
684  case KEY_DOWN:
685  y = GetNextMenuIndex(tp);
686  break;
687  case KEY_RIGHT:
688  tp = menu;
689  y = 0;
690  break;
691  case KEY_LEFT:
692  if(tp->parent) {
693  tp = tp->parent;
694  if(tp->currentIndex >= 0) {
695  y = tp->currentIndex;
696  } else {
697  y = 0;
698  }
699  }
700  break;
701  case KEY_ESC:
702  return MENU_SUBSELECT;
703  case KEY_ENTER:
704  ip = GetMenuItem(tp, tp->currentIndex);
705  if(ip != NULL) {
706  HideMenu(menu);
707  (runner)(&ip->action, 0);
708  }
709  return MENU_SUBSELECT;
710  default:
711  break;
712  }
713 
714  if(y >= 0) {
715  SetPosition(tp, y);
716  }
717 
718  return MENU_NOSELECTION;
719 
720  } else {
721  Debug("invalid event type in menu.c:UpdateMotion");
722  return MENU_SUBSELECT;
723  }
724 
725  /* Update the selection on the current menu */
726  if(x > 0 && y > 0 && x < menu->width && y < menu->height) {
727  menu->currentIndex = GetMenuIndex(menu, y);
728  } else if(menu->parent && subwindow != menu->parent->window) {
729 
730  /* Leave if over a menu window. */
731  for(tp = menu->parent->parent; tp; tp = tp->parent) {
732  if(tp->window == subwindow) {
733  return MENU_LEAVE;
734  }
735  }
736  menu->currentIndex = -1;
737 
738  } else {
739 
740  /* Leave if over the parent, but not on this selection. */
741  tp = menu->parent;
742  if(tp && subwindow == tp->window) {
743  if(y < menu->parentOffset
744  || y > tp->itemHeight + menu->parentOffset) {
745  return MENU_LEAVE;
746  }
747  }
748 
749  menu->currentIndex = -1;
750 
751  }
752 
753  /* Move the menu if needed. */
754  if(menu->height > menu->screen->height && menu->currentIndex >= 0) {
755 
756  /* If near the top, shift down. */
757  if(y + menu->y <= 0) {
758  if(menu->currentIndex > 0) {
759  menu->currentIndex -= 1;
760  SetPosition(menu, menu->currentIndex);
761  }
762  }
763 
764  /* If near the bottom, shift up. */
765  if(y + menu->y + menu->itemHeight / 2
766  >= menu->screen->y + menu->screen->height) {
767  if(menu->currentIndex + 1 < menu->itemCount) {
768  menu->currentIndex += 1;
769  SetPosition(menu, menu->currentIndex);
770  }
771  }
772 
773  }
774 
775  if(menu->lastIndex != menu->currentIndex) {
776  UpdateMenu(menu);
777  menu->lastIndex = menu->currentIndex;
778  }
779 
780  /* If the selected item is a submenu, show it. */
781  ip = GetMenuItem(menu, menu->currentIndex);
782  if(ip && IsMenuValid(ip->submenu)) {
783  const int x = menu->x + menu->width
784  - (settings.menuDecorations == DECO_MOTIF ? 0 : 1);
785  const int y = menu->y + menu->offsets[menu->currentIndex] - 1;
786  if(ShowSubmenu(ip->submenu, menu, runner, x, y, 0)) {
787 
788  /* Item selected; destroy the menu tree. */
789  return MENU_SUBSELECT;
790 
791  } else {
792 
793  /* No selection made. */
794  UpdateMenu(menu);
795 
796  }
797  }
798 
799  return MENU_NOSELECTION;
800 
801 }
802 
804 void UpdateMenu(Menu *menu)
805 {
806 
807  MenuItem *ip;
808 
809  /* Clear the old selection. */
810  ip = GetMenuItem(menu, menu->lastIndex);
811  DrawMenuItem(menu, ip, menu->lastIndex);
812 
813  /* Highlight the new selection. */
814  ip = GetMenuItem(menu, menu->currentIndex);
815  if(ip != NULL) {
816  DrawMenuItem(menu, ip, menu->currentIndex);
817  }
818 
819  JXCopyArea(display, menu->pixmap, menu->window, rootGC,
820  0, 0, menu->width, menu->height, 0, 0);
821 
822 }
823 
825 void DrawMenuItem(Menu *menu, MenuItem *item, int index)
826 {
827 
828  ButtonNode button;
829 
830  Assert(menu);
831 
832  if(!item) {
833  if(index == -1 && menu->label) {
834  ResetButton(&button, menu->pixmap);
835  button.x = MENU_BORDER_SIZE;
836  button.y = MENU_BORDER_SIZE;
837  button.width = menu->width - MENU_BORDER_SIZE * 2;
838  button.height = menu->itemHeight - 1;
839  button.font = FONT_MENU;
840  button.type = BUTTON_LABEL;
841  button.text = menu->label;
842  button.alignment = ALIGN_CENTER;
843  DrawButton(&button);
844  }
845  return;
846  }
847 
848  if(item->type != MENU_ITEM_SEPARATOR) {
849  ColorType fg;
850 
851  ResetButton(&button, menu->pixmap);
852  if(menu->currentIndex == index) {
853  button.type = BUTTON_MENU_ACTIVE;
855  } else {
856  button.type = BUTTON_LABEL;
857  fg = COLOR_MENU_FG;
858  }
859 
860  button.x = MENU_BORDER_SIZE;
861  button.y = menu->offsets[index];
862  button.font = FONT_MENU;
863  button.width = menu->width - MENU_BORDER_SIZE * 2;
864  button.height = menu->itemHeight;
865  button.text = item->name;
866  button.icon = item->icon;
867  DrawButton(&button);
868 
869  if(item->submenu) {
870 
871  const int asize = (menu->itemHeight + 7) / 8;
872  const int y = menu->offsets[index] + (menu->itemHeight + 1) / 2;
873  int x = menu->width - 2 * asize - 1;
874  int i;
875 
877  for(i = 0; i < asize; i++) {
878  const int y1 = y - asize + i;
879  const int y2 = y + asize - i;
880  JXDrawLine(display, menu->pixmap, rootGC, x, y1, x, y2);
881  x += 1;
882  }
883  JXDrawPoint(display, menu->pixmap, rootGC, x, y);
884 
885  }
886 
887  } else {
890  JXDrawLine(display, menu->pixmap, rootGC, 4,
891  menu->offsets[index] + 2, menu->width - 6,
892  menu->offsets[index] + 2);
894  JXDrawLine(display, menu->pixmap, rootGC, 4,
895  menu->offsets[index] + 3, menu->width - 6,
896  menu->offsets[index] + 3);
897  } else {
899  JXDrawLine(display, menu->pixmap, rootGC, 4,
900  menu->offsets[index] + 2, menu->width - 6,
901  menu->offsets[index] + 2);
902  }
903  }
904 
905 }
906 
909 {
910 
911  MenuItem *item;
912  int x;
913 
914  for(x = menu->currentIndex + 1; x < menu->itemCount; x++) {
915  item = GetMenuItem(menu, x);
916  if(item->type != MENU_ITEM_SEPARATOR) {
917  return x;
918  }
919  }
920 
921  return 0;
922 
923 }
924 
927 {
928 
929  MenuItem *item;
930  int x;
931 
932  for(x = menu->currentIndex - 1; x >= 0; x--) {
933  item = GetMenuItem(menu, x);
934  if(item->type != MENU_ITEM_SEPARATOR) {
935  return x;
936  }
937  }
938 
939  return menu->itemCount - 1;
940 
941 }
942 
944 int GetMenuIndex(Menu *menu, int y)
945 {
946 
947  int x;
948 
949  if(y < menu->offsets[0]) {
950  return -1;
951  }
952  for(x = 0; x < menu->itemCount - 1; x++) {
953  if(y >= menu->offsets[x] && y < menu->offsets[x + 1]) {
954  return x;
955  }
956  }
957  return x;
958 
959 }
960 
962 MenuItem *GetMenuItem(Menu *menu, int index)
963 {
964 
965  MenuItem *ip;
966 
967  if(index >= 0) {
968  for(ip = menu->items; ip; ip = ip->next) {
969  if(!index) {
970  return ip;
971  }
972  --index;
973  }
974  } else {
975  ip = NULL;
976  }
977 
978  return ip;
979 
980 }
981 
983 void SetPosition(Menu *tp, int index)
984 {
985  int y = tp->offsets[index] + tp->itemHeight / 2;
986  if(tp->height > tp->screen->height) {
987 
988  int updated = 0;
989  while(y + tp->y < tp->itemHeight / 2) {
990  tp->y += tp->itemHeight;
991  updated = tp->itemHeight;
992  }
993  while(y + tp->y >= tp->screen->y + tp->screen->height) {
994  tp->y -= tp->itemHeight;
995  updated = -tp->itemHeight;
996  }
997  if(updated) {
998  JXMoveWindow(display, tp->window, tp->x, tp->y);
999  y += updated;
1000  }
1001 
1002  }
1003 
1004  /* We need to do this twice so the event gets registered
1005  * on the submenu if one exists. */
1006  MoveMouse(tp->window, tp->itemHeight / 2, y);
1007  MoveMouse(tp->window, tp->itemHeight / 2, y);
1008 
1009 }
1010 
1012 char IsMenuValid(const Menu *menu)
1013 {
1014  if(menu) {
1015  MenuItem *ip;
1016  for(ip = menu->items; ip; ip = ip->next) {
1017  if(ip->type != MENU_ITEM_SEPARATOR) {
1018  return 1;
1019  }
1020  }
1021  }
1022  return 0;
1023 }
1024 

joewing.net / Projects / JWM