]> Dogcows Code - chaz/openbox/commitdiff
Merge branch 'master' into chaz
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Fri, 26 Oct 2012 19:03:24 +0000 (13:03 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Fri, 26 Oct 2012 19:03:24 +0000 (13:03 -0600)
Conflicts:
openbox/config.c

57 files changed:
Makefile.am
README
bootstrap
data/autostart/openbox-autostart.in
data/autostart/openbox-xdg-autostart
data/openbox.desktop
data/rc.xml
data/rc.xsd
data/xsession/openbox-gnome.desktop.in
data/xsession/openbox-kde.desktop.in
data/xsession/openbox.desktop.in
doc/openbox.1.in
doc/openbox.1.sgml
obrender/theme.c
obt/bsearch.h
obt/keyboard.c
obt/keyboard.h
obt/paths.c
obt/prop.c
obt/prop.h
obt/xml.c
obt/xml.h
openbox/actions.c
openbox/actions.h
openbox/actions/cyclewindows.c
openbox/actions/desktop.c
openbox/actions/directionalwindows.c
openbox/actions/execute.c
openbox/actions/moveresizeto.c
openbox/actions/resizerelative.c
openbox/client.c
openbox/client.h
openbox/config.c
openbox/config.h
openbox/event.c
openbox/event.h
openbox/focus_cycle.c
openbox/focus_cycle.h
openbox/frame.c
openbox/geom.h
openbox/menu.c
openbox/menu.h
openbox/menuframe.c
openbox/menuframe.h
openbox/moveresize.c
openbox/openbox.c
openbox/place.c
openbox/place.h
openbox/place_overlap.c [new file with mode: 0644]
openbox/place_overlap.h [new file with mode: 0644]
openbox/prompt.c
openbox/screen.c
po/de.po
release/bugs [new file with mode: 0755]
release/common [new file with mode: 0644]
release/email [new file with mode: 0755]
release/go [new file with mode: 0755]

index 1f7427cd2c0247036faf10770b0141055f991bae..f32591f571b3c4c53503092119015c8c3c57fb57 100644 (file)
@@ -114,6 +114,7 @@ obrender_libobrender_la_SOURCES = \
 ## obt ##
 
 obt_libobt_la_CPPFLAGS = \
+       $(X_CFLAGS) \
        $(XINERAMA_CFLAGS) \
        $(XKB_CFLAGS) \
        $(XRANDR_CFLAGS) \
@@ -128,6 +129,7 @@ obt_libobt_la_CPPFLAGS = \
 obt_libobt_la_LDFLAGS = \
        -version-info $(OBT_CURRENT):$(OBT_REVISION):$(OBT_AGE)
 obt_libobt_la_LIBADD = \
+       $(X_LIBS) \
        $(XINERAMA_LIBS) \
        $(XKB_LIBS) \
        $(XRANDR_LIBS) \
@@ -284,6 +286,8 @@ openbox_openbox_SOURCES = \
        openbox/ping.h \
        openbox/place.c \
        openbox/place.h \
+       openbox/place_overlap.c \
+       openbox/place_overlap.h \
        openbox/prompt.c \
        openbox/prompt.h \
        openbox/popup.c \
diff --git a/README b/README
index 9c6686406103920941eebe149d60f204f4e06a50..ff52ea93d26d75c5742179e7baa4cd5722e8b225 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
 Openbox
-Copyright (C) 2004-2007  Mikael Magnusson
-Copyright (C) 2002-2007  Dana Jansens
+Copyright (C) 2004  Mikael Magnusson
+Copyright (C) 2002  Dana Jansens
 
 ----
 
index 6a5b10fb6cbe143e9aa9321485ba661deded1b60..076005ccf2d5f38af47a63a37aa0fd9963468876 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -4,7 +4,7 @@ sh() {
   /bin/sh -c "set -x; $*"
 }
 
-export WANT_AUTOMAKE=1.9
+export WANT_AUTOMAKE=1.11
 
 sh autopoint --force || exit 1 # for GNU gettext
 sh libtoolize --copy --force --automake || exit 1
index 063c635feb39c78767d8826c56eb01a14c6c746b..5c7277740396c9cf92a6fc6a1a316324f867f043 100755 (executable)
@@ -2,11 +2,11 @@
 
 # Set a background color
 BG=""
-if which hsetroot >/dev/null; then
+if which hsetroot >/dev/null 2>/dev/null; then
   BG=hsetroot
-elif which esetroot >/dev/null; then
+elif which esetroot >/dev/null 2>/dev/null; then
   BG=esetroot
-elif which xsetroot >/dev/null; then
+elif which xsetroot >/dev/null 2>/dev/null; then
   BG=xsetroot
 fi
 test -z $BG || $BG -solid "#303030"
index ea76028182b7b46e1fca667bf9e0a9cfcaa165b8..04a17a199476e3a0932c920a18ae02d83489b83d 100755 (executable)
@@ -29,7 +29,7 @@ try:
     from xdg.Exceptions import ParsingError
 except ImportError:
     print
-    print "ERROR:", ME, "requires PyXDG to be installed"
+    print >>sys.stderr, "ERROR:", ME, "requires PyXDG to be installed"
     print
     sys.exit(1)
 
index d49ae2275c60e7fadf9b194dc5be13e30cf72e9a..631778bf1dc445147507235138c2a640feb6ac86 100644 (file)
@@ -1,6 +1,5 @@
 [Desktop Entry]
 Type=Application
-Encoding=UTF-8
 Name=Openbox
 Exec=openbox
 Icon=openbox
index 70ad9bf9c39bdc72fc83caa0d1c2f2726e2995f8..ce1e8fc1f8d0775f323c1cef3231ae2d9d2cb789 100644 (file)
@@ -33,9 +33,6 @@
 <placement>
   <policy>Smart</policy>
   <!-- 'Smart' or 'UnderMouse' -->
-  <center>yes</center>
-  <!-- whether to place windows in the center of the free area found or
-       the top left corner -->
   <monitor>Primary</monitor>
   <!-- with Smart placement on a multi-monitor system, try to place new windows
        on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
        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 -->
+       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>
+  <showIcons>yes</showIcons>
   <!-- controls if icons appear in the client-list-(combined-)menu -->
   <manageDesktops>yes</manageDesktops>
   <!-- show the manage desktops section in the client-list-(combined-)menu -->
 
   <application name="the window's _OB_APP_NAME property (see obxprop)"
               class="the window's _OB_APP_CLASS property (see obxprop)"
+          groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
+         groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
                role="the window's _OB_APP_ROLE property (see obxprop)"
               title="the window's _OB_APP_TITLE property (see obxprop)"
                type="the window's _OB_APP_TYPE property (see obxprob)..
       # applications who refuse to behave
       <x>center</x>
       # a number like 50, or 'center' to center on screen. use a negative number
-      # to start from the right (or bottom for <y>), ie -50 is 50 pixels from the
-      # right edge (or bottom).
+      # to start from the right (or bottom for <y>), ie -50 is 50 pixels from
+      # the right edge (or bottom). use 'default' to specify using value
+      # provided by the application, or chosen by openbox, instead.
       <y>200</y>
       <monitor>1</monitor>
       # specifies the monitor in a xinerama setup.
       # 1 is the first head, or 'mouse' for wherever the mouse is
     </position>
 
+    <size>
+      # the size to make the window.
+      <width>20</width>
+      # a number like 20, or 'default' to use the size given by the application.
+      # you can use fractions such as 1/2 or percentages such as 75% in which
+      # case the value is relative to the size of the monitor that the window
+      # appears on.
+      <height>30%</height>
+    </size>
+
     <focus>yes</focus>
     # if the window should try be given focus when it appears. if this is set
     # to yes it doesn't guarantee the window will be given focus. some
index 4585f7cc461fcbf36fc9f8fdd7c6578581fb8d86..0fd98ffb87c01c0df645f813c5b772ad5ee48699 100644 (file)
@@ -70,9 +70,8 @@
         </xsd:annotation>
         <xsd:sequence>
             <xsd:element minOccurs="0" name="policy" type="ob:placementpolicy"/>
-            <xsd:element minOccurs="0" name="center" type="ob:bool"/>
             <xsd:element minOccurs="0" name="monitor" type="ob:placementmonitor"/>
-            <xsd:element minOccurs="0" name="monitor" type="ob:primarymonitor"/>
+            <xsd:element minOccurs="0" name="primaryMonitor" type="ob:primarymonitor"/>
         </xsd:sequence>
     </xsd:complexType>
     <xsd:complexType name="margins">
         </xsd:all>
         <xsd:attribute name="force" type="ob:bool"/>
     </xsd:complexType>
+    <xsd:complexType name="window_size">
+        <xsd:all>
+            <xsd:element name="width" type="ob:size_value"/>
+            <xsd:element name="height" type="ob:size_value"/>
+        </xsd:all>
+    </xsd:complexType>
     <xsd:complexType name="application">
         <xsd:all>
             <xsd:element minOccurs="0" name="decor" type="ob:bool"/>
             <xsd:element minOccurs="0" name="shade" type="ob:bool"/>
             <xsd:element minOccurs="0" name="position" type="ob:window_position"/>
+            <xsd:element minOccurs="0" name="size" type="ob:window_size"/>
             <xsd:element minOccurs="0" name="focus" type="xsd:string"/>
             <xsd:element minOccurs="0" name="desktop" type="xsd:integer"/>
             <xsd:element minOccurs="0" name="layer" type="ob:layer"/>
             <xsd:enumeration value="InactiveWindow"/>
             <xsd:enumeration value="MenuHeader"/>
             <xsd:enumeration value="MenuItem"/>
-            <xsd:enumeration value="OnScreenDisplay"/>
+            <xsd:enumeration value="ActiveOnScreenDisplay"/>
+            <xsd:enumeration value="InactiveOnScreenDisplay"/>
         </xsd:restriction>
     </xsd:simpleType>
     <xsd:simpleType name="fontweight">
         <xsd:restriction base="xsd:string">
             <!-- ob: atoi($_) unless $_ eq 'center'; -->
             <!-- I think the regexp DTRT WRT atoi. -->
-            <xsd:pattern value="center|-?(0|[1-9][0-9]*)"/>
+            <xsd:pattern value="default|center|-?(0|[1-9][0-9]*)(|%|/[1-9][0-9]*)"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+    <xsd:simpleType name="size_value">
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="default|([1-9][0-9]*)(|%|/[1-9][0-9]*)"/>
         </xsd:restriction>
     </xsd:simpleType>
     <xsd:simpleType name="mouse_or_int">
         <xsd:restriction base="xsd:string">
             <xsd:enumeration value="Mouse"/>
             <xsd:enumeration value="Active"/>
-            <xsd:enumeration value="[0-9][0-9][0-9][0-9][0-9]"/>
+            <xsd:pattern value="[0-9][0-9][0-9][0-9][0-9]"/>
         </xsd:restriction>
     </xsd:simpleType>
     <xsd:simpleType name="popupposition">
index 19ae82e094c4542f541633a1a9bfe26898e6cbbc..24931b50afd56e871fb30ec453747d0fa6687a6b 100644 (file)
@@ -1,8 +1,7 @@
 [Desktop Entry]
-Encoding=UTF-8
 Name=GNOME/Openbox
 Comment=Use the Openbox window manager inside of the GNOME desktop environment
 Exec=@bindir@/openbox-gnome-session
 TryExec=gnome-session
-Icon=openbox.png
-Type=XSession
+Icon=openbox
+Type=Application
index ddfc72d714c41b6b98cebe948636b5db58b29092..bc78a97c72750250b5d7b170f02f3b07d6fe62de 100644 (file)
@@ -1,8 +1,7 @@
 [Desktop Entry]
-Encoding=UTF-8
 Name=KDE/Openbox
 Comment=Use the Openbox window manager inside of the K Desktop Environment
 Exec=@bindir@/openbox-kde-session
 TryExec=startkde
-Icon=openbox.png
-Type=XSession
+Icon=openbox
+Type=Application
index 0914e5b4d9e18abf3b973133601cc5a9a0838d7e..21669cf8f9b7734ada4dad625646ed0b1e1f9061 100644 (file)
@@ -1,8 +1,7 @@
 [Desktop Entry]
-Encoding=UTF-8
 Name=Openbox
 Comment=Log in using the Openbox window manager (without a session manager)
 Exec=@bindir@/openbox-session
 TryExec=@bindir@/openbox-session
-Icon=openbox.png
-Type=XSession
+Icon=openbox
+Type=Application
index d508e8c699e09a9404cb88fbcadbd4810bd8a05a..bd3747fbc6392ce4b7fa135aac2cbd4083dacea0 100644 (file)
@@ -58,11 +58,14 @@ to ~/.config/openbox and edit it to your liking.
 .PP 
 These are the possible options that \fBopenbox\fR accepts: 
 .IP "\fB\-\-help\fP" 10 
-Show a summary of the options available. 
+Show a summary of the command line options available 
+and exit. 
 .IP "\fB\-\-version\fP" 10 
-Show the version of the program. 
+Show the version of the program and exit
 .IP "\fB\-\-replace\fP" 10 
 Replace the currently running window manager. 
+.IP "\fB\-\-config-file FILE\fP" 10 
+Specify the path to the config file to use. 
 .IP "\fB\-\-reconfigure\fP" 10 
 If Openbox is already running on the display, tell it to 
 reload its configuration. 
@@ -70,14 +73,20 @@ reload its configuration.
 If Openbox is already running on the display, tell it to 
 restart. This is useful if you have upgraded Openbox and don't 
 want to restart X. 
+.IP "\fB\-\-exit\fP" 10 
+Exit Openbox. 
 .IP "\fB\-\-sm-disable\fP" 10 
 Do not connect to the session manager. 
 .IP "\fB\-\-sync\fP" 10 
 Run in synchronous mode (for debugging). 
+.IP "\fB\-\-startup CMD\fP" 10 
+Run CMD after starting. 
 .IP "\fB\-\-debug\fP" 10 
 Display debugging output. 
 .IP "\fB\-\-debug-focus\fP" 10 
 Display debugging output for focus handling. 
+.IP "\fB\-\-debug-session\fP" 10 
+Display debugging output for session management. 
 .IP "\fB\-\-debug-xinerama\fP" 10 
 Split the display into two fake xinerama regions, if 
 xinerama is not already enabled. This is for debugging 
@@ -92,4 +101,4 @@ The program's full documentation is available on the website:
 .PP 
 Please report bugs to: \fBhttp://bugzilla.icculus.org/ 
 \fP 
-.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 
+.\" created by instant / docbook-to-man, Sun 30 Sep 2012, 17:58 
index 275c3246ee075516d239e5477b272374707663b8..281f2a01f839d4eefa881b0e1c7b059f1a1c30e6 100644 (file)
@@ -106,13 +106,14 @@ manpage.1: manpage.sgml
       <varlistentry>
         <term><option>--help</option></term>
         <listitem>
-          <para>Show a summary of the options available.</para>
+          <para>Show a summary of the command line options available
+            and exit.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><option>--version</option></term>
         <listitem>
-          <para>Show the version of the program.</para>
+          <para>Show the version of the program and exit.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
@@ -121,6 +122,12 @@ manpage.1: manpage.sgml
           <para>Replace the currently running window manager.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><option>--config-file FILE</option></term>
+        <listitem>
+          <para>Specify the path to the config file to use.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><option>--reconfigure</option></term>
         <listitem>
@@ -136,6 +143,12 @@ manpage.1: manpage.sgml
             want to restart X.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><option>--exit</option></term>
+        <listitem>
+          <para>Exit Openbox.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><option>--sm-disable</option></term>
         <listitem>
@@ -148,6 +161,12 @@ manpage.1: manpage.sgml
           <para>Run in synchronous mode (for debugging).</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><option>--startup CMD</option></term>
+        <listitem>
+          <para>Run CMD after starting.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><option>--debug</option></term>
         <listitem>
@@ -160,6 +179,12 @@ manpage.1: manpage.sgml
           <para>Display debugging output for focus handling.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><option>--debug-session</option></term>
+        <listitem>
+          <para>Display debugging output for session management.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><option>--debug-xinerama</option></term>
         <listitem>
index b20182ad23217b148626e77202f259c4c61bfbb2..d5ce865981f7d207db289bf9150676402d042832 100644 (file)
@@ -324,8 +324,9 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
                 "osd.label.text.color",
                 theme->osd_text_active_color, RrColorCopy(theme->title_focused_color));
 
-    READ_COLOR("osd.inactive.label.text.color", theme->osd_text_inactive_color,
-               RrColorNew(inst, 0xff, 0xff, 0xff));
+    READ_COLOR_("osd.inactive.label.text.color",
+                "osd.label.text.color",
+                theme->osd_text_inactive_color, RrColorCopy(theme->title_unfocused_color));
 
     READ_COLOR("window.active.button.unpressed.image.color",
                theme->titlebut_focused_unpressed_color,
@@ -698,36 +699,36 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
     /* hover */
     READ_APPEARANCE_COPY("window.active.button.max.hover.bg",
                          theme->btn_max->a_hover_focused, TRUE,
-                         theme->btn_max->a_focused_unpressed);
+                         a_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.max.hover.bg",
                          theme->btn_max->a_hover_unfocused, TRUE,
-                         theme->btn_max->a_unfocused_unpressed);
+                         a_hover_unfocused_tmp);
 
     /* toggled unpressed */
     READ_APPEARANCE_("window.active.button.max.toggled.unpressed.bg",
                      "window.active.button.max.toggled.bg",
                      theme->btn_max->a_toggled_focused_unpressed, TRUE,
-                     theme->btn_max->a_focused_pressed);
+                     a_toggled_focused_unpressed_tmp);
     READ_APPEARANCE_("window.inactive.button.max.toggled.unpressed.bg",
                      "window.inactive.button.max.toggled.bg",
                      theme->btn_max->a_toggled_unfocused_unpressed, TRUE,
-                     theme->btn_max->a_unfocused_pressed);
+                     a_toggled_unfocused_unpressed_tmp);
 
     /* toggled pressed */
     READ_APPEARANCE_COPY("window.active.button.max.toggled.pressed.bg",
                          theme->btn_max->a_toggled_focused_pressed, TRUE,
-                         theme->btn_max->a_focused_pressed);
+                         a_toggled_focused_pressed_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.max.toggled.pressed.bg",
                          theme->btn_max->a_toggled_unfocused_pressed, TRUE,
-                         theme->btn_max->a_unfocused_pressed);
+                         a_toggled_unfocused_pressed_tmp);
 
     /* toggled hover */
     READ_APPEARANCE_COPY("window.active.button.max.toggled.hover.bg",
                          theme->btn_max->a_toggled_hover_focused, TRUE,
-                         theme->btn_max->a_toggled_focused_unpressed);
+                         a_toggled_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.max.toggled.hover.bg",
                          theme->btn_max->a_toggled_hover_unfocused, TRUE,
-                         theme->btn_max->a_toggled_unfocused_unpressed);
+                         a_toggled_hover_unfocused_tmp);
 
     /* close button */
     read_button_colors(db, inst, theme, theme->btn_close, "close");
@@ -752,10 +753,10 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
                          a_disabled_unfocused_tmp);
     READ_APPEARANCE_COPY("window.active.button.close.hover.bg",
                          theme->btn_close->a_hover_focused, TRUE,
-                         theme->btn_close->a_focused_unpressed);
+                         a_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.close.hover.bg",
                          theme->btn_close->a_hover_unfocused, TRUE,
-                         theme->btn_close->a_unfocused_unpressed);
+                         a_hover_unfocused_tmp);
 
     /* desk button */
     read_button_colors(db, inst, theme, theme->btn_desk, "desk");
@@ -783,36 +784,36 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
     /* hover */
     READ_APPEARANCE_COPY("window.active.button.desk.hover.bg",
                          theme->btn_desk->a_hover_focused, TRUE,
-                         theme->btn_desk->a_focused_unpressed);
+                         a_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.desk.hover.bg",
                          theme->btn_desk->a_hover_unfocused, TRUE,
-                         theme->btn_desk->a_unfocused_unpressed);
+                         a_hover_unfocused_tmp);
 
     /* toggled unpressed */
     READ_APPEARANCE_("window.active.button.desk.toggled.unpressed.bg",
                      "window.active.button.desk.toggled.bg",
                      theme->btn_desk->a_toggled_focused_unpressed, TRUE,
-                     theme->btn_desk->a_focused_pressed);
+                     a_toggled_focused_unpressed_tmp);
     READ_APPEARANCE_("window.inactive.button.desk.toggled.unpressed.bg",
                      "window.inactive.button.desk.toggled.bg",
                      theme->btn_desk->a_toggled_unfocused_unpressed, TRUE,
-                     theme->btn_desk->a_unfocused_pressed);
+                     a_toggled_unfocused_unpressed_tmp);
 
     /* toggled pressed */
     READ_APPEARANCE_COPY("window.active.button.desk.toggled.pressed.bg",
                          theme->btn_desk->a_toggled_focused_pressed, TRUE,
-                         theme->btn_desk->a_focused_pressed);
+                         a_toggled_focused_pressed_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.pressed.bg",
                          theme->btn_desk->a_toggled_unfocused_pressed, TRUE,
