diff lwlib/xlwmenu.c @ 175:2d532a89d707 r20-3b14

Import from CVS: tag r20-3b14
author cvs
date Mon, 13 Aug 2007 09:50:14 +0200
parents 0132846995bd
children 6075d714658b
line wrap: on
line diff
--- a/lwlib/xlwmenu.c	Mon Aug 13 09:49:11 2007 +0200
+++ b/lwlib/xlwmenu.c	Mon Aug 13 09:50:14 2007 +0200
@@ -47,6 +47,11 @@
 #endif
 #include "xlwmenuP.h"
 
+/* simple, naieve integer maximum */
+#ifndef max
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
 static char 
 xlwMenuTranslations [] = 
 "<BtnDown>:	start()\n\
@@ -54,6 +59,8 @@
 <BtnUp>:	select()\n\
 ";
 
+extern Widget lw_menubar_widget;
+
 #define offset(field) XtOffset(XlwMenuWidget, field)
 static XtResource 
 xlwMenuResources[] =
@@ -199,6 +206,8 @@
 
 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
 
+extern int lw_menu_accelerate;
+
 /* Utilities */
 #if 0 /* Apparently not used anywhere */
 
@@ -301,14 +310,16 @@
 {
   if (!mw->menu.old_stack)
     {
-      mw->menu.old_stack_length = 10;
+      mw->menu.old_stack_length = max (10, n);
       mw->menu.old_stack =
 	(widget_value**)XtCalloc (mw->menu.old_stack_length,
 				  sizeof (widget_value*));
     }
   else if (mw->menu.old_stack_length < n)
     {
+      while (mw->menu.old_stack_length < n)
       mw->menu.old_stack_length *= 2;
+      
       mw->menu.old_stack =
 	(widget_value**)XtRealloc ((char *)mw->menu.old_stack,
 				   mw->menu.old_stack_length *
@@ -372,6 +383,39 @@
   massaged_resource_char ['.'] = '_'; /* Convert Buffers... to buffers___ */
 }
 
+static int
+string_width_u (XlwMenuWidget mw,
+#ifdef NEED_MOTIF
+	      XmString s
+#else
+	      char *string
+#endif
+	      )
+{
+#ifdef NEED_MOTIF
+  Dimension width, height;
+  XmStringExtent (mw->menu.font_list, s, &width, &height);
+  return width;
+#else
+  XCharStruct xcs;
+  int i,s=0,w=0;
+  int drop;
+  for (i=0;string[i];++i) {
+    if (string[i]=='%'&&string[i+1]=='_') {
+      XTextExtents (mw->menu.font, &string[s], i-s, &drop, &drop, &drop, &xcs);
+      w += xcs.width;
+      s = i + 2;
+      ++i;
+    }
+  }
+  if (string[s]) {
+	  XTextExtents (mw->menu.font, &string[s], i-s, &drop, &drop, &drop, &xcs);
+	  w += xcs.width;
+  }
+  return w;
+#endif
+}
+
 static void
 massage_resource_name (CONST char *in, char *out)
 {
@@ -714,6 +758,81 @@
 #endif
 }
 
+static void
+string_draw_u (XlwMenuWidget mw,
+	       Window window,
+	       int x, int y,
+	       GC gc,
+#ifdef NEED_MOTIF
+	       XmString string
+#else
+	       char *string
+#endif
+)
+{
+#ifdef NEED_MOTIF
+  XmStringDraw (XtDisplay (mw), window,
+		mw->menu.font_list,
+		string, gc,
+		x, y,
+		1000,	/* ???? width */
+		XmALIGNMENT_BEGINNING,
+		0, /* ???? layout_direction */
+		0);
+#else
+  int i,s=0;
+  for (i=0;string[i];++i) {
+      if (string[i]=='%'&&string[i+1]=='_') {
+	  XCharStruct xcs;
+	  int drop;
+	  /* underline next character */
+	  if (i>s)
+# ifdef USE_XFONTSET
+	    XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
+			   x, y + mw->menu.font_ascent, &string[s], i-s);
+# else
+	  XDrawString (XtDisplay (mw), window, gc,
+		       x, y + mw->menu.font_ascent, &string[s], i-s);
+# endif /* USE_XFONTSET */
+	  
+	  XTextExtents (mw->menu.font, &string[s], i-s, &drop, &drop, &drop,
+			&xcs);
+	  x += xcs.width;
+	  
+	  s=i+3;
+	  i+=2;
+	  
+# ifdef USE_XFONTSET
+	  XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
+			 x, y + mw->menu.font_ascent, &string[i], 1);
+# else
+	  XDrawString (XtDisplay (mw), window, gc,
+		       x, y + mw->menu.font_ascent, &string[i], 1);
+# endif /* USE_XFONTSET */
+	  
+	  XTextExtents (mw->menu.font, &string[i], 1, &drop, &drop, &drop,
+			&xcs);
+	  
+	  XDrawLine (XtDisplay (mw), window, gc, x - 1,
+		     y + mw->menu.font_ascent + 1,
+		     x + xcs.width - 1, y + mw->menu.font_ascent + 1 );
+	  
+	  x += xcs.width;
+      }
+  }
+  if (string[s]) 
+# ifdef USE_XFONTSET
+    XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
+		   x, y + mw->menu.font_ascent, &string[s],
+		   strlen (&string[s]));
+# else
+  XDrawString (XtDisplay (mw), window, gc,
+	       x, y + mw->menu.font_ascent, &string[s],
+	       strlen (&string[s]));
+# endif /* USE_XFONTSET */
+#endif /* NEED_MOTIF */
+}
+
 static void 
 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
 {
@@ -1354,7 +1473,7 @@
   /* no left column decoration */
   *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;;
   
-  *label_width  = string_width (mw, resource_widget_value (mw, val));
+  *label_width  = string_width_u (mw, resource_widget_value (mw, val));
   *bindings_width =  mw->menu.horizontal_margin + mw->menu.shadow_thickness;
 }
 
