Merge branch 'backport' into 3.4-working
authorDana Jansens <danakj@orodu.net>
Wed, 6 Jan 2010 22:32:14 +0000 (17:32 -0500)
committerDana Jansens <danakj@orodu.net>
Wed, 6 Jan 2010 22:32:14 +0000 (17:32 -0500)
Conflicts:

openbox/actions/focus.c

22 files changed:
Makefile.am
data/rc.xml
data/xsession/openbox-gnome-session.in
debian/control
doc/obxprop.1.in [new file with mode: 0644]
doc/obxprop.1.sgml [new file with mode: 0644]
doc/openbox-gnome-session.1.in
doc/openbox-gnome-session.1.sgml
doc/openbox-kde-session.1.in
doc/openbox-kde-session.1.sgml
doc/openbox-session.1.in
doc/openbox-session.1.sgml
doc/openbox.1.in
doc/openbox.1.sgml
openbox/actions/focus.c
openbox/config.c
openbox/config.h
openbox/event.c
openbox/focus_cycle.c
openbox/menuframe.c
openbox/menuframe.h
tools/obxprop/obxprop.c

index a1160a4..a3dd267 100644 (file)
@@ -493,6 +493,8 @@ dist_noinst_DATA = \
        doc/openbox-gnome-session.1.in \
        doc/openbox-kde-session.1.sgml \
        doc/openbox-kde-session.1.in \
+       doc/obxprop.1.sgml \
+       doc/obxprop.1.in \
        render/obrender-3.0.pc.in \
        parser/obparser-3.0.pc.in \
        tools/themeupdate/themeupdate.py \
index 7f6846b..ca17cf0 100644 (file)
        menu is hidden again -->
   <middle>no</middle>
   <!-- center submenus vertically about the parent entry -->
-  <submenuShowDelay>100</submenuShowDelay>
-  <!-- this one is easy, time to delay before showing a submenu after hovering
-       over the parent entry -->
+  <submenuShowDelay>200</submenuShowDelay>
+  <!-- time to delay before showing a submenu after hovering over the parent
+       entry.
+       if this is a negative value, then the delay is infinite and the
+       submenu will not be shown until it is clicked on -->
+  <submenuHideDelay>400</submenuHideDelay>
+  <!-- time to delay before hiding a submenu when selecting another
+       entry in parent menu -->
+       if this is a negative value, then the delay is infinite and the
+       submenu will not be hidden until a different submenu is opened -->
   <applicationIcons>yes</applicationIcons>
   <!-- controls if icons appear in the client-list-(combined-)menu -->
   <manageDesktops>yes</manageDesktops>
index de46bec..6765ae8 100644 (file)
@@ -46,7 +46,7 @@ else
   OB_SESSION=$(gconftool-2 -g $SPATH/openbox_session 2> /dev/null)
 
   # update the GNOME/Openbox session if needed
-  if x$OB_SESSION != x$SESSION; then
+  if test x$OB_SESSION != x$SESSION; then
       # the default session changed or we didn't run GNOME/Openbox before
       gconftool-2 -t list --list-type=strings -s $SPATH/openbox_session \
         "$SESSION" 2> /dev/null
index 0a15a77..61f72cc 100644 (file)
@@ -13,4 +13,4 @@ Suggests: menu, x-display-manager, ksmserver | gnome-session, kdm | gdm
 Conflicts: menu (<< 2.1.12)
 Provides: x-window-manager
 Description: A minimalistic, highly configurable, next generation window manager with extensive standards support.