-                         theme->btn_desk->a_unfocused_pressed);
+                         a_toggled_unfocused_pressed_tmp);
 
     /* toggled hover */
     READ_APPEARANCE_COPY("window.active.button.desk.toggled.hover.bg",
                          theme->btn_desk->a_toggled_hover_focused, TRUE,
-                         theme->btn_desk->a_toggled_focused_unpressed);
+                         a_toggled_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.hover.bg",
                          theme->btn_desk->a_toggled_hover_unfocused, TRUE,
-                         theme->btn_desk->a_toggled_unfocused_unpressed);
+                         a_toggled_hover_unfocused_tmp);
 
     /* shade button */
     read_button_colors(db, inst, theme, theme->btn_shade, "shade");
@@ -840,36 +841,36 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
     /* hover */
     READ_APPEARANCE_COPY("window.active.button.shade.hover.bg",
                          theme->btn_shade->a_hover_focused, TRUE,
-                         theme->btn_shade->a_focused_unpressed);
+                         a_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.shade.hover.bg",
                          theme->btn_shade->a_hover_unfocused, TRUE,
-                         theme->btn_shade->a_unfocused_unpressed);
+                         a_hover_unfocused_tmp);
 
     /* toggled unpressed */
     READ_APPEARANCE_("window.active.button.shade.toggled.unpressed.bg",
                      "window.active.button.shade.toggled.bg",
                      theme->btn_shade->a_toggled_focused_unpressed, TRUE,
-                     theme->btn_shade->a_focused_pressed);
+                     a_toggled_focused_unpressed_tmp);
     READ_APPEARANCE_("window.inactive.button.shade.toggled.unpressed.bg",
                      "window.inactive.button.shade.toggled.bg",
                      theme->btn_shade->a_toggled_unfocused_unpressed, TRUE,
-                     theme->btn_shade->a_unfocused_pressed);
+                     a_toggled_unfocused_unpressed_tmp);
 
     /* toggled pressed */
     READ_APPEARANCE_COPY("window.active.button.shade.toggled.pressed.bg",
                          theme->btn_shade->a_toggled_focused_pressed, TRUE,
-                         theme->btn_shade->a_focused_pressed);
+                         a_toggled_focused_pressed_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.pressed.bg",
                          theme->btn_shade->a_toggled_unfocused_pressed, TRUE,
-                         theme->btn_shade->a_unfocused_pressed);
+                         a_toggled_unfocused_pressed_tmp);
 
     /* toggled hover */
     READ_APPEARANCE_COPY("window.active.button.shade.toggled.hover.bg",
                          theme->btn_shade->a_toggled_hover_focused, TRUE,
-                         theme->btn_shade->a_toggled_focused_unpressed);
+                         a_toggled_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.hover.bg",
                          theme->btn_shade->a_toggled_hover_unfocused, TRUE,
-                         theme->btn_shade->a_toggled_unfocused_unpressed);
+                         a_toggled_hover_unfocused_tmp);
 
     /* iconify button */
     read_button_colors(db, inst, theme, theme->btn_iconify, "iconify");
@@ -894,10 +895,10 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
                          a_disabled_unfocused_tmp);
     READ_APPEARANCE_COPY("window.active.button.iconify.hover.bg",
                          theme->btn_iconify->a_hover_focused, TRUE,
-                         theme->btn_iconify->a_focused_unpressed);
+                         a_hover_focused_tmp);
     READ_APPEARANCE_COPY("window.inactive.button.iconify.hover.bg",
                          theme->btn_iconify->a_hover_unfocused, TRUE,
-                         theme->btn_iconify->a_unfocused_unpressed);
+                         a_hover_unfocused_tmp);
 
     /* osd buttons */
     READ_APPEARANCE_COPY("osd.button.unpressed.bg", theme->osd_unpressed_button, TRUE, a_focused_unpressed_tmp);
index 9613c51b36dda4f86cddabfc19bec7a4033cc9a6..60da51d33bb728021e451df9e0fd8a291ec330b8 100644 (file)
 
 G_BEGIN_DECLS
 
-/*! Setup to do a binary search on an array holding elements of type @t */
-#define BSEARCH_SETUP(t) \
-    register t l_BSEARCH, r_BSEARCH, out_BSEARCH;
+/*! Setup to do a binary search on an array. */
+#define BSEARCH_SETUP() \
+    register guint l_BSEARCH, r_BSEARCH, out_BSEARCH;
+
+/*! Helper macro that just returns the input */
+#define BSEARCH_IS_T(t) (t)
 
 /*! Search an array @ar holding elements of type @t, starting at index @start,
   with @size elements, looking for value @val. */
-#define BSEARCH(t, ar, start, size, val)         \
+#define BSEARCH(t, ar, start, size, val) \
+    BSEARCH_CMP(t, ar, start, size, val, BSEARCH_IS_T)
+
+/*! Search an array @ar, starting at index @start,
+  with @size elements, looking for value @val of type @t,
+  using the macro @to_t to convert values in the arrau @ar to type @t.*/
+#define BSEARCH_CMP(t, ar, start, size, val, to_t)  \
 { \
     l_BSEARCH = (start);              \
     r_BSEARCH = (start)+(size)-1;     \
@@ -37,10 +46,10 @@ G_BEGIN_DECLS
         /* m is in the middle, but to the left if there's an even number \
            of elements */ \
         out_BSEARCH = l_BSEARCH + (r_BSEARCH - l_BSEARCH)/2;      \
-        if ((val) == (ar)[out_BSEARCH]) {                           \
+        if ((val) == to_t((ar)[out_BSEARCH])) {             \
             break; \
         } \
-        else if ((val) < (ar)[out_BSEARCH] && out_BSEARCH > 0) {     \
+        else if ((val) < to_t((ar)[out_BSEARCH]) && out_BSEARCH > 0) {   \
             r_BSEARCH = out_BSEARCH-1; /* search to the left side */ \
         } \
         else \
index ef2678b529f15a8ab9811e798a8ceb06621d0d7f..4e84f4819e24a8b872cbbbcf52a4b72a477461aa 100644 (file)
@@ -49,7 +49,7 @@ void obt_keyboard_context_renew(ObtIC *ic);
 static XModifierKeymap *modmap;
 static KeySym *keymap;
 static gint min_keycode, max_keycode, keysyms_per_keycode;
-/* This is a bitmask of the different masks for each modifier key */
+/*! This is a bitmask of the different masks for each modifier key */
 static guchar modkeys_keys[OBT_KEYBOARD_NUM_MODKEYS];
 
 static gboolean alt_l = FALSE;
@@ -190,40 +190,20 @@ void xim_init(void)
     g_free(aname);
 }
 
-ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e)
+guint obt_keyboard_keyevent_to_modmask(XEvent *e)
 {
-    KeySym sym;
+    gint i, masknum;
 
     g_return_val_if_fail(e->type == KeyPress || e->type == KeyRelease,
                          OBT_KEYBOARD_MODKEY_NONE);
 
-    XLookupString(&e->xkey, NULL, 0, &sym, NULL);
-
-    switch (sym) {
-    case XK_Num_Lock: return OBT_KEYBOARD_MODKEY_NUMLOCK;
-    case XK_Scroll_Lock: return OBT_KEYBOARD_MODKEY_SCROLLLOCK;
-    case XK_Caps_Lock: return OBT_KEYBOARD_MODKEY_SHIFT;
-    case XK_Alt_L:
-    case XK_Alt_R: return OBT_KEYBOARD_MODKEY_ALT;
-    case XK_Super_L:
-    case XK_Super_R: return OBT_KEYBOARD_MODKEY_SUPER;
-    case XK_Hyper_L:
-    case XK_Hyper_R: return OBT_KEYBOARD_MODKEY_HYPER;
-    case XK_Meta_L:
-    case XK_Meta_R: return OBT_KEYBOARD_MODKEY_META;
-    case XK_Control_L:
-    case XK_Control_R: return OBT_KEYBOARD_MODKEY_CONTROL;
-    case XK_Shift_L:
-    case XK_Shift_R: return OBT_KEYBOARD_MODKEY_SHIFT;
-    default: return OBT_KEYBOARD_MODKEY_NONE;
-    }
-}
-
-guint obt_keyboard_keyevent_to_modmask(XEvent *e)
-{
-    g_return_val_if_fail(e->type == KeyPress || e->type == KeyRelease, 0);
-
-    return obt_keyboard_modkey_to_modmask(obt_keyboard_keyevent_to_modkey(e));
+    for (masknum = 0; masknum < NUM_MASKS; ++masknum)
+        for (i = 0; i < modmap->max_keypermod; ++i) {
+            KeyCode c = modmap->modifiermap[masknum*modmap->max_keypermod + i];
+            if (c == e->xkey.keycode)
+                return 1<<masknum;
+        }
+    return 0;
 }
 
 guint obt_keyboard_only_modmasks(guint mask)
index 868cccf48d9922afbbc69fbb6c6723de609ceccf..8f2badf8a6db0f16204dc1149741960e10c15cfe 100644 (file)
@@ -59,9 +59,6 @@ guint obt_keyboard_only_modmasks(guint mask);
   right keys when there are both. */
 guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key);
 
-/*! Get the modifier key which was pressed or released in a keyboard event */
-ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e);
-
 /*! Convert a KeySym to all the KeyCodes which generate it. */
 KeyCode* obt_keyboard_keysym_to_keycode(KeySym sym);
 
index d2e230dffd028e39cf144b6811c559536eda2e0c..25cb6b0eb58c7a4c415f3c24dd7dd2d9d8da8498 100644 (file)
@@ -88,8 +88,10 @@ static GSList* split_paths(const gchar *paths)
     if (!paths)
         return NULL;
     spl = g_strsplit(paths, ":", -1);
-    for (it = spl; *it; ++it)
-        list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
+    for (it = spl; *it; ++it) {
+        if ((*it)[0]) /* skip empty strings */
+            list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
+    }
     g_free(spl);
     return list;
 }
@@ -333,7 +335,7 @@ static inline gboolean try_exec(const ObtPaths *const p,
                                 const gchar *const path)
 {
     struct stat st;
-    BSEARCH_SETUP(guint);
+    BSEARCH_SETUP();
 
     if (stat(path, &st) != 0)
         return FALSE;
@@ -357,7 +359,7 @@ gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path)
         GSList *it;
 
         for (it = p->exec_dirs; it; it = g_slist_next(it)) {
-            gchar *f = g_strdup_printf(it->data, G_DIR_SEPARATOR_S, path);
+            gchar *f = g_build_filename(it->data, path, NULL);
             gboolean e = try_exec(p, f);
             g_free(f);
             if (e) return TRUE;
index 638373fdfd531173a7868e49348ce8ef1d0bdce9..f7919d6c380a64d919d2d909a5f6cede435f5490 100644 (file)
@@ -197,6 +197,8 @@ void obt_prop_startup(void)
     CREATE_(OB_APP_TITLE);
     CREATE_(OB_APP_NAME);
     CREATE_(OB_APP_CLASS);
+    CREATE_(OB_APP_GROUP_NAME);
+    CREATE_(OB_APP_GROUP_CLASS);
     CREATE_(OB_APP_TYPE);
 }
 