@@ -1378,7 +1497,7 @@
   /*
    *    Draw the label string.
    */
-  string_draw (mw,
+  string_draw_u (mw,
 	       window,
 	       x + label_offset, y + y_offset, 
 	       mw->menu.foreground_gc,
@@ -1452,7 +1571,7 @@
 	gc = mw->menu.inactive_gc;
     }
 
-  string_draw (mw,
+  string_draw_u (mw,
 	       window,
 	       x + label_offset, y + y_offset, 
 	       gc,
@@ -2025,7 +2144,13 @@
   if (level < mw->menu.old_depth - 1)
     following_item = mw->menu.old_stack [level + 1];
   else 
-    following_item = NULL;
+    {
+      if (lw_menu_accelerate
+	  && level == mw->menu.old_depth - 1
+	  && mw->menu.old_stack [level]->type == CASCADE_TYPE)
+	just_compute_p = True;
+      following_item = NULL;
+    }
 
 #if SLOPPY_TYPES == 1
   puts("===================================================================");
@@ -2285,6 +2410,12 @@
       break;
   last_same = i - 1;
 
+  if (lw_menu_accelerate
+      && last_same
+      && last_same == old_depth - 1
+      && old_stack [last_same]->contents)
+    last_same--;
+  
   /* Memorize the previously selected item to be able to refresh it */
   old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
   if (old_selection && !old_selection->enabled)
@@ -2297,6 +2428,7 @@
      display_menu (called below) uses the old_stack to know what to display. */
   for (i = last_same + 1; i < new_depth; i++)
     old_stack [i] = new_stack [i];
+  
   mw->menu.old_depth = new_depth;
 
   /* refresh the last seletion */
@@ -2311,6 +2443,9 @@
       window_state *previous_ws = &windows [i - 1];
       window_state *ws = &windows [i];
 
+      if (lw_menu_accelerate && i == new_depth - 1)
+	break;
+      
       ws->x = previous_ws->x + selection_position.x;
       ws->y = previous_ws->y + selection_position.y;
 
@@ -2329,8 +2464,15 @@
     }
 
   /* unmap the menus that popped down */
-  for (i = new_depth - 1; i < old_depth; i++)
-    if (i >= new_depth || !new_stack [i]->contents)
+  
+  last_same = new_depth;
+  if (lw_menu_accelerate
+      && last_same > 1
+      && new_stack [last_same - 1]->contents)
+    last_same--;
+  
+  for (i = last_same - 1; i < old_depth; i++)
+    if (i >= last_same || !new_stack [i]->contents)
       XUnmapWindow (XtDisplay (mw), windows [i].window);
 }
 
@@ -3065,21 +3207,29 @@
       event = &dummy;
     }
 
+  lw_menu_accelerate = False;
   handle_single_motion_event (mw, event, select_p);
 }
 