- http://icculus.org/openbox/
+ http://openbox.org/
diff --git a/doc/obxprop.1.in b/doc/obxprop.1.in
new file mode 100644 (file)
index 0000000..f3ac096
--- /dev/null
@@ -0,0 +1,42 @@
+.TH "OBXPROP" "1" 
+.SH "NAME" 
+openbox \(em A minimalistic, highly configurable, next generation window 
+manager with extensive standards support. 
+.SH "SYNOPSIS" 
+.PP 
+\fBopenbox\fR [\fB\-\-help\fP]  [\fB\-\-display\fP]  [DISPLAY]  [\fB\-\-id\fP]  [ID]  [\fB\-\-root\fP]  
+.SH "DESCRIPTION" 
+.PP 
+obxprop is a tool for displaying the properties on an x 
+window. 
+.PP 
+This tool has a similar functionality to \fBxprop\fR, 
+but obxprop allows you to see UTF-8 strings as text. 
+.PP 
+You may use the \fB\-\-id\fR option to specify a window 
+identifier, otherwise obxprop will allow you to select a window by 
+clicking on it. 
+.PP 
+Primarily, this tool exists for Openbox users to see the value of the 
+_OB_NAME, _OB_CLASS, and _OB_ROLE properties, which Openbox uses for 
+matching windows against user-defined application rules. 
+.SH "OPTIONS" 
+.PP 
+These are the possible options that \fBopenbox\fR accepts: 
+.IP "\fB\-\-help\fP" 10 
+Show a summary of the options available. 
+.IP "\fB\-\-display\fP" 10 
+.IP "DISPLAY" 10 
+Specify the X display to use. 
+.IP "\fB\-\-id\fP" 10 
+.IP "ID" 10 
+Specify the window identifier for the window whose properties 
+will be displayed. 
+.SH "SEE ALSO" 
+.PP 
+openbox (1), openbox-session(1), openbox-gnome-session(1), 
+openbox-kde-session(1). 
+.PP 
+Please report bugs to: \fBhttp://bugzilla.icculus.org/ 
+\fP 
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 
diff --git a/doc/obxprop.1.sgml b/doc/obxprop.1.sgml
new file mode 100644 (file)
index 0000000..06e7817
--- /dev/null
@@ -0,0 +1,116 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+     page: `docbook-to-man manpage.sgml > manpage.1'.  You may view
+     the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+     less'.  A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+       docbook-to-man $< > $@
+
+    
+       The docbook-to-man binary is found in the docbook-to-man package.
+       Please remember that if you create the nroff version in one of the
+       debian/rules file targets (such as build), you will need to include
+       docbook-to-man in your Build-Depends control field.
+
+  -->
+
+  <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
+  <!ENTITY dhucpackage "<refentrytitle>OBXPROP</refentrytitle>">
+  <!ENTITY dhpackage   "openbox">
+]>
+
+<refentry>
+  <refmeta>
+    &dhucpackage;
+
+    &dhsection;
+  </refmeta>
+  <refnamediv>
+    <refname>&dhpackage;</refname>
+
+    <refpurpose>A minimalistic, highly configurable, next generation window
+      manager with extensive standards support.</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>&dhpackage;</command>
+      <arg><option>--help</option></arg>
+      <arg><option>--display</option></arg><arg>DISPLAY</arg>
+      <arg><option>--id</option></arg><arg>ID</arg>
+      <arg><option>--root</option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para>obxprop is a tool for displaying the properties on an x
+      window.</para>
+
+    <para>This tool has a similar functionality to <command>xprop</command>,
+      but obxprop allows you to see UTF-8 strings as text.</para>
+
+    <para>You may use the <command>--id</command> option to specify a window
+      identifier, otherwise obxprop will allow you to select a window by
+      clicking on it.</para>
+
+    <para>Primarily, this tool exists for Openbox users to see the value of the
+      _OB_NAME, _OB_CLASS, and _OB_ROLE properties, which Openbox uses for
+      matching windows against user-defined application rules.</para>
+  </refsect1>
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para>These are the possible options that <command>&dhpackage;</command> accepts:</para>
+    <variablelist>
+      <varlistentry>
+        <term><option>--help</option></term>
+        <listitem>
+          <para>Show a summary of the options available.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><option>--display</option></term><term>DISPLAY</term>
+        <listitem>
+          <para>Specify the X display to use.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><option>--id</option></term><term>ID</term>
+        <listitem>
+          <para>Specify the window identifier for the window whose properties
+            will be displayed.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para>openbox (1), openbox-session(1), openbox-gnome-session(1),
+      openbox-kde-session(1).</para>
+
+    <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
+      </literal></para>
+  </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
index f38c9bf..7256874 100644 (file)
@@ -15,8 +15,8 @@ openbox(1), openbox-kde-session(1), openbox-session(1).
  
 .PP 
 The program's full documentation is available on the website: 
-\fBhttp://icculus.org/openbox/\fP 
+\fBhttp://openbox.org/\fP 
 .PP 
 Please report bugs to: \fBhttp://bugzilla.icculus.org/ 
 \fP 
-.\" created by instant / docbook-to-man, Wed 23 May 2007, 14:03 
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 
index c72865a..66add1b 100644 (file)
@@ -49,7 +49,7 @@ manpage.1: manpage.sgml
       </para>
 
     <para>The program's full documentation is available on the website:
-      <literal>http://icculus.org/openbox/</literal></para>
+      <literal>http://openbox.org/</literal></para>
 
     <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
       </literal></para>
index d46d14b..9a96275 100644 (file)
@@ -15,8 +15,8 @@ openbox(1), openbox-gnome-session(1), openbox-session(1).
  
 .PP 
 The program's full documentation is available on the website: 
-\fBhttp://icculus.org/openbox/\fP 
+\fBhttp://openbox.org/\fP 
 .PP 
 Please report bugs to: \fBhttp://bugzilla.icculus.org/ 
 \fP 
-.\" created by instant / docbook-to-man, Wed 23 May 2007, 14:03 
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 
index e4f049d..70c5117 100644 (file)
@@ -49,7 +49,7 @@ manpage.1: manpage.sgml
       </para>
 
     <para>The program's full documentation is available on the website:
-      <literal>http://icculus.org/openbox/</literal></para>
+      <literal>http://openbox.org/</literal></para>
 
     <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
       </literal></para>
index 2623247..483cdfd 100644 (file)
@@ -35,8 +35,8 @@ openbox(1), openbox-session(1), openbox-gnome-session(1).
  
 .PP 
 The program's full documentation is available on the website: 
-\fBhttp://icculus.org/openbox/\fP 
+\fBhttp://openbox.org/\fP 
 .PP 
 Please report bugs to: \fBhttp://bugzilla.icculus.org/ 
 \fP 
-.\" created by instant / docbook-to-man, Wed 23 May 2007, 14:03 
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 
index 3c31755..ccece7a 100644 (file)
@@ -66,7 +66,7 @@ manpage.1: manpage.sgml
       </para>
 
     <para>The program's full documentation is available on the website:
-      <literal>http://icculus.org/openbox/</literal></para>
+      <literal>http://openbox.org/</literal></para>
 
     <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
       </literal></para>
index 2a5446f..d508e8c 100644 (file)
@@ -88,8 +88,8 @@ obconf (1), openbox-session(1), openbox-gnome-session(1),
 openbox-kde-session(1). 
 .PP 
 The program's full documentation is available on the website: 
-\fBhttp://icculus.org/openbox/\fP 
+\fBhttp://openbox.org/\fP 
 .PP 
 Please report bugs to: \fBhttp://bugzilla.icculus.org/ 
 \fP 
-.\" created by instant / docbook-to-man, Tue 22 May 2007, 00:06 
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 
index 6921a66..275c324 100644 (file)
@@ -177,7 +177,7 @@ manpage.1: manpage.sgml
       openbox-kde-session(1).</para>
 
     <para>The program's full documentation is available on the website:
-      <literal>http://icculus.org/openbox/</literal></para>
+      <literal>http://openbox.org/</literal></para>
 
     <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
       </literal></para>
index 5c86808..be5de2f 100644 (file)
@@ -7,6 +7,7 @@
 typedef struct {
     gboolean here;
     gboolean activate;
+    gboolean stop_int;
 } Options;
 
 static gpointer setup_func(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node);
@@ -35,9 +36,12 @@ static gpointer setup_func(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node)
     Options *o;
 
     o = g_new0(Options, 1);
+    o->stop_int = TRUE;
 
     if ((n = parse_find_node("here", node)))
         o->here = parse_bool(doc, n);
+    if ((n = parse_find_node("stopInteractive", node)))
+        o->stop_int = parse_bool(doc, n);
     return o;
 }
 
@@ -72,12 +76,18 @@ static gboolean run_func(ObActionsData *data, gpointer options)
             (data->context != OB_FRAME_CONTEXT_CLIENT &&
              data->context != OB_FRAME_CONTEXT_FRAME))
         {
+            if (o->stop_int)
+                actions_interactive_cancel_act();
+
             actions_client_move(data, TRUE);
             client_activate(data->client, TRUE, o->here,
                             o->activate, o->activate, TRUE);
             actions_client_move(data, FALSE);
         }
     } else if (data->context == OB_FRAME_CONTEXT_DESKTOP) {
+        if (o->stop_int)
+            actions_interactive_cancel_act();
+
         /* focus action on the root window. make keybindings work for this
            openbox instance, but don't focus any specific client */
         focus_nothing();
index 10b3a1d..1a99539 100644 (file)
@@ -91,6 +91,7 @@ gint config_mouse_screenedgetime;
 guint    config_menu_hide_delay;
 gboolean config_menu_middle;
 guint    config_submenu_show_delay;
+guint    config_submenu_hide_delay;
 gboolean config_menu_client_list_icons;
 gboolean config_menu_manage_desktops;
 
@@ -812,6 +813,8 @@ static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
         config_menu_middle = parse_bool(doc, n);
     if ((n = parse_find_node("submenuShowDelay", node)))
         config_submenu_show_delay = parse_int(doc, n);
+    if ((n = parse_find_node("submenuHideDelay", node)))
+        config_submenu_hide_delay = parse_int(doc, n);
     if ((n = parse_find_node("applicationIcons", node)))
         config_menu_client_list_icons = parse_bool(doc, n);
     if ((n = parse_find_node("manageDesktops", node)))
@@ -1015,7 +1018,8 @@ void config_startup(ObParseInst *i)
 
     config_menu_hide_delay = 250;
     config_menu_middle = FALSE;
-    config_submenu_show_delay = 0;
+    config_submenu_show_delay = 200;
+    config_submenu_hide_delay = 400;
     config_menu_client_list_icons = TRUE;
     config_menu_manage_desktops = TRUE;
     config_menu_files = NULL;
index 3dfb5c5..c82ffbf 100644 (file)
@@ -185,6 +185,8 @@ extern guint    config_menu_hide_delay;
 extern gboolean config_menu_middle;
 /*! Delay before opening a submenu in milliseconds */
 extern guint    config_submenu_show_delay;
+/*! Delay before closing a submenu in milliseconds */
+extern guint    config_submenu_hide_delay;
 /*! Show icons in client_list_menu */
 extern gboolean config_menu_client_list_icons;
 /*! Show manage desktops in client_list_menu */
index bbe7038..ad9dade 100644 (file)
@@ -1700,8 +1700,13 @@ static gboolean event_handle_menu_keyboard(XEvent *ev)
 
         else if (ob_keycode_match(keycode, OB_KEY_LEFT)) {
             /* Left goes to the parent menu */
-            if (frame->parent)
+            if (frame->parent) {
+                /* remove focus from the child */
                 menu_frame_select(frame, NULL, TRUE);
+                /* and put it in the parent */
+                menu_frame_select(frame->parent, frame->parent->selected,
+                                  TRUE);
+            }
             ret = TRUE;
         }
 
@@ -1800,6 +1805,15 @@ static gboolean event_handle_menu_keyboard(XEvent *ev)
     return ret;
 }
 
+static Bool event_look_for_menu_enter(Display *d, XEvent *ev, XPointer arg)
+{
+    ObMenuFrame *f = (ObMenuFrame*)arg;
+    ObMenuEntryFrame *e;
+    return ev->type == EnterNotify &&
+        (e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
+        !e->ignore_enters && e->frame == f;
+}
+
 static gboolean event_handle_menu(XEvent *ev)
 {
     ObMenuFrame *f;
@@ -1837,14 +1851,16 @@ static gboolean event_handle_menu(XEvent *ev)
         if (ev->xcrossing.detail == NotifyInferior)
             break;
 
-        if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
-            (f = find_active_menu()) && f->selected == e)
+        if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)))
         {
-            ObMenuEntryFrame *u = menu_entry_frame_under(ev->xcrossing.x_root,
-                                                         ev->xcrossing.y_root);
-            /* if we're just going from one entry in the menu to the next,
-               don't unselect stuff first */
-            if (!u || e->frame != u->frame)
+            XEvent ce;
+
+            /* check if an EnterNotify event is coming, and if not, then select
+               nothing in the menu */
+            if (XCheckIfEvent(ob_display, &ce, event_look_for_menu_enter,
+                              (XPointer)e->frame))
+                XPutBackEvent(ob_display, &ce);
+            else
                 menu_frame_select(e->frame, NULL, FALSE);
         }
         break;
index 8d04749..c92b5a5 100644 (file)
@@ -55,9 +55,10 @@ void focus_cycle_stop(ObClient *ifclient)
 {
     /* stop focus cycling if the given client is a valid focus target,
        and so the cycling is being disrupted */
-    if (focus_cycle_target && ifclient &&
-        (ifclient == focus_cycle_target ||
-         focus_cycle_popup_is_showing(ifclient)))
+    if (focus_cycle_target &&
+        ((ifclient && (ifclient == focus_cycle_target ||
+                       focus_cycle_popup_is_showing(ifclient))) ||
+         !ifclient))
     {
         focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,TRUE);
         focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
index 8177f51..088986f 100644 (file)
@@ -49,10 +49,11 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
                                               ObMenuFrame *frame);
 static void menu_entry_frame_free(ObMenuEntryFrame *self);
 static void menu_frame_update(ObMenuFrame *self);
-static gboolean menu_entry_frame_submenu_hide_timeout(gpointer data);
-static gboolean menu_entry_frame_submenu_show_timeout(gpointer data);
+static gboolean submenu_show_timeout(gpointer data);
 static void menu_frame_hide(ObMenuFrame *self);
 
+static gboolean submenu_hide_timeout(gpointer data);
+
 static Window createWindow(Window parent, gulong mask,
                            XSetWindowAttributes *attrib)
 {
@@ -96,7 +97,6 @@ ObMenuFrame* menu_frame_new(ObMenu *menu, guint show_from, ObClient *client)
     self->type = Window_Menu;
     self->menu = menu;
     self->selected = NULL;
-    self->open_submenu = NULL;
     self->client = client;
     self->direction_right = TRUE;
     self->show_from = show_from;
@@ -974,6 +974,15 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
     return TRUE;
 }
 
+/*! Stop hiding an open submenu.
+    @child The OnMenuFrame of the submenu to be hidden
+*/
+static void remove_submenu_hide_timeout(ObMenuFrame *child)
+{
+    ob_main_loop_timeout_remove_data(ob_main_loop, submenu_hide_timeout,
+                                     child, FALSE);
+}
+
 gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
                                  ObMenuEntryFrame *parent_entry)
 {
@@ -986,12 +995,14 @@ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
     self->monitor = parent->monitor;
     self->parent = parent;
     self->parent_entry = parent_entry;
-    parent->open_submenu = parent_entry;
 
     /* set up parent's child to be us */
-    if (parent->child)
-        menu_frame_hide(parent->child);
-    parent->child = self;
+    if ((parent->child) != self) {
+        if (parent->child)
+            menu_frame_hide(parent->child);
+        parent->child = self;
+        parent->child_entry = parent_entry;
+    }
 
     if (!menu_frame_show(self))
         return FALSE;
@@ -1032,9 +1043,11 @@ static void menu_frame_hide(ObMenuFrame *self)
     if (self->child)
         menu_frame_hide(self->child);
 
-    if (self->parent && self->parent->child == self) {
+    if (self->parent) {
+        remove_submenu_hide_timeout(self);
+
         self->parent->child = NULL;
-        self->parent->open_submenu = NULL;
+        self->parent->child_entry = NULL;
     }
     self->parent = NULL;
     self->parent_entry = NULL;
@@ -1060,11 +1073,7 @@ void menu_frame_hide_all(void)
 
     if (config_submenu_show_delay) {
         /* remove any submenu open requests */
-        ob_main_loop_timeout_remove(ob_main_loop,
-                                    menu_entry_frame_submenu_show_timeout);
-        /* remove any submenu close delays */
-        ob_main_loop_timeout_remove(ob_main_loop,
-                                    menu_entry_frame_submenu_hide_timeout);
+        ob_main_loop_timeout_remove(ob_main_loop, submenu_show_timeout);
     }
     if ((it = g_list_last(menu_frame_visible)))
         menu_frame_hide(it->data);
@@ -1078,13 +1087,8 @@ void menu_frame_hide_all_client(ObClient *client)
         if (f->client == client) {
             if (config_submenu_show_delay) {
                 /* remove any submenu open requests */
-                ob_main_loop_timeout_remove
-                    (ob_main_loop,
-                     menu_entry_frame_submenu_show_timeout);
-                /* remove any submenu close delays */
-                ob_main_loop_timeout_remove
-                    (ob_main_loop,
-                     menu_entry_frame_submenu_hide_timeout);
+                ob_main_loop_timeout_remove(ob_main_loop,
+                                            submenu_show_timeout);
             }
             menu_frame_hide(f);
         }
@@ -1119,6 +1123,7 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
 
         for (it = frame->entries; it; it = g_list_next(it)) {
             ObMenuEntryFrame *e = it->data;
+
             if (RECT_CONTAINS(e->area, x, y)) {
                 ret = e;
                 break;
@@ -1128,18 +1133,17 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
     return ret;
 }
 
-static gboolean menu_entry_frame_submenu_hide_timeout(gpointer data)
+static gboolean submenu_show_timeout(gpointer data)
 {
     g_assert(menu_frame_visible);
-    g_assert(((ObMenuFrame*)data)->parent != NULL);
-    menu_frame_hide((ObMenuFrame*)data);
+    menu_entry_frame_show_submenu((ObMenuEntryFrame*)data);
     return FALSE;
 }
 
-static gboolean menu_entry_frame_submenu_show_timeout(gpointer data)
+static gboolean submenu_hide_timeout(gpointer data)
 {
     g_assert(menu_frame_visible);
-    menu_entry_frame_show_submenu((ObMenuEntryFrame*)data);
+    menu_frame_hide((ObMenuFrame*)data);
     return FALSE;
 }
 
@@ -1148,27 +1152,24 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
 {
     ObMenuEntryFrame *old = self->selected;
     ObMenuFrame *oldchild = self->child;
+    ObMenuEntryFrame *oldchild_entry = self->child_entry;
 
+    /* if the user selected a separator, ignore it and reselect what we had
+       selected before */
     if (entry && entry->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR)
         entry = old;
 
-    if (old == entry) return;
+    if (old == entry && (old->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU))
+        return;
+
+    /* if the user left this menu but we have a submenu open, move the
+       selection back to that submenu */
+    if (!entry && oldchild_entry)
+        entry = oldchild_entry;
 
     if (config_submenu_show_delay) {
         /* remove any submenu open requests */
-        ob_main_loop_timeout_remove(ob_main_loop,
-                                    menu_entry_frame_submenu_show_timeout);
-    }
-
-    if (!entry && self->open_submenu) {
-        /* we moved out of the menu, so move the selection back to the open
-           submenu */
-        entry = self->open_submenu;
-        oldchild = NULL;
-
-        /* remove any submenu close delays */
-        ob_main_loop_timeout_remove(ob_main_loop,
-                                    menu_entry_frame_submenu_hide_timeout);
+        ob_main_loop_timeout_remove(ob_main_loop, submenu_show_timeout);
     }
 
     self->selected = entry;
@@ -1176,48 +1177,47 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
     if (old)
         menu_entry_frame_render(old);
 
-    if (oldchild) {
-        /* there is an open submenu */
-
-        if (config_submenu_show_delay && !immediate) {
-            if (entry == self->open_submenu) {
-                /* we moved onto the entry that has an open submenu, so stop
-                   trying to close the submenu */
-                ob_main_loop_timeout_remove
-                    (ob_main_loop,
-                     menu_entry_frame_submenu_hide_timeout);
-            }
-            else if (old == self->open_submenu) {
-                /* we just moved off the entry with an open submenu, so
-                   close the open submenu after a delay */
+    if (oldchild_entry) {
+        /* There is an open submenu */
+        if (oldchild_entry == self->selected) {
+            /* The open submenu has been reselected, so stop hiding the
+               submenu */
+            remove_submenu_hide_timeout(oldchild);
+        }
+        else if (oldchild_entry == old) {
+            /* The open submenu was selected and is no longer, so hide the
+               submenu */
+            if (immediate || config_submenu_hide_delay == 0)
+                menu_frame_hide(oldchild);
+            else if (config_submenu_hide_delay > 0)
                 ob_main_loop_timeout_add(ob_main_loop,
-                                         config_submenu_show_delay * 1000,
-                                         menu_entry_frame_submenu_hide_timeout,
-                                         self->child, g_direct_equal,
+                                         config_submenu_hide_delay * 1000,
+                                         submenu_hide_timeout,
+                                         oldchild, g_direct_equal,
                                          NULL);
-            }
         }
-        else
-            menu_frame_hide(oldchild);
     }
 
     if (self->selected) {
         menu_entry_frame_render(self->selected);
 
-        /* if we've selected a submenu and it wasn't already open, then
-           show it */
-        if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU &&
-            self->selected != self->open_submenu)
-        {
-            if (config_submenu_show_delay && !immediate) {
-                /* initiate a new submenu open request */
-                ob_main_loop_timeout_add(ob_main_loop,
-                                         config_submenu_show_delay * 1000,
-                                         menu_entry_frame_submenu_show_timeout,
-                                         self->selected, g_direct_equal,
-                                         NULL);
-            } else {
-                menu_entry_frame_show_submenu(self->selected);
+        if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+            /* only show if the submenu isn't already showing */
+            if (oldchild_entry != self->selected) {
+                if (immediate || config_submenu_hide_delay == 0)
+                    menu_entry_frame_show_submenu(self->selected);
+                else if (config_submenu_hide_delay > 0)
+                    ob_main_loop_timeout_add(ob_main_loop,
+                                             config_submenu_show_delay * 1000,
+                                             submenu_show_timeout,
+                                             self->selected, g_direct_equal,
+                                             NULL);
+            }
+            /* hide the grandchildren of this menu. and move the cursor to
+               the current menu */
+            else if (immediate && self->child && self->child->child) {
+                menu_frame_hide(self->child->child);
+                menu_frame_select(self->child, NULL, TRUE);
             }
         }
     }
index c1b4a86..8cc626d 100644 (file)
@@ -50,12 +50,10 @@ struct _ObMenuFrame
     ObMenuFrame *parent;
     ObMenuEntryFrame *parent_entry;
     ObMenuFrame *child;
+    ObMenuEntryFrame *child_entry;
 
     GList *entries;
     ObMenuEntryFrame *selected;
-    /* if a submenu was selected, then this holds the entry for that submenu
-       until it is closed */
-    ObMenuEntryFrame *open_submenu;
 
     /* show entries from the menu starting at this index */
     guint show_from;
index 2685154..6ea8a4d 100644 (file)
@@ -16,7 +16,8 @@ gint fail(const gchar *s) {
              "Options:\n"
              "    --help              Display this help and exit\n"
              "    --display DISPLAY   Connect to this X display\n"
-             "    --id ID             Show the properties for this window\n");
+             "    --id ID             Show the properties for this window\n"
+             "    --root              Show the properties for the root window\n");
     return 1;
 }
 
@@ -256,11 +257,14 @@ int main(int argc, char **argv)
     Window id, userid = None;
     int i;
     char *dname = NULL;
+    gboolean root = FALSE;
 
     for (i = 1; i < argc; ++i) {
         if (!strcmp(argv[i], "--help")) {
             return fail(0);
         }
+        else if (!strcmp(argv[i], "--root"))
+            root = TRUE;
         else if (!strcmp(argv[i], "--id")) {
             if (++i == argc)
                 return fail(0);
@@ -287,6 +291,9 @@ int main(int argc, char **argv)
                     "Ensure you have permission to connect to the display.");
     }
 
+    if (root)
+        userid = RootWindow(d, DefaultScreen(d));
+
     if (userid == None) {
         i = XGrabPointer(d, RootWindow(d, DefaultScreen(d)),
                          False, ButtonPressMask,