index b30232e9d8282d455e1ae78ae3d18d6614eebd18..acb5c956ede42d3c1ca2182de40218e5d071df1c 100644 (file)
@@ -219,6 +219,8 @@ typedef enum {
     OBT_PROP_OB_APP_TITLE,
     OBT_PROP_OB_APP_NAME,
     OBT_PROP_OB_APP_CLASS,
+    OBT_PROP_OB_APP_GROUP_NAME,
+    OBT_PROP_OB_APP_GROUP_CLASS,
     OBT_PROP_OB_APP_TYPE,
 
     OBT_PROP_NUM_ATOMS
index c872912926405959ecd85063b9feb317e225af4d..5b7e77b5cb95c8b2ce94b5efca40cffed2593faa 100644 (file)
--- a/obt/xml.c
+++ b/obt/xml.c
@@ -112,6 +112,11 @@ void obt_xml_register(ObtXmlInst *i, const gchar *tag,
     g_hash_table_insert(i->callbacks, c->tag, c);
 }
 
+void obt_xml_unregister(ObtXmlInst *i, const gchar *tag)
+{
+    g_hash_table_remove(i->callbacks, tag);
+}
+
 static gboolean load_file(ObtXmlInst *i,
                           const gchar *domain,
                           const gchar *filename,
@@ -316,16 +321,22 @@ void obt_xml_tree_from_root(ObtXmlInst *i)
     obt_xml_tree(i, i->root->children);
 }
 
-gchar *obt_xml_node_string(xmlNodePtr node)
+gchar *obt_xml_node_string_unstripped(xmlNodePtr node)
 {
     xmlChar *c = xmlNodeGetContent(node);
     gchar *s;
-    if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */
     s = g_strdup(c ? (gchar*)c : "");
     xmlFree(c);
     return s;
 }
 
+gchar *obt_xml_node_string(xmlNodePtr node)
+{
+    gchar* result = obt_xml_node_string_unstripped(node);
+    g_strstrip(result); /* strip leading/trailing whitespace */
+    return result;
+}
+
 gint obt_xml_node_int(xmlNodePtr node)
 {
     xmlChar *c = xmlNodeGetContent(node);
@@ -408,13 +419,12 @@ gboolean obt_xml_attr_int(xmlNodePtr node, const gchar *name, gint *value)
     return r;
 }
 
-gboolean obt_xml_attr_string(xmlNodePtr node, const gchar *name,
-                             gchar **value)
+gboolean obt_xml_attr_string_unstripped(xmlNodePtr node, const gchar *name,
+                                        gchar **value)
 {
     xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
     gboolean r = FALSE;
     if (c) {
-        g_strstrip((char*)c); /* strip leading/trailing whitespace */
         *value = g_strdup((gchar*)c);
         r = TRUE;
     }
@@ -422,6 +432,15 @@ gboolean obt_xml_attr_string(xmlNodePtr node, const gchar *name,
     return r;
 }
 
+gboolean obt_xml_attr_string(xmlNodePtr node, const gchar *name,
+                             gchar **value)
+{
+    gboolean result = obt_xml_attr_string_unstripped(node, name, value);
+    if (result)
+        g_strstrip(*value); /* strip leading/trailing whitespace */
+    return result;
+}
+
 gboolean obt_xml_attr_contains(xmlNodePtr node, const gchar *name,
                                const gchar *val)
 {
index ac2dc57f3eab64a828e2ed2ef820f74980bed7ff..831aba63714a5cf5e5f69d8a77c599ae30ae5793 100644 (file)
--- a/obt/xml.h
+++ b/obt/xml.h
@@ -62,6 +62,7 @@ void obt_xml_close(ObtXmlInst *inst);
 
 void obt_xml_register(ObtXmlInst *inst, const gchar *tag,
                       ObtXmlCallback func, gpointer data);
+void obt_xml_unregister(ObtXmlInst *inst, const gchar *tag);
 void obt_xml_tree(ObtXmlInst *i, xmlNodePtr node);
 void obt_xml_tree_from_root(ObtXmlInst *i);
 
@@ -71,12 +72,15 @@ void obt_xml_tree_from_root(ObtXmlInst *i);
 xmlNodePtr obt_xml_find_node   (xmlNodePtr node, const gchar *name);
 
 gboolean obt_xml_node_contains (xmlNodePtr node, const gchar *val);
+gchar   *obt_xml_node_string_unstripped(xmlNodePtr node);
 gchar   *obt_xml_node_string   (xmlNodePtr node);
 gint     obt_xml_node_int      (xmlNodePtr node);
 gboolean obt_xml_node_bool     (xmlNodePtr node);
 
 gboolean obt_xml_attr_contains (xmlNodePtr node, const gchar *name,
                                 const gchar *val);
+gboolean obt_xml_attr_string_unstripped(xmlNodePtr node, const gchar *name,
+                                        gchar **value);
 gboolean obt_xml_attr_string   (xmlNodePtr node, const gchar *name,
                                 gchar **value);
 gboolean obt_xml_attr_int      (xmlNodePtr node, const gchar *name,
index ee9d55f1bf8f42936c41594d263e0c2fab07d0a2..ac849a9741b794070537ef1094e35787985628cd 100644 (file)
@@ -51,6 +51,7 @@ struct _ObActionsDefinition {
     ObActionsDataFreeFunc free;
     ObActionsRunFunc run;
     ObActionsShutdownFunc shutdown;
+    gboolean modifies_focused_window;
 };
 
 struct _ObActionsAct {
@@ -103,12 +104,13 @@ ObActionsDefinition* do_register(const gchar *name,
             return NULL;
     }
 
-    def = g_slice_new(ObActionsDefinition);
+    def = g_slice_new0(ObActionsDefinition);
     def->ref = 1;
     def->name = g_strdup(name);
     def->free = free;
     def->run = run;
     def->shutdown = NULL;
+    def->modifies_focused_window = TRUE;
 
     registered = g_slist_prepend(registered, def);
     return def;
@@ -156,6 +158,22 @@ gboolean actions_set_shutdown(const gchar *name,
     return FALSE;
 }
 
+gboolean actions_set_modifies_focused_window(const gchar *name,
+                                             gboolean modifies)
+{
+    GSList *it;
+    ObActionsDefinition *def;
+
+    for (it = registered; it; it = g_slist_next(it)) {
+        def = it->data;
+        if (!g_ascii_strcasecmp(name, def->name)) {
+            def->modifies_focused_window = modifies;
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
 static void actions_definition_ref(ObActionsDefinition *def)
 {
     ++def->ref;
@@ -340,8 +358,11 @@ void actions_run_acts(GSList *acts,
             if (!act->def->run(&data, act->options)) {
                 if (actions_act_is_interactive(act))
                     actions_interactive_end_act();
-                if (client && client == focus_client)
+                if (client && client == focus_client &&
+                    act->def->modifies_focused_window)
+                {
                     update_user_time = TRUE;
+                }
             } else {
                 /* make sure its interactive if it returned TRUE */
                 g_assert(act->i_input);
@@ -375,7 +396,7 @@ static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state)
         interactive_act = act;
         actions_act_ref(interactive_act);
 
-        interactive_initial_state = obt_keyboard_only_modmasks(state);
+        interactive_initial_state = state;
 
         /* if using focus_delay, stop the timer now so that focus doesn't go
            moving on us, which would kill the action */
@@ -453,7 +474,7 @@ void actions_client_move(ObActionsData *data, gboolean start)
                     ob_debug_type(OB_DEBUG_FOCUS,
                                   "Generating fake leave because we did a "
                                   "mouse-event action");
-                    event_enter_client(data->client);
+                    event_leave_client(data->client);
                 }
             }
         }
index e03bc577742fd0eb92d03019c53061b1a3ea9393..f413ad8284386389015d07d43197a42ae8ad8e68 100644 (file)
@@ -82,6 +82,8 @@ gboolean actions_register(const gchar *name,
 
 gboolean actions_set_shutdown(const gchar *name,
                               ObActionsShutdownFunc shutdown);
+gboolean actions_set_modifies_focused_window(const gchar *name,
+                                             gboolean modifies);
 
 ObActionsAct* actions_parse(xmlNodePtr node);
 ObActionsAct* actions_parse_string(const gchar *name);
index 5f0db27cb5173c0f6e09a9a9e944e428548bb945..f834951502a0db96393ee3b21ccbd64071a9ee59 100644 (file)
@@ -16,6 +16,7 @@ typedef struct {
     gboolean forward;
     gboolean bar;
     gboolean raise;
+    gboolean interactive;
     ObFocusCyclePopupMode dialog_mode;
     GSList *actions;
 
@@ -69,6 +70,7 @@ static gpointer setup_func(xmlNodePtr node,
     o = g_slice_new0(Options);
     o->bar = TRUE;
     o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_LIST;
+    o->interactive = TRUE;
 
     if ((n = obt_xml_find_node(node, "linear")))
         o->linear = obt_xml_node_bool(n);
@@ -80,6 +82,8 @@ static gpointer setup_func(xmlNodePtr node,
         else if (obt_xml_node_contains(n, "icons"))
             o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_ICONS;
     }
+    if ((n = obt_xml_find_node(node, "interactive")))
+        o->interactive = obt_xml_node_bool(n);
     if ((n = obt_xml_find_node(node, "bar")))
         o->bar = obt_xml_node_bool(n);
     if ((n = obt_xml_find_node(node, "raise")))
@@ -157,21 +161,24 @@ static gboolean run_func(ObActionsData *data, gpointer options)
     Options *o = options;
     struct _ObClient *ft;
 
-    ft = focus_cycle(o->forward,
-                     o->all_desktops,
-                     !o->only_hilite_windows,
-                     o->dock_windows,
-                     o->desktop_windows,
-                     o->linear,
-                     TRUE,
-                     o->bar,
-                     o->dialog_mode,
-                     FALSE, FALSE);
+    gboolean done = FALSE;
+    gboolean cancel = FALSE;
+
+    ft = focus_cycle(
+        o->forward,
+        o->all_desktops,
+        !o->only_hilite_windows,
+        o->dock_windows,
+        o->desktop_windows,
+        o->linear,
+        (o->interactive ? o->bar : FALSE),
+        (o->interactive ? o->dialog_mode : OB_FOCUS_CYCLE_POPUP_MODE_NONE),
+        done, cancel);
 
     stacking_restore();
     if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
 
-    return TRUE;
+    return o->interactive;
 }
 
 static gboolean i_input_func(guint initial_state,
@@ -181,8 +188,9 @@ static gboolean i_input_func(guint initial_state,
                              gboolean *used)
 {
     Options *o = options;
-    guint mods;
+    guint mods, initial_mods;
 
+    initial_mods = obt_keyboard_only_modmasks(initial_state);
     mods = obt_keyboard_only_modmasks(e->xkey.state);
     if (e->type == KeyRelease) {
         /* remove from the state the mask of the modifier key being
@@ -201,14 +209,14 @@ static gboolean i_input_func(guint initial_state,
         }
 
         /* There were no modifiers and they pressed enter */
-        else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
+        else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods) {
             o->cancel = FALSE;
             o->state = e->xkey.state;
             return FALSE;
         }
     }
     /* They released the modifiers */
-    else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+    else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
     {
         o->cancel = FALSE;
         o->state = e->xkey.state;
@@ -230,16 +238,17 @@ static void i_post_func(gpointer options)
     Options *o = options;
     struct _ObClient *ft;
 
+    gboolean done = TRUE;
+
     ft = focus_cycle(o->forward,
                      o->all_desktops,
                      !o->only_hilite_windows,
                      o->dock_windows,
                      o->desktop_windows,
                      o->linear,
-                     TRUE,
                      o->bar,
                      o->dialog_mode,
-                     TRUE, o->cancel);
+                     done, o->cancel);
 
     if (ft)
         actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
index a3a1f6b9c121b00f6158a5ef42d14a1fb6681d7f..8dadf550af3b2ea2787015be50528e48ce8c7a2d 100644 (file)
@@ -319,8 +319,9 @@ static gboolean i_input_func(guint initial_state,
                              gpointer options,
                              gboolean *used)
 {
-    guint mods;
+    guint mods, initial_mods;
 
+    initial_mods = obt_keyboard_only_modmasks(initial_state);
     mods = obt_keyboard_only_modmasks(e->xkey.state);
     if (e->type == KeyRelease) {
         /* remove from the state the mask of the modifier key being
@@ -336,11 +337,11 @@ static gboolean i_input_func(guint initial_state,
             return FALSE;
 
         /* There were no modifiers and they pressed enter */
-        else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state)
+        else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods)
             return FALSE;
     }
     /* They released the modifiers */
-    else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+    else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
     {
         return FALSE;
     }
@@ -350,7 +351,8 @@ static gboolean i_input_func(guint initial_state,
 
 static gboolean i_pre_func(guint initial_state, gpointer options)
 {
-    if (!initial_state) {
+    guint initial_mods = obt_keyboard_only_modmasks(initial_state);
+    if (!initial_mods) {
         Options *o = options;
         o->interactive = FALSE;
         return FALSE;
index 602e7edc33fb1dc26b67fc5fe28fc8335b5d64cc..0ad464b735837bc6e18b3b2030f5c180442e0ef4 100644 (file)
@@ -259,8 +259,9 @@ static gboolean i_input_func(guint initial_state,
                              gpointer options,
                              gboolean *used)
 {
-    guint mods;
+    guint mods, initial_mods;
 
+    initial_mods = obt_keyboard_only_modmasks(initial_state);
     mods = obt_keyboard_only_modmasks(e->xkey.state);
     if (e->type == KeyRelease) {
         /* remove from the state the mask of the modifier key being
@@ -278,14 +279,13 @@ static gboolean i_input_func(guint initial_state,
         }
 
         /* There were no modifiers and they pressed enter */
-        else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
+        else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods) {
             end_cycle(FALSE, e->xkey.state, options);
             return FALSE;
         }
     }
     /* They released the modifiers */
-    else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
-    {
+    else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods)) {
         end_cycle(FALSE, e->xkey.state, options);
         return FALSE;
     }
index 380ffa008996936d4484d3d6bb15914cf861417c..2f76c45d1835b7e79b02c0aab5d73e258cba3ccd 100644 (file)
@@ -33,6 +33,7 @@ void action_execute_startup(void)
 {
     actions_register("Execute", setup_func, free_func, run_func);
     actions_set_shutdown("Execute", shutdown_func);
+    actions_set_modifies_focused_window("Execute", FALSE);
 
     client_add_destroy_notify(client_dest, NULL);
 }
@@ -130,6 +131,103 @@ static void prompt_cleanup(ObPrompt *p, gpointer options)
     free_func(options);
 }
 
+/* Replace occurrences of $variables */
+static gchar* expand_variables(gchar* cmd, ObActionsData* data)
+{
+    gchar *c, *before, *expand;
+
+    expand = NULL;
+    before = cmd;
+
+    while ((c = strchr(before, '$'))) {
+        if ((c[1] == 'p' || c[1] == 'P') &&
+            (c[2] == 'i' || c[2] == 'I') &&
+            (c[3] == 'd' || c[3] == 'D') &&
+            !g_ascii_isalnum(c[4]))
+        {
+            /* found $pid */
+            gchar *tmp;
+
+            *c = '\0';
+            tmp = expand;
+            expand = g_strdup_printf("%s%s%u",
+                                     (expand ? expand : ""),
+                                     before,
+                                     data->client ? data->client->pid : 0);
+            g_free(tmp);
+
+            before = c + 4; /* 4 = strlen("$pid") */
+        }
+        else if ((c[1] == 'w' || c[1] == 'W') &&
+                 (c[2] == 'i' || c[2] == 'I') &&
+                 (c[3] == 'd' || c[3] == 'D') &&
+                 !g_ascii_isalnum(c[4]))
+        {
+            /* found $wid */
+            gchar *tmp;
+
+            *c = '\0';
+            tmp = expand;
+            expand = g_strdup_printf("%s%s%lu",
+                                     (expand ? expand : ""),
+                                     before,
+                                     data->client ? data->client->window : 0);
+            g_free(tmp);
+
+            before = c + 4; /* 4 = strlen("$wid") */
+        }
+        else if ((c[1] == 'p' || c[1] == 'P') &&
+                 (c[2] == 'o' || c[2] == 'O') &&
+                 (c[3] == 'i' || c[3] == 'I') &&
+                 (c[4] == 'n' || c[4] == 'N') &&
+                 (c[5] == 't' || c[5] == 'T') &&
+                 (c[6] == 'e' || c[6] == 'E') &&
+                 (c[7] == 'r' || c[7] == 'R') &&
+                 !g_ascii_isalnum(c[8]))
+        {
+            /* found $pointer */
+            gchar *tmp;
+
+            *c = '\0';
+            tmp = expand;
+            expand = g_strdup_printf("%s%s%u %u",
+                                     (expand ? expand : ""),
+                                     before,
+                                     data->x, data->y);
+            g_free(tmp);
+
+            before = c + 8; /* 4 = strlen("$pointer") */
+        }
+        else {
+            /* found something unknown, copy the $ and continue */
+            gchar *tmp;
+
+            *c = '\0';
+            tmp = expand;
+            expand = g_strdup_printf("%s%s$",
+                                     (expand ? expand : ""),
+                                     before);
+            g_free(tmp);
+
+            before = c + 1; /* 1 = strlen("$") */
+        }
+    }
+
+    if (expand) {
+        gchar *tmp;
+
+        /* add on the end of the string after the last replacement */
+        tmp = expand;
+        expand = g_strconcat(expand, before, NULL);
+        g_free(tmp);
+
+        /* replace the command with the expanded one */
+        g_free(cmd);
+        cmd = expand;
+    }
+    return cmd;
+}
+
 /* Always return FALSE because its not interactive */
 static gboolean run_func(ObActionsData *data, gpointer options)
 {
@@ -162,68 +260,7 @@ static gboolean run_func(ObActionsData *data, gpointer options)
         return FALSE;
     }
 
-    if (data->client) {
-        gchar *c, *before, *expand;
-
-        /* replace occurrences of $pid and $wid */
-
-        expand = NULL;
-        before = cmd;
-
-        while ((c = strchr(before, '$'))) {
-            if ((c[1] == 'p' || c[1] == 'P') &&
-                (c[2] == 'i' || c[2] == 'I') &&
-                (c[3] == 'd' || c[3] == 'D') &&
-                !g_ascii_isalnum(c[4]))
-            {
-                /* found $pid */
-                gchar *tmp;
-
-                *c = '\0';
-                tmp = expand;
-                expand = g_strdup_printf("%s%s%u",
-                                         (expand ? expand : ""),
-                                         before,
-                                         data->client->pid);
-                g_free(tmp);
-
-                before = c + 4; /* 4 = strlen("$pid") */
-            }
-            else if ((c[1] == 'w' || c[1] == 'W') &&
-                     (c[2] == 'i' || c[2] == 'I') &&
-                     (c[3] == 'd' || c[3] == 'D') &&
-                     !g_ascii_isalnum(c[4]))
-            {
-                /* found $wid */
-                gchar *tmp;
-
-                *c = '\0';
-                tmp = expand;
-                expand = g_strdup_printf("%s%s%lu",
-                                         (expand ? expand : ""),
-                                         before,
-                                         data->client->window);
-                g_free(tmp);
-
-                before = c + 4; /* 4 = strlen("$wid") */
-            }
-            else
-                before = c + 1; /* no infinite loops plz */
-        }
-
-        if (expand) {
-            gchar *tmp;
-
-            /* add on the end of the string after the last replacement */
-            tmp = expand;
-            expand = g_strconcat(expand, before, NULL);
-            g_free(tmp);
-
-            /* replace the command with the expanded one */
-            g_free(cmd);
-            cmd = expand;
-        }
-    }
+    cmd = expand_variables(cmd, data);
 
     /* If there is a keyboard grab going on then we need to cancel
        it so the application can grab things */
index c23661cd401c2fd37645455456de69ba5f60e467..4b3f269986daa2e867f11c53b143f70181656e30 100644 (file)
@@ -19,6 +19,8 @@ typedef struct {
     gint h;
     gint h_denom;
     gint monitor;
+    gboolean w_sets_client_size;
+    gboolean h_sets_client_size;
 } Options;
 
 static gpointer setup_func(xmlNodePtr node);
@@ -57,12 +59,16 @@ static gpointer setup_func(xmlNodePtr node)
         if (g_ascii_strcasecmp(s, "current") != 0)
             config_parse_relative_number(s, &o->w, &o->w_denom);
         g_free(s);
+
+        obt_xml_attr_bool(n, "client", &o->w_sets_client_size);
     }
     if ((n = obt_xml_find_node(node, "height"))) {
         gchar *s = obt_xml_node_string(n);
         if (g_ascii_strcasecmp(s, "current") != 0)
             config_parse_relative_number(s, &o->h, &o->h_denom);
         g_free(s);
+
+        obt_xml_attr_bool(n, "client", &o->h_sets_client_size);
     }
 
     if ((n = obt_xml_find_node(node, "monitor"))) {
@@ -118,14 +124,31 @@ static gboolean run_func(ObActionsData *data, gpointer options)
         area = screen_area(c->desktop, mon, NULL);
         carea = screen_area(c->desktop, cmon, NULL);
 
+        /* find a target size for the client/frame. */
         w = o->w;
-        if (w == G_MININT) w = c->area.width;
+        if (w == G_MININT) {
+            if (o->w_sets_client_size)
+                w = c->area.width;
+            else
+                w = c->frame->area.width;
+        }
         else if (o->w_denom) w = (w * area->width) / o->w_denom;
 
         h = o->h;
-        if (h == G_MININT) h = c->area.height;
+        if (h == G_MININT) {
+            if (o->w_sets_client_size)
+                h = c->area.height;
+            else
+                h = c->frame->area.height;
+        }
         else if (o->h_denom) h = (h * area->height) / o->h_denom;
 
+        /* get back to the client's size. */
+        if (!o->w_sets_client_size)
+            w -= c->frame->size.left + c->frame->size.right;
+        if (!o->h_sets_client_size)
+            h -= c->frame->size.top + c->frame->size.bottom;
+
         /* it might not be able to resize how they requested, so find out what
            it will actually be resized to */
         x = c->area.x;
index e32aff32d55e417888ae26985fcc7226f144a4bc..a4739104631f22a510469cb99b1dbe8e273b394e 100644 (file)
@@ -70,24 +70,34 @@ static gboolean run_func(ObActionsData *data, gpointer options)
         gint left = o->left, right = o->right, top = o->top, bottom = o->bottom;
 
         if (o->left_denom)
-            left = (left * c->area.width / c->size_inc.width) / o->left_denom;
+            left = left * c->area.width / o->left_denom;
         if (o->right_denom)
-            right = (right * c->area.width / c->size_inc.width) / o->right_denom;
+            right = right * c->area.width / o->right_denom;
         if (o->top_denom)
-            top = (top * c->area.height / c->size_inc.height) / o->top_denom;
+            top = top * c->area.height / o->top_denom;
         if (o->bottom_denom)
-            bottom = (bottom * c->area.height / c->size_inc.height) / o->bottom_denom;
-
+            bottom = bottom * c->area.height / o->bottom_denom;
+
+        if (left && ABS(left) < c->size_inc.width)
+            left = left < 0 ? -c->size_inc.width : c->size_inc.width;
+        if (right && ABS(right) < c->size_inc.width)
+            right = right < 0 ? -c->size_inc.width : c->size_inc.width;
+        if (top && ABS(top) < c->size_inc.height)
+            top = top < 0 ? -c->size_inc.height : c->size_inc.height;
+        if (bottom && ABS(bottom) < c->size_inc.height)
+            bottom = bottom < 0 ? -c->size_inc.height : c->size_inc.height;
+
+        // When resizing, if the resize has a non-zero value then make sure it
+        // is at least as big as the size increment so the window does actually
+        // resize.
         x = c->area.x;
         y = c->area.y;
         ow = c->area.width;
-        xoff = -left * c->size_inc.width;
-        nw = ow + right * c->size_inc.width
-            + left * c->size_inc.width;
+        xoff = -left;
+        nw = ow + right + left;
         oh = c->area.height;
-        yoff = -top * c->size_inc.height;
-        nh = oh + bottom * c->size_inc.height
-            + top * c->size_inc.height;
+        yoff = -top;
+        nh = oh + bottom + top;
 
         client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
         xoff = xoff == 0 ? 0 :
index 46162c319791a118aab0b2c8bcf1f057d3480152..1478d840f832265471d208fac1fead0b3199ec64 100644 (file)
@@ -167,6 +167,21 @@ void client_remove_destroy_notify(ObClientCallback func)
     }
 }
 
+void client_remove_destroy_notify_data(ObClientCallback func, gpointer data)
+{
+    GSList *it;
+
+    for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+        ClientCallback *d = it->data;
+        if (d->func == func && d->data == data) {
+            g_slice_free(ClientCallback, d);
+            client_destroy_notifies =
+                g_slist_delete_link(client_destroy_notifies, it);
+            break;
+        }
+    }
+}
+
 void client_set_list(void)
 {
     Window *windows, *win_it;
@@ -203,6 +218,7 @@ void client_manage(Window window, ObPrompt *prompt)
     Time launch_time;
     guint32 user_time;
     gboolean obplaced;
+    gulong ignore_start;
 
     ob_debug("Managing window: 0x%lx", window);
 
@@ -234,6 +250,8 @@ void client_manage(Window window, ObPrompt *prompt)
     ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
     ob_debug("Window name: %s class: %s role: %s title: %s",
              self->name, self->class, self->role, self->title);
+    ob_debug("Window group name: %s group class: %s",
+             self->group_name, self->group_class);
 
     /* per-app settings override stuff from client_get_all, and return the
        settings for other uses too. the returned settings is a shallow copy,
@@ -352,8 +370,7 @@ void client_manage(Window window, ObPrompt *prompt)
                      "program + user specified" :
                      "BADNESS !?")))), place.width, place.height);
 
-        obplaced = place_client(self, do_activate, &place.x, &place.y,
-                                settings);
+        obplaced = place_client(self, do_activate, &place, settings);
 
         /* watch for buggy apps that ask to be placed at (0,0) when there is
            a strut there */
@@ -468,19 +485,13 @@ void client_manage(Window window, ObPrompt *prompt)
     /* grab mouse bindings before showing the window */
     mouse_grab_for_client(self, TRUE);
 
+    if (!config_focus_under_mouse)
+        ignore_start = event_start_ignore_all_enters();
+
     /* this has to happen before we try focus the window, but we want it to
        happen after the client's stacking has been determined or it looks bad
     */
-    {
-        gulong ignore_start;
-        if (!config_focus_under_mouse)
-            ignore_start = event_start_ignore_all_enters();
-
-        client_show(self);
-
-        if (!config_focus_under_mouse)
-            event_end_ignore_all_enters(ignore_start);
-    }
+    client_show(self);
 
     /* activate/hilight/raise the window */
     if (try_activate) {
@@ -508,6 +519,9 @@ void client_manage(Window window, ObPrompt *prompt)
             stacking_raise(CLIENT_AS_WINDOW(self));
     }
 
+    if (!config_focus_under_mouse)
+        event_end_ignore_all_enters(ignore_start);
+
     /* add to client list/map */
     client_list = g_list_append(client_list, self);
     window_add(&self->window, CLIENT_AS_WINDOW(self));
@@ -710,6 +724,8 @@ void client_unmanage(ObClient *self)
     g_free(self->name);
     g_free(self->class);
     g_free(self->role);
+    g_free(self->group_name);
+    g_free(self->group_class);
     g_free(self->client_machine);
     g_free(self->sm_client_id);
     g_slice_free(ObClient, self);
@@ -902,15 +918,25 @@ static ObAppSettings *client_get_settings_state(ObClient *self)
 
         g_assert(app->name != NULL || app->class != NULL ||
                  app->role != NULL || app->title != NULL ||
+                 app->group_name != NULL || app->group_class != NULL ||
                  (signed)app->type >= 0);
 
         if (app->name &&
             !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
             match = FALSE;
+        else if (app->group_name &&
+            !g_pattern_match(app->group_name,
+                             strlen(self->group_name), self->group_name, NULL))
+            match = FALSE;
         else if (app->class &&
                  !g_pattern_match(app->class,
                                   strlen(self->class), self->class, NULL))
             match = FALSE;
+        else if (app->group_class &&
+                 !g_pattern_match(app->group_class,
+                                  strlen(self->group_class), self->group_class,
+                                  NULL))
+            match = FALSE;
         else if (app->role &&
                  !g_pattern_match(app->role,
                                   strlen(self->role), self->role, NULL))
@@ -1202,13 +1228,15 @@ static void client_get_all(ObClient *self, gboolean real)
        from per-app settings */
     client_get_session_ids(self);
 
-    /* now we got everything that can affect the decorations */
+    /* get this early so we have it for debugging, also this can be used
+     by app rule matching */
+    client_update_title(self);
+
+    /* now we got everything that can affect the decorations or app rule
+       matching */
     if (!real)
         return;
 
-    /* get this early so we have it for debugging */
-    client_update_title(self);
-
     /* save the values of the variables used for app rule matching */
     client_save_app_rule_values(self);
 
@@ -2357,6 +2385,25 @@ static void client_get_session_ids(ObClient *self)
     if (self->name == NULL) self->name = g_strdup("");
     if (self->class == NULL) self->class = g_strdup("");
 
+    /* get the WM_CLASS (name and class) from the group leader. make them "" if
+       they are not provided */
+    if (leader)
+        got = OBT_PROP_GETSS_TYPE(leader, WM_CLASS, STRING_NO_CC, &ss);
+    else
+        got = FALSE;
+
+    if (got) {
+        if (ss[0]) {
+            self->group_name = g_strdup(ss[0]);
+            if (ss[1])
+                self->group_class = g_strdup(ss[1]);
+        }
+        g_strfreev(ss);
+    }
+
+    if (self->group_name == NULL) self->group_name = g_strdup("");
+    if (self->group_class == NULL) self->group_class = g_strdup("");
+
     /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
     got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
 
@@ -2426,6 +2473,8 @@ static void client_save_app_rule_values(ObClient *self)
     OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
     OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
     OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
+    OBT_PROP_SETS(self->window, OB_APP_GROUP_NAME, self->group_name);
+    OBT_PROP_SETS(self->window, OB_APP_GROUP_CLASS, self->group_class);
     OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
 
     switch (self->type) {
@@ -2766,6 +2815,12 @@ gboolean client_helper(ObClient *self)
             self->type == OB_CLIENT_TYPE_TOOLBAR);
 }
 
+gboolean client_occupies_space(ObClient *self)
+{
+    return !(self->type == OB_CLIENT_TYPE_DESKTOP ||
+             self->type == OB_CLIENT_TYPE_SPLASH);
+}
+
 gboolean client_mouse_focusable(ObClient *self)
 {
     return !(self->type == OB_CLIENT_TYPE_MENU ||
index 99fdfd053fc3a99811bc3a16c23c38a32658b9de..a946f274bc906a19cab39ca66dfa7f8251eb3de3 100644 (file)
@@ -127,6 +127,10 @@ struct _ObClient
     gchar *class;
     /*! The specified role of the window, used for identification */
     gchar *role;
+    /*! The application that created the window's group. */
+    gchar *group_name;
+    /*! The class of the window's group, can used for grouping */
+    gchar *group_class;
     /*! The session client id for the window. *This can be NULL!* */
     gchar *sm_client_id;
 
@@ -328,6 +332,7 @@ typedef void (*ObClientCallback)(ObClient *client, gpointer data);
 /*! Get notified when the client is unmanaged */
 void client_add_destroy_notify(ObClientCallback func, gpointer data);
 void client_remove_destroy_notify(ObClientCallback func);
+void client_remove_destroy_notify_data(ObClientCallback func, gpointer data);
 
 /*! Manages a given window
   @param prompt This specifies an ObPrompt which is being managed.  It is
@@ -364,6 +369,10 @@ gboolean client_normal(ObClient *self);
   (utilty, menu, etc) */
 gboolean client_helper(ObClient *self);
 
+/*! Returns true if the window occupies space in the monitor conceptually, or
+  false if it does not and its presence should be ignored when possible. */
+gboolean client_occupies_space(ObClient *self);
+
 /*! Return if the client is a type which should be given focus from mouse
   presses on the *client* window. This doesn't affect clicking on the
   decorations. This doesn't count for focus cycling, different rules apply to
@@ -495,7 +504,7 @@ void client_fullscreen(ObClient *self, gboolean fs);
 /*! Determine if the window, using the given client-area, would be considered
   as an "oldschool fullscreen" window, that is, if it is filling a whole
   monitor. */
-gboolean client_is_oldfullscreen(const ObClient const *self, const Rect *area);
+gboolean client_is_oldfullscreen(const ObClient *self, const Rect *area);
 
 /*! Iconifies or uniconifies the client window
   @param iconic true if the window should be iconified; false if it should be
index f0f6c7b3de73d7fa25f938aff22a3eeaff55dcb9..c21b47d88554162d189ebc6f2ff66c0ba90265c0 100644 (file)
@@ -37,7 +37,6 @@ gboolean config_focus_under_mouse;
 gboolean config_unfocus_leave;
 
 ObPlacePolicy  config_place_policy;
-gboolean       config_place_center;
 ObPlaceMonitor config_place_monitor;
 
 guint          config_primary_monitor_index;
@@ -158,6 +157,14 @@ void config_app_settings_copy_non_defaults(const ObAppSettings *src,
         dst->position = src->position;
         /* monitor is copied above */
     }
+
+    if (src->size_given) {
+        dst->size_given = TRUE;
+        dst->width_num = src->width_num;
+        dst->width_denom = src->width_denom;
+        dst->height_num = src->height_num;
+        dst->height_denom = src->height_denom;
+    }
 }
 
 void config_parse_relative_number(gchar *s, gint *num, gint *denom)
@@ -204,6 +211,148 @@ void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
   </applications>
 */
 
+static void parse_single_per_app_settings(xmlNodePtr app,
+                                          ObAppSettings *settings)
+{
+    xmlNodePtr n, c;
+    gboolean x_pos_given = FALSE;
+    gboolean width_given = FALSE;
+
+    if ((n = obt_xml_find_node(app->children, "decor")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->decor = obt_xml_node_bool(n);
+
+    if ((n = obt_xml_find_node(app->children, "shade")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->shade = obt_xml_node_bool(n);
+
+    if ((n = obt_xml_find_node(app->children, "position"))) {
+        if ((c = obt_xml_find_node(n->children, "x"))) {
+            if (!obt_xml_node_contains(c, "default")) {
+                config_parse_gravity_coord(c, &settings->position.x);
+                x_pos_given = TRUE;
+            }
+        }
+
+        if (x_pos_given && (c = obt_xml_find_node(n->children, "y"))) {
+            if (!obt_xml_node_contains(c, "default")) {
+                config_parse_gravity_coord(c, &settings->position.y);
+                settings->pos_given = TRUE;
+            }
+        }
+
+        /* monitor can be set without setting x or y */
+        if ((c = obt_xml_find_node(n->children, "monitor"))) {
+            if (!obt_xml_node_contains(c, "default")) {
+                gchar *s = obt_xml_node_string(c);
+                if (!g_ascii_strcasecmp(s, "mouse"))
+                    settings->monitor_type = OB_PLACE_MONITOR_MOUSE;
+                else if (!g_ascii_strcasecmp(s, "active"))
+                    settings->monitor_type = OB_PLACE_MONITOR_ACTIVE;
+                else if (!g_ascii_strcasecmp(s, "primary"))
+                    settings->monitor_type = OB_PLACE_MONITOR_PRIMARY;
+                else
+                    settings->monitor = obt_xml_node_int(c);
+                g_free(s);
+            }
+        }
+
+        obt_xml_attr_bool(n, "force", &settings->pos_force);
+    }
+
+    if ((n = obt_xml_find_node(app->children, "size"))) {
+        if ((c = obt_xml_find_node(n->children, "width"))) {
+            if (!obt_xml_node_contains(c, "default")) {
+                gchar *s = obt_xml_node_string(c);
+                config_parse_relative_number(s,
+                                             &settings->width_num,
+                                             &settings->width_denom);
+                if (settings->width_num > 0 && settings->width_denom >= 0)
+                    width_given = TRUE;
+                g_free(s);
+            }
+        }
+
+        if (width_given && (c = obt_xml_find_node(n->children, "height"))) {
+            gchar *s = obt_xml_node_string(c);
+            config_parse_relative_number(s,
+                                         &settings->height_num,
+                                         &settings->height_denom);
+            if (settings->height_num > 0 && settings->height_denom >= 0)
+                settings->size_given = TRUE;
+            g_free(s);
+        }
+    }
+
+    if ((n = obt_xml_find_node(app->children, "focus"))) {
+        if (!obt_xml_node_contains(n, "default"))
+            settings->focus = obt_xml_node_bool(n);
+    }
+
+    if ((n = obt_xml_find_node(app->children, "desktop"))) {
+        if (!obt_xml_node_contains(n, "default")) {
+            gchar *s = obt_xml_node_string(n);
+            if (!g_ascii_strcasecmp(s, "all"))
+                settings->desktop = DESKTOP_ALL;
+            else {
+                gint i = obt_xml_node_int(n);
+                if (i > 0)
+                    settings->desktop = i;
+            }
+            g_free(s);
+        }
+    }
+
+    if ((n = obt_xml_find_node(app->children, "layer"))) {
+        if (!obt_xml_node_contains(n, "default")) {
+            gchar *s = obt_xml_node_string(n);
+            if (!g_ascii_strcasecmp(s, "above"))
+                settings->layer = 1;
+            else if (!g_ascii_strcasecmp(s, "below"))
+                settings->layer = -1;
+            else
+                settings->layer = 0;
+            g_free(s);
+        }
+    }
+
+    if ((n = obt_xml_find_node(app->children, "iconic")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->iconic = obt_xml_node_bool(n);
+
+    if ((n = obt_xml_find_node(app->children, "skip_pager")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->skip_pager = obt_xml_node_bool(n);
+
+    if ((n = obt_xml_find_node(app->children, "skip_taskbar")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->skip_taskbar = obt_xml_node_bool(n);
+
+    if ((n = obt_xml_find_node(app->children, "fullscreen")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->fullscreen = obt_xml_node_bool(n);
+
+    if ((n = obt_xml_find_node(app->children, "maximized"))) {
+        if (!obt_xml_node_contains(n, "default")) {
+            gchar *s = obt_xml_node_string(n);
+            if (!g_ascii_strcasecmp(s, "horizontal")) {
+                settings->max_horz = TRUE;
+                settings->max_vert = FALSE;
+            } else if (!g_ascii_strcasecmp(s, "vertical")) {
+                settings->max_horz = FALSE;
+                settings->max_vert = TRUE;
+            } else
+                settings->max_horz = settings->max_vert =
+                    obt_xml_node_bool(n);
+            g_free(s);
+        }
+    }
+
+    if ((n = obt_xml_find_node(app->children, "opacity")))
+        if (!obt_xml_node_contains(n, "default"))
+            settings->opacity = obt_xml_node_int(n);
+}
+
 /* Manages settings for individual applications.
    Some notes: monitor is the screen number in a multi monitor
    (Xinerama) setup (starting from 0), or mouse: the monitor the pointer
@@ -218,17 +367,19 @@ void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
 static void parse_per_app_settings(xmlNodePtr node, gpointer d)
 {
     xmlNodePtr app = obt_xml_find_node(node->children, "application");
-    gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL,
-        *type_str = NULL;
-    gboolean name_set, class_set, type_set, role_set, title_set;
-    ObClientType type;
-    gboolean x_pos_given;
+    for (; app; app = obt_xml_find_node(app->next, "application")) {
+        ObAppSettings *settings;
 
-    while (app) {
-        x_pos_given = FALSE;
+        gboolean name_set, class_set, role_set, title_set,
+            type_set, group_name_set, group_class_set;
+        gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL,
+            *type_str = NULL, *group_name = NULL, *group_class = NULL;
+        ObClientType type;
 
         class_set = obt_xml_attr_string(app, "class", &class);
         name_set = obt_xml_attr_string(app, "name", &name);
+        group_class_set = obt_xml_attr_string(app, "groupclass", &group_class);
+        group_name_set = obt_xml_attr_string(app, "groupname", &group_name);
         type_set = obt_xml_attr_string(app, "type", &type_str);
         role_set = obt_xml_attr_string(app, "role", &role);
         title_set = obt_xml_attr_string(app, "title", &title);
@@ -255,143 +406,38 @@ static void parse_per_app_settings(xmlNodePtr node, gpointer d)
                 type_set = FALSE; /* not valid! */
         }
 
-        if (class_set || name_set || role_set || title_set || type_set) {
-            xmlNodePtr n, c;
-            ObAppSettings *settings = config_create_app_settings();
-
-            if (name_set)
-                settings->name = g_pattern_spec_new(name);
-
-            if (class_set)
-                settings->class = g_pattern_spec_new(class);
-
-            if (role_set)
-                settings->role = g_pattern_spec_new(role);
-
-            if (title_set)
-                settings->title = g_pattern_spec_new(title);
-
-            if (type_set)
-                settings->type = type;
-
-            if ((n = obt_xml_find_node(app->children, "decor")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->decor = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "shade")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->shade = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "position"))) {
-                if ((c = obt_xml_find_node(n->children, "x")))
-                    if (!obt_xml_node_contains(c, "default")) {
-                        config_parse_gravity_coord(c, &settings->position.x);
-                        x_pos_given = TRUE;
-                    }
-
-                if (x_pos_given && (c = obt_xml_find_node(n->children, "y")))
-                    if (!obt_xml_node_contains(c, "default")) {
-                        config_parse_gravity_coord(c, &settings->position.y);
-                        settings->pos_given = TRUE;
-                    }
-
-                /* monitor can be set without setting x or y */
-                if ((c = obt_xml_find_node(n->children, "monitor")))
-                    if (!obt_xml_node_contains(c, "default")) {
-                        gchar *s = obt_xml_node_string(c);
-                        if (!g_ascii_strcasecmp(s, "mouse"))
-                            settings->monitor_type =
-                                    OB_PLACE_MONITOR_MOUSE;
-                        else if (!g_ascii_strcasecmp(s, "active"))
-                            settings->monitor_type =
-                                    OB_PLACE_MONITOR_ACTIVE;
-                        else if (!g_ascii_strcasecmp(s, "primary"))
-                            settings->monitor_type =
-                                    OB_PLACE_MONITOR_PRIMARY;
-                        else
-                            settings->monitor = obt_xml_node_int(c);
-                        g_free(s);
-                    }
-
-                obt_xml_attr_bool(n, "force", &settings->pos_force);
-            }
-
-            if ((n = obt_xml_find_node(app->children, "focus")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->focus = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "desktop"))) {
-                if (!obt_xml_node_contains(n, "default")) {
-                    gchar *s = obt_xml_node_string(n);
-                    if (!g_ascii_strcasecmp(s, "all"))
-                        settings->desktop = DESKTOP_ALL;
-                    else {
-                        gint i = obt_xml_node_int(n);
-                        if (i > 0)
-                            settings->desktop = i;
-                    }
-                    g_free(s);
-                }
-            }
+        if (!(class_set || name_set || role_set || title_set ||
+              type_set || group_class_set || group_name_set))
+            continue;
+
+        settings = config_create_app_settings();
+
+        if (name_set)
+            settings->name = g_pattern_spec_new(name);
+        if (class_set)
+            settings->class = g_pattern_spec_new(class);
+        if (group_name_set)
+            settings->group_name = g_pattern_spec_new(group_name);
+        if (group_class_set)
+            settings->group_class = g_pattern_spec_new(group_class);
+        if (role_set)
+            settings->role = g_pattern_spec_new(role);
+        if (title_set)
+            settings->title = g_pattern_spec_new(title);
+        if (type_set)
+            settings->type = type;
 
-            if ((n = obt_xml_find_node(app->children, "layer")))
-                if (!obt_xml_node_contains(n, "default")) {
-                    gchar *s = obt_xml_node_string(n);
-                    if (!g_ascii_strcasecmp(s, "above"))
-                        settings->layer = 1;
-                    else if (!g_ascii_strcasecmp(s, "below"))
-                        settings->layer = -1;
-                    else
-                        settings->layer = 0;
-                    g_free(s);
-                }
-
-            if ((n = obt_xml_find_node(app->children, "iconic")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->iconic = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "skip_pager")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->skip_pager = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "skip_taskbar")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->skip_taskbar = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "fullscreen")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->fullscreen = obt_xml_node_bool(n);
-
-            if ((n = obt_xml_find_node(app->children, "maximized")))
-                if (!obt_xml_node_contains(n, "default")) {
-                    gchar *s = obt_xml_node_string(n);
-                    if (!g_ascii_strcasecmp(s, "horizontal")) {
-                        settings->max_horz = TRUE;
-                        settings->max_vert = FALSE;
-                    } else if (!g_ascii_strcasecmp(s, "vertical")) {
-                        settings->max_horz = FALSE;
-                        settings->max_vert = TRUE;
-                    } else
-                        settings->max_horz = settings->max_vert =
-                            obt_xml_node_bool(n);
-                    g_free(s);
-                }
-
-            if ((n = obt_xml_find_node(app->children, "opacity")))
-                if (!obt_xml_node_contains(n, "default"))
-                    settings->opacity = obt_xml_node_int(n);
-
-            config_per_app_settings = g_slist_append(config_per_app_settings,
-                                                     (gpointer) settings);
-            g_free(name);
-            g_free(class);
-            g_free(role);
-            g_free(title);
-            g_free(type_str);
-            name = class = role = title = type_str = NULL;
-        }
-
-        app = obt_xml_find_node(app->next, "application");
+        g_free(name);
+        g_free(class);
+        g_free(group_name);
+        g_free(group_class);
+        g_free(role);
+        g_free(title);
+        g_free(type_str);
+
+        parse_single_per_app_settings(app, settings);
+        config_per_app_settings = g_slist_append(config_per_app_settings,
+                                                 (gpointer)settings);
     }
 }
 
@@ -592,8 +638,6 @@ static void parse_placement(xmlNodePtr node, gpointer d)
     if ((n = obt_xml_find_node(node, "policy")))
         if (obt_xml_node_contains(n, "UnderMouse"))
             config_place_policy = OB_PLACE_POLICY_MOUSE;
-    if ((n = obt_xml_find_node(node, "center")))
-        config_place_center = obt_xml_node_bool(n);
     if ((n = obt_xml_find_node(node, "monitor"))) {
         if (obt_xml_node_contains(n, "active"))
             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
@@ -1025,7 +1069,6 @@ void config_startup(ObtXmlInst *i)
     obt_xml_register(i, "focus", parse_focus, NULL);
 
     config_place_policy = OB_PLACE_POLICY_SMART;
-    config_place_center = TRUE;
     config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
 
     config_primary_monitor_index = 1;
@@ -1146,10 +1189,12 @@ void config_shutdown(void)
 
     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
         ObAppSettings *itd = (ObAppSettings *)it->data;
-        if (itd->name)  g_pattern_spec_free(itd->name);
-        if (itd->role)  g_pattern_spec_free(itd->role);
+        if (itd->name) g_pattern_spec_free(itd->name);
+        if (itd->role) g_pattern_spec_free(itd->role);
         if (itd->title) g_pattern_spec_free(itd->title);
         if (itd->class) g_pattern_spec_free(itd->class);
+        if (itd->group_name) g_pattern_spec_free(itd->group_name);
+        if (itd->group_class) g_pattern_spec_free(itd->group_class);
         g_slice_free(ObAppSettings, it->data);
     }
     g_slist_free(config_per_app_settings);
index beb54be4e6393102c23e521a9aeb12774efa5c75..c09a1f4f1cfb5335add5a71c4a6cef14eb5a06f6 100644 (file)
@@ -38,6 +38,8 @@ struct _ObAppSettings
     GPatternSpec *class;
     GPatternSpec *name;
     GPatternSpec *role;
+    GPatternSpec *group_class;
+    GPatternSpec *group_name;
     GPatternSpec *title;
     ObClientType  type;
 
@@ -45,6 +47,12 @@ struct _ObAppSettings
     gboolean pos_given;
     gboolean pos_force;
 
+    gint width_num;
+    gint width_denom;
+    gint height_num;
+    gint height_denom;
+    gboolean size_given;
+
     guint desktop;
     gint shade;
     gint decor;
@@ -82,8 +90,6 @@ extern gboolean config_unfocus_leave;
 
 /*! The algorithm to use for placing new windows */
 extern ObPlacePolicy config_place_policy;
-/*! Place windows in the center of the free area */
-extern gboolean config_place_center;
 /*! Place windows on the active monitor (unless they are part of an application
   already on another monitor) */
 extern ObPlaceMonitor config_place_monitor;
index cf089b64e3c5212ee3ff70baac9e904f3b82e3f9..ccbb56e6ad5b3f6036e0404e744be94b32b9cbf0 100644 (file)
@@ -698,7 +698,6 @@ static void event_process(const XEvent *ec, gpointer data)
     if (e->type == ButtonPress || e->type == ButtonRelease) {
         ObWindow *w;
         static guint pressed = 0;
-        static Window pressed_win = None;
 
         event_sourcetime = event_curtime;
 
@@ -719,10 +718,8 @@ static void event_process(const XEvent *ec, gpointer data)
             if (prompt && !used)
                 used = event_handle_prompt(prompt, e);
 
-            if (e->type == ButtonPress) {
+            if (e->type == ButtonPress)
                 pressed = e->xbutton.button;
-                pressed_win = e->xbutton.subwindow;
-            }
         }
     }
     else if (e->type == KeyPress || e->type == KeyRelease ||
@@ -813,11 +810,14 @@ void event_enter_client(ObClient *client)
     g_assert(config_focus_follow);
 
     if (is_enter_focus_event_ignored(event_curserial)) {
-        ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n"
+        ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu "
                       "on client 0x%x", event_curserial, client->window);
         return;
     }
 
+    ob_debug_type(OB_DEBUG_FOCUS, "using enter event with serial %lu "
+                  "on client 0x%x", event_curserial, client->window);
+
     if (client_enter_focusable(client) && client_can_focus(client)) {
         if (config_focus_delay) {
             ObFocusDelayData *data;
@@ -1495,7 +1495,8 @@ static void event_handle_client(ObClient *client, XEvent *e)
             }
             else if ((Atom)e->xclient.data.l[2] ==
                      OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
-                moveresize_end(TRUE);
+                if (moveresize_client)
+                    moveresize_end(TRUE);
         } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
             gint ograv, x, y, w, h;
 
@@ -2002,6 +2003,20 @@ static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
     ObMenuEntryFrame *e;
 
     switch (ev->type) {
+    case MotionNotify:
+        // We need to catch MotionNotify in addition to EnterNotify because
+        // it is possible for the menu to be opened under the mouse cursor, and
+        // moving the mouse should select the item.
+        if ((e = g_hash_table_lookup(menu_frame_map, &ev->xmotion.window))) {
+            if (e->ignore_enters)
+                --e->ignore_enters;
+            else if (!(f = find_active_menu()) ||
+                     f == e->frame ||
+                     f->parent == e->frame ||
+                     f->child == e->frame)
+                menu_frame_select(e->frame, e, FALSE);
+        }
+        break;
     case EnterNotify:
         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
             if (e->ignore_enters)
index 4d9984e16c04ded47a499d047e9e6ba729092f3a..cc441405a4638e582f0119149ec76b5ff991981a 100644 (file)
@@ -26,7 +26,7 @@ struct _ObClient;
 
 /*! The amount of time before a window appears that is checked for user input
     to determine if the user is working in another window */
-#define OB_EVENT_USER_TIME_DELAY (1000) /* 1.0 seconds */
+#define OB_EVENT_USER_TIME_DELAY (1000) /* milliseconds */
 
 /*! The last user-interaction time, as given by the clients */
 extern Time event_last_user_time;
index 6d4cc2a5ea932188b4ff908e8051916b1036d6d1..de176501e562de9844b8327e86a44a93e8bea11b 100644 (file)
@@ -74,7 +74,7 @@ void focus_cycle_addremove(ObClient *c, gboolean redraw)
         gboolean v, s;
 
         v = focus_cycle_valid(c);
-        s = focus_cycle_popup_is_showing(c);
+        s = focus_cycle_popup_is_showing(c) || c == focus_cycle_target;
 
         if (v != s)
             focus_cycle_reorder();
@@ -93,7 +93,7 @@ void focus_cycle_reorder()
         focus_cycle_update_indicator(focus_cycle_target);
         if (!focus_cycle_target)
             focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
-                        TRUE, TRUE, OB_FOCUS_CYCLE_POPUP_MODE_NONE,
+                        TRUE, OB_FOCUS_CYCLE_POPUP_MODE_NONE,
                         TRUE, TRUE);
     }
 }
@@ -101,8 +101,8 @@ void focus_cycle_reorder()
 ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
                       gboolean nonhilite_windows,
                       gboolean dock_windows, gboolean desktop_windows,
-                      gboolean linear, gboolean interactive,
-                      gboolean showbar, ObFocusCyclePopupMode mode,
+                      gboolean linear, gboolean showbar,
+                      ObFocusCyclePopupMode mode,
                       gboolean done, gboolean cancel)
 {
     static GList *order = NULL;
@@ -110,23 +110,17 @@ ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
     ObClient *ft = NULL;
     ObClient *ret = NULL;
 
-    if (interactive) {
-        if (cancel) {
-            focus_cycle_target = NULL;
-            goto done_cycle;
-        } else if (done)
-            goto done_cycle;
+    if (cancel) {
+        focus_cycle_target = NULL;
+        goto done_cycle;
+    } else if (done)
+        goto done_cycle;
 
-        if (!focus_order)
-            goto done_cycle;
+    if (!focus_order)
+        goto done_cycle;
 
-        if (linear) list = client_list;
-        else        list = focus_order;
-    } else {
-        if (!focus_order)
-            goto done_cycle;
-        list = client_list;
-    }
+    if (linear) list = client_list;
+    else        list = focus_order;
 
     if (focus_cycle_target == NULL) {
         focus_cycle_linear = linear;
@@ -153,21 +147,14 @@ ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
         }
         ft = it->data;
         if (focus_cycle_valid(ft)) {
-            if (interactive) {
-                if (ft != focus_cycle_target) { /* prevents flicker */
-                    focus_cycle_target = ft;
-                    focus_cycle_type = OB_CYCLE_NORMAL;
-                    focus_cycle_draw_indicator(showbar ? ft : NULL);
-                }
-                /* same arguments as focus_target_valid */
-                focus_cycle_popup_show(ft, mode, focus_cycle_linear);
-                return focus_cycle_target;
-            } else if (ft != focus_cycle_target) {
+            if (ft != focus_cycle_target) { /* prevents flicker */
                 focus_cycle_target = ft;
                 focus_cycle_type = OB_CYCLE_NORMAL;
-                done = TRUE;
-                break;
+                focus_cycle_draw_indicator(showbar ? ft : NULL);
             }
+            /* same arguments as focus_target_valid */
+            focus_cycle_popup_show(ft, mode, focus_cycle_linear);
+            return focus_cycle_target;
         }
     } while (it != start);
 
@@ -179,10 +166,8 @@ done_cycle:
     g_list_free(order);
     order = NULL;
 
-    if (interactive) {
-        focus_cycle_draw_indicator(NULL);
-        focus_cycle_popup_hide();
-    }
+    focus_cycle_draw_indicator(NULL);
+    focus_cycle_popup_hide();
 
     return ret;
 }
index 9394b3df8bb65ba187b4f1e37329a942d65900b5..8acb53d9bee6ab45424c5f29959a48aead5f0429 100644 (file)
@@ -38,8 +38,8 @@ void focus_cycle_shutdown(gboolean reconfig);
 struct _ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
                               gboolean nonhilite_windows,
                               gboolean dock_windows, gboolean desktop_windows,
-                              gboolean linear, gboolean interactive,
-                              gboolean showbar, ObFocusCyclePopupMode mode,
+                              gboolean linear, gboolean showbar,
+                              ObFocusCyclePopupMode mode,
                               gboolean done, gboolean cancel);
 struct _ObClient* focus_directional_cycle(ObDirection dir,
                                           gboolean dock_windows,
index 4468e1910bf75d33ef3e5da5cc24f4b2bc966561..0f2d56ebaf7ce37c6e55c445444f7328afe94ced 100644 (file)
@@ -378,13 +378,22 @@ void frame_adjust_area(ObFrame *self, gboolean moved,
 
         STRUT_SET(self->size,
                   self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
-                  self->cbwidth_t + self->bwidth,
+                  self->cbwidth_t +
+                  (!self->max_horz || !self->max_vert ? self->bwidth : 0),
                   self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
                   self->cbwidth_b +
                   (!self->max_horz || !self->max_vert ? self->bwidth : 0));
 
         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
             self->size.top += ob_rr_theme->title_height + self->bwidth;
+        else if (self->max_horz && self->max_vert) {
+            /* A maximized and undecorated window needs a small border on the
+               top of the window to let the user still undecorate/unmaximize the
+               window via the client menu. */
+            /* XXX This size should probably be a theme option. */
+            self->size.top += 1;
+        }
+
         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
             ob_rr_theme->handle_height > 0)
         {
index 003b0081dbe9f8221df8961db0eb4eeb02e5aae7..8e50834bc7084420e79840b09b02577ae5630a18 100644 (file)
@@ -65,6 +65,8 @@ typedef struct _Rect {
 #define RECT_RIGHT(r) ((r).x + (r).width - 1)
 #define RECT_BOTTOM(r) ((r).y + (r).height - 1)
 
+#define RECT_AREA(r) ((r).width * (r).height)
+
 #define RECT_SET_POINT(r, nx, ny) \
     (r).x = (nx), (r).y = (ny)
 #define RECT_SET_SIZE(r, w, h) \
@@ -102,6 +104,25 @@ typedef struct _Rect {
      (r).height = MIN((a).y + (a).height - 1, \
                       (b).y + (b).height - 1) - (r).y + 1)
 
+/* Returns the shortest manhatten distance between two rects, or 0 if they
+   intersect. */
+static inline gint rect_manhatten_distance(Rect r, Rect o)
+{
+    if (RECT_INTERSECTS_RECT(r, o))
+        return 0;
+
+    gint min_distance = G_MAXINT;
+    if (RECT_RIGHT(o) < RECT_LEFT(r))
+        min_distance = MIN(min_distance, RECT_LEFT(r) - RECT_RIGHT(o));
+    if (RECT_LEFT(o) > RECT_RIGHT(r))
+        min_distance = MIN(min_distance, RECT_LEFT(o) - RECT_RIGHT(r));
+    if (RECT_BOTTOM(o) < RECT_TOP(r))
+        min_distance = MIN(min_distance, RECT_TOP(r) - RECT_BOTTOM(o));
+    if (RECT_TOP(o) > RECT_BOTTOM(r))
+        min_distance = MIN(min_distance, RECT_TOP(o) - RECT_BOTTOM(r));
+    return min_distance;
+}
+
 typedef struct _Strut {
     int left;
     int top;
index 1294c4c95600d7b4bd18b79e7545bb0f575efdac..7c49cedbe3d411b22b7fbb0647d7bb37aeeb332d 100644 (file)
@@ -90,7 +90,16 @@ void menu_startup(gboolean reconfig)
             loaded = TRUE;
             obt_xml_tree_from_root(menu_parse_inst);
             obt_xml_close(menu_parse_inst);
-        } else
+        }
+        else if (obt_xml_load_file(menu_parse_inst,
+                                   it->data,
+                                   "openbox_menu"))
+        {
+            loaded = TRUE;
+            obt_xml_tree_from_root(menu_parse_inst);
+            obt_xml_close(menu_parse_inst);
+        }
+        else
             g_message(_("Unable to find a valid menu file \"%s\""),
                       (const gchar*)it->data);
     }
@@ -115,10 +124,11 @@ void menu_shutdown(gboolean reconfig)
     obt_xml_instance_unref(menu_parse_inst);
     menu_parse_inst = NULL;
 
-    client_list_menu_shutdown(reconfig);
+    menu_frame_hide_all();
+
     client_list_combined_menu_shutdown(reconfig);
+    client_list_menu_shutdown(reconfig);
 
-    menu_frame_hide_all();
     g_hash_table_destroy(menu_hash);
     menu_hash = NULL;
 }
@@ -278,7 +288,7 @@ static void parse_menu_item(xmlNodePtr node,  gpointer data)
         /* Don't try to extract "icon" attribute if icons in user-defined
            menus are not enabled. */
 
-        if (obt_xml_attr_string(node, "label", &label)) {
+        if (obt_xml_attr_string_unstripped(node, "label", &label)) {
             xmlNodePtr c;
             GSList *acts = NULL;
 
@@ -313,7 +323,7 @@ static void parse_menu_separator(xmlNodePtr node, gpointer data)
     if (state->parent) {
         gchar *label;
 
-        if (!obt_xml_attr_string(node, "label", &label))
+        if (!obt_xml_attr_string_unstripped(node, "label", &label))
             label = NULL;
 
         menu_add_separator(state->parent, -1, label);
@@ -333,7 +343,7 @@ static void parse_menu(xmlNodePtr node, gpointer data)
         goto parse_menu_fail;
 
     if (!g_hash_table_lookup(menu_hash, name)) {
-        if (!obt_xml_attr_string(node, "label", &title))
+        if (!obt_xml_attr_string_unstripped(node, "label", &title))
             goto parse_menu_fail;
 
         if ((menu = menu_new(name, title, TRUE, NULL))) {
@@ -384,6 +394,7 @@ ObMenu* menu_new(const gchar *name, const gchar *title,
     self->shortcut = parse_shortcut(title, allow_shortcut_selection,
                                     &self->title, &self->shortcut_position,
                                     &self->shortcut_always_show);
+    self->collate_key = g_utf8_collate_key(self->title, -1);
 
     g_hash_table_replace(menu_hash, self->name, self);
 
@@ -399,6 +410,7 @@ ObMenu* menu_new(const gchar *name, const gchar *title,
     self->more_menu = g_slice_new0(ObMenu);
     self->more_menu->name = _("More...");
     self->more_menu->title = _("More...");
+    self->more_menu->collate_key = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
     self->more_menu->data = data;
     self->more_menu->shortcut = g_unichar_tolower(g_utf8_get_char("M"));
 
@@ -425,6 +437,7 @@ static void menu_destroy_hash_value(ObMenu *self)
     menu_clear_entries(self);
     g_free(self->name);
     g_free(self->title);
+    g_free(self->collate_key);
     g_free(self->execute);
     g_slice_free(ObMenu, self->more_menu);
 
@@ -540,6 +553,7 @@ void menu_entry_unref(ObMenuEntry *self)
         case OB_MENU_ENTRY_TYPE_NORMAL:
             RrImageUnref(self->data.normal.icon);
             g_free(self->data.normal.label);
+            g_free(self->data.normal.collate_key);
             while (self->data.normal.actions) {
                 actions_act_unref(self->data.normal.actions->data);
                 self->data.normal.actions =
@@ -713,10 +727,13 @@ void menu_entry_set_label(ObMenuEntry *self, const gchar *label,
         break;
     case OB_MENU_ENTRY_TYPE_NORMAL:
         g_free(self->data.normal.label);
+        g_free(self->data.normal.collate_key);
         self->data.normal.shortcut =
             parse_shortcut(label, allow_shortcut, &self->data.normal.label,
                            &self->data.normal.shortcut_position,
                            &self->data.normal.shortcut_always_show);
+        self->data.normal.collate_key =
+            g_utf8_collate_key(self->data.normal.label, -1);
         break;
     default:
         g_assert_not_reached();
@@ -727,3 +744,69 @@ void menu_show_all_shortcuts(ObMenu *self, gboolean show)
 {
     self->show_all_shortcuts = show;
 }
+
+static int sort_func(const void *a, const void *b) {
+    const ObMenuEntry *e[2] = {*(ObMenuEntry**)a, *(ObMenuEntry**)b};
+    const gchar *k[2];
+    gint i;
+
+    for (i = 0; i < 2; ++i) {
+        if (e[i]->type == OB_MENU_ENTRY_TYPE_NORMAL)
+            k[i] = e[i]->data.normal.collate_key;
+        else {
+            g_assert(e[i]->type == OB_MENU_ENTRY_TYPE_SUBMENU);
+            if (e[i]->data.submenu.submenu)
+                k[i] = e[i]->data.submenu.submenu->collate_key;
+            else
+                return -1; /* arbitrary really.. the submenu doesn't exist. */
+        }
+    }
+    return strcmp(k[0], k[1]);
+}
+
+/*!
+  @param start The first entry in the range to sort.
+  @param end The last entry in the range to sort.
+*/
+static void sort_range(ObMenu *self, GList *start, GList *end, guint len)
+{
+    ObMenuEntry **ar;
+    GList *it;
+    guint i;
+    if (!len) return;
+
+    ar = g_slice_alloc(sizeof(ObMenuEntry*) * len);
+    for (i = 0, it = start; it != g_list_next(end); ++i, it = g_list_next(it))
+        ar[i] = it->data;
+    qsort(ar, len, sizeof(ObMenuEntry*), sort_func);
+    for (i = 0, it = start; it != g_list_next(end); ++i, it = g_list_next(it))
+        it->data = ar[i];
+    g_slice_free1(sizeof(ObMenuEntry*) * len, ar);
+}
+
+void menu_sort_entries(ObMenu *self)
+{
+    GList *it, *start, *end, *last;
+    guint len;
+
+    /* need the submenus to know their labels for sorting */
+    menu_find_submenus(self);
+
+    start = self->entries;
+    len = 0;
+    for (it = self->entries; it; it = g_list_next(it)) {
+        ObMenuEntry *e = it->data;
+        if (e->type == OB_MENU_ENTRY_TYPE_SEPARATOR) {
+            end = g_list_previous(it);
+            sort_range(self, start, end, len);
+
+            it = g_list_next(it); /* skip over the separator */
+            start = it;
+            len = 0;
+        }
+        else
+            len += 1;
+        last = it;
+    }
+    sort_range(self, start, last, len);
+}
index 76cc238feafe578400d715bc0c17190d578e31f8..7d719729bbbc88e0c1631317160b766c1d53a3d8 100644 (file)
@@ -59,6 +59,7 @@ struct _ObMenu
     gchar *name;
     /* Displayed title */
     gchar *title;
+    gchar *collate_key;
     /*! The shortcut key that would be used to activate this menu if it was
       displayed as a submenu */
     gunichar shortcut;
@@ -108,6 +109,7 @@ struct _ObNormalMenuEntry {
     gint     icon_alpha;
 
     gchar *label;
+    gchar *collate_key;
     /*! The shortcut key that would be used to activate this menu entry */
     gunichar shortcut;
     /*! The shortcut's position in the string */
@@ -211,7 +213,10 @@ ObMenuEntry* menu_add_normal(ObMenu *menu, gint id, const gchar *label,
 ObMenuEntry* menu_add_submenu(ObMenu *menu, gint id, const gchar *submenu);
 ObMenuEntry* menu_add_separator(ObMenu *menu, gint id, const gchar *label);
 
-void menu_clear_entries(ObMenu *menu);
+/*! This sorts groups of menu entries between consecutive separators */
+void menu_sort_entries(ObMenu *self);
+
+void menu_clear_entries(ObMenu *self);
 void menu_entry_remove(ObMenuEntry *self);
 
 void menu_entry_set_label(ObMenuEntry *self, const gchar *label,
index 6110045cc459dd2596781242c90c4be3d72b2d93..b49a221beeab41e20a60f52f152aad8b050404a4 100644 (file)
@@ -38,7 +38,8 @@
 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
                          LeaveWindowMask)
 #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
-                         ButtonPressMask | ButtonReleaseMask)
+                         ButtonPressMask | ButtonReleaseMask | \
+                         PointerMotionMask)
 
 GList *menu_frame_visible;
 GHashTable *menu_frame_map;
@@ -1046,8 +1047,11 @@ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
         parent->child_entry = parent_entry;
     }
 
-    if (!menu_frame_show(self))
+    if (!menu_frame_show(self)) {
+        parent->child = NULL;
+        parent->child_entry = NULL;
         return FALSE;
+    }
 
     menu_frame_place_submenu(self, &x, &y);
     menu_frame_move_on_screen(self, x, y, &dx, &dy);
@@ -1272,7 +1276,8 @@ void menu_entry_frame_show_submenu(ObMenuEntryFrame *self)
     /* pass our direction on to our child */
     f->direction_right = self->frame->direction_right;
 
-    menu_frame_show_submenu(f, self->frame, self);
+    if (!menu_frame_show_submenu(f, self->frame, self))
+        menu_frame_free(f);
 }
 
 void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
index 44c02562220a983128b6fe2d36dcec3f7cb06510..2d7a2ae072921eb3431db9368aff97d511f11c4a 100644 (file)
@@ -121,7 +121,7 @@ void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y,
                                gint *dx, gint *dy);
 
 gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
-                                 gint button);
+                                 gboolean mouse);
 gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
                                  ObMenuEntryFrame *parent_entry);
 
index d700da9ebd66a0ecacb8d10c4e15bc0a2b75c4f2..ffed909315037ae4b6882f92b391dbc3b0dc8a7d 100644 (file)
@@ -630,6 +630,10 @@ static void do_edge_warp(gint x, gint y)
 
     for (i = 0; i < screen_num_monitors; ++i) {
         const Rect *a = screen_physical_area_monitor(i);
+
+        if (!RECT_CONTAINS(*a, x, y))
+            continue;
+
         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
index fbc01fdddd20739ab9ddb16b55b3ee2e87d46ad0..4ac09cd6665f02cb47870a40acbb61e057542691 100644 (file)
@@ -515,9 +515,9 @@ static void print_version(void)
 {
     g_print("Openbox %s\n", PACKAGE_VERSION);
     g_print(_("Copyright (c)"));
-    g_print(" 2008-2011   Mikael Magnusson\n");
+    g_print(" 2004   Mikael Magnusson\n");
     g_print(_("Copyright (c)"));
-    g_print(" 2003-2011   Dana Jansens\n\n");
+    g_print(" 2002   Dana Jansens\n\n");
     g_print("This program comes with ABSOLUTELY NO WARRANTY.\n");
     g_print("This is free software, and you are welcome to redistribute it\n");
     g_print("under certain conditions. See the file COPYING for details.\n\n");
index 2cd21bb0379778c3c9a9b947a996b798944c0d93..48e4fb16a8d0a17873a56d8af34dde8fef056735 100644 (file)
 #include "config.h"
 #include "dock.h"
 #include "debug.h"
+#include "place_overlap.h"
 
 extern ObDock *dock;
 
-static Rect *pick_pointer_head(ObClient *c)
+static Rect *choose_pointer_monitor(ObClient *c)
 {
     return screen_area(c->desktop, screen_monitor_pointer(), NULL);
 }
 
-/* use the following priority lists for pick_head()
+/* use the following priority lists for choose_monitor()
 
    When a window is being placed in the FOREGROUND, use a monitor chosen in
    the following order:
@@ -155,8 +156,8 @@ gint cmp_background(const void *a, const void *b)
 }
 
 /*! Pick a monitor to place a window on. */
-static Rect *pick_head(ObClient *c, gboolean foreground,
-                       ObAppSettings *settings)
+static Rect* choose_monitor(ObClient *c, gboolean client_to_be_foregrounded,
+                            ObAppSettings *settings)
 {
     Rect *area;
     ObPlaceHead *choice;
@@ -229,7 +230,7 @@ static Rect *pick_head(ObClient *c, gboolean foreground,
     }
 
     qsort(choice, screen_num_monitors, sizeof(ObPlaceHead),
-          foreground ? cmp_foreground : cmp_background);
+          client_to_be_foregrounded ? cmp_foreground : cmp_background);
 
     /* save the areas of the monitors in order of their being chosen */
     for (i = 0; i < screen_num_monitors; ++i)
@@ -255,205 +256,12 @@ static Rect *pick_head(ObClient *c, gboolean foreground,
     return area;
 }
 
-static gboolean place_random(ObClient *client, Rect *area, gint *x, gint *y)
+static gboolean place_under_mouse(ObClient *client, gint *x, gint *y,
+                                  Size frame_size)
 {
-    gint l, r, t, b;
-
-    ob_debug("placing randomly");
-
-    l = area->x;
-    t = area->y;
-    r = area->x + area->width - client->frame->area.width;
-    b = area->y + area->height - client->frame->area.height;
-
-    if (r > l) *x = g_random_int_range(l, r + 1);
-    else       *x = area->x;
-    if (b > t) *y = g_random_int_range(t, b + 1);
-    else       *y = area->y;
-
-    return TRUE;
-}
-
-static GSList* area_add(GSList *list, Rect *a)
-{
-    Rect *r = g_slice_new(Rect);
-    *r = *a;
-    return g_slist_prepend(list, r);
-}
-
-static GSList* area_remove(GSList *list, Rect *a)
-{
-    GSList *sit;
-    GSList *result = NULL;
-
-    for (sit = list; sit; sit = g_slist_next(sit)) {
-        Rect *r = sit->data;
-
-        if (!RECT_INTERSECTS_RECT(*r, *a)) {
-            result = g_slist_prepend(result, r);
-            /* dont free r, it's moved to the result list */
-        } else {
-            Rect isect, extra;
-
-            /* Use an intersection of a and r to determine the space
-               around r that we can use.
-
-               NOTE: the spaces calculated can overlap.
-            */
-
-            RECT_SET_INTERSECTION(isect, *r, *a);
-
-            if (RECT_LEFT(isect) > RECT_LEFT(*r)) {
-                RECT_SET(extra, r->x, r->y,
-                         RECT_LEFT(isect) - r->x, r->height);
-                result = area_add(result, &extra);
-            }
-
-            if (RECT_TOP(isect) > RECT_TOP(*r)) {
-                RECT_SET(extra, r->x, r->y,
-                         r->width, RECT_TOP(isect) - r->y + 1);
-                result = area_add(result, &extra);
-            }
-
-            if (RECT_RIGHT(isect) < RECT_RIGHT(*r)) {
-                RECT_SET(extra, RECT_RIGHT(isect) + 1, r->y,
-                         RECT_RIGHT(*r) - RECT_RIGHT(isect), r->height);
-                result = area_add(result, &extra);
-            }
-
-            if (RECT_BOTTOM(isect) < RECT_BOTTOM(*r)) {
-                RECT_SET(extra, r->x, RECT_BOTTOM(isect) + 1,
-                         r->width, RECT_BOTTOM(*r) - RECT_BOTTOM(isect));
-                result = area_add(result, &extra);
-            }
-
-            /* 'r' is not being added to the result list, so free it */
-            g_slice_free(Rect, r);
-        }
-    }
-    g_slist_free(list);
-    return result;
-}
-
-enum {
-    IGNORE_FULLSCREEN = 1,
-    IGNORE_MAXIMIZED  = 2,
-    IGNORE_MENUTOOL   = 3,
-    /*IGNORE_SHADED     = 3,*/
-    IGNORE_NONGROUP   = 4,
-    IGNORE_BELOW      = 5,
-    /*IGNORE_NONFOCUS   = 1 << 5,*/
-    IGNORE_DOCK       = 6,
-    IGNORE_END        = 7
-};
-
-static gboolean place_nooverlap(ObClient *c, Rect *area, gint *x, gint *y)
-{
-    gint ignore;
-    gboolean ret;
-    gint maxsize;
-    GSList *spaces = NULL, *sit, *maxit;
-
-    ob_debug("placing nonoverlap");
-
-    ret = FALSE;
-    maxsize = 0;
-    maxit = NULL;
-
-    /* try ignoring different things to find empty space */
-    for (ignore = 0; ignore < IGNORE_END && !ret; ignore++) {
-        GList *it;
-
-        /* add the whole monitor */
-        spaces = area_add(spaces, area);
-
-        /* go thru all the windows */
-        for (it = client_list; it; it = g_list_next(it)) {
-            ObClient *test = it->data;
-
-            /* should we ignore this client? */
-            if (screen_showing_desktop) continue;
-            if (c == test) continue;
-            if (test->iconic) continue;
-            if (c->desktop != DESKTOP_ALL) {
-                if (test->desktop != c->desktop &&
-                    test->desktop != DESKTOP_ALL) continue;
-            } else {
-                if (test->desktop != screen_desktop &&
-                    test->desktop != DESKTOP_ALL) continue;
-            }
-            if (test->type == OB_CLIENT_TYPE_SPLASH ||
-                test->type == OB_CLIENT_TYPE_DESKTOP) continue;
-
-
-            if ((ignore >= IGNORE_FULLSCREEN) &&
-                test->fullscreen) continue;
-            if ((ignore >= IGNORE_MAXIMIZED) &&
-                test->max_horz && test->max_vert) continue;
-            if ((ignore >= IGNORE_MENUTOOL) &&
-                (test->type == OB_CLIENT_TYPE_MENU ||
-                 test->type == OB_CLIENT_TYPE_TOOLBAR) &&
-                client_has_parent(c)) continue;
-            /*
-              if ((ignore >= IGNORE_SHADED) &&
-              test->shaded) continue;
-            */
-            if ((ignore >= IGNORE_NONGROUP) &&
-                client_has_group_siblings(c) &&
-                test->group != c->group) continue;
-            if ((ignore >= IGNORE_BELOW) &&
-                test->layer < c->layer) continue;
-            /*
-              if ((ignore >= IGNORE_NONFOCUS) &&
-              focus_client != test) continue;
-            */
-            /* don't ignore this window, so remove it from the available
-               area */
-            spaces = area_remove(spaces, &test->frame->area);
-        }
-
-        if (ignore < IGNORE_DOCK) {
-            Rect a;
-            dock_get_area(&a);
-            spaces = area_remove(spaces, &a);
-        }
-
-        for (sit = spaces; sit; sit = g_slist_next(sit)) {
-            Rect *r = sit->data;
-
-            if (r->width >= c->frame->area.width &&
-                r->height >= c->frame->area.height &&
-                r->width * r->height > maxsize)
-            {
-                maxsize = r->width * r->height;
-                maxit = sit;
-            }
-        }
-
-        if (maxit) {
-            Rect *r = maxit->data;
-
-            /* center it in the area */
-            *x = r->x;
-            *y = r->y;
-            if (config_place_center) {
-                *x += (r->width - c->frame->area.width) / 2;
-                *y += (r->height - c->frame->area.height) / 2;
-            }
-            ret = TRUE;
-        }
-
-        while (spaces) {
-            g_slice_free(Rect, spaces->data);
-            spaces = g_slist_delete_link(spaces, spaces);
-        }
-    }
-
-    return ret;
-}
+    if (config_place_policy != OB_PLACE_POLICY_MOUSE)
+        return FALSE;
 
-static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
-{
     gint l, r, t, b;
     gint px, py;
     Rect *area;
@@ -462,16 +270,16 @@ static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
 
     if (!screen_pointer_pos(&px, &py))
         return FALSE;
-    area = pick_pointer_head(client);
+    area = choose_pointer_monitor(client);
 
     l = area->x;
     t = area->y;
-    r = area->x + area->width - client->frame->area.width;
-    b = area->y + area->height - client->frame->area.height;
+    r = area->x + area->width - frame_size.width;
+    b = area->y + area->height - frame_size.height;
 
-    *x = px - client->area.width / 2 - client->frame->size.left;
+    *x = px - frame_size.width / 2;
     *x = MIN(MAX(*x, l), r);
-    *y = py - client->area.height / 2 - client->frame->size.top;
+    *y = py - frame_size.height / 2;
     *y = MIN(MAX(*y, t), b);
 
     g_slice_free(Rect, area);
@@ -479,11 +287,12 @@ static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
     return TRUE;
 }
 
-static gboolean place_per_app_setting(ObClient *client, Rect *screen,
-                                      gint *x, gint *y,
-                                      ObAppSettings *settings)
+static gboolean place_per_app_setting_position(ObClient *client, Rect *screen,
+                                               gint *x, gint *y,
+                                               ObAppSettings *settings,
+                                               Size frame_size)
 {
-    if (!settings || (settings && !settings->pos_given))
+    if (!settings || !settings->pos_given)
         return FALSE;
 
     ob_debug("placing by per-app settings");
@@ -491,7 +300,7 @@ static gboolean place_per_app_setting(ObClient *client, Rect *screen,
     if (settings->position.x.center)
         *x = screen->x + screen->width / 2 - client->area.width / 2;
     else if (settings->position.x.opposite)
-        *x = screen->x + screen->width - client->frame->area.width -
+        *x = screen->x + screen->width - frame_size.width -
             settings->position.x.pos;
     else
         *x = screen->x + settings->position.x.pos;
@@ -501,7 +310,7 @@ static gboolean place_per_app_setting(ObClient *client, Rect *screen,
     if (settings->position.y.center)
         *y = screen->y + screen->height / 2 - client->area.height / 2;
     else if (settings->position.y.opposite)
-        *y = screen->y + screen->height - client->frame->area.height -
+        *y = screen->y + screen->height - frame_size.height -
             settings->position.y.pos;
     else
         *y = screen->y + settings->position.y.pos;
@@ -511,8 +320,37 @@ static gboolean place_per_app_setting(ObClient *client, Rect *screen,
     return TRUE;
 }
 
+static void place_per_app_setting_size(ObClient *client, Rect *screen,
+                                       gint *w, gint *h,
+                                       ObAppSettings *settings)
+{
+    if (!settings || !settings->size_given)
+        return;
+
+    ob_debug("sizing by per-app settings");
+
+    g_assert(settings->width_num > 0);
+    g_assert(settings->width_denom >= 0);
+    g_assert(settings->height_num > 0);
+    g_assert(settings->height_denom >= 0);
+
+    if (!settings->width_denom)
+        *w = settings->width_num;
+    else {
+        *w = screen->width * settings->width_num / settings->width_denom;
+        *w = MIN(*w, screen->width);
+    }
+
+    if (!settings->height_denom)
+        *h = settings->height_num;
+    else {
+        *h = screen->height * settings->height_num / settings->height_denom;
+        *h = MIN(*h, screen->height);
+    }
+}
+
 static gboolean place_transient_splash(ObClient *client, Rect *area,
-                                       gint *x, gint *y)
+                                       gint *x, gint *y, Size frame_size)
 {
     if (client->type == OB_CLIENT_TYPE_DIALOG) {
         GSList *it;
@@ -538,8 +376,8 @@ static gboolean place_transient_splash(ObClient *client, Rect *area,
                 }
             }
             if (!first) {
-                *x = ((r + 1 - l) - client->frame->area.width) / 2 + l;
-                *y = ((b + 1 - t) - client->frame->area.height) / 2 + t;
+                *x = ((r + 1 - l) - frame_size.width) / 2 + l;
+                *y = ((b + 1 - t) - frame_size.height) / 2 + t;
                 return TRUE;
             }
         }
@@ -550,42 +388,118 @@ static gboolean place_transient_splash(ObClient *client, Rect *area,
     {
         ob_debug("placing dialog or splash");
 
-        *x = (area->width - client->frame->area.width) / 2 + area->x;
-        *y = (area->height - client->frame->area.height) / 2 + area->y;
+        *x = (area->width - frame_size.width) / 2 + area->x;
+        *y = (area->height - frame_size.height) / 2 + area->y;
         return TRUE;
     }
 
     return FALSE;
 }
 
-/*! Return TRUE if openbox chose the position for the window, and FALSE if
-  the application chose it */
-gboolean place_client(ObClient *client, gboolean foreground, gint *x, gint *y,
-                      ObAppSettings *settings)
+static gboolean place_least_overlap(ObClient *c, Rect *head, int *x, int *y,
+                                    Size frame_size)
+{
+    /* Assemble the list of windows that could overlap with @c in the user's
+       current view. */
+    GSList* potential_overlap_clients = NULL;
+    gint n_client_rects = 0;
+
+    /* if we're "showing desktop", ignore all existing windows */
+    if (!screen_showing_desktop) {
+        GList* it;
+        for (it = client_list; it != NULL; it = g_list_next(it)) {
+            ObClient* maybe_client = (ObClient*)it->data;
+            if (maybe_client == c)
+                continue;
+            if (maybe_client->iconic)
+                continue;
+            if (!client_occupies_space(maybe_client))
+                continue;
+            if (c->desktop != DESKTOP_ALL) {
+                if (maybe_client->desktop != c->desktop &&
+                    maybe_client->desktop != DESKTOP_ALL)
+                    continue;
+            } else {
+                if (maybe_client->desktop != screen_desktop &&
+                    maybe_client->desktop != DESKTOP_ALL)
+                    continue;
+            }
+
+            potential_overlap_clients = g_slist_prepend(
+                potential_overlap_clients, maybe_client);
+            n_client_rects += 1;
+        }
+    }
+    Rect client_rects[n_client_rects];
+
+    GSList* it;
+    guint i = 0;
+    for (it = potential_overlap_clients; it != NULL; it = g_slist_next(it)) {
+        ObClient* potential_overlap_client = (ObClient*)it->data;
+        client_rects[i] = potential_overlap_client->frame->area;
+        i += 1;
+    }
+    g_slist_free(potential_overlap_clients);
+
+    Point result;
+    place_overlap_find_least_placement(client_rects, n_client_rects, head,
+                                       &frame_size, &result);
+    *x = result.x;
+    *y = result.y;
+
+    return TRUE;
+}
+
+static gboolean should_set_client_position(ObClient *client,
+                                           ObAppSettings *settings)
+{
+    gboolean has_position = settings && settings->pos_given;
+    gboolean has_forced_position = has_position && settings->pos_force;
+
+    gboolean user_positioned = client->positioned & USPosition;
+    if (user_positioned && !has_forced_position)
+        return FALSE;
+
+    gboolean program_positioned = client->positioned & PPosition;
+    if (program_positioned && !has_position)
+        return FALSE;
+
+    return TRUE;
+}
+
+gboolean place_client(ObClient *client, gboolean client_to_be_foregrounded,
+                      Rect* client_area, ObAppSettings *settings)
 {
-    Rect *area;
     gboolean ret;
+    Rect *monitor_area;
+    int *x, *y, *w, *h;
+    Size frame_size;
+
+    monitor_area = choose_monitor(client, client_to_be_foregrounded, settings);
 
-    /* per-app settings override program specified position
-     * but not user specified, unless pos_force is enabled */
-    if (((client->positioned & USPosition) &&
-         !(settings && settings->pos_given && settings->pos_force)) ||
-        ((client->positioned & PPosition) &&
-         !(settings && settings->pos_given)))
+    w = &client_area->width;
+    h = &client_area->height;
+    place_per_app_setting_size(client, monitor_area, w, h, settings);
+
+    if (!should_set_client_position(client, settings))
         return FALSE;
 
-    area = pick_head(client, foreground, settings);
+    x = &client_area->x;
+    y = &client_area->y;
+
+    SIZE_SET(frame_size,
+             *w + client->frame->size.left + client->frame->size.right,
+             *h + client->frame->size.top + client->frame->size.bottom);
 
-    /* try a number of methods */
-    ret = place_per_app_setting(client, area, x, y, settings) ||
-        place_transient_splash(client, area, x, y) ||
-        (config_place_policy == OB_PLACE_POLICY_MOUSE &&
-         place_under_mouse(client, x, y)) ||
-        place_nooverlap(client, area, x, y) ||
-        place_random(client, area, x, y);
+    ret =
+        place_per_app_setting_position(client, monitor_area, x, y, settings,
+                                       frame_size) ||
+        place_transient_splash(client, monitor_area, x, y, frame_size) ||
+        place_under_mouse(client, x, y, frame_size) ||
+        place_least_overlap(client, monitor_area, x, y, frame_size);
     g_assert(ret);
 
-    g_slice_free(Rect, area);
+    g_slice_free(Rect, monitor_area);
 
     /* get where the client should be */
     frame_frame_gravity(client->frame, x, y);
index 94e2dc0fb4d805b78890d419385eefe7a4edfd29..3bc679e0d536aa8227f4a1e15f7e7da1d9513288 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef ob__place_h
 #define ob__place_h
 
+#include "geom.h"
+
 #include <glib.h>
 
 struct _ObClient;
@@ -39,7 +41,10 @@ typedef enum
     OB_PLACE_MONITOR_PRIMARY
 } ObPlaceMonitor;
 
-gboolean place_client(struct _ObClient *client, gboolean foreground,
-                      gint *x, gint *y, struct _ObAppSettings *settings);
+/*! Return TRUE if openbox chose the position for the window, and FALSE if
+  the application chose it */
+gboolean place_client(struct _ObClient *client,
+                      gboolean client_to_be_foregrounded,
+                      Rect* client_area, struct _ObAppSettings *settings);
 
 #endif
diff --git a/openbox/place_overlap.c b/openbox/place_overlap.c
new file mode 100644 (file)
index 0000000..ef73bd8
--- /dev/null
@@ -0,0 +1,175 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   overlap.c for the Openbox window manager
+   Copyright (c) 2011        Ian Zimmerman
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "config.h"
+#include "geom.h"
+#include "place_overlap.h"
+
+#include <stdlib.h>
+
+static void make_grid(const Rect* client_rects, int n_client_rects,
+                      const Rect* bound, int* x_edges, int* y_edges,
+                      int max_edges);
+
+static int best_direction(const Point* grid_point,
+                          const Rect* client_rects, int n_client_rects,
+                          const Rect* bound, const Size* req_size,
+                          Point* best_top_left);
+
+/* Choose the placement on a grid with least overlap */
+
+void place_overlap_find_least_placement(const Rect* client_rects,
+                                        int n_client_rects,
+                                        Rect *const bound,
+                                        const Size* req_size,
+                                        Point* result)
+{
+    POINT_SET(*result, 0, 0);
+    int overlap = G_MAXINT;
+    int max_edges = 2 * (n_client_rects + 1);
+
+       int x_edges[max_edges];
+       int y_edges[max_edges];
+       make_grid(client_rects, n_client_rects, bound,
+                         x_edges, y_edges, max_edges);
+       int i;
+       for (i = 0; i < max_edges; ++i) {
+               if (x_edges[i] == G_MAXINT)
+                       break;
+               int j;
+               for (j = 0; j < max_edges; ++j) {
+                       if (y_edges[j] == G_MAXINT)
+                               break;
+                       Point grid_point = {.x = x_edges[i], .y = y_edges[j]};
+                       Point best_top_left;
+                       int this_overlap =
+                               best_direction(&grid_point, client_rects, n_client_rects,
+                                                          bound, req_size, &best_top_left);
+                       if (this_overlap < overlap) {
+                               overlap = this_overlap;
+                               *result = best_top_left;
+                       }
+                       if (overlap == 0)
+                               break;
+               }
+               if (overlap == 0)
+                       break;
+       }
+}
+
+static int compare_ints(const void* a, const void* b)
+{
+    const int* ia = (const int*)a;
+    const int* ib = (const int*)b;
+    return *ia - *ib;
+}
+
+static void uniquify(int* edges, int n_edges)
+{
+    int i = 0;
+    int j = 0;
+
+    while (j < n_edges) {
+        int last = edges[j++];
+        edges[i++] = last;
+        while (j < n_edges && edges[j] == last)
+            ++j;
+    }
+    /* fill the rest with nonsense */
+    for (; i < n_edges ; ++i)
+        edges[i] = G_MAXINT;
+}
+
+static void make_grid(const Rect* client_rects, int n_client_rects,
+                      const Rect* bound, int* x_edges, int* y_edges,
+                      int max_edges)
+{
+    int i;
+    int n_edges = 0;
+    for (i = 0; i < n_client_rects; ++i) {
+        if (!RECT_INTERSECTS_RECT(client_rects[i], *bound))
+            continue;
+        x_edges[n_edges] = client_rects[i].x;
+        y_edges[n_edges++] = client_rects[i].y;
+        x_edges[n_edges] = client_rects[i].x + client_rects[i].width;
+        y_edges[n_edges++] = client_rects[i].y + client_rects[i].height;
+    }
+    x_edges[n_edges] = bound->x;
+    y_edges[n_edges++] = bound->y;
+    x_edges[n_edges] = bound->x + bound->width;
+    y_edges[n_edges++] = bound->y + bound->height;
+    for (i = n_edges; i < max_edges; ++i)
+        x_edges[i] = y_edges[i] = G_MAXINT;
+    qsort(x_edges, n_edges, sizeof(int), compare_ints);
+    uniquify(x_edges, n_edges);
+    qsort(y_edges, n_edges, sizeof(int), compare_ints);
+    uniquify(y_edges, n_edges);
+}
+
+static int total_overlap(const Rect* client_rects, int n_client_rects,
+                         const Rect* proposed_rect)
+{
+    int overlap = 0;
+    int i;
+    for (i = 0; i < n_client_rects; ++i) {
+        if (!RECT_INTERSECTS_RECT(*proposed_rect, client_rects[i]))
+            continue;
+        Rect rtemp;
+        RECT_SET_INTERSECTION(rtemp, *proposed_rect, client_rects[i]);
+        overlap += RECT_AREA(rtemp);
+    }
+    return overlap;
+}
+
+/* Given a list of Rect RECTS, a Point PT and a Size size, determine the
+   direction from PT which results in the least total overlap with RECTS
+   if a rectangle is placed in that direction.  Return the top/left
+   Point of such rectangle and the resulting overlap amount.  Only
+   consider placements within BOUNDS. */
+
+#define NUM_DIRECTIONS 4
+
+static int best_direction(const Point* grid_point,
+                          const Rect* client_rects, int n_client_rects,
+                          const Rect* bound, const Size* req_size,
+                          Point* best_top_left)
+{
+    static const Size directions[NUM_DIRECTIONS] = {
+        {0, 0}, {0, -1}, {-1, 0}, {-1, -1}
+    };
+    int overlap = G_MAXINT;
+    int i;
+    for (i = 0; i < NUM_DIRECTIONS; ++i) {
+        Point pt = {
+            .x = grid_point->x + (req_size->width * directions[i].width),
+            .y = grid_point->y + (req_size->height * directions[i].height)
+        };
+        Rect r;
+        RECT_SET(r, pt.x, pt.y, req_size->width, req_size->height);
+        if (!RECT_CONTAINS_RECT(*bound, r))
+            continue;
+        int this_overlap = total_overlap(client_rects, n_client_rects, &r);
+        if (this_overlap < overlap) {
+            overlap = this_overlap;
+            *best_top_left = pt;
+        }
+        if (overlap == 0)
+            break;
+    }
+    return overlap;
+}
diff --git a/openbox/place_overlap.h b/openbox/place_overlap.h
new file mode 100644 (file)
index 0000000..9ceed34
--- /dev/null
@@ -0,0 +1,25 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   overlap.h for the Openbox window manager
+   Copyright (c) 2011        Ian Zimmerman
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "geom.h"
+
+void place_overlap_find_least_placement(const Rect* client_rects,
+                                        int n_client_rects,
+                                        Rect *const bounds,
+                                        const Size* req_size,
+                                        Point* result);
index 1aa79d2a122e8a57f2b0859bf4815ca912da66bf..88c74b68d048baf3519a753303b547be07e4b7c0 100644 (file)
@@ -75,11 +75,12 @@ void prompt_startup(gboolean reconfig)
 
 void prompt_shutdown(gboolean reconfig)
 {
-    GList *it;
+    GList *it, *next;
 
     if (!reconfig) {
-        for (it = prompt_list; it; it = g_list_next(it)) {
+        for (it = prompt_list; it; it = next) {
             ObPrompt *p = it->data;
+            next = it->next;
             if (p->cleanup) p->cleanup(p, p->data);
         }
 
@@ -594,7 +595,7 @@ static void prompt_run_callback(ObPrompt *self, gint result)
 {
     prompt_ref(self);
     if (self->func) {
-        gboolean clean = self->func(self, self->focus->result, self->data);
+        gboolean clean = self->func(self, result, self->data);
         if (clean && self->cleanup)
             self->cleanup(self, self->data);
     }
index ffe74a07078f39d60509571e1da995e5d4939ba7..33acb4a16a2828f62bcd5bb769807be32f0eeff3 100644 (file)
@@ -305,6 +305,8 @@ gboolean screen_annex(void)
     supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
     supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
     supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
+    supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
+    supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
     supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
     g_assert(i == num_support);
 
@@ -1369,6 +1371,14 @@ static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
         b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
     }
     RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
+
+    for (i = 0; i < *nxin; ++i)
+        ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
+                 (*xin_areas)[i].x, (*xin_areas)[i].y,
+                 (*xin_areas)[i].width, (*xin_areas)[i].height);
+    ob_debug("Full desktop @ %d,%d %dx%d\n",
+             (*xin_areas)[i].x, (*xin_areas)[i].y,
+             (*xin_areas)[i].width, (*xin_areas)[i].height);
 }
 
 void screen_update_areas(void)
@@ -1628,28 +1638,153 @@ Rect* screen_area(guint desktop, guint head, Rect *search)
     return a;
 }
 
+typedef struct {
+    Rect r;
+    gboolean subtract;
+} RectArithmetic;
+
 guint screen_find_monitor(const Rect *search)
 {
     guint i;
-    guint most = screen_num_monitors;
-    guint mostv = 0;
+    guint mostpx_index = screen_num_monitors;
+    guint mostpx = 0;
+    guint closest_distance_index = screen_num_monitors;
+    guint closest_distance = G_MAXUINT;
+    GSList *counted = NULL;
+
+    /* we want to count the number of pixels search has on each monitor, but not
+       double count.  so if a pixel is counted on monitor A then we should not
+       count it again on monitor B. in the end we want to return the monitor
+       that had the most pixels counted under this scheme.
+
+       this assumes that monitors earlier in the list are more desirable to be
+       considered the search area's monitor.  we try the configured primary
+       monitor first, so it gets the highest preference.
+
+       if we have counted an area A, then we want to subtract the intersection
+       of A with the area on future monitors.
+       but now consider if we count an area B that intersects A. we want to
+       subtract the area B from that counted on future monitors, but not
+       subtract the intersection of A and B twice! so we would add the
+       intersection of A and B back, to account for it being subtracted both
+       for A and B.
+
+       this is the idea behind the algorithm.  we always subtract the full area
+       for monitor M intersected with the search area. we'll call that AREA.
+       but then we go through the list |counted| and for each rectangle in
+       the list that is being subtracted from future monitors, we insert a
+       request to add back the intersection of the subtracted rect with AREA.
+       vice versa for a rect in |counted| that is getting added back.
+    */
+
+    if (config_primary_monitor_index < screen_num_monitors) {
+        const Rect *monitor;
+        Rect on_current_monitor;
+        glong area;
+
+        monitor = screen_physical_area_monitor(config_primary_monitor_index);
+
+        if (RECT_INTERSECTS_RECT(*monitor, *search)) {
+            RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
+            area = RECT_AREA(on_current_monitor);
+
+            if (area > mostpx) {
+                mostpx = area;
+                mostpx_index = config_primary_monitor_index;
+            }
+
+            /* add the intersection rect on the current monitor to the
+               counted list. that's easy for the first one, we just mark it for
+               subtraction */
+            {
+                RectArithmetic *ra = g_slice_new(RectArithmetic);
+                ra->r = on_current_monitor;
+                ra->subtract = TRUE;
+                counted = g_slist_prepend(counted, ra);
+            }
+        }
+    }
 
     for (i = 0; i < screen_num_monitors; ++i) {
-        const Rect *area = screen_physical_area_monitor(i);
-        if (RECT_INTERSECTS_RECT(*area, *search)) {
-            Rect r;
-            guint v;
+        const Rect *monitor;
+        Rect on_current_monitor;
+        glong area;
+        GSList *it;
 
-            RECT_SET_INTERSECTION(r, *area, *search);
-            v = r.width * r.height;
+        monitor = screen_physical_area_monitor(i);
 
-            if (v > mostv) {
-                mostv = v;
-                most = i;
+        if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
+            /* If we don't intersect then find the distance between the search
+               rect and the monitor. We'll use the closest monitor from this
+               metric if none of the monitors intersect. */
+            guint distance = rect_manhatten_distance(*monitor, *search);
+
+            if (distance < closest_distance) {
+                closest_distance = distance;
+                closest_distance_index = i;
             }
+            continue;
+        }
+
+        if (i == config_primary_monitor_index)
+            continue;  /* already did this one */
+
+        RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
+        area = RECT_AREA(on_current_monitor);
+
+        /* remove pixels we already counted on any previous monitors. */
+        for (it = counted; it; it = g_slist_next(it)) {
+            RectArithmetic *ra = it->data;
+            Rect intersection;
+
+            RECT_SET_INTERSECTION(intersection, ra->r, *search);
+            if (ra->subtract) area -= RECT_AREA(intersection);
+            else area += RECT_AREA(intersection);
+        }
+
+        if (area > mostpx) {
+            mostpx = area;
+            mostpx_index = i;
+        }
+
+        /* add the intersection rect on the current monitor I to the counted
+           list.
+           but now we need to compensate for every rectangle R already in the
+           counted list, and add a new rect R' that is the intersection of
+           R and I, but with the reverse subtraction/addition operation.
+        */
+        for (it = counted; it; it = g_slist_next(it)) {
+            RectArithmetic *saved = it->data;
+
+            if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
+                continue;
+            /* we are going to subtract our rect from future monitors, but
+               part of it may already be being subtracted/added, so compensate
+               to not double add/subtract. */
+            RectArithmetic *reverse = g_slice_new(RectArithmetic);
+            RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
+            reverse->subtract = !saved->subtract;
+            /* prepend so we can continue thru the list uninterupted */
+            counted = g_slist_prepend(counted, reverse);
+        }
+        {
+            RectArithmetic *ra = g_slice_new(RectArithmetic);
+            ra->r = on_current_monitor;
+            ra->subtract = TRUE;
+            counted = g_slist_prepend(counted, ra);
         }
     }
-    return most < screen_num_monitors ? most : screen_monitor_primary(FALSE);
+
+    while (counted) {
+        g_slice_free(RectArithmetic, counted->data);
+        counted = g_slist_delete_link(counted, counted);
+    }
+
+    if (mostpx_index < screen_num_monitors)
+        return mostpx_index;
+
+    g_assert(closest_distance_index < screen_num_monitors);
+    return closest_distance_index;
 }
 
 const Rect* screen_physical_area_all_monitors(void)
index 8d12a1f4a39d7a4f7eed09850dee68584ee7d445..c4dec13e565d119c2136ce1950efed457ffa4d62 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -6,15 +6,16 @@
 # Peter Schwindt <schwindt@ba-loerrach.de>
 # Finn Zirngibl <finn@s23.org>, 2008
 # Florian Walch <florian.walch@gmx.at>, 2008
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2011.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: Openbox 3.4.7\n"
+"Project-Id-Version: Openbox 3.5.0\n"
 "Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2011-08-01 18:20+0200\n"
-"PO-Revision-Date: 2008-03-13 13:38+0100\n"
-"Last-Translator: Florian Walch <florian.walch@gmx.at>\n"
-"Language-Team:  <de@li.org>\n"
+"POT-Creation-Date: 2011-11-17 22:54+0100\n"
+"PO-Revision-Date: 2011-11-17 22:54+0100\n"
+"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
+"Language-Team:  <gnome-de@gnome.org>\n"
 "Language: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -24,7 +25,7 @@ msgstr ""
 #: openbox/actions.c:198
 #, c-format
 msgid "Invalid action \"%s\" requested. No such action exists."
-msgstr "Unzulässige Aktion \"%s\" angefordert. Diese Aktion existiert nicht."
+msgstr "Unzulässige Aktion »%s« angefordert. Diese Aktion existiert nicht."
 
 #: openbox/actions/execute.c:147
 msgid "No"
@@ -41,9 +42,9 @@ msgstr "Ausführen"
 #: openbox/actions/execute.c:161
 #, c-format
 msgid "Failed to convert the path \"%s\" from utf8"
-msgstr "Konnte Pfad \"%s\" nicht von UTF-8 konvertieren"
+msgstr "Pfad »%s« konnte nicht aus UTF-8 konvertiert werden"
 
-#: openbox/actions/exit.c:69 openbox/client.c:3550
+#: openbox/actions/exit.c:69 openbox/client.c:3567
 msgid "Cancel"
 msgstr "Abbrechen"
 
@@ -65,61 +66,61 @@ msgstr "Sind Sie sicher, dass Openbox beendet werden soll?"
 
 #: openbox/actions/exit.c:79
 msgid "Exit Openbox"
-msgstr "Beende Openbox"
+msgstr "Openbox beenden"
 
-#: openbox/client.c:2037
+#: openbox/client.c:2054
 msgid "Unnamed Window"
 msgstr "Unbenanntes Fenster"
 
-#: openbox/client.c:2051 openbox/client.c:2082
+#: openbox/client.c:2068 openbox/client.c:2099
 msgid "Killing..."
-msgstr "Wird beendet..."
+msgstr "Wird beendet …"
 
-#: openbox/client.c:2053 openbox/client.c:2084
+#: openbox/client.c:2070 openbox/client.c:2101
 msgid "Not Responding"
 msgstr "Reagiert nicht"
 
-#: openbox/client.c:3539
+#: openbox/client.c:3556
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to force it "
 "to exit by sending the %s signal?"
 msgstr ""
-"Das Fenster \"%s\" scheint nicht zu reagieren. Wollen Sie die Beendigung "
-"durch das Senden des %s-Signals erzwingen?"
+"Das Fenster »%s« scheint nicht zu reagieren. Wollen Sie die Beendigung durch "
+"das Senden des %s-Signals erzwingen?"
 
-#: openbox/client.c:3541
+#: openbox/client.c:3558
 msgid "End Process"
-msgstr "Beende Prozess"
+msgstr "Prozess beenden"
 
-#: openbox/client.c:3545
+#: openbox/client.c:3562
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to disconnect "
 "it from the X server?"
 msgstr ""
-"Das Fenster \"%s\" scheint nicht zu reagieren. Soll es vom X-Server getrennt "
+"Das Fenster »%s« scheint nicht zu reagieren. Soll es vom X-Server getrennt "
 "werden?"
 
-#: openbox/client.c:3547
+#: openbox/client.c:3564
 msgid "Disconnect"
 msgstr "Trennen"
 
 #: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
 msgid "Go there..."
-msgstr "Hierher wechseln..."
+msgstr "Hierher wechseln …"
 
 #: openbox/client_list_combined_menu.c:100
 msgid "Manage desktops"
-msgstr "Desktops verwalten"
+msgstr "Arbeitsflächen verwalten"
 
 #: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
 msgid "_Add new desktop"
-msgstr "_Neuen Desktop hinzufügen"
+msgstr "_Neue Arbeitsfläche hinzufügen"
 
 #: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
 msgid "_Remove last desktop"
-msgstr "_Letzten Desktop entfernen"
+msgstr "_Letzte Arbeitsfläche entfernen"
 
 #: openbox/client_list_combined_menu.c:157
 msgid "Windows"
@@ -127,15 +128,15 @@ msgstr "Fenster"
 
 #: openbox/client_list_menu.c:214
 msgid "Desktops"
-msgstr "Desktops"
+msgstr "Arbeitsflächen"
 
 #: openbox/client_menu.c:259
 msgid "All desktops"
-msgstr "Alle Desktops"
+msgstr "Alle Arbeitsflächen"
 
 #: openbox/client_menu.c:371
 msgid "_Layer"
-msgstr "_Layer"
+msgstr "_Ebene"
 
 #: openbox/client_menu.c:376
 msgid "Always on _top"
@@ -151,15 +152,15 @@ msgstr "Immer im _Hintergrund"
 
 #: openbox/client_menu.c:380
 msgid "_Send to desktop"
-msgstr "_An Desktop senden"
+msgstr "_An Arbeitsfläche senden"
 
 #: openbox/client_menu.c:384
 msgid "Client menu"
-msgstr "Client menu"
+msgstr "Client-Menü"
 
 #: openbox/client_menu.c:394
 msgid "R_estore"
-msgstr "Wi_ederherstellen"
+msgstr "_Wiederherstellen"
 
 #: openbox/client_menu.c:398
 msgid "_Move"
@@ -192,25 +193,27 @@ msgstr "_Schließen"
 #: openbox/config.c:503
 #, c-format
 msgid "Invalid context \"%s\" in mouse binding"
-msgstr "Maus-Binding enthält ungültigen Kontext \"%s\""
+msgstr "Maus-Zuordnung enthält ungültigen Kontext »%s«"
 
 #: openbox/config.c:857
 #, c-format
 msgid "Invalid button \"%s\" specified in config file"
-msgstr "Unzulässiger Button \"%s\" in der Konfigurationsdatei angegeben"
+msgstr "Unzulässige Taste »%s« in der Konfigurationsdatei angegeben"
 
 #: openbox/config.c:882
 msgid ""
 "Openbox was compiled without Imlib2 image loading support. Icons in menus "
 "will not be loaded."
 msgstr ""
+"Openbox wurde ohne die Unterstützung der Imlib2 zum Laden von Grafiken "
+"kompiliert. Symbole in Menüs werden nicht geladen."
 
-#: openbox/debug.c:55
+#: openbox/debug.c:57
 #, c-format
 msgid "Unable to make directory '%s': %s"
-msgstr "Das Verzeichnis '%s' konnte nicht angelegt werden: %s"
+msgstr "Der Ordner »%s« konnte nicht angelegt werden: %s"
 
-#: openbox/debug.c:138 openbox/openbox.c:372
+#: openbox/debug.c:195 openbox/openbox.c:372
 msgid "Close"
 msgstr "Schließen"
 
@@ -221,45 +224,46 @@ msgstr "Konflikt mit Tastenkombination in der Konfigurationsdatei"
 #: openbox/menu.c:94 openbox/menu.c:106
 #, c-format
 msgid "Unable to find a valid menu file \"%s\""
-msgstr "Konnte keine gültige Menü-Datei \"%s\" finden"
+msgstr "Es konnte keine gültige Menü-Datei »%s« gefunden werden"
 
 #: openbox/menu.c:158
 #, c-format
 msgid "Failed to execute command for pipe-menu \"%s\": %s"
-msgstr "Konnte Befehl \"%s\" für pipe-menu nicht ausführen: %s"
+msgstr "Befehl »%s« für Pipe-Menü konnte nicht ausgeführt werden: %s"
 
 #: openbox/menu.c:172
 #, c-format
 msgid "Invalid output from pipe-menu \"%s\""
-msgstr "Ungültige Ausgabe vom pipe-menu \"%s\""
+msgstr "Ungültige Ausgabe vom Pipe-Menü »%s«"
 
 #: openbox/menu.c:185
 #, c-format
 msgid "Attempted to access menu \"%s\" but it does not exist"
 msgstr ""
-"Auf das Menü \"%s\" konnte nicht zugegriffen werden, da es nicht existiert"
+"Auf das Menü »%s« konnte nicht zugegriffen werden, da es nicht existiert"
 
 #: openbox/menu.c:400 openbox/menu.c:401
 msgid "More..."
-msgstr "Mehr..."
+msgstr "Mehr …"
 
-#: openbox/mouse.c:376
+#: openbox/mouse.c:382
 #, c-format
 msgid "Invalid button \"%s\" in mouse binding"
-msgstr "Maus-Binding enthält ungültigen Button \"%s\""
+msgstr "Maus-Zuordnung enthält ungültige Taste »%s«"
 
 #: openbox/openbox.c:137
 #, c-format
 msgid "Unable to change to home directory \"%s\": %s"
-msgstr "Konnte nicht in das Heimatverzeichnis \"%s\" wechseln: %s"
+msgstr "Wechsel in den persönlichen Ordner »%s« ist gescheitert: %s"
 
 #: openbox/openbox.c:152
 msgid "Failed to open the display from the DISPLAY environment variable."
-msgstr "Konnte das Display aus der Umgebungsvariable DISPLAY nicht öffnen."
+msgstr ""
+"Die Anzeige aus der Umgebungsvariable DISPLAY konnte nicht geöffnet werden."
 
 #: openbox/openbox.c:182
 msgid "Failed to initialize the obrender library."
-msgstr "Konnte die obrender-Bibliothek nicht initialisieren."
+msgstr "Die obrender-Bibliothek konnte nicht initialisiert werden."
 
 #: openbox/openbox.c:193
 msgid "X server does not support locale."
@@ -273,12 +277,12 @@ msgstr ""
 #: openbox/openbox.c:253
 msgid "Unable to find a valid config file, using some simple defaults"
 msgstr ""
-"Es wurde keine gültige Konfigurationsdatei gefunden, benutze einfache "
-"Standardwerte"
+"Es wurde keine gültige Konfigurationsdatei gefunden, es werden einfache "
+"Standardwerte verwendet"
 
 #: openbox/openbox.c:286
 msgid "Unable to load a theme."
-msgstr "Konnte kein Thema laden."
+msgstr "Es konnte kein Thema geladen werden."
 
 #: openbox/openbox.c:370
 #, c-format
@@ -287,9 +291,10 @@ msgid ""
 "configuration files.  See stdout for more information.  The last error seen "
 "was in file \"%s\" line %d, with message: %s"
 msgstr ""
-"Beim Parsen der Openbox-Konfigurationsdateien wurden ein oder mehrere XML-"
-"Syntaxfehler gefunden. Die Standardausgabe enthält weitere Informationen. "
-"Der letzte Fehler wurde in der Datei \"%s\" in Zeile %d festgestellt: %s"
+"Beim Verarbeiten der Openbox-Konfigurationsdateien wurden ein oder mehrere "
+"XML-Syntaxfehler gefunden. Die Standardausgabe enthält weitere "
+"Informationen. Der letzte Fehler wurde in der Datei »%s« in Zeile %d "
+"festgestellt: %s"
 
 #: openbox/openbox.c:372
 msgid "Openbox Syntax Error"
@@ -298,8 +303,7 @@ msgstr "Openbox Syntax-Fehler"
 #: openbox/openbox.c:438
 #, c-format
 msgid "Restart failed to execute new executable \"%s\": %s"
-msgstr ""
-"Neustart fehlgeschlagen, um die ausführbare Datei \"%s\" zu starten: %s"
+msgstr "Neustart fehlgeschlagen, um die ausführbare Datei »%s« zu starten: %s"
 
 #: openbox/openbox.c:517 openbox/openbox.c:519
 msgid "Copyright (c)"
@@ -338,7 +342,8 @@ msgstr "  --config-file DATEI Pfad zur Konfigurationsdatei\n"
 
 #: openbox/openbox.c:537
 msgid "  --sm-disable        Disable connection to the session manager\n"
-msgstr "  --sm-disable        Keine Verbindung zum Sitzungsmanager aufbauen\n"
+msgstr ""
+"  --sm-disable        Keine Verbindung zur Sitzungsverwaltung aufbauen\n"
 
 #: openbox/openbox.c:538
 msgid ""
@@ -350,7 +355,7 @@ msgstr ""
 
 #: openbox/openbox.c:539
 msgid "  --reconfigure       Reload Openbox's configuration\n"
-msgstr "  --reconfigure       Openbox's Konfiguration neu laden\n"
+msgstr "  --reconfigure       Openbox-Konfiguration neu laden\n"
 
 #: openbox/openbox.c:540
 msgid "  --restart           Restart Openbox\n"
@@ -358,7 +363,7 @@ msgstr "  --restart           Openbox neu starten\n"
 
 #: openbox/openbox.c:541
 msgid "  --exit              Exit Openbox\n"
-msgstr "  --exit              Beende Openbox\n"
+msgstr "  --exit              Openbox beenden\n"
 
 #: openbox/openbox.c:542
 msgid ""
@@ -366,7 +371,7 @@ msgid ""
 "Debugging options:\n"
 msgstr ""
 "\n"
-"Debugging Optionen:\n"
+"Debugging-Optionen:\n"
 
 #: openbox/openbox.c:543
 msgid "  --sync              Run in synchronous mode\n"
@@ -374,7 +379,7 @@ msgstr "  --sync              im Synchronisierungsmodus starten\n"
 
 #: openbox/openbox.c:544
 msgid "  --startup CMD       Run CMD after starting\n"
-msgstr ""
+msgstr "  --startup BEFEHL    Befehl nach dem Start ausführen\n"
 
 #: openbox/openbox.c:545
 msgid "  --debug             Display debugging output\n"
@@ -383,13 +388,12 @@ msgstr "  --debug             Debugging-Informationen anzeigen\n"
 #: openbox/openbox.c:546
 msgid "  --debug-focus       Display debugging output for focus handling\n"
 msgstr ""
-"  --debug-focus       Debugging-Informationen für's Fokus-Handling anzeigen\n"
+"  --debug-focus       Debugging-Informationen für Fokus-Handling anzeigen\n"
 
 #: openbox/openbox.c:547
-#, fuzzy
 msgid "  --debug-session     Display debugging output for session management\n"
 msgstr ""
-"  --debug-session     Debugging-Informationen für's Session-Handling "
+"  --debug-session     Debugging-Informationen für die Sitzungsverwaltung "
 "anzeigen\n"
 
 #: openbox/openbox.c:548
@@ -404,7 +408,7 @@ msgid ""
 "Please report bugs at %s\n"
 msgstr ""
 "\n"
-"Bitte melden Sie Bugreports an: %s\n"
+"Bitte melden Sie Fehler an: %s\n"
 
 #: openbox/openbox.c:632 openbox/openbox.c:666
 #, c-format
@@ -414,7 +418,7 @@ msgstr "%s erfordert einen Parameter\n"
 #: openbox/openbox.c:709
 #, c-format
 msgid "Invalid command line argument \"%s\"\n"
-msgstr "Ungültiges Kommandozeilen Argument \"%s\"\n"
+msgstr "Ungültiges Befehlszeilenargument »%s«\n"
 
 #: openbox/screen.c:106 openbox/screen.c:191
 #, c-format
@@ -424,7 +428,8 @@ msgstr "Ein Fenstermanager läuft bereits auf Bildschirm %d"
 #: openbox/screen.c:127
 #, c-format
 msgid "Could not acquire window manager selection on screen %d"
-msgstr "Konnte die Fenstermanagerauswahl auf Bildschirm %d nicht reservieren"
+msgstr ""
+"Die Fenstermanagerauswahl auf Bildschirm %d konnte nicht reserviert werden"
 
 #: openbox/screen.c:150
 #, c-format
@@ -444,74 +449,42 @@ msgid_plural ""
 "Openbox is configured for %d desktops, but the current session has %d.  "
 "Overriding the Openbox configuration."
 msgstr[0] ""
-"Openbox wurde für %d Desktop konfiguriert, aber die aktuelle Sitzung hat %d. "
-"Überschreibe die Openbox-Konfiguration."
+"Openbox wurde für %d Arbeitsfläche konfiguriert, aber die aktuelle Sitzung "
+"hat %d. Die Openbox-Konfiguration wird überschrieben."
 msgstr[1] ""
-"Openbox wurde für %d Desktops konfiguriert, aber die aktuelle Sitzung hat "
-"%d. Überschreibe die Openbox-Konfiguration."
+"Openbox wurde für %d Arbeitsflächen konfiguriert, aber die aktuelle Sitzung "
+"hat %d. Die Openbox-Konfiguration wird überschrieben."
 
 #: openbox/screen.c:1205
 #, c-format
 msgid "desktop %i"
-msgstr "Desktop %i"
+msgstr "Arbeitsfläche %i"
 
 #: openbox/startupnotify.c:241
 #, c-format
 msgid "Running %s"
-msgstr "Starte %s"
+msgstr "%s starten"
 
 #: openbox/translate.c:59
 #, c-format
 msgid "Invalid modifier key \"%s\" in key/mouse binding"
-msgstr "Ungültige Modifier-Taste \"%s\" in Tastenbelegung/Maus-Binding"
+msgstr "Ungültige Modifier-Taste »%s« in Tastenbelegung/Maus-Zuordnung"
 
 #: openbox/translate.c:138
 #, c-format
 msgid "Invalid key code \"%s\" in key binding"
-msgstr "Ungültiger Tastencode \"%s\" in Tastenkombination"
+msgstr "Ungültiger Tastencode »%s« in Tastenkombination"
 
 #: openbox/translate.c:145
 #, c-format
 msgid "Invalid key name \"%s\" in key binding"
-msgstr "Ungültiger Tastenname \"%s\" in Tastenkombination"
+msgstr "Ungültiger Tastenname »%s« in Tastenkombination"
 
 #: openbox/translate.c:151
 #, c-format
 msgid "Requested key \"%s\" does not exist on the display"
-msgstr "Angeforderte Taste \"%s\" existiert nicht auf dem Display"
+msgstr "Angeforderte Taste »%s« existiert nicht in der Anzeige"
 
 #: openbox/prompt.c:153
 msgid "OK"
 msgstr "OK"
-
-#, fuzzy
-#~ msgid "Openbox"
-#~ msgstr "Beende Openbox"
-
-#~ msgid "--config-file requires an argument\n"
-#~ msgstr "--config-file erfordert einen Parameter\n"
-
-#~ msgid ""
-#~ "The SessionLogout action is not available since Openbox was built without "
-#~ "session management support"
-#~ msgstr ""
-#~ "Die SessionLogout-Aktion ist nicht verfügbar, da Openbox ohne "
-#~ "Unterstützung für Sitzungsmanagement kompiliert wurde"
-
-#~ msgid "Unable to save the session to \"%s\": %s"
-#~ msgstr "Konnte die Sitzung \"%s\" nicht sichern: %s"
-
-#~ msgid "Error while saving the session to \"%s\": %s"
-#~ msgstr "Fehler beim Speichern der Sitzung nach \"%s\": %s"
-
-#~ msgid "Not connected to a session manager"
-#~ msgstr "Nicht mit einem Sitzungsmanager verbunden"
-
-#~ msgid "X Error: %s"
-#~ msgstr "X-Fehler: %s"
-
-#~ msgid "Failed to execute \"%s\": %s"
-#~ msgstr "Konnte \"%s\" nicht ausführen: %s"
-
-#~ msgid "Invalid use of action \"%s\". Action will be ignored."
-#~ msgstr "Unzulässiger Einsatz der Aktion \"%s\". Aktion wird ignoriert."
diff --git a/release/bugs b/release/bugs
new file mode 100755 (executable)
index 0000000..f36a7aa
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+help() {
+  echo "Usage: $0 <revision> [lastrelease]"
+  echo
+  echo "  <revision>    The revision which should be used for release."
+  echo "  [lastrelease] The revision of the most recent release made."
+  echo "                By default it uses the most recent release-tag."
+  exit 1
+}
+
+error() {
+  echo "error: $1"
+  exit 1
+}
+
+test -e "./openbox/openbox.c" || \
+  error "must be run from the project's top level directory"
+
+REV="$1"
+test -z "$REV" && help
+RELEASE_SHA=$(git rev-parse "$REV")
+test $? = 0 || error "revision $REV not found"
+
+LAST="$2"
+if test -z "$LAST"; then
+    LAST=$(git describe --match 'release-*' $REV)
+    test $? = 0 || \
+       error "unable to find last release"
+fi
+
+#### CHANGELOG #####
+git log --no-merges $LAST..$REV --oneline|egrep '[bB][uU][Gg] #?[0-9]+'
+git log --no-merges $LAST..$REV --oneline | \
+  perl -n -e'/[bB][uU][Gg] #?([0-9]+)/ && print "#$1, "'
+perl -e'print "\b\b \n"'
+
+exit 0
diff --git a/release/common b/release/common
new file mode 100644 (file)
index 0000000..56093c0
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+NOW=$(date +%s)
+SRCDIR="$PWD"
+WORKDIR="/tmp/openbox.$NOW"
+TESTDIR="$WORKDIR/.test"
+
+clean() {
+  cd "$SRCDIR"
+  if test -d "$WORKDIR"; then
+    chmod -R +w "$WORKDIR"
+    rm -r -f "$WORKDIR"
+  fi
+}
+
+error() {
+  echo "error: $1"
+  test -n "$2" && echo "$2"
+  clean
+  exit 1
+}
+
+RELEASE_SHA=$(git rev-parse "$REV")
+test $? = 0 || error "revision $REV not found"
+
+#NON_MASTER=$(git rev-list $RELEASE_SHA ^master)
+#test $? = 0 || error "git rev-list failed"
+#test -z $NON_MASTER || error "REVISION contains commits not on master"
+
+git new-workdir "$SRCDIR" "$WORKDIR"
+test $? = 0 || error "git-new-workdir failed"
+
+echo "working in $WORKDIR"
+
+cd "$WORKDIR"
+test $? = 0 || error "cd to $WORKDIR failed"
+
+git checkout -q $RELEASE_SHA
+test $? = 0 || error "git-checkout failed"
+
+# get last release
+if test -z "$LAST"; then
+    LAST=$(git describe --match 'release-*' --abbrev=0 $REV)
+    test $? = 0 || \
+       error "unable to find last release"
+    echo "Using previous release as $LAST"
+fi
+
+H="$(head -1 CHANGELOG|cut -d: -f 1)"
+test $H = $VERSION ||
+  error "CHANGELOG does not contain version $VERSION"
+
+CL="$(cat CHANGELOG|sed -n /^$VERSION:\$/,/^\$/p|tail -n +2|head -n -1)"
+CLNOWRAP=$(echo "$CL" \
+  |sed -e "s/^  \*/*/"|sed -n '1h;1!H;${;g;s/\n    / /g;p;}')
+SHORTLOG="$(git shortlog --no-merges $LAST..$REV)"
diff --git a/release/email b/release/email
new file mode 100755 (executable)
index 0000000..7fa9e57
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+help() {
+  echo "Usage: $0 <revision> <version> [lastrelease]"
+  echo
+  echo "  <revision>    The revision which should be used for release."
+  echo "  <version>     The version of the release."
+  echo "  [lastrelease] The revision of the most recent release made."
+  echo "                By default it uses the most recent release-tag."
+  exit 1
+}
+
+REV="$1"
+test -z "$REV" && help
+VERSION="$2"
+test -z "$VERSION" && help
+LAST="$3"
+
+. release/common
+
+SUBJECT="[RELEASE] Openbox $VERSION"
+MAILINGLIST=openbox@icculus.org
+MIKACHU=mikachu@icculus.org
+
+cat <<EOF > $WORKDIR/.email
+Hello,
+
+Openbox $VERSION is now available!
+
+Some noteworthy changes are:
+$CLNOWRAP
+
+======== Download ========
+
+Download links are here: http://openbox.org/wiki/Openbox:Download
+
+======== Commits ========
+
+The following is a full list of commits appearing in this release.
+You can see the full commits here: http://git.openbox.org/?p=dana/openbox.git;a=shortlog;h=refs/tags/release-$VERSION
+
+
+$SHORTLOG
+EOF
+
+if test -z $EDITOR; then
+    nano -w $WORKDIR/.email || error "failed to edit email, set \$EDITOR"
+else
+    $EDITOR $WORKDIR/.email || error "failed to edit email with \$EDTIOR"
+fi
+
+test -e $WORKDIR/.email || error "email file disappeared"
+cat $WORKDIR/.email | mail -s "$SUBJECT" "$MAILINGLIST" || \
+    error "mail to $MAILINGLIST failed"
+
+echo "Hi Mikachu,
+
+Please update the freshmeat.net Openbox stuff for $VERSION. Thanks!" | \
+mail -s "Freshmeat.net for Openbox $VERSION" "$MIKACHU" || \
+    error "mail to $MIKACHU failed"
+
+clean
+exit 0
diff --git a/release/go b/release/go
new file mode 100755 (executable)
index 0000000..3158ac2
--- /dev/null
@@ -0,0 +1,158 @@
+#!/bin/sh
+
+help() {
+  echo "Usage: $0 <revision> <version> [lastrelease]"
+  echo
+  echo "  <revision>    The revision which should be used for release."
+  echo "  <version>     The version of the release."
+  echo "  [lastrelease] The revision of the most recent release made."
+  echo "                By default it uses the most recent release-tag."
+  exit 1
+}
+
+REV="$1"
+test -z "$REV" && help
+VERSION="$2"
+test -z "$VERSION" && help
+LAST="$3"
+
+. release/common
+
+#### CONFIRM SHORTLOG #####
+
+echo Shortlog from previous release:
+echo "$SHORTLOG"
+echo
+echo Shortlog from $LAST contains $(echo "$SHORTLOG"|wc -l) lines
+echo -n "ok? (y/n) "
+read a
+test "$a" = "y" || error "aborted"
+
+#### TEST english po VERSIONS ####
+
+BAD_PO="$(grep Project-Id-Version po/en*.po|grep -v "openbox $VERSION\\\\n")"
+test -z "$BAD_PO" || error "wrong version in po files" "$BAD_PO"
+
+#### TEST COMPILATION ####
+
+# check that it builds
+./bootstrap >/dev/null || "bootstrap failed"
+#CFLAGS="-Werror -isystem /usr/lib/glib-2.0" \
+./configure -C --enable-debug >/dev/null || \
+  error "configure (with debug) failed"
+make || error "make (with debug and Werror) failed"
+git clean -f -x -d -q
+
+# check that it builds with each optional featureset
+./bootstrap >/dev/null || "bootstrap failed"
+
+echo Check compile with all options enabled
+./configure -C >/dev/null || \
+  error "configure failed"
+make >/dev/null 2>/dev/null || \
+  error "make failed"
+grep "XKB 1" config.log >/dev/null || error "missing xkb extension"
+grep "XRANDR 1" config.log >/dev/null || error "missing xrandr extension"
+grep "XINERAMA 1" config.log >/dev/null || error "missing xinerama extension"
+grep "SYNC 1" config.log >/dev/null || error "missing sync extension"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with startup notification disabled
+./configure -C --disable-startup-notification >/dev/null || \
+  error "configure failed"
+make >/dev/null 2>/dev/null || \
+  error "make (with --disable-startup-notification) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xcursor disabled
+./configure -C --disable-xcursor >/dev/null || \
+  error "configure failed"
+make >/dev/null 2>/dev/null || \
+  error "make (with --disable-xcursor) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with imlib2 disabled
+./configure -C --disable-imlib2 >/dev/null || \
+  error "configure failed"
+make >/dev/null 2>/dev/null || \
+  error "make (with --disable-imlib2) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with session management disabled
+./configure -C --disable-session-management >/dev/null || \
+  error "configure failed"
+make >/dev/null 2>/dev/null || \
+  error "make (with --disable-session-management) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xkb disabled
+./configure -C --disable-xkb >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xkb) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xrandr disabled
+./configure -C --disable-xrandr >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xrandr) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xinerama disabled
+./configure -C --disable-xinerama >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xinerama) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xsync disabled
+./configure -C --disable-xsync >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xsync) failed"
+make clean >/dev/null || error "make clean failed"
+
+# check that it installs sanely
+echo Check installation correctness
+./configure -C >/dev/null || \
+  error "configure failed"
+make distcheck >/dev/null || \
+  error "make distcheck failed"
+
+# VERIFY TARBALL
+
+TAR="openbox-$VERSION.tar.gz"
+ASC="openbox-$VERSION.tar.gz.asc"
+
+echo Found Openbox release tarball:
+ls -d openbox-*.tar.gz
+test -e "$TAR" || \
+  error "Specified version does not match configure.am"
+
+# SIGN THE TARBALL
+
+echo Signing the release tarball:
+gpg --sign --detach-sign --armor "$TAR"
+test $? = 0 || \
+  error "Failed to sign release tarball"
+
+echo Tagging the release:
+git tag -s -m "tagging the $VERSION release" "release-$VERSION" $REV || \
+  error "Failed to tag the release"
+
+mv "$TAR" "$ASC" "$SRCDIR"
+
+echo "=$VERSION="
+echo "$CLNOWRAP"
+echo
+echo
+echo Edit download page:
+echo "  http://openbox.org/oldwiki/index.php?title=Openbox:Download&action=edit&section=1"
+echo
+echo Edit changelog:
+echo "  http://openbox.org/oldwiki/index.php?title=Openbox:Changelog&action=edit&section=1"
+echo
+echo Push the tag:
+echo "  git push origin tag release-$VERSION"
+echo
+echo Email:
+echo "  ./release/email $*"
+echo
+cd "$SRCDIR"
+ls -l "$TAR" "$ASC"
+
+clean
+exit 0
This page took 0.144847 seconds and 4 git commands to generate.