+Time x_focus_timestamp_really_sucks_fix_me_better;
+
 static void 
 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
 {
   XlwMenuWidget mw = (XlwMenuWidget)w;
 
+  lw_menubar_widget = w;
+  
+  lw_menu_active = True;
+  
   if (!mw->menu.pointer_grabbed)
     {
       mw->menu.menu_post_time = ev->xbutton.time;
       mw->menu.menu_bounce_time = 0;
       mw->menu.next_release_must_exit = True;
       mw->menu.last_selected_val = NULL;
-
+      x_focus_timestamp_really_sucks_fix_me_better =
+	((XButtonPressedEvent*)ev)->time;
       XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
       
       /* notes the absolute position of the menubar window */
@@ -3111,6 +3261,8 @@
   XlwMenuWidget mw = (XlwMenuWidget)w;
   widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
   
+  lw_menu_accelerate = False;
+  
   /* If user releases the button quickly, without selecting anything,
      after the initial down-click that brought the menu up,
      do nothing. */
@@ -3140,10 +3292,175 @@
       XtPopdown (XtParent (mw));
     }
   
+  lw_menu_active = False;
+  
+  x_focus_timestamp_really_sucks_fix_me_better =
+    ((XButtonPressedEvent*)ev)->time;
+  
   /* callback */
   XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
 }
 
+/* Action procedures for keyboard accelerators */
+
+/* set the menu */
+void
+xlw_set_menu (Widget w, widget_value *val)
+{
+  lw_menubar_widget = w;
+  set_new_state ((XlwMenuWidget)w, val, 1);
+}
+
+/* prepare the menu structure via the call-backs */
+void
+xlw_map_menu (Time t)
+{
+  XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
+
+  lw_menu_accelerate = True;
+  
+  if (!mw->menu.pointer_grabbed)
+    {
+      XWindowAttributes ret;
+      Window parent,root;
+      Window *waste;
+      unsigned int num_waste;
+      
+      lw_menu_active = True;
+      
+      mw->menu.menu_post_time = t;
+      mw->menu.menu_bounce_time = 0;
+      
+      mw->menu.next_release_must_exit = True;
+      mw->menu.last_selected_val = NULL;
+      
+      XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
+      
+      /* do this for keyboards too! */
+      /* notes the absolute position of the menubar window */
+      /*
+      mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
+      mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
+      */
+      
+      /* get the geometry of the menubar */
+      
+      /* there has to be a better way than this. */
+      
+      mw->menu.windows [0].x = 0;
+      mw->menu.windows [0].y = 0;
+      
+      parent = XtWindow (lw_menubar_widget);
+      do
+	{
+	  XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
+	  mw->menu.windows [0].x += ret.x;
+	  mw->menu.windows [0].y += ret.y;
+	  
+	  if (parent)
+	    XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
+			&num_waste);
+	  if (waste)
+	    {
+	      XFree (waste);
+	    }
+	}
+      while (parent != root);
+      
+      XtGrabPointer ((Widget)mw, False,
+		     (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
+		     GrabModeAsync, GrabModeAsync,
+		     None, mw->menu.cursor_shape, t);
+      mw->menu.pointer_grabbed = True;
+    }
+}
+
+/* display the stupid menu already */
+void
+xlw_display_menu (Time t)
+{
+  XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
+
+  lw_menu_accelerate = True;
+  
+  remap_menubar (mw);
+  
+  /* Sync with the display.  Makes it feel better on X terms. */
+  XFlush (XtDisplay (mw));
+}
+
+/* push a sub menu */
+void
+xlw_push_menu (widget_value *val)
+{
+  push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
+}
+
+/* pop a sub menu */
+int
+xlw_pop_menu (void)
+{
+  if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
+    ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
+  else
+    return 0;
+  return 1;
+}
+
+void
+xlw_kill_menus (widget_value *val)
+{
+  XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
+  
+  lw_menu_accelerate = False;
+  
+  mw->menu.new_depth = 1;
+  remap_menubar (mw);
+      
+  if (mw->menu.pointer_grabbed)
+    {
+      XtUngrabPointer (lw_menubar_widget, CurrentTime);
+      mw->menu.pointer_grabbed = False;
+    }
+
+  lw_menu_active = False;
+  XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
+}
+
+/* set the menu item */
+void
+xlw_set_item (widget_value *val)
+{
+  if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
+    ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
+  push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
+}
+
+/* get either the current entry or a list of all entries in the current submenu */
+widget_value *
+xlw_get_entries (int allp)
+{
+  XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
+  if (allp)
+    {
+      if (mw->menu.new_depth >= 2)
+	return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
+      else
+	return mw->menu.new_stack[0];
+    }
+  else
+    if (mw->menu.new_depth >= 1)
+      return mw->menu.new_stack [mw->menu.new_depth - 1];
+  
+  return NULL;
+}
+
+int
+xlw_menu_level (void)
+{
+  return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
+}
+
 
 /* Special code to pop-up a menu */
 void