Merge branch 'm4/master'
authorMikael Magnusson <mikachu@gmail.com>
Tue, 2 Aug 2011 20:18:14 +0000 (22:18 +0200)
committerMikael Magnusson <mikachu@gmail.com>
Tue, 2 Aug 2011 20:18:14 +0000 (22:18 +0200)
89 files changed:
.gitignore
CHANGELOG
Makefile.am
README.GIT
configure.ac
data/autostart/openbox-autostart.in
data/rc.xml
data/rc.xsd
doc/rc-mouse-focus.xml
m4/openbox.m4
m4/x11.m4
obrender/image.c
obrender/image.h
obrender/imagecache.c
obrender/imagecache.h
obrender/render.h
obt/bsearch.h
obt/keyboard.c
obt/prop.c
obt/prop.h
obt/tests/bstest.c [new file with mode: 0755]
obt/tests/ddtest.c [new file with mode: 0755]
obt/tests/ddtest.desktop [new file with mode: 0644]
obt/tests/linktest.c [moved from obt/watch.h with 57% similarity, mode: 0755]
obt/tests/watchtest.c [new file with mode: 0755]
obt/watch.c [deleted file]
obt/xml.c
openbox/actions.c
openbox/actions/execute.c
openbox/actions/if.c
openbox/client.c
openbox/client.h
openbox/config.c
openbox/debug.c
openbox/dock.c
openbox/event.c
openbox/event.h
openbox/focus.c
openbox/focus_cycle.c
openbox/frame.c
openbox/menu.c
openbox/menu.h
openbox/menuframe.c
openbox/moveresize.c
openbox/openbox.c
openbox/place.c
openbox/place.h
openbox/prompt.c
openbox/screen.c
openbox/screen.h
openbox/stacking.c
openbox/startupnotify.c
openbox/window.c
po/ar.po
po/bn_IN.po
po/ca.po
po/cs.po
po/da.po
po/de.po
po/en@boldquot.po
po/en@quot.po
po/es.po
po/et.po
po/eu.po
po/fi.po
po/fr.po
po/hr.po
po/hu.po
po/it.po
po/ja.po
po/lt.po
po/lv.po
po/nl.po
po/no.po
po/openbox.pot
po/pl.po
po/pt.po
po/pt_BR.po
po/ru.po
po/sk.po
po/sr.po
po/sr@latin.po
po/sv.po
po/tr.po
po/uk.po
po/vi.po
po/zh_CN.po
po/zh_TW.po
tests/icons.c

index 79e2b42..758f243 100644 (file)
@@ -102,3 +102,7 @@ tests/wmhints
 data/autostart/openbox-autostart
 data/autostart/autostart
 obrender/rendertest
+obt/tests/bstest
+obt/tests/ddtest
+obt/tests/linktest
+obt/tests/watchtest
index 8636efb..9bd1021 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,61 @@
-3.5.0-rc1:
+3.5.0:
+  * New alt-tab dialog shows windows in a vertical list.
+  * Improved Xinerama support.
+  * Allow icons in menus.
+  * Theme options for prompt dialogs (osd.button.unpressed.*,
+    osd.button.pressed.*, osd.button.focused.*)
+  * Addresses bug #4877, #4596, #4617, #4752, #4663, #4662, #4586, #2319,
+    #4341, #4519, #4543, #4503, #4355, #4072, #3702, #4284
+  * Lots of additional bug fixes and performance improvements.
+
+3.4.11.2:
+  * Updated Estonian and Portuguese translations.
+  * Fix a rare crash involving moving fullscreen windows to different monitors
+  * Fix a more common crash involving pressing right in a menu
+
+3.4.11.1:
+  * Updated Polish translation.
+  * Fixed bug #4519 (Incorrect focus in reused windows).
+  * Lower the default submenu open/hide delay from 200ms to 100ms.
+  * Fix some more problems with gnome integration.
+  * Update Clearlooks theme.
+  * Some other small fixes.
+
+3.4.11:
+  * Update Hungarian, Japanese, and Latvian translations.
+  * Make xdg-autostart use the OPENBOX environment by default, so you can use
+    OnlyShowIn=OPENBOX in an autostart .desktop and it will work as expected.
+  * Don't close the menu when you hold control and execute something.
+  * Fix bug #4503 (Adjust who shows up in the Alt-Tab list using SKIP_TASKBAR).
+  * Fix flickering window when moving maximized window between monitors of
+    different sizes.
+  * Fix bug #4355 (Allow multiple escaped _'s in a menu label and allow
+    a menu shortcut to come after an escaped _).
+  * Remember the maximized state of a window when it goes fullscreen, and
+    restore it when leaving fullscreen state.
+  * Fix bug #4072 (Openbox is stopped by terminal applications writing to
+    stdout).
+  * Fix bug #4492 (Mistake in openbox-gnome-session check while setting up).
+  * Fix obxprop to make --root and --id work correctly.
+  * Add _OB_APP_ROLE/CLASS/NAME/TYPE properties (replaces _OB_ROLE/CLASS/NAME).
+  * Make the focus cycling popup dynamic when windows appear/disappear.
+  * Fix bug #4411 (Crash when window appears during focus cycling).
+  * Allow the user to specify which properties should be shown by obxprop.
+  * Fix tilde expansion in the Execute action
+  * Make Home and End keys move to the top/bottom of the active menu.
+  * Use the submenuShowDelay when navigating menus with the keyboard.
+
+3.4.10:
+  * Improve keyboard navigation in Openbox menus.
+  * Add a --root option and a manpage for obxprop.
+  * Use a negative value for submenuShowDelay and submenuHideDelay to cause
+    an infinite delay.  This means you have to click to show a submenu, rather
+    than just hover over it.
+  * Improved code for submenu show/hide delay.  Added the submenuHideDelay
+    config file option, under the "menu" section.
+  * Fixed bug #4464 (Typo in openbox-gnome-session script).
+  * Fixed bug #4436 (Focusing a window used to stop focus cycling).
+  * Renamed obprop to obxprop due to collision with Open Babel (See bug #4419).
 
 3.4.9:
   * Allow focus to move while inside an Openbox menu, or during an interactive
index 8cdb126..c62a594 100644 (file)
@@ -47,8 +47,7 @@ nodist_rc_SCRIPTS = \
        data/autostart/autostart
 
 dist_libexec_SCRIPTS = \
-       data/autostart/openbox-xdg-autostart \
-       data/autostart/openbox-autostart
+       data/autostart/openbox-xdg-autostart
 
 nodist_libexec_SCRIPTS = \
        data/autostart/openbox-autostart
@@ -154,8 +153,6 @@ obt_libobt_la_SOURCES = \
        obt/signal.h \
        obt/signal.c \
        obt/util.h \
-       obt/watch.h \
-       obt/watch.c \
        obt/xqueue.h \
        obt/xqueue.c
 
@@ -175,6 +172,8 @@ openbox_openbox_CPPFLAGS = \
        -DCONFIGDIR=\"$(configdir)\" \
        -DG_LOG_DOMAIN=\"Openbox\"
 openbox_openbox_LDADD = \
+       $(XINERAMA_LIBS) \
+       $(XRANDR_LIBS) \
        $(SM_LIBS) \
        $(GLIB_LIBS) \
        $(X_LIBS) \
@@ -326,9 +325,11 @@ tools_obxprop_obxprop_SOURCES = \
 ## gdm-control ##
 
 tools_gdm_control_gdm_control_CPPFLAGS = \
+       $(XAUTH_CFLAGS) \
        $(X_CFLAGS) \
        $(GLIB_CFLAGS)
 tools_gdm_control_gdm_control_LDADD = \
+       $(XAUTH_LIBS) \
        $(X_LIBS) \
        $(GLIB_LIBS)
 tools_gdm_control_gdm_control_SOURCES = \
@@ -451,7 +452,6 @@ obtpubinclude_HEADERS = \
        obt/signal.h \
        obt/util.h \
        obt/version.h \
-       obt/watch.h \
        obt/xqueue.h
 
 nodist_pkgconfig_DATA = \
@@ -473,20 +473,21 @@ dist_rc_DATA = \
 edit = $(SED) \
        -e 's!@version\@!$(VERSION)!' \
        -e 's!@configdir\@!$(configdir)!' \
+       -e 's!@rcdir\@!$(rcdir)!' \
        -e 's!@libexecdir\@!$(libexecdir)!' \
        -e 's!@bindir\@!$(bindir)!'
 
-data/autostart/autostart: $(srcdir)/data/autostart/autostart.in Makefile
+data/autostart/autostart: $(top_srcdir)/data/autostart/autostart.in Makefile
        @echo make: creating $@
-       @test -d $(shell dirname $(builddir)/$@) || \
-         mkdir $(shell dirname $(builddir)/$@)
-       @$(edit) $< >$(builddir)/$@
+       @test -d $(shell dirname $(top_builddir)/$@) || \
+         mkdir $(shell dirname $(top_builddir)/$@)
+       @$(edit) $< >$(top_builddir)/$@
 
-data/autostart/openbox-autostart: $(srcdir)/data/autostart/openbox-autostart.in Makefile
+data/autostart/openbox-autostart: $(top_srcdir)/data/autostart/openbox-autostart.in Makefile
        @echo make: creating $@
-       @test -d $(shell dirname $(builddir)/$@) || \
-         mkdir $(shell dirname $(builddir)/$@)
-       @$(edit) $< >$(builddir)/$@
+       @test -d $(shell dirname $(top_builddir)/$@) || \
+         mkdir $(shell dirname $(top_builddir)/$@)
+       @$(edit) $< >$(top_builddir)/$@
 
 %.desktop: %.desktop.in Makefile
        @echo make: creating $@
index b4b0557..dfed9c4 100644 (file)
@@ -1,23 +1,26 @@
 To build Openbox from git you need:
 
 A C Compiler (GNU GCC 3.2+ suggested)
-GNU Gettext 0.14.4
-GNU Autoconf 2.50+
-GNU Automake 1.9 (no more, no less)
-GNU Libtool
-Xlib library/headers (devel package)
-Pkg-Config
-Glib 2.0+ library/headers (devel package) (http://www.gtk.org)
-libxml2 2.0+ library/headers (devel package)
-Pango 1.10+ library/headers (devel package)
-cvs
-
-Optionally you may want:
-X Cursor library/headers (devel package)
-Startup Notification library/headers 0.8+ (devel package)
+-GNU Gettext 0.14.4
+-GNU Autoconf 2.50+
+-GNU Automake 1.9 (no more, no less)
+-GNU Libtool
+-Xlib library/headers (devel package)
+-Pkg-Config
+-Glib 2.0+ library/headers (devel package) (http://www.gtk.org)
+-libxml2 2.0+ library/headers (devel package)
+-Pango 1.10+ library/headers (devel package)
+-cvs or git or neither, depending on how gettext was installed*
+
+Also you will probably want:
+-Imlib2 library/headers (devel package)
+-X Cursor library/headers (devel package)
+-Startup Notification library/headers 0.8+ (devel package)
 
 We recommend the latest versions of all these packages.
 
+*) for versions below 0.18.1 cvs is needed, above that, see autopoint --version
+
 Do the following to build and install Openbox in git:
 
 % ./bootstrap
@@ -28,8 +31,8 @@ su to root and
 or
 % sudo make install
 
-Don't try running it from the openbox/ directory without installing, it won't work.
-It needs to be installed before it is run.
+Don't try running it from the openbox/ directory without installing, it won't
+work. It needs to be installed before it is run.
 
 The following commands will be available: openbox-session,
   openbox-gnome-session, openbox-kde-session, and openbox.
@@ -40,21 +43,19 @@ See the man pages for details about them.  If you want to run Openbox on its
 
 
 ----
-In debian unstable, you want these packages:
+In Ubuntu, you want these packages:
+
 gcc
 gettext
-automake1.9
+automake
 autoconf
 libtool
 libpango1.0-dev
 pkg-config
 libglib2.0-dev
 libxml2-dev
-libxcursor-dev
 libstartup-notification0-dev
-xlibs-dev
-
-tip: run "update-alternatives --config automake" and select automake1.9 before
-  running ./configure
+xorg-dev
+libimlib2-dev
 
 ----
index 78b5aa6..82c627f 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ([2.54])
-AC_INIT([openbox], [3.5.0-rc1], [http://bugzilla.icculus.org])
+AC_INIT([openbox], [3.5.0], [http://bugzilla.icculus.org])
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([openbox/openbox.c])
 
@@ -18,16 +18,16 @@ dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
 dnl
 RR_MAJOR_VERSION=3
 RR_MINOR_VERSION=5
-RR_MICRO_VERSION=27
-RR_INTERFACE_AGE=0
-RR_BINARY_AGE=0
+RR_MICRO_VERSION=28
+RR_INTERFACE_AGE=1
+RR_BINARY_AGE=1
 RR_VERSION=$RR_MAJOR_VERSION.$RR_MINOR_VERSION
 
 OBT_MAJOR_VERSION=3
 OBT_MINOR_VERSION=5
-OBT_MICRO_VERSION=0
-OBT_INTERFACE_AGE=0
-OBT_BINARY_AGE=0
+OBT_MICRO_VERSION=1
+OBT_INTERFACE_AGE=1
+OBT_BINARY_AGE=1
 OBT_VERSION=$OBT_MAJOR_VERSION.$OBT_MINOR_VERSION
 
 AC_SUBST(RR_MAJOR_VERSION)
@@ -87,11 +87,9 @@ AC_PROG_INSTALL
 AM_GNU_GETTEXT_VERSION(0.15)
 AM_GNU_GETTEXT([external])
 
-AC_CHECK_HEADERS(ctype.h fcntl.h grp.h locale.h pwd.h signal.h string.h)
-AC_CHECK_HEADERS(stdio.h stdlib.h unistd.h sys/stat.h sys/select.h)
-AC_CHECK_HEADERS(sys/socket.h sys/time.h sys/wait.h sys/inotify.h)
-# AC_HEADER_TIME
-# AC_TYPE_SIGNAL
+AC_CHECK_HEADERS(ctype.h dirent.h errno.h fcntl.h grp.h locale.h pwd.h)
+AC_CHECK_HEADERS(signal.h string.h stdio.h stdlib.h unistd.h sys/stat.h)
+AC_CHECK_HEADERS(sys/select.h sys/socket.h sys/time.h sys/types.h sys/wait.h)
 
 AC_PATH_PROG([SED], [sed], [no])
 if test "$SED" = "no"; then
@@ -118,7 +116,7 @@ AC_SUBST(XML_LIBS)
 AC_ARG_ENABLE(startup-notification,
   AC_HELP_STRING(
     [--disable-startup-notification],
-    [disable the startup notification library. [[default=enabled]]]
+    [disable the startup notification library. [default=enabled]]
   ),
   [enable_sn=$enableval],
   [enable_sn=yes]
@@ -143,7 +141,7 @@ fi
 AC_ARG_ENABLE(xcursor,
   AC_HELP_STRING(
     [--disable-xcursor],
-    [disable use of the X Cursor library. [[default=enabled]]]
+    [disable use of the X Cursor library. [default=enabled]]
   ),
   [enable_xcursor=$enableval],
   [enable_xcursor=yes]
@@ -168,7 +166,7 @@ fi
 AC_ARG_ENABLE(imlib2,
   AC_HELP_STRING(
     [--disable-imlib2],
-    [disable use of Imlib2 image library for loading icons. [[default=enabled]]]
+    [disable use of Imlib2 image library for loading icons. [default=enabled]]
   ),
   [enable_imlib2=$enableeval],
   [enable_imlib2=yes]
@@ -208,6 +206,7 @@ X11_EXT_XRANDR
 X11_EXT_SHAPE
 X11_EXT_XINERAMA
 X11_EXT_SYNC
+X11_EXT_AUTH
 
 AC_CONFIG_FILES([
   Makefile
index 3b2f5ec..063c635 100755 (executable)
@@ -11,7 +11,7 @@ elif which xsetroot >/dev/null; then
 fi
 test -z $BG || $BG -solid "#303030"
 
-GLOBALAUTOSTART="@configdir@/autostart"
+GLOBALAUTOSTART="@rcdir@/autostart"
 AUTOSTART="${XDG_CONFIG_HOME:-"$HOME/.config"}/openbox/autostart"
 
 # Run the global openbox autostart script
@@ -31,4 +31,4 @@ fi
 # Run the XDG autostart stuff.  These are found in /etc/xdg/autostart and
 # in $HOME/.config/autostart.  This requires PyXDG to be installed.
 # See openbox-xdg-autostart --help for more details.
-@libexecdir@/openbox-xdg-autostart OPENBOX
+@libexecdir@/openbox-xdg-autostart "$@"
index 21b0c87..7598a72 100644 (file)
@@ -3,7 +3,8 @@
 <!-- Do not edit this file, it will be overwritten on install.
         Copy the file to $HOME/.config/openbox/ instead. -->
 
-<openbox_config xmlns="http://openbox.org/3.4/rc">
+<openbox_config xmlns="http://openbox.org/3.4/rc"
+               xmlns:xi="http://www.w3.org/2001/XInclude">
 
 <resistance>
   <strength>10</strength>
   <center>yes</center>
   <!-- whether to place windows in the center of the free area found or
        the top left corner -->
-  <monitor>Active</monitor>
+  <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
-       the active window is -->
+       the active window is, 'Primary' - only on the primary monitor -->
   <primaryMonitor>1</primaryMonitor>
   <!-- The monitor where Openbox should place popup dialogs such as the
        focus cycling popup, or the desktop switch popup.  It can be an index
 </keyboard>
 
 <mouse>
-  <dragThreshold>8</dragThreshold>
+  <dragThreshold>1</dragThreshold>
   <!-- number of pixels the mouse must move before a drag begins -->
   <doubleClickTime>200</doubleClickTime>
   <!-- in milliseconds (1000 = 1 second) -->
index 3a1d654..ad96994 100644 (file)
@@ -72,6 +72,7 @@
             <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:sequence>
     </xsd:complexType>
     <xsd:complexType name="margins">
             <xsd:element minOccurs="0" name="edge" type="xsd:string"/>
             <xsd:element minOccurs="0" name="wrap" type="ob:bool"/>
             <xsd:element minOccurs="0" name="follow" type="ob:bool"/>
-            <xsd:element minOccurs="0" name="dialog" type="ob:bool"/>
+            <xsd:element minOccurs="0" name="dialog" type="ob:dialogtype"/>
             <xsd:element minOccurs="0" name="panels" type="ob:bool"/>
             <xsd:element minOccurs="0" name="here" type="ob:bool"/>
             <xsd:element minOccurs="0" name="linear" type="ob:bool"/>
             <xsd:enumeration value="Any"/>
             <xsd:enumeration value="Mouse"/>
             <xsd:enumeration value="Active"/>
+            <xsd:enumeration value="Primary"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+    <xsd:simpleType name="primarymonitor">
+        <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:restriction>
     </xsd:simpleType>
     <xsd:simpleType name="popupposition">
             <xsd:enumeration value="Nonpixel"/>
         </xsd:restriction>
     </xsd:simpleType>
+    <xsd:simpleType name="dialogtype">
+        <xsd:restriction base="xsd:string">
+            <xsd:enumeration value="None"/>
+            <xsd:enumeration value="Icons"/>
+            <xsd:enumeration value="List"/>
+        </xsd:restriction>
+    </xsd:simpleType>
 </xsd:schema>
index e045321..06c3ce5 100644 (file)
@@ -3,7 +3,8 @@
 <!-- Do not edit this file, it will be overwritten on install.
         Copy the file to $HOME/.config/openbox/ instead. -->
 
-<openbox_config xmlns="http://openbox.org/3.4/rc">
+<openbox_config xmlns="http://openbox.org/3.4/rc"
+               xmlns:xi="http://www.w3.org/2001/XInclude">
 
 <resistance>
   <strength>10</strength>
index 86d3e0b..0c37a58 100644 (file)
@@ -11,6 +11,10 @@ AC_DEFUN([OB_DEBUG],
     AC_HELP_STRING([--enable-strict-ansi],[Enable strict ANSI compliance build [[default=no]]]),
     [STRICT=$enableval], [STRICT="no"])
 
+    AC_ARG_ENABLE([super-warnings],
+    AC_HELP_STRING([--enable-super-warnings],[Enable extra compiler warnings [[default=no]]]),
+    [SUPERWARN=$enableval], [SUPERWARN="no"])
+
     AC_ARG_ENABLE([debug],
     AC_HELP_STRING([--enable-debug],[build a debug version [[default=no]]]),
     [DEBUG=$enableval], [DEBUG="no"])
@@ -43,6 +47,9 @@ AC_DEFUN([OB_DEBUG],
     if test "$STRICT" = "yes"; then
        MSG="$MSG with strict ANSI compliance"
     fi
+    if test "$SUPERWARN" = "yes"; then
+       MSG="$MSG with super warnings"
+    fi
     AC_MSG_RESULT([$MSG])
     
     test "$DEBUG" = "yes" && \
@@ -77,11 +84,14 @@ AC_DEFUN([OB_COMPILER_FLAGS],
            FLAGS="$FLAGS -O0 -ggdb -fno-inline -Wwrite-strings"
            FLAGS="$FLAGS -Wall -Wsign-compare -Waggregate-return"
            FLAGS="$FLAGS -Wbad-function-cast -Wpointer-arith"
-           # glib can't handle this flag
-           # -Wcast-qual
+           FLAGS="$FLAGS -Wno-write-strings"
             # for Python.h
            #FLAGS="$FLAGS -Wno-long-long"
        fi
+       if test "$SUPERWARN" = "yes"; then
+           # glib can't handle -Wcast-qual
+           FLAGS="$FLAGS -Wcast-qual -Wextra"
+       fi
        if test "$STRICT" = "yes"; then
            FLAGS="$FLAGS -ansi -pedantic -D_XOPEN_SOURCE"
        fi
index d840d34..f3b1ede 100644 (file)
--- a/m4/x11.m4
+++ b/m4/x11.m4
@@ -11,7 +11,7 @@ AC_DEFUN([X11_DEVEL],
   # Store these
   OLDLIBS=$LIBS
   OLDCPPFLAGS=$CPPFLAGS
-     
+
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
   X_LIBS="$X_PRE_LIBS $X_LIBS -lX11"
   LIBS="$LIBS $X_LIBS"
@@ -38,44 +38,52 @@ AC_DEFUN([X11_EXT_XKB],
 [
   AC_REQUIRE([X11_DEVEL])
 
-  # Store these
-  OLDLIBS=$LIBS
-  OLDCPPFLAGS=$CPPFLAGS
-     
-  CPPFLAGS="$CPPFLAGS $X_CFLAGS"
-  LIBS="$LIBS $X_LIBS"
+  AC_ARG_ENABLE([xkb],
+  AC_HELP_STRING(
+  [--disable-xkb],
+  [build without support for xkb extension [default=enabled]]),
+  [USE=$enableval], [USE="yes"])
 
-  AC_CHECK_LIB([X11], [XkbBell],
-    AC_MSG_CHECKING([for X11/XKBlib.h])
-    AC_TRY_LINK(
-    [
-      #include <X11/Xlib.h>
-      #include <X11/Xutil.h>
-      #include <X11/XKBlib.h>
-    ],
-    [
-      Display *d;
-      Window w;
-      XkbBell(d, w, 0, 0);
-    ],
-    [
-      AC_MSG_RESULT([yes])
-      XKB="yes"
-      AC_DEFINE([XKB], [1], [Found the XKB extension])
+  if test "$USE" = "yes"; then
+    # Store these
+    OLDLIBS=$LIBS
+    OLDCPPFLAGS=$CPPFLAGS
 
-      XKB_CFLAGS=""
-      XKB_LIBS=""
-      AC_SUBST(XKB_CFLAGS)
-      AC_SUBST(XKB_LIBS)
-    ],
-    [ 
-      AC_MSG_RESULT([no])
-      XKB="no"
-    ])
-  )
+    CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+    LIBS="$LIBS $X_LIBS"
 
-  LIBS=$OLDLIBS
-  CPPFLAGS=$OLDCPPFLAGS
+    AC_CHECK_LIB([X11], [XkbBell],
+      AC_MSG_CHECKING([for X11/XKBlib.h])
+      AC_TRY_LINK(
+      [
+        #include <X11/Xlib.h>
+        #include <X11/Xutil.h>
+        #include <X11/XKBlib.h>
+      ],
+      [
+        Display *d;
+        Window w;
+        XkbBell(d, w, 0, 0);
+      ],
+      [
+        AC_MSG_RESULT([yes])
+        XKB="yes"
+        AC_DEFINE([XKB], [1], [Found the XKB extension])
+
+        XKB_CFLAGS=""
+        XKB_LIBS=""
+        AC_SUBST(XKB_CFLAGS)
+        AC_SUBST(XKB_LIBS)
+      ],
+      [
+        AC_MSG_RESULT([no])
+        XKB="no"
+      ])
+    )
+
+    LIBS=$OLDLIBS
+    CPPFLAGS=$OLDCPPFLAGS
+  fi
 
   AC_MSG_CHECKING([for the Xkb extension])
   if test "$XKB" = "yes"; then
@@ -94,45 +102,53 @@ AC_DEFUN([X11_EXT_XRANDR],
 [
   AC_REQUIRE([X11_DEVEL])
 
-  # Store these
-  OLDLIBS=$LIBS
-  OLDCPPFLAGS=$CPPFLAGS
-     
-  CPPFLAGS="$CPPFLAGS $X_CFLAGS"
-  LIBS="$LIBS $X_LIBS -lXext -lXrender -lXrandr"
+  AC_ARG_ENABLE([xrandr],
+  AC_HELP_STRING(
+  [--disable-xrandr],
+  [build without support for xrandr extension [default=enabled]]),
+  [USE=$enableval], [USE="yes"])
 
-  AC_CHECK_LIB([Xrandr], [XRRSelectInput],
-    AC_MSG_CHECKING([for X11/extensions/Xrandr.h])
-    AC_TRY_LINK(
-    [
-      #include <X11/Xlib.h>
-      #include <X11/extensions/Xrandr.h>
-    ],
-    [
-      Display *d;
-      Drawable r;
-      int i;
-      XRRQueryExtension(d, &i, &i);
-      XRRGetScreenInfo(d, r);
-    ],
-    [
-      AC_MSG_RESULT([yes])
-      XRANDR="yes"
-      AC_DEFINE([XRANDR], [1], [Found the XRandR extension])
+  if test "$USE" = "yes"; then
+    # Store these
+    OLDLIBS=$LIBS
+    OLDCPPFLAGS=$CPPFLAGS
 
-      XRANDR_CFLAGS=""
-      XRANDR_LIBS="-lXext -lXrender -lXrandr"
-      AC_SUBST(XRANDR_CFLAGS)
-      AC_SUBST(XRANDR_LIBS)
-    ],
-    [ 
-      AC_MSG_RESULT([no])
-      XRANDR="no"
-    ])
-  )
+    CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+    LIBS="$LIBS $X_LIBS -lXext -lXrender -lXrandr"
+
+    AC_CHECK_LIB([Xrandr], [XRRSelectInput],
+      AC_MSG_CHECKING([for X11/extensions/Xrandr.h])
+      AC_TRY_LINK(
+      [
+        #include <X11/Xlib.h>
+        #include <X11/extensions/Xrandr.h>
+      ],
+      [
+        Display *d;
+        Drawable r;
+        int i;
+        XRRQueryExtension(d, &i, &i);
+        XRRGetScreenInfo(d, r);
+      ],
+      [
+        AC_MSG_RESULT([yes])
+        XRANDR="yes"
+        AC_DEFINE([XRANDR], [1], [Found the XRandR extension])
+
+        XRANDR_CFLAGS=""
+        XRANDR_LIBS="-lXext -lXrender -lXrandr"
+        AC_SUBST(XRANDR_CFLAGS)
+        AC_SUBST(XRANDR_LIBS)
+      ],
+      [
+        AC_MSG_RESULT([no])
+        XRANDR="no"
+      ])
+    )
 
-  LIBS=$OLDLIBS
-  CPPFLAGS=$OLDCPPFLAGS
+    LIBS=$OLDLIBS
+    CPPFLAGS=$OLDCPPFLAGS
+  fi
 
   AC_MSG_CHECKING([for the XRandR extension])
   if test "$XRANDR" = "yes"; then
@@ -151,43 +167,51 @@ AC_DEFUN([X11_EXT_SHAPE],
 [
   AC_REQUIRE([X11_DEVEL])
 
-  # Store these
-  OLDLIBS=$LIBS
-  OLDCPPFLAGS=$CPPFLAGS
-     
-  CPPFLAGS="$CPPFLAGS $X_CFLAGS"
-  LIBS="$LIBS $X_LIBS"
+  AC_ARG_ENABLE([xshape],
+  AC_HELP_STRING(
+  [--disable-xshape],
+  [build without support for xshape extension [default=enabled]]),
+  [USE=$enableval], [USE="yes"])
 
-  AC_CHECK_LIB([Xext], [XShapeCombineShape],
-    AC_MSG_CHECKING([for X11/extensions/shape.h])
-    AC_TRY_LINK(
-    [
-      #include <X11/Xlib.h>
-      #include <X11/Xutil.h>
-      #include <X11/extensions/shape.h>
-    ],
-    [
-      long foo = ShapeSet;
-    ],
-    [
-      AC_MSG_RESULT([yes])
-      SHAPE="yes"
-      AC_DEFINE([SHAPE], [1], [Found the XShape extension])
+  if test "$USE" = "yes"; then
+    # Store these
+    OLDLIBS=$LIBS
+    OLDCPPFLAGS=$CPPFLAGS
 
-      XSHAPE_CFLAGS=""
-      XSHAPE_LIBS="-lXext"
-      AC_SUBST(XSHAPE_CFLAGS)
-      AC_SUBST(XSHAPE_LIBS)
-    ],
-    [ 
-      AC_MSG_RESULT([no])
-      SHAPE="no"
-    ])
-  )
+    CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+    LIBS="$LIBS $X_LIBS"
+
+    AC_CHECK_LIB([Xext], [XShapeCombineShape],
+      AC_MSG_CHECKING([for X11/extensions/shape.h])
+      AC_TRY_LINK(
+      [
+        #include <X11/Xlib.h>
+        #include <X11/Xutil.h>
+        #include <X11/extensions/shape.h>
+      ],
+      [
+        long foo = ShapeSet;
+      ],
+      [
+        AC_MSG_RESULT([yes])
+        SHAPE="yes"
+        AC_DEFINE([SHAPE], [1], [Found the XShape extension])
+
+        XSHAPE_CFLAGS=""
+        XSHAPE_LIBS="-lXext"
+        AC_SUBST(XSHAPE_CFLAGS)
+        AC_SUBST(XSHAPE_LIBS)
+      ],
+      [
+        AC_MSG_RESULT([no])
+        SHAPE="no"
+      ])
+    )
+
+    LIBS=$OLDLIBS
+    CPPFLAGS=$OLDCPPFLAGS
+  fi
 
-  LIBS=$OLDLIBS
-  CPPFLAGS=$OLDCPPFLAGS
   AC_MSG_CHECKING([for the Shape extension])
   if test "$SHAPE" = "yes"; then
     AC_MSG_RESULT([yes])
@@ -206,39 +230,47 @@ AC_DEFUN([X11_EXT_XINERAMA],
 [
   AC_REQUIRE([X11_DEVEL])
 
-  # Store these
-  OLDLIBS=$LIBS
-  OLDCPPFLAGS=$CPPFLAGS
-     
-  CPPFLAGS="$CPPFLAGS $X_CFLAGS"
-  LIBS="$LIBS $X_LIBS -lXext"
+  AC_ARG_ENABLE([xinerama],
+  AC_HELP_STRING(
+  [--disable-xinerama],
+  [build without support for xinerama [default=enabled]]),
+  [USE=$enableval], [USE="yes"])
 
-  AC_CHECK_LIB([Xinerama], [XineramaQueryExtension],
-  [
-    AC_MSG_CHECKING([for X11/extensions/Xinerama.h])
-    AC_TRY_LINK(
-    [
-      #include <X11/Xlib.h>
-      #include <X11/extensions/Xinerama.h>
-    ],
-    [
-      XineramaScreenInfo foo;
-    ],
-    [
-      AC_MSG_RESULT([yes])
-      XINERAMA="yes"
-      AC_DEFINE([XINERAMA], [1], [Enable support of the Xinerama extension])
-      XINERAMA_LIBS="-lXext -lXinerama"
-      AC_SUBST(XINERAMA_LIBS)
-    ],
+  if test "$USE" = "yes"; then
+    # Store these
+    OLDLIBS=$LIBS
+    OLDCPPFLAGS=$CPPFLAGS
+
+    CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+    LIBS="$LIBS $X_LIBS -lXext"
+
+    AC_CHECK_LIB([Xinerama], [XineramaQueryExtension],
     [
-      AC_MSG_RESULT([no])
-      XINERAMA="no"
+      AC_MSG_CHECKING([for X11/extensions/Xinerama.h])
+      AC_TRY_LINK(
+      [
+        #include <X11/Xlib.h>
+        #include <X11/extensions/Xinerama.h>
+      ],
+      [
+        XineramaScreenInfo foo;
+      ],
+      [
+        AC_MSG_RESULT([yes])
+        XINERAMA="yes"
+        AC_DEFINE([XINERAMA], [1], [Enable support of the Xinerama extension])
+        XINERAMA_LIBS="-lXext -lXinerama"
+        AC_SUBST(XINERAMA_LIBS)
+      ],
+      [
+        AC_MSG_RESULT([no])
+        XINERAMA="no"
+      ])
     ])
-  ])
 
-  LIBS=$OLDLIBS
-  CPPFLAGS=$OLDCPPFLAGS
+    LIBS=$OLDLIBS
+    CPPFLAGS=$OLDCPPFLAGS
+  fi
 
   AC_MSG_CHECKING([for the Xinerama extension])
   if test "$XINERAMA" = "yes"; then
@@ -257,45 +289,106 @@ AC_DEFUN([X11_EXT_SYNC],
 [
   AC_REQUIRE([X11_DEVEL])
 
+  AC_ARG_ENABLE([xsync],
+  AC_HELP_STRING(
+  [--disable-xsync],
+  [build without support for xsync extension [default=enabled]]),
+  [USE=$enableval], [USE="yes"])
+
+  if test "$USE" = "yes"; then
+    # Store these
+    OLDLIBS=$LIBS
+    OLDCPPFLAGS=$CPPFLAGS
+
+    CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+    LIBS="$LIBS $X_LIBS"
+
+    AC_CHECK_LIB([Xext], [XSyncInitialize],
+      AC_MSG_CHECKING([for X11/extensions/sync.h])
+      AC_TRY_LINK(
+      [
+        #include <X11/Xlib.h>
+        #include <X11/Xutil.h>
+        #include <X11/extensions/sync.h>
+      ],
+      [
+        XSyncValueType foo;
+      ],
+      [
+        AC_MSG_RESULT([yes])
+        SYNC="yes"
+        AC_DEFINE([SYNC], [1], [Found the XSync extension])
+
+        XSYNC_CFLAGS=""
+        XSYNC_LIBS="-lXext"
+        AC_SUBST(XSYNC_CFLAGS)
+        AC_SUBST(XSYNC_LIBS)
+      ],
+      [
+        AC_MSG_RESULT([no])
+        SYNC="no"
+      ])
+    )
+
+    LIBS=$OLDLIBS
+    CPPFLAGS=$OLDCPPFLAGS
+  fi
+
+  AC_MSG_CHECKING([for the Sync extension])
+  if test "$SYNC" = "yes"; then
+    AC_MSG_RESULT([yes])
+  else
+    AC_MSG_RESULT([no])
+  fi
+])
+
+# X11_EXT_AUTH()
+#
+# Check for the presence of the "Xau" X Window System extension.
+# Defines "AUTH, sets the $(AUTH) variable to "yes", and sets the $(LIBS)
+# appropriately if the extension is present.
+AC_DEFUN([X11_EXT_AUTH],
+[
+  AC_REQUIRE([X11_DEVEL])
+
   # Store these
   OLDLIBS=$LIBS
   OLDCPPFLAGS=$CPPFLAGS
-     
+
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
   LIBS="$LIBS $X_LIBS"
 
-  AC_CHECK_LIB([Xext], [XSyncInitialize],
-    AC_MSG_CHECKING([for X11/extensions/sync.h])
+  AC_CHECK_LIB([Xau], [XauReadAuth],
+    AC_MSG_CHECKING([for X11/Xauth.h])
     AC_TRY_LINK(
     [
       #include <X11/Xlib.h>
       #include <X11/Xutil.h>
-      #include <X11/extensions/sync.h>
+      #include <X11/Xauth.h>
     ],
     [
-      XSyncValueType foo;
     ],
     [
       AC_MSG_RESULT([yes])
-      SYNC="yes"
-      AC_DEFINE([SYNC], [1], [Found the XSync extension])
+      AUTH="yes"
+      AC_DEFINE([AUTH], [1], [Found the Xauth extension])
 
-      XSYNC_CFLAGS=""
-      XSYNC_LIBS="-lXext"
-      AC_SUBST(XSYNC_CFLAGS)
-      AC_SUBST(XSYNC_LIBS)
+      XAUTH_CFLAGS=""
+      XAUTH_LIBS="-lXau"
+      AC_SUBST(XAUTH_CFLAGS)
+      AC_SUBST(XAUTH_LIBS)
     ],
-    [ 
+    [
       AC_MSG_RESULT([no])
-      SYNC="no"
+      AUTH="no"
     ])
   )
 
   LIBS=$OLDLIBS
   CPPFLAGS=$OLDCPPFLAGS
-  AC_MSG_CHECKING([for the Sync extension])
-  if test "$SYNC" = "yes"; then
+
+  AC_MSG_CHECKING([for the Xauth extension])
+  if test "$AUTH" = "yes"; then
     AC_MSG_RESULT([yes])
   else
     AC_MSG_RESULT([no])
@@ -312,14 +405,15 @@ AC_DEFUN([X11_SM],
 
   AC_ARG_ENABLE([session-management],
   AC_HELP_STRING(
-  [--disable-session-management], [build without support for session managers [[default=enabled]]]),
+  [--disable-session-management],
+  [build without support for session managers [default=enabled]]),
   [SM=$enableval], [SM="yes"])
-  
+
   if test "$SM" = "yes"; then
     # Store these
     OLDLIBS=$LIBS
     OLDCPPFLAGS=$CPPFLAGS
-     
+
     CPPFLAGS="$CPPFLAGS $X_CFLAGS"
     LIBS="$LIBS $X_LIBS"
 
@@ -335,10 +429,10 @@ AC_DEFUN([X11_SM],
         SM="yes"
       ])
     ])
-  fi
 
-  LIBS=$OLDLIBS
-  CPPFLAGS=$OLDCPPFLAGS
+    LIBS=$OLDLIBS
+    CPPFLAGS=$OLDCPPFLAGS
+  fi
 
   AC_MSG_CHECKING([for session management support])
   if test "$SM" = "yes"; then
index bb65e3c..196d9d1 100644 (file)
 #define FLOOR(i)        ((i) & (~0UL << FRACTION))
 #define AVERAGE(a, b)   (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
 
-void RrImagePicInit(RrImagePic *pic, const gchar *name,
-                    gint w, gint h, RrPixel32 *data)
+/************************************************************************
+ RrImagePic functions.
+
+ RrImagePics are pictures that are grouped together into RrImageSets.  Each
+ RrImagePic in the set has the same logical image inside it, but they are
+ of different sizes.  An RrImagePic can be an original (which comes from some
+ outside source, such as an image file), or resized from some other RrImagePic
+ to meet the needs of the user.
+**************************************************************************/
+
+
+/*! Set up an RrImagePic.
+  This does _not_ make a copy of the data. So the value of data must be
+  owned by the caller of this function, and not freed afterward.
+  This function does not allocate an RrImagePic, and can be used for setting
+  up a temporary RrImagePic on the stack.  Such an object would then also
+  not be freed with RrImagePicFree.
+*/
+static void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data)
 {
     gint i;
 
@@ -42,49 +59,168 @@ void RrImagePicInit(RrImagePic *pic, const gchar *name,
     pic->sum = 0;
     for (i = w*h; i > 0; --i)
         pic->sum += *(data++);
-    pic->name = g_strdup(name);
 }
 
+/*! Create a new RrImagePic from some picture data.
+  This makes a duplicate of the data.
+*/
+static RrImagePic* RrImagePicNew(gint w, gint h, RrPixel32 *data)
+{
+    RrImagePic *pic;
+
+    pic = g_slice_new(RrImagePic);
+    RrImagePicInit(pic, w, h, g_memdup(data, w*h*sizeof(RrPixel32)));
+    return pic;
+}
+
+
+/*! Destroy an RrImagePic.
+  This frees the RrImagePic object and everything inside it.
+*/
 static void RrImagePicFree(RrImagePic *pic)
 {
     if (pic) {
         g_free(pic->data);
-        g_free(pic->name);
         g_slice_free(RrImagePic, pic);
     }
 }
 
-/*! Add a picture to an Image, that is, add another copy of the image at
-  another size.  This may add it to the "originals" list or to the
-  "resized" list. */
-static void AddPicture(RrImage *self, RrImagePic ***list, gint *len,
-                       RrImagePic *pic)
+/************************************************************************
+ RrImageSet functions.
+
+ RrImageSets hold a group of pictures, each of which represent the same logical
+ image, but are physically different sizes.
+ At any time, it may be discovered that two different RrImageSets are actually
+ holding the same logical image.  At that time, they would be merged.
+ An RrImageSet holds both original images which come from an outside source,
+ and resized images, which are generated when requests for a specific size are
+ made, and kept around in case they are request again.  There is a maximum
+ number of resized images that an RrImageSet will keep around, however.
+
+ Each RrImage points to a single RrImageSet, which keeps track of which
+ RrImages point to it.  If two RrImageSets are merged, then the RrImages which
+ pointed to the two RrImageSets will all point at the resulting merged set.
+**************************************************************************/
+
+
+/*! Free an RrImageSet and the stuff inside it.
+  This should only occur when there are no more RrImages pointing to the set.
+*/
+static void RrImageSetFree(RrImageSet *self)
 {
+    GSList *it;
     gint i;
 
+    if (self) {
+        g_assert(self->images == NULL);
+
+        /* remove all names associated with this RrImageSet */
+        for (it = self->names; it; it = g_slist_next(it)) {
+            g_hash_table_remove(self->cache->name_table, it->data);
+            g_free(it->data);
+        }
+        g_slist_free(self->names);
+
+        /* destroy the RrImagePic objects stored in the RrImageSet.  they will
+           be keys in the cache to RrImageSet objects, so remove them from
+           the cache's pic_table as well. */
+        for (i = 0; i < self->n_original; ++i) {
+            g_hash_table_remove(self->cache->pic_table, self->original[i]);
+            RrImagePicFree(self->original[i]);
+        }
+        g_free(self->original);
+        for (i = 0; i < self->n_resized; ++i) {
+            g_hash_table_remove(self->cache->pic_table, self->resized[i]);
+            RrImagePicFree(self->resized[i]);
+        }
+        g_free(self->resized);
+
+        g_slice_free(RrImageSet, self);
+    }
+}
+
+/*! Remove a picture from an RrImageSet as a given position.
+   @param set The RrImageSet to remove the picture from.
+   @param i The index of the picture in the RrImageSet in the list of
+     originals (if @original is TRUE), or in the list of resized pictures (if
+     @original is FALSE).
+   @param original TRUE if the picture is an original, FALSE if it is a resized
+     version of another picture in the RrImageSet.
+ */
+static void RrImageSetRemovePictureAt(RrImageSet *self, gint i,
+                                      gboolean original)
+{
+    RrImagePic ***list;
+    gint *len;
+
+    if (original) {
+        list = &self->original;
+        len = &self->n_original;
+    }
+    else {
+        list = &self->resized;
+        len = &self->n_resized;
+    }
+
+    g_assert(i >= 0 && i < *len);
+
+    /* remove the picture data as a key in the cache */
+    g_hash_table_remove(self->cache->pic_table, (*list)[i]);
+
+    /* free the picture being removed */
+    RrImagePicFree((*list)[i]);
+
+    /* copy the elements after the removed one in the array forward one space
+       and shrink the array down one size */
+    for (i = i+1; i < *len; ++i)
+        (*list)[i-1] = (*list)[i];
+    --(*len);
+    *list = g_renew(RrImagePic*, *list, *len);
+}
+
+/*! Add an RrImagePic to an RrImageSet.
+  The RrImagePic should _not_ exist in the image cache already.
+  Pictures are added to the front of the list, to maintain the ordering of
+  newest to oldest.
+*/
+static void RrImageSetAddPicture(RrImageSet *self, RrImagePic *pic,
+                                 gboolean original)
+{
+    gint i;
+    RrImagePic ***list;
+    gint *len;
+
     g_assert(pic->width > 0 && pic->height > 0);
+    g_assert(g_hash_table_lookup(self->cache->pic_table, pic) == NULL);
+
+    /* choose which list in the RrImageSet to add the new picture to. */
+    if (original) {
+        /* remove the resized picture of the same size if one exists */
+        for (i = 0; i < self->n_resized; ++i)
+            if (self->resized[i]->width == pic->width ||
+                self->resized[i]->height == pic->height)
+            {
+                RrImageSetRemovePictureAt(self, i, FALSE);
+                break;
+            }
 
-    if (pic->name)
-        g_assert(!g_hash_table_lookup(self->cache->name_table, pic->name));
-    else
-        g_assert(!g_hash_table_lookup(self->cache->pic_table, pic));
+        list = &self->original;
+        len = &self->n_original;
+    }
+    else {
+        list = &self->resized;
+        len = &self->n_resized;
+    }
 
-    /* grow the list */
+    /* grow the list by one spot, shift everything down one, and insert the new
+       picture at the front of the list */
     *list = g_renew(RrImagePic*, *list, ++*len);
-
-    /* move everything else down one */
     for (i = *len-1; i > 0; --i)
         (*list)[i] = (*list)[i-1];
-
-    /* set the new picture up at the front of the list */
     (*list)[0] = pic;
 
-    /* add the name or the picture as a key to point to this image in the
-       cache */
-    if (pic->name)
-        g_hash_table_insert(self->cache->name_table, pic->name, self);
-    else
-        g_hash_table_insert(self->cache->pic_table, (*list)[0], self);
+    /* add the picture as a key to point to this image in the cache */
+    g_hash_table_insert(self->cache->pic_table, (*list)[0], self);
 
 /*
 #ifdef DEBUG
@@ -96,42 +232,306 @@ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len,
 */
 }
 
-/*! Remove a picture from an Image.  This may remove it from the "originals"
-  list or the "resized" list. */
-static void RemovePicture(RrImage *self, RrImagePic ***list,
-                          gint i, gint *len)
+/*! Merges two image sets, destroying one, and returning the other. */
+RrImageSet* RrImageSetMergeSets(RrImageSet *b, RrImageSet *a)
+{
+    gint a_i, b_i, merged_i;
+    RrImagePic **original, **resized;
+    gint n_original, n_resized, tmp;
+    GSList *it;
+
+    const gint max_resized = a->cache->max_resized_saved;
+
+    if (!a)
+        return b;
+    if (!b)
+        return a;
+    if (a == b)
+        return b;
+
+    /* the original and resized picture lists in an RrImageSet are kept ordered
+       as newest to oldest.  we don't have timestamps for them, so we cannot
+       preserve this in the merged RrImageSet exactly.  a decent approximation,
+       i think, is to add them in alternating order (one from a, one from b,
+       repeat).  this way, the newest from each will be near the front at
+       least, and in the resized list, when we drop an old picture, we will
+       not always only drop from a or b only, but from each of them equally (or
+       from whichever has more resized pictures.
+    */
+
+    g_assert(b->cache == a->cache);
+
+    a_i = b_i = merged_i = 0;
+    n_original = a->n_original + b->n_original;
+    original = g_new(RrImagePic*, n_original);
+    while (merged_i < n_original) {
+        if (a_i < a->n_original)
+            original[merged_i++] = a->original[a_i++];
+        if (b_i < b->n_original)
+            original[merged_i++] = b->original[b_i++];
+    }
+
+    a_i = b_i = merged_i = 0;
+    n_resized = MIN(max_resized, a->n_resized + b->n_resized);
+    resized = g_new(RrImagePic*, n_resized);
+    while (merged_i < n_resized) {
+        if (a_i < a->n_resized)
+            resized[merged_i++] = a->resized[a_i++];
+        if (b_i < b->n_resized && merged_i < n_resized)
+            resized[merged_i++] = b->resized[b_i++];
+    }
+
+    /* if there are any RrImagePic objects left over in a->resized or
+       b->resized, they need to be disposed of, and removed from the cache.
+
+       updates the size of the list, as we want to remember which pointers
+       were merged from which list (and don't want to remember the ones we
+       did not merge and have freed).
+    */
+    tmp = a_i;
+    for (; a_i < a->n_resized; ++a_i) {
+        g_hash_table_remove(a->cache->pic_table, a->resized[a_i]);
+        RrImagePicFree(a->resized[a_i]);
+    }
+    a->n_resized = tmp;
+
+    tmp = b_i;
+    for (; b_i < b->n_resized; ++b_i) {
+        g_hash_table_remove(a->cache->pic_table, b->resized[b_i]);
+        RrImagePicFree(b->resized[b_i]);
+    }
+    b->n_resized = tmp;
+
+    /* we will use the a object as the merge destination, so things in b will
+       be moving.
+
+       the cache's name_table will point to b for all the names in b->names,
+       so these need to be updated to point at a instead.
+       also, the cache's pic_table will point to b for all the pictures in b,
+       so these need to be updated to point at a as well.
+
+       any RrImage objects that were using b should now use a instead.
+
+       the names and images will be all moved into a, and the merged picture
+       lists will be placed in a.  the pictures in a and b are moved to new
+       arrays, so the arrays in a and b need to be freed explicitly (the
+       RrImageSetFree function would free the picture data too which we do not
+       want here). then b can be freed.
+    */
+
+    for (it = b->names; it; it = g_slist_next(it))
+        g_hash_table_insert(a->cache->name_table, it->data, a);
+    for (b_i = 0; b_i < b->n_original; ++b_i)
+        g_hash_table_insert(a->cache->pic_table, b->original[b_i], a);
+    for (b_i = 0; b_i < b->n_resized; ++b_i)
+        g_hash_table_insert(a->cache->pic_table, b->resized[b_i], a);
+
+    for (it = b->images; it; it = g_slist_next(it))
+        ((RrImage*)it->data)->set = a;
+
+    a->images = g_slist_concat(a->images, b->images);
+    b->images = NULL;
+    a->names = g_slist_concat(a->names, b->names);
+    b->names = NULL;
+
+    a->n_original = a->n_resized = 0;
+    g_free(a->original);
+    g_free(a->resized);
+    a->original = a->resized = NULL;
+    b->n_original = b->n_resized = 0;
+    g_free(b->original);
+    g_free(b->resized);
+    b->original = b->resized = NULL;
+
+    a->n_original = n_original;
+    a->original = original;
+    a->n_resized = n_resized;
+    a->resized = resized;
+
+    RrImageSetFree(b);
+
+    return a;
+}
+
+static void RrImageSetAddName(RrImageSet *set, const gchar *name)
+{
+    gchar *n;
+
+    n = g_strdup(name);
+    set->names = g_slist_prepend(set->names, n);
+
+    /* add the new name to the hash table */
+    g_assert(g_hash_table_lookup(set->cache->name_table, n) == NULL);
+    g_hash_table_insert(set->cache->name_table, n, set);
+}
+
+
+/************************************************************************
+ RrImage functions.
+**************************************************************************/
+
+
+void RrImageRef(RrImage *self)
 {
-    gint j;
+    ++self->ref;
+}
 
+void RrImageUnref(RrImage *self)
+{
+    if (self && --self->ref == 0) {
+        RrImageSet *set;
 /*
 #ifdef DEBUG
-    g_debug("Removing %s picture from the cache:\n    "
-            "Image 0x%lx, w %d h %d Hash %u",
-            (*list == self->original ? "ORIGINAL" : "RESIZED"),
-            (gulong)self, (*list)[i]->width, (*list)[i]->height,
-            RrImagePicHash((*list)[i]));
+        g_debug("Refcount to 0, removing ALL pictures from the cache:\n    "
+                "Image 0x%lx", (gulong)self);
 #endif
 */
+        if (self->destroy_func)
+            self->destroy_func(self, self->destroy_data);
 
-    /* remove the name or picture as a key in the cache */
-    if ((*list)[i]->name)
-        g_hash_table_remove(self->cache->name_table, (*list)[i]->name);
-    else
-        g_hash_table_remove(self->cache->pic_table, (*list)[i]);
+        set = self->set;
+        set->images = g_slist_remove(set->images, self);
 
-    /* free the picture */
-    RrImagePicFree((*list)[i]);
-    /* shift everything down one */
-    for (j = i; j < *len-1; ++j)
-        (*list)[j] = (*list)[j+1];
-    /* shrink the list */
-    *list = g_renew(RrImagePic*, *list, --*len);
+        /* free the set as well if there are no images pointing to it */
+        if (!set->images)
+            RrImageSetFree(set);
+        g_slice_free(RrImage, self);
+    }
+}
+
+/*! Set function that will be called just before RrImage is destroyed. */
+void RrImageSetDestroyFunc(RrImage *self, RrImageDestroyFunc func,
+                           gpointer data)
+{
+    self->destroy_func = func;
+    self->destroy_data = data;
+}
+
+void RrImageAddFromData(RrImage *self, RrPixel32 *data, gint w, gint h)
+{
+    RrImagePic pic, *ppic;
+    RrImageSet *set;
+
+    g_return_if_fail(self != NULL);
+    g_return_if_fail(data != NULL);
+    g_return_if_fail(w > 0 && h > 0);
+
+    RrImagePicInit(&pic, w, h, data);
+    set = g_hash_table_lookup(self->set->cache->pic_table, &pic);
+    if (set)
+        self->set = RrImageSetMergeSets(self->set, set);
+    else {
+        ppic = RrImagePicNew(w, h, data);
+        RrImageSetAddPicture(self->set, ppic, TRUE);
+    }
+}
+
+RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data,
+                            gint w, gint h)
+{
+    RrImagePic pic, *ppic;
+    RrImage *self;
+    RrImageSet *set;
+
+    g_return_val_if_fail(cache != NULL, NULL);
+    g_return_val_if_fail(data != NULL, NULL);
+    g_return_val_if_fail(w > 0 && h > 0, NULL);
+
+    /* finds a picture in the cache, if it is already in there, and use the
+       RrImageSet the picture lives in. */
+    RrImagePicInit(&pic, w, h, data);
+    set = g_hash_table_lookup(cache->pic_table, &pic);
+    if (set) {
+        self = set->images->data; /* just grab any RrImage from the list */
+        RrImageRef(self);
+        return self;
+    }
+
+    /* the image does not exist in any RrImageSet in the cache, so make
+       a new RrImageSet, and a new RrImage that points to it, and place the
+       new image inside the new RrImageSet */
+
+    self = g_slice_new0(RrImage);
+    self->ref = 1;
+    self->set = g_slice_new0(RrImageSet);
+    self->set->cache = cache;
+    self->set->images = g_slist_append(self->set->images, self);
+
+    ppic = RrImagePicNew(w, h, data);
+    RrImageSetAddPicture(self->set, ppic, TRUE);
+
+    return self;
+}
+
+RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
+{
+#ifndef USE_IMLIB2
+    return NULL;
+#else
+    RrImage *self;
+    RrImageSet *set;
+    Imlib_Image img;
+    gint w, h;
+    RrPixel32 *data;
+    gchar *path;
+
+    g_return_val_if_fail(cache != NULL, NULL);
+    g_return_val_if_fail(name != NULL, NULL);
+
+    set = g_hash_table_lookup(cache->name_table, name);
+    if (set) {
+        self = set->images->data;
+        RrImageRef(self);
+        return self;
+    }
+
+    /* XXX find the path via freedesktop icon spec (use obt) ! */
+    path = g_strdup(name);
+
+    if (!(img = imlib_load_image(path)))
+        g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
+    g_free(path);
+
+    if (!img)
+        return NULL;
+
+    /* Get data and dimensions of the image.
+
+       WARNING: This stuff is NOT threadsafe !!
+    */
+    imlib_context_set_image(img);
+    data = imlib_image_get_data_for_reading_only();
+    w = imlib_image_get_width();
+    h = imlib_image_get_height();
+
+    /* get an RrImage that contains an RrImageSet with this picture in it.
+       the RrImage might be new, or reused if the picture was already in the
+       cache.
+
+       either way, we get back an RrImageSet (via the RrImage), and we must add
+       the name to that RrImageSet.  because of the check above, we know that
+       there is no RrImageSet in the cache which already has the given name
+       asosciated with it.
+    */
+
+    self = RrImageNewFromData(cache, data, w, h);
+    RrImageSetAddName(self->set, name);
+
+    imlib_free_image();
+    return self;
+#endif
 }
 
+/************************************************************************
+ Image drawing and resizing operations.
+**************************************************************************/
+
 /*! Given a picture in RGBA format, of a specified size, resize it to the new
   requested size (but keep its aspect ratio).  If the image does not need to
   be resized (it is already the right size) then this returns NULL.  Otherwise
   it returns a newly allocated RrImagePic with the resized picture inside it
+  @return Returns a newly allocated RrImagePic object with a new version of the
+    image in the requested size (keeping aspect ratio).
 */
 static RrImagePic* ResizeImage(RrPixel32 *src,
                                gulong srcW, gulong srcH,
@@ -144,11 +544,10 @@ static RrImagePic* ResizeImage(RrPixel32 *src,
     gulong ratioX, ratioY;
     gulong aspectW, aspectH;
 
-    /* XXX should these variables be ensured to not be zero in the callers? */
-    srcW = srcW ? srcW : 1;
-    srcH = srcH ? srcH : 1;
-    dstW = dstW ? dstW : 1;
-    dstH = dstH ? dstH : 1;
+    g_assert(srcW > 0);
+    g_assert(srcH > 0);
+    g_assert(dstW > 0);
+    g_assert(dstH > 0);
 
     /* keep the aspect ratio */
     aspectW = dstW;
@@ -236,7 +635,7 @@ static RrImagePic* ResizeImage(RrPixel32 *src,
     }
 
     pic = g_slice_new(RrImagePic);
-    RrImagePicInit(pic, NULL, dstW, dstH, dststart);
+    RrImagePicInit(pic, dstW, dstH, dststart);
 
     return pic;
 }
@@ -328,142 +727,6 @@ void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
                  rgba->alpha, area);
 }
 
-/*! Create a new RrImage, which is linked to an image cache */
-RrImage* RrImageNew(RrImageCache *cache)
-{
-    RrImage *self;
-
-    g_assert(cache != NULL);
-
-    self = g_slice_new0(RrImage);
-    self->ref = 1;
-    self->cache = cache;
-    return self;
-}
-
-/*! Set function that will be called just before RrImage is destroyed. */
-void RrImageSetDestroyFunc(RrImage *image, RrImageDestroyFunc func,
-                           gpointer data)
-{
-    image->destroy_func = func;
-    image->destroy_data = data;
-}
-
-void RrImageRef(RrImage *self)
-{
-    ++self->ref;
-}
-
-void RrImageUnref(RrImage *self)
-{
-    if (self && --self->ref == 0) {
-/*
-#ifdef DEBUG
-        g_debug("Refcount to 0, removing ALL pictures from the cache:\n    "
-                "Image 0x%lx", (gulong)self);
-#endif
-*/
-        if (self->destroy_func)
-            self->destroy_func(self, self->destroy_data);
-        while (self->n_original > 0)
-            RemovePicture(self, &self->original, 0, &self->n_original);
-        while (self->n_resized > 0)
-            RemovePicture(self, &self->resized, 0, &self->n_resized);
-        g_slice_free(RrImage, self);
-    }
-}
-
-static void AddPictureFromData(RrImage *self, const char *name,
-                               const RrPixel32 *data, gint w, gint h)
-{
-    gint i;
-    RrImagePic *pic;
-
-    /* make sure we don't already have this size.. */
-    for (i = 0; i < self->n_original; ++i)
-        if (self->original[i]->width == w && self->original[i]->height == h) {
-/*
-#ifdef DEBUG
-            g_debug("Found duplicate ORIGINAL image:\n    "
-                    "Image 0x%lx, w %d h %d", (gulong)self, w, h);
-#endif
-*/
-            return;
-        }
-
-    /* remove any resized pictures of this same size */
-    for (i = 0; i < self->n_resized; ++i)
-        if (self->resized[i]->width == w || self->resized[i]->height == h) {
-            RemovePicture(self, &self->resized, i, &self->n_resized);
-            break;
-        }
-
-    /* add the new picture */
-    pic = g_slice_new(RrImagePic);
-    RrImagePicInit(pic, name, w, h, g_memdup(data, w*h*sizeof(RrPixel32)));
-    AddPicture(self, &self->original, &self->n_original, pic);
-}
-
-gboolean RrImageAddPictureName(RrImage *self, const gchar *name)
-{
-#ifdef USE_IMLIB2
-    Imlib_Image img;
-    gint w, h;
-    RrPixel32 *data;
-    gchar *path;
-
-    /* XXX find the path via freedesktop icon spec (use obt) ! */
-    path = g_strdup(name);
-
-    if (!(img = imlib_load_image(path)))
-        g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
-    g_free(path);
-
-    if (!img)
-        return FALSE; /* failed to load it */
-
-    /* Get data and dimensions of the image.
-
-       WARNING: This stuff is NOT threadsafe !!
-    */
-    imlib_context_set_image(img);
-    data = imlib_image_get_data_for_reading_only();
-    w = imlib_image_get_width();
-    h = imlib_image_get_height();
-
-    /* add it to the RrImage, and set its name */
-    AddPictureFromData(self, name, data, w, h);
-
-    imlib_free_image();
-    return TRUE;
-#else
-    return FALSE;
-#endif
-}
-
-/*! Add a new picture with the given RGBA pixel data and dimensions into the
-  RrImage.  This adds an "original" picture to the image.
-*/
-void RrImageAddPicture(RrImage *self, const RrPixel32 *data, gint w, gint h)
-{
-    AddPictureFromData(self, NULL, data, w, h);    
-}
-
-/*! Remove the picture from the RrImage which has the given dimensions. This
- removes an "original" picture from the image.
-*/
-void RrImageRemovePicture(RrImage *self, gint w, gint h)
-{
-    gint i;
-
-    /* remove any resized pictures of this same size */
-    for (i = 0; i < self->n_original; ++i)
-        if (self->original[i]->width == w && self->original[i]->height == h) {
-            RemovePicture(self, &self->original, i, &self->n_original);
-            break;
-        }
-}
-
 /*! Draw an RrImage texture into a target pixel buffer.  If the RrImage does
   not contain a picture of the appropriate size, then one of its "original"
   pictures will be resized and used (and stored in the RrImage as a "resized"
@@ -475,65 +738,68 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
 {
     gint i, min_diff, min_i, min_aspect_diff, min_aspect_i;
     RrImage *self;
+    RrImageSet *set;
     RrImagePic *pic;
     gboolean free_pic;
 
     self = img->image;
+    set = self->set;
     pic = NULL;
     free_pic = FALSE;
 
     /* is there an original of this size? (only the larger of
        w or h has to be right cuz we maintain aspect ratios) */
-    for (i = 0; i < self->n_original; ++i)
-        if ((self->original[i]->width >= self->original[i]->height &&
-             self->original[i]->width == area->width) ||
-            (self->original[i]->width <= self->original[i]->height &&
-             self->original[i]->height == area->height))
+    for (i = 0; i < set->n_original; ++i)
+        if ((set->original[i]->width >= set->original[i]->height &&
+             set->original[i]->width == area->width) ||
+            (set->original[i]->width <= set->original[i]->height &&
+             set->original[i]->height == area->height))
         {
-            pic = self->original[i];
+            pic = set->original[i];
             break;
         }
 
     /* is there a resize of this size? */
-    for (i = 0; i < self->n_resized; ++i)
-        if ((self->resized[i]->width >= self->resized[i]->height &&
-             self->resized[i]->width == area->width) ||
-            (self->resized[i]->width <= self->resized[i]->height &&
-             self->resized[i]->height == area->height))
+    for (i = 0; i < set->n_resized; ++i)
+        if ((set->resized[i]->width >= set->resized[i]->height &&
+             set->resized[i]->width == area->width) ||
+            (set->resized[i]->width <= set->resized[i]->height &&
+             set->resized[i]->height == area->height))
         {
             gint j;
             RrImagePic *saved;
 
             /* save the selected one */
-            saved = self->resized[i];
+            saved = set->resized[i];
 
             /* shift all the others down */
             for (j = i; j > 0; --j)
-                self->resized[j] = self->resized[j-1];
+                set->resized[j] = set->resized[j-1];
 
             /* and move the selected one to the top of the list */
-            self->resized[0] = saved;
+            set->resized[0] = saved;
 
-            pic = self->resized[0];
+            pic = set->resized[0];
             break;
         }
 
     if (!pic) {
         gdouble aspect;
+        RrImageSet *cache_set;
 
         /* find an original with a close size */
         min_diff = min_aspect_diff = -1;
         min_i = min_aspect_i = 0;
         aspect = ((gdouble)area->width) / area->height;
-        for (i = 0; i < self->n_original; ++i) {
+        for (i = 0; i < set->n_original; ++i) {
             gint diff;
             gint wdiff, hdiff;
             gdouble myasp;
 
             /* our size difference metric.. */
-            wdiff = self->original[i]->width - area->width;
+            wdiff = set->original[i]->width - area->width;
             if (wdiff < 0) wdiff *= 2; /* prefer scaling down than up */
-            hdiff = self->original[i]->height - area->height;
+            hdiff = set->original[i]->height - area->height;
             if (hdiff < 0) hdiff *= 2; /* prefer scaling down than up */
             diff = (wdiff * wdiff) + (hdiff * hdiff);
 
@@ -544,8 +810,8 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
             }
             /* and also find the smallest difference with the same aspect
                ratio (and prefer this one) */
-            myasp = ((gdouble)self->original[i]->width) /
-                self->original[i]->height;
+            myasp = ((gdouble)set->original[i]->width) /
+                set->original[i]->height;
             if (ABS(aspect - myasp) < 0.0000001 &&
                 (min_aspect_diff < 0 || diff < min_aspect_diff))
             {
@@ -559,24 +825,39 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
             min_i = min_aspect_i;
 
         /* resize the original to the given area */
-        pic = ResizeImage(self->original[min_i]->data,
-                          self->original[min_i]->width,
-                          self->original[min_i]->height,
+        pic = ResizeImage(set->original[min_i]->data,
+                          set->original[min_i]->width,
+                          set->original[min_i]->height,
                           area->width, area->height);
 
-        /* add the resized image to the image, as the first in the resized
-           list */
-        if (self->n_resized >= self->cache->max_resized_saved)
-            /* remove the last one (last used one) */
-            RemovePicture(self, &self->resized, self->n_resized - 1,
-                          &self->n_resized);
-        if (self->cache->max_resized_saved)
-            /* add it to the top of the resized list */
-            AddPicture(self, &self->resized, &self->n_resized, pic);
-        else
-            free_pic = TRUE; /* don't leak mem! */
+        /* is it already in the cache ? */
+        cache_set = g_hash_table_lookup(set->cache->pic_table, pic);
+        if (cache_set) {
+            /* merge this set with the one found in the cache - they are
+               apparently the same image !  then next time we won't have to do
+               this resizing, we will use the cache_set's pic instead. */
+            set = RrImageSetMergeSets(set, cache_set);
+            free_pic = TRUE;
+        }
+        else {
+            /* add the resized image to the image, as the first in the resized
+               list */
+            while (set->n_resized >= set->cache->max_resized_saved)
+                /* remove the last one (last used one) to make space for
+                 adding our resized picture */
+                RrImageSetRemovePictureAt(set, set->n_resized-1, FALSE);
+            if (set->cache->max_resized_saved)
+                /* add it to the resized list */
+                RrImageSetAddPicture(set, pic, FALSE);
+            else
+                free_pic = TRUE; /* don't leak mem! */
+        }
     }
 
+    /* The RrImageSet may have changed if we merged it with another, so the
+       RrImage object needs to be updated to use the new merged RrImageSet. */
+    self->set = set;
+
     g_assert(pic != NULL);
 
     DrawRGBA(target, target_w, target_h,
index 6e4a50e..28f29c2 100644 (file)
 #include "render.h"
 #include "geom.h"
 
-/*! Initialize an RrImagePicture to the specified dimensions and pixel data */
-void RrImagePicInit(RrImagePic *pic, const gchar *path,
-                    gint w, gint h, RrPixel32 *data);
-
 void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
                       gint target_w, gint target_h,
                       RrRect *area);
index ad1183e..909d874 100644 (file)
@@ -58,21 +58,6 @@ void RrImageCacheUnref(RrImageCache *self)
     }
 }
 
-RrImage* RrImageCacheFindName(RrImageCache *self, const gchar *name)
-{
-    return g_hash_table_lookup(self->name_table, name);
-}
-
-/*! Finds an image in the cache, if it is already in there */
-RrImage* RrImageCacheFind(RrImageCache *self,
-                          RrPixel32 *data, gint w, gint h)
-{
-    RrImagePic pic;
-
-    RrImagePicInit(&pic, NULL, w, h, data);
-    return g_hash_table_lookup(self->pic_table, &pic);
-}
-
 #define hashsize(n) ((RrPixel32)1<<(n))
 #define hashmask(n) (hashsize(n)-1)
 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
index 9baf34b..f9f5e4d 100644 (file)
@@ -45,13 +45,13 @@ struct _RrImageCache {
     */
     gint max_resized_saved;
 
-    /*! A hash table of images in the cache that don't have a file name
+    /*! A hash table of image sets in the cache that don't have a file name
       attached to them, with their key being a hash of the contents of the
       image. */
     GHashTable *pic_table;
 
-    /*! Used to find out if an image file has already been loaded.
-      Provides a quick file_name -> RrImage lookup. */
+    /*! Used to find out if an image file has already been loaded into an
+      image set. Provides a quick file_name -> RrImageSet lookup. */
     GHashTable *name_table;
 };
 
index d8d47c9..a5d6500 100644 (file)
@@ -44,11 +44,12 @@ typedef struct _RrPixmapMask       RrPixmapMask;
 typedef struct _RrInstance         RrInstance;
 typedef struct _RrColor            RrColor;
 typedef struct _RrImage            RrImage;
+typedef struct _RrImageSet         RrImageSet;
 typedef struct _RrImagePic         RrImagePic;
 typedef struct _RrImageCache       RrImageCache;
 typedef struct _RrButton           RrButton;
 
-typedef guint32 RrPixel32;
+typedef guint32 RrPixel32;  /* RGBA format */
 typedef guint16 RrPixel16;
 typedef guchar  RrPixel8;
 
@@ -239,24 +240,43 @@ struct _RrImagePic {
     /* The sum of all the pixels.  This is used to compare pictures if their
        hashes match. */
     gint sum;
-    /* The name of the image.  This is used to determine
-       if the named image already is loaded.  May be NULL if the image
-       was not loaded from disk. */
-    gchar *name;
 };
 
 typedef void (*RrImageDestroyFunc)(RrImage *image, gpointer data);
 
-/*! An RrImage is a sort of meta-image.  It can contain multiple versions of
-  an image at different sizes, which may or may not be completely different
-  pictures */
+/*! An RrImage refers to a RrImageSet.  If multiple RrImageSets end up
+  holding the same image data, they will be marged and the RrImages that
+  point to them would be updated. */
 struct _RrImage {
     gint ref;
+    RrImageSet *set;
+
+    /* This function (if not NULL) will be called just before destroying
+      RrImage. */
+    RrImageDestroyFunc destroy_func;
+    gpointer           destroy_data;
+};
+
+/*! An RrImage is a sort of meta-image.  It can contain multiple versions
+  of an image at different sizes, which may or may not be completely different
+  pictures */
+struct _RrImageSet
+{
     RrImageCache *cache;
 
+    /*! If a picture is loaded by a name, then it has a name attached to it.
+      This contains a list of strings, containing all names that have ever
+      been associated with the RrImageSet. A name in the RrImageCache can
+      only be associated with a single RrImageSet. */
+    GSList *names;
+
+    /*! RrImages that point at this RrImageSet. If this is empty, then there
+      are no images using the set and it can be freed. */
+    GSList *images;
+
     /*! An array of "originals", that is of RrPictures that have been added
       to the image in various sizes, and that have not been resized.  These
-      are explicitly added to the RrImage. */
+      are explicitly added to the RrImageSet. */
     RrImagePic **original;
     gint n_original;
     /*! An array of "resized" pictures.  When an "original" RrPicture
@@ -265,11 +285,6 @@ struct _RrImage {
       RrImage. */
     RrImagePic **resized;
     gint n_resized;
-
-    /* This function (if not NULL) will be called just before destroying
-      RrImage. */
-    RrImageDestroyFunc destroy_func;
-    gpointer           destroy_data;
 };
 
 struct _RrButton {
@@ -406,23 +421,41 @@ RrImageCache* RrImageCacheNew(gint max_resized_saved);
 void          RrImageCacheRef(RrImageCache *self);
 void          RrImageCacheUnref(RrImageCache *self);
 
-/*! Finds an image in the cache, if it is already in there */
-RrImage*      RrImageCacheFind(RrImageCache *self,
-                               RrPixel32 *data, gint w, gint h);
-/*! Finds an image in the cache, by searching for the name of the image */
-RrImage*      RrImageCacheFindName(RrImageCache *self,
-                                   const gchar *name);
-
-RrImage* RrImageNew(RrImageCache *cache);
-void     RrImageRef(RrImage *im);
-void     RrImageUnref(RrImage *im);
-
-void     RrImageAddPicture(RrImage *im, const RrPixel32 *data, gint w, gint h);
-/*! Adds a picture by name, from a file on disk. 
-  @name Can be a full path to an image, or it can be a name as per the
-        freedesktop.org icon spec. */
-gboolean RrImageAddPictureName(RrImage *im, const gchar *name);
-void     RrImageRemovePicture(RrImage *im, gint w, gint h);
+/*! Create a new image, or return one from the cache that matches.
+  @param cache The image cache.
+  @param old The current RrImage, which the new image should be added to.
+    Use this if loading a different sized version of the same image.
+    The returned RrImage should replace the one passed in as old.
+    Pass NULL here if adding an image which is (or may be) entirely new.
+  @param name The name of the icon to be loaded off disk, or used in the cache
+  @return Returns NULL if unable to load an image by the name and it is not in
+    the cache already
+*/
+RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name);
+
+/*! Create a new image, or return one from the cache that matches.
+  @param cache The image cache.
+  @param data The image data in RGBA32 format.  There should be @w * @h many
+    values in the data array.
+  @param w The width of the image data.
+  @param h The height of the image data.
+  @return Returns NULL if unable to load an image by the name and it is not in
+    the cache already
+*/
+RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data,
+                            gint w, gint h);
+
+/*! Add a new size of a picture to an image.
+  If a picture has multiple versions of different sizes (example 16x16, 32x32
+  and so on), they should all be under the same RrImage.  This adds a new
+  size to an existing RrImage, associating the newly sized picture with the
+  others in the RrImage - classifying them as being the same logical image at a
+  different dimention.
+*/
+void RrImageAddFromData(RrImage *image, RrPixel32 *data, gint w, gint h);
+
+void RrImageRef(RrImage *im);
+void RrImageUnref(RrImage *im);
 
 G_END_DECLS
 
index 65e4268..9613c51 100644 (file)
@@ -40,8 +40,9 @@ G_BEGIN_DECLS
         if ((val) == (ar)[out_BSEARCH]) {                           \
             break; \
         } \
-        else if ((val) < (ar)[out_BSEARCH])                       \
+        else if ((val) < (ar)[out_BSEARCH] && out_BSEARCH > 0) {     \
             r_BSEARCH = out_BSEARCH-1; /* search to the left side */ \
+        } \
         else \
             l_BSEARCH = out_BSEARCH+1; /* search to the left side */ \
     } \
index db327a6..8bfdd39 100644 (file)
@@ -439,7 +439,8 @@ void obt_keyboard_context_unref(ObtIC *ic)
 {
     if (--ic->ref < 1) {
         xic_all = g_slist_remove(xic_all, ic);
-        XDestroyIC(ic->xic);
+        if (ic->xic)
+            XDestroyIC(ic->xic);
         g_slice_free(ObtIC, ic);
     }
 }
index bef9c99..d5af6a0 100644 (file)
@@ -45,7 +45,8 @@ void obt_prop_startup(void)
     CREATE(PIXMAP);
     CREATE(ATOM);
     CREATE(STRING);
-    CREATE_NAME(UTF8, "UTF8_STRING");
+    CREATE(COMPOUND_TEXT);
+    CREATE(UTF8_STRING);
 
     CREATE(MANAGER);
 
@@ -283,128 +284,236 @@ static gboolean get_all(Window win, Atom prop, Atom type, gint size,
     return ret;
 }
 
-static gboolean get_stringlist(Window win, Atom prop, gchar ***list, gint *nstr)
+/*! Get a text property from a window, and fill out the XTextProperty with it.
+  @param win The window to read the property from.
+  @param prop The atom of the property to read off the window.
+  @param tprop The XTextProperty to fill out.
+  @param type 0 to get text of any type, or a value from
+    ObtPropTextType to restrict the value to a specific type.
+  @return TRUE if the text was read and validated against the @type, and FALSE
+    otherwise.
+*/
+static gboolean get_text_property(Window win, Atom prop,
+                                  XTextProperty *tprop, ObtPropTextType type)
 {
-    XTextProperty tprop;
-    gboolean ret = FALSE;
-
-    if (XGetTextProperty(obt_display, win, &tprop, prop) && tprop.nitems) {
-        if (XTextPropertyToStringList(&tprop, list, nstr))
-            ret = TRUE;
-        XFree(tprop.value);
+    if (!(XGetTextProperty(obt_display, win, tprop, prop) && tprop->nitems))
+        return FALSE;
+    if (!type)
+        return TRUE; /* no type checking */
+    switch (type) {
+    case OBT_PROP_TEXT_STRING:
+    case OBT_PROP_TEXT_STRING_XPCS:
+    case OBT_PROP_TEXT_STRING_NO_CC:
+        return tprop->encoding == OBT_PROP_ATOM(STRING);
+    case OBT_PROP_TEXT_COMPOUND_TEXT:
+        return tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT);
+    case OBT_PROP_TEXT_UTF8_STRING:
+        return tprop->encoding == OBT_PROP_ATOM(UTF8_STRING);
+    default:
+        g_assert_not_reached();
     }
-    return ret;
-}
-
-gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret)
-{
-    return get_prealloc(win, prop, type, 32, (guchar*)ret, 1);
 }
 
-gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
-                              guint *nret)
+/*! Returns one or more UTF-8 encoded strings from the text property.
+  @param tprop The XTextProperty to convert into UTF-8 string(s).
+  @param type The type which specifies the format that the text must meet, or
+    0 to allow any valid characters that can be converted to UTF-8 through.
+  @param max The maximum number of strings to return.  -1 to return them all.
+  @return If max is 1, then this returns a gchar* with the single string.
+    Otherwise, this returns a gchar** of no more than max strings (or all
+    strings read, if max is negative). If an error occurs, NULL is returned.
+ */
+static void* convert_text_property(XTextProperty *tprop,
+                                   ObtPropTextType type, gint max)
 {
-    return get_all(win, prop, type, 32, (guchar**)ret, nret);
-}
-
-gboolean obt_prop_get_string_locale(Window win, Atom prop, gchar **ret)
-{
-    gchar **list;
-    gint nstr;
-    gchar *s;
-
-    if (get_stringlist(win, prop, &list, &nstr) && nstr) {
-        s = g_locale_to_utf8(list[0], -1, NULL, NULL, NULL);
-        XFreeStringList(list);
-        if (s) {
-            *ret = s;
-            return TRUE;
+    enum {
+        LATIN1,
+        UTF8,
+        LOCALE
+    } encoding;
+    const gboolean return_single = (max == 1);
+    gboolean ok = FALSE;
+    gchar **strlist = NULL;
+    gchar *single[1] = { NULL };
+    gchar **retlist = single; /* single is used when max == 1 */
+    gint i, n_strs;
+
+    /* Read each string in the text property and store a pointer to it in
+       retlist.  These pointers point into the X data structures directly.
+
+       Then we will convert them to UTF-8, and replace the retlist pointer with
+       a new one.
+    */
+    if (tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT))
+    {
+        encoding = LOCALE;
+        ok = (XmbTextPropertyToTextList(
+                   obt_display, tprop, &strlist, &n_strs) == Success);
+        if (ok) {
+            if (max >= 0)
+                n_strs = MIN(max, n_strs);
+            if (!return_single)
+                retlist = g_new0(gchar*, n_strs+1);
+            if (retlist)
+                for (i = 0; i < n_strs; ++i)
+                    retlist[i] = strlist[i];
         }
     }
-    return FALSE;
-}
-
-gboolean obt_prop_get_strings_locale(Window win, Atom prop, gchar ***ret)
-{
-    GSList *strs = NULL, *it;
-    gchar *raw, *p;
-    guint num, i, count = 0;
-
-    if (get_all(win, prop, OBT_PROP_ATOM(STRING), 8,
-                (guchar**)&raw, &num))
+    else if (tprop->encoding == OBT_PROP_ATOM(UTF8_STRING) ||
+             tprop->encoding == OBT_PROP_ATOM(STRING))
     {
-        p = raw;
-        while (p < raw + num) {
-            ++count;
-            strs = g_slist_append(strs, p);
+        gchar *p; /* iterator */
+
+        if (tprop->encoding == OBT_PROP_ATOM(STRING))
+            encoding = LATIN1;
+        else
+            encoding = UTF8;
+        ok = TRUE;
+
+        /* First, count the number of strings. Then make a structure for them
+           and copy pointers to them into it. */
+        p = (gchar*)tprop->value;
+        n_strs = 0;
+        while (p < (gchar*)tprop->value + tprop->nitems) {
             p += strlen(p) + 1; /* next string */
+            ++n_strs;
         }
 
-        *ret = g_new0(gchar*, count + 1);
-        (*ret)[count] = NULL; /* null terminated list */
+        if (max >= 0)
+            n_strs = MIN(max, n_strs);
+        if (!return_single)
+            retlist = g_new0(gchar*, n_strs+1);
+        if (retlist) {
+            p = (gchar*)tprop->value;
+            for (i = 0; i < n_strs; ++i) {
+                retlist[i] = p;
+                p += strlen(p) + 1; /* next string */
+            }
+        }
+    }
+
+    if (!(ok && retlist)) {
+        if (strlist) XFreeStringList(strlist);
+        return NULL;
+    }
+
+    /* convert each element in retlist to UTF-8, and replace it. */
+    for (i = 0; i < n_strs; ++i) {
+        if (encoding == UTF8) {
+            const gchar *end; /* the first byte past the valid data */
 
-        for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
-            (*ret)[i] = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL);
-            /* make sure translation did not fail */
-            if (!(*ret)[i])
-                (*ret)[i] = g_strdup("");
+            g_utf8_validate(retlist[i], -1, &end);
+            retlist[i] = g_strndup(retlist[i], end-retlist[i]);
+        }
+        else if (encoding == LOCALE) {
+            gsize nvalid; /* the number of valid bytes at the front of the
+                             string */
+            gchar *utf; /* the string converted into utf8 */
+
+            utf = g_locale_to_utf8(retlist[i], -1, &nvalid, NULL, NULL);
+            if (!utf)
+                utf = g_locale_to_utf8(retlist[i], nvalid, NULL, NULL, NULL);
+            g_assert(utf);
+            retlist[i] = utf;
+        }
+        else { /* encoding == LATIN1 */
+            gsize nvalid; /* the number of valid bytes at the front of the
+                             string */
+            gchar *utf; /* the string converted into utf8 */
+            gchar *p; /* iterator */
+
+            /* look for invalid characters */
+            for (p = retlist[i], nvalid = 0; *p; ++p, ++nvalid) {
+                /* The only valid control characters are TAB(HT)=9 and
+                   NEWLINE(LF)=10.
+                   This is defined in ICCCM section 2:
+                     http://tronche.com/gui/x/icccm/sec-2.html.
+                   See a definition of the latin1 codepage here:
+                     http://en.wikipedia.org/wiki/ISO/IEC_8859-1.
+                   The above page includes control characters in the table,
+                   which we must explicitly exclude, as the g_convert function
+                   will happily take them.
+                */
+                const register guchar c = (guchar)*p; /* unsigned value at p */
+                if ((c < 32 && c != 9 && c != 10) || (c >= 127 && c <= 160))
+                    break; /* found a control character that isn't allowed */
+
+                if (type == OBT_PROP_TEXT_STRING_NO_CC && c < 32)
+                    break; /* absolutely no control characters are allowed */
+
+                if (type == OBT_PROP_TEXT_STRING_XPCS) {
+                    const gboolean valid = (
+                        (c >= 32 && c < 128) || c == 9 || c == 10);
+                    if (!valid)
+                        break; /* strict whitelisting for XPCS */
+                }
+            }
+            /* look for invalid latin1 characters */
+            utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1",
+                            &nvalid, NULL, NULL);
+            if (!utf)
+                utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1",
+                                NULL, NULL, NULL);
+            g_assert(utf);
+            retlist[i] = utf;
         }
-        g_free(raw);
-        g_slist_free(strs);
-        return TRUE;
     }
-    return FALSE;
+
+    if (strlist) XFreeStringList(strlist);
+    if (return_single)
+        return retlist[0];
+    else
+        return retlist;
 }
 
-gboolean obt_prop_get_string_utf8(Window win, Atom prop, gchar **ret)
+gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret)
 {
-    gchar *raw;
+    return get_prealloc(win, prop, type, 32, (guchar*)ret, 1);
+}
+
+gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
+                              guint *nret)
+{
+    return get_all(win, prop, type, 32, (guchar**)ret, nret);
+}
+
+gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type,
+                           gchar **ret_string)
+{
+    XTextProperty tprop;
     gchar *str;
-    guint num;
+    gboolean ret = FALSE;
 
-    if (get_all(win, prop, OBT_PROP_ATOM(UTF8), 8,
-                (guchar**)&raw, &num))
-    {
-        str = g_strndup(raw, num); /* grab the first string from the list */
-        g_free(raw);
-        if (g_utf8_validate(str, -1, NULL)) {
-            *ret = str;
-            return TRUE;
+    if (get_text_property(win, prop, &tprop, type)) {
+        str = (gchar*)convert_text_property(&tprop, type, 1);
+
+        if (str) {
+            *ret_string = str;
+            ret = TRUE;
         }
-        g_free(str);
     }
-    return FALSE;
+    XFree(tprop.value);
+    return ret;
 }
 
-gboolean obt_prop_get_strings_utf8(Window win, Atom prop, gchar ***ret)
+gboolean obt_prop_get_array_text(Window win, Atom prop,
+                                 ObtPropTextType type,
+                                 gchar ***ret_strings)
 {
-    GSList *strs = NULL, *it;
-    gchar *raw, *p;
-    guint num, i, count = 0;
-
-    if (get_all(win, prop, OBT_PROP_ATOM(UTF8), 8,
-                (guchar**)&raw, &num))
-    {
-        p = raw;
-        while (p < raw + num) {
-            ++count;
-            strs = g_slist_append(strs, p);
-            p += strlen(p) + 1; /* next string */
-        }
+    XTextProperty tprop;
+    gchar **strs;
+    gboolean ret = FALSE;
 
-        *ret = g_new0(gchar*, count + 1);
+    if (get_text_property(win, prop, &tprop, type)) {
+        strs = (gchar**)convert_text_property(&tprop, type, -1);
 
-        for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
-            if (g_utf8_validate(it->data, -1, NULL))
-                (*ret)[i] = g_strdup(it->data);
-            else
-                (*ret)[i] = g_strdup("");
+        if (strs) {
+            *ret_strings = strs;
+            ret = TRUE;
         }
-        g_free(raw);
-        g_slist_free(strs);
-        return TRUE;
     }
-    return FALSE;
+    XFree(tprop.value);
+    return ret;
 }
 
 void obt_prop_set32(Window win, Atom prop, Atom type, gulong val)
@@ -420,45 +529,13 @@ void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val,
                     (guchar*)val, num);
 }
 
-void obt_prop_set_string_locale(Window win, Atom prop, const gchar *val)
-{
-    gchar const *s[2] = { val, NULL };
-    obt_prop_set_strings_locale(win, prop, s);
-}
-
-void obt_prop_set_strings_locale(Window win, Atom prop,
-                                 const gchar *const *strs)
-{
-    gint i, count;
-    gchar **lstrs;
-    XTextProperty tprop;
-
-    /* count the strings in strs, and convert them to the locale format */
-    for (count = 0; strs[count]; ++count);
-    lstrs = g_new0(char*, count);
-    for (i = 0; i < count; ++i) {
-        lstrs[i] = g_locale_from_utf8(strs[i], -1, NULL, NULL, NULL);
-        if (!lstrs[i]) {
-            lstrs[i] = g_strdup(""); /* make it an empty string */
-            g_warning("Unable to translate string '%s' from UTF8 to locale "
-                      "format", strs[i]);
-        }
-    }
-
-
-    XStringListToTextProperty(lstrs, count, &tprop);
-    XSetTextProperty(obt_display, win, &tprop, prop);
-    XFree(tprop.value);
-}
-
-void obt_prop_set_string_utf8(Window win, Atom prop, const gchar *val)
+void obt_prop_set_text(Window win, Atom prop, const gchar *val)
 {
-    XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8), 8,
+    XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8,
                     PropModeReplace, (const guchar*)val, strlen(val));
 }
 
-void obt_prop_set_strings_utf8(Window win, Atom prop,
-                               const gchar *const *strs)
+void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs)
 {
     GString *str;
     gchar const *const *s;
@@ -468,7 +545,7 @@ void obt_prop_set_strings_utf8(Window win, Atom prop,
         str = g_string_append(str, *s);
         str = g_string_append_c(str, '\0');
     }
-    XChangeProperty(obt_display, win, prop, obt_prop_atom(OBT_PROP_UTF8), 8,
+    XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8,
                     PropModeReplace, (guchar*)str->str, str->len);
     g_string_free(str, TRUE);
 }
index 7ccc218..9c4ace3 100644 (file)
@@ -28,11 +28,13 @@ G_BEGIN_DECLS
 typedef enum {
     /* types */
     OBT_PROP_CARDINAL, /*!< The atom which represents the Cardinal data type */
-    OBT_PROP_WINDOW,   /*!< The atom which represents window ids */
-    OBT_PROP_PIXMAP,   /*!< The atom which represents pixmap ids */
-    OBT_PROP_ATOM,     /*!< The atom which represents atom values */
-    OBT_PROP_STRING,   /*!< The atom which represents ascii strings */
-    OBT_PROP_UTF8,     /*!< The atom which represents utf8-encoded strings */
+    OBT_PROP_WINDOW, /*!< The atom which represents window ids */
+    OBT_PROP_PIXMAP, /*!< The atom which represents pixmap ids */
+    OBT_PROP_ATOM, /*!< The atom which represents atom values */
+    OBT_PROP_STRING, /*!< The atom which represents latin1 strings */
+    OBT_PROP_COMPOUND_TEXT, /*!< The atom which represents locale-encoded
+                              strings */
+    OBT_PROP_UTF8_STRING, /*!< The atom which represents utf8-encoded strings*/
 
     /* selection stuff */
     OBT_PROP_MANAGER,
@@ -223,23 +225,39 @@ typedef enum {
 
 Atom obt_prop_atom(ObtPropAtom a);
 
+typedef enum {
+    /*! STRING is latin1 encoded.  It cannot contain control characters except
+       for tab and line-feed. */
+    OBT_PROP_TEXT_STRING = 1,
+    /*! STRING text restricted to characters in the X Portable Character
+      Set, which is a subset of latin1.
+      http://static.cray-cyber.org/Documentation/NEC_SX_R10_1/G1AE02E/CHAP1.HTML
+    */
+    OBT_PROP_TEXT_STRING_XPCS = 2,
+    /*! STRING text restricted to not allow any control characters to be
+      present. */
+    OBT_PROP_TEXT_STRING_NO_CC = 3,
+    /* COMPOUND_TEXT is encoded in the current locale setting. */
+    OBT_PROP_TEXT_COMPOUND_TEXT = 4,
+    /* UTF8_STRING is encoded as utf-8. */
+    OBT_PROP_TEXT_UTF8_STRING = 5,
+} ObtPropTextType;
+
 gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret);
 gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
                               guint *nret);
-gboolean obt_prop_get_string_locale(Window win, Atom prop, gchar **ret);
-gboolean obt_prop_get_string_utf8(Window win, Atom prop, gchar **ret);
-gboolean obt_prop_get_strings_locale(Window win, Atom prop, gchar ***ret);
-gboolean obt_prop_get_strings_utf8(Window win, Atom prop, gchar ***ret);
+
+gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type,
+                           gchar **ret);
+gboolean obt_prop_get_array_text(Window win, Atom prop,
+                                 ObtPropTextType type,
+                                 gchar ***ret);
 
 void obt_prop_set32(Window win, Atom prop, Atom type, gulong val);
 void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val,
                           guint num);
-void obt_prop_set_string_locale(Window win, Atom prop, const gchar *val);
-void obt_prop_set_string_utf8(Window win, Atom prop, const gchar *val);
-void obt_prop_set_strings_locale(Window win, Atom prop,
-                                 const gchar *const *strs);
-void obt_prop_set_strings_utf8(Window win, Atom prop,
-                               const gchar *const *strs);
+void obt_prop_set_text(Window win, Atom prop, const gchar *str);
+void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs);
 
 void obt_prop_erase(Window win, Atom prop);
 
@@ -257,20 +275,33 @@ void obt_prop_message_to(Window to, Window about, Atom messagetype,
 #define OBT_PROP_GETA32(win, prop, type, ret, nret) \
     (obt_prop_get_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \
                           ret, nret))
-#define OBT_PROP_GETS(win, prop, type, ret) \
-    (obt_prop_get_string_##type(win, OBT_PROP_ATOM(prop), ret))
-#define OBT_PROP_GETSS(win, prop, type, ret) \
-    (obt_prop_get_strings_##type(win, OBT_PROP_ATOM(prop), ret))
+#define OBT_PROP_GETS(win, prop, ret) \
+    (obt_prop_get_text(win, OBT_PROP_ATOM(prop), 0, ret))
+#define OBT_PROP_GETSS(win, prop, ret) \
+    (obt_prop_get_array_text(win, OBT_PROP_ATOM(prop), 0, ret))
+
+#define OBT_PROP_GETS_TYPE(win, prop, type, ret) \
+    (obt_prop_get_text(win, OBT_PROP_ATOM(prop), OBT_PROP_TEXT_##type, ret))
+#define OBT_PROP_GETSS_TYPE(win, prop, type, ret) \
+    (obt_prop_get_array_text(win, OBT_PROP_ATOM(prop), \
+                             OBT_PROP_TEXT_##type, ret))
+
+#define OBT_PROP_GETS_UTF8(win, prop, ret) \
+    OBT_PROP_GETS_TYPE(win, prop, UTF8_STRING, ret)
+#define OBT_PROP_GETSS_UTF8(win, prop, ret) \
+    OBT_PROP_GETSS_TYPE(win, prop, UTF8_STRING, ret)
+#define OBT_PROP_GETS_XPCS(win, prop, ret) \
+    OBT_PROP_GETS_TYPE(win, prop, STRING_XPCS, ret)
 
 #define OBT_PROP_SET32(win, prop, type, val) \
     (obt_prop_set32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), val))
 #define OBT_PROP_SETA32(win, prop, type, val, num) \
     (obt_prop_set_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \
                           val, num))
-#define OBT_PROP_SETS(win, prop, type, val) \
-    (obt_prop_set_string_##type(win, OBT_PROP_ATOM(prop), val))
-#define OBT_PROP_SETSS(win, prop, type, strs) \
-    (obt_prop_set_strings_##type(win, OBT_PROP_ATOM(prop), strs))
+#define OBT_PROP_SETS(win, prop, val) \
+    (obt_prop_set_text(win, OBT_PROP_ATOM(prop), val))
+#define OBT_PROP_SETSS(win, prop, strs) \
+    (obt_prop_set_array_text(win, OBT_PROP_ATOM(prop), strs))
 
 #define OBT_PROP_ERASE(win, prop) (obt_prop_erase(win, OBT_PROP_ATOM(prop)))
 
diff --git a/obt/tests/bstest.c b/obt/tests/bstest.c
new file mode 100755 (executable)
index 0000000..7581855
--- /dev/null
@@ -0,0 +1,58 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./bstest `pkg-config --cflags --libs obt-3.5` bstest.c && \
+./bstest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   bstest.c for the Openbox window manager
+   Copyright (c) 2010        Dana Jansens
+
+   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 "../bsearch.h"
+#include <stdio.h>
+
+int main() {
+    int ar[] = {
+        2, 4, 5, 7, 12, 34, 45, 56, 57, 67, 67, 68, 68, 69, 70, 71, 89, 100 };
+    int n = sizeof(ar)/sizeof(ar[0]);
+    BSEARCH_SETUP(int);
+    BSEARCH(int, ar, 0, n, 1);
+    g_assert(!!BSEARCH_FOUND() == FALSE);
+    BSEARCH(int, ar, 0, n, 0);
+    g_assert(!!BSEARCH_FOUND() == FALSE);
+    BSEARCH(int, ar, 0, n, 2);
+    g_assert(!!BSEARCH_FOUND() == TRUE);
+    g_assert(BSEARCH_AT() == 0);
+    BSEARCH(int, ar, 0, n, 58);
+    g_assert(!!BSEARCH_FOUND() == FALSE);
+    BSEARCH(int, ar, 0, n, 57);
+    g_assert(!!BSEARCH_FOUND() == TRUE);
+    g_assert(BSEARCH_AT() == 8);
+    BSEARCH(int, ar, 0, n, 55);
+    g_assert(!!BSEARCH_FOUND() == FALSE);
+    BSEARCH(int, ar, 0, n, 99);
+    g_assert(!!BSEARCH_FOUND() == FALSE);
+    BSEARCH(int, ar, 0, n, 100);
+    g_assert(!!BSEARCH_FOUND() == TRUE);
+    g_assert(BSEARCH_AT() == 17);
+    BSEARCH(int, ar, 0, n, 101);
+    g_assert(!!BSEARCH_FOUND() == FALSE);
+    g_print("ok\n");
+}
diff --git a/obt/tests/ddtest.c b/obt/tests/ddtest.c
new file mode 100755 (executable)
index 0000000..69a9e1c
--- /dev/null
@@ -0,0 +1,61 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./ddtest `pkg-config --cflags --libs obt-3.5` ddtest.c && \
+./ddtest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   ddtest.c for the Openbox window manager
+   Copyright (c) 2010        Dana Jansens
+
+   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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "obt/paths.h"
+#include "obt/link.h"
+#include <glib.h>
+
+gint main(int argc, char **argv)
+{
+    ObtPaths *obtpaths;
+    ObtLink *dd;
+    gchar *id;
+
+    if (argc < 2) {
+        g_print("pass path to .desktop\n");
+        return 1;
+    }
+
+    obtpaths = obt_paths_new();
+    dd = obt_link_from_ddfile(argv[1], obtpaths, "et", NULL, NULL);
+    obt_paths_unref(obtpaths);
+    if (dd) {
+        g_print("Success\n");
+        {
+            gulong i, n;
+            const GQuark *c = obt_link_app_categories(dd, &n);
+            for (i = 0; i < n; ++i)
+                g_print("Category: %s\n",
+                        g_quark_to_string(c[i]));
+        }
+        obt_link_unref(dd);
+    }
+    return 0;
+}
diff --git a/obt/tests/ddtest.desktop b/obt/tests/ddtest.desktop
new file mode 100644 (file)
index 0000000..16d76aa
--- /dev/null
@@ -0,0 +1,15 @@
+[Desktop Entry]
+test=
+test2
+foo = 
+#hi
+gewh= yuhself
+a-r950   =  tek;la; fi
+hi=bye
+
+you=yeh
+hi=double
+Type=Application
+Exec=foo
+Name=myname
+Categories=one;two;;three
old mode 100644 (file)
new mode 100755 (executable)
similarity index 57%
rename from obt/watch.h
rename to obt/tests/linktest.c
index c8556bc..022ba35
@@ -1,6 +1,15 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./linktest `pkg-config --cflags --libs obt-3.5` linktest.c && \
+./linktest
+exit
+#endif
+
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
 
-   obt/watch.h for the Openbox window manager
+   linktest.c for the Openbox window manager
    Copyright (c) 2010        Dana Jansens
 
    This program is free software; you can redistribute it and/or modify
    See the COPYING file for a copy of the GNU General Public License.
 */
 
-#ifndef __obt_watch_h
-#define __obt_watch_h
-
+#include "obt/linkbase.h"
+#include "obt/paths.h"
 #include <glib.h>
+#include <locale.h>
 
-G_BEGIN_DECLS
-
-typedef struct _ObtWatch ObtWatch;
+gint main()
+{
+    ObtLinkBase *base;
+    ObtPaths *paths;
+    GMainLoop *loop;
 
-struct _ObtMainLoop;
+    paths = obt_paths_new();
+    base = obt_linkbase_new(paths, setlocale(LC_MESSAGES, ""));
+    printf("done\n");
+    return 0;
 
-typedef void (*ObtWatchFunc)(ObtWatch *w, gchar *subpath, gpointer data);
+    loop = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(loop);
 
-ObtWatch* obt_watch_new();
-void obt_watch_ref(ObtWatch *w);
-void obt_watch_unref(ObtWatch *w);
-
-void obt_watch_dir(ObtWatch *w, const gchar *path,
-                   ObtWatchFunc func, gpointer data);
-
-G_END_DECLS
-
-#endif
+    return 0;
+}
diff --git a/obt/tests/watchtest.c b/obt/tests/watchtest.c
new file mode 100755 (executable)
index 0000000..9ebdbae
--- /dev/null
@@ -0,0 +1,50 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./watchtest `pkg-config --cflags --libs obt-3.5` watchtest.c && \
+./watchtest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   watchtest.c for the Openbox window manager
+   Copyright (c) 2010        Dana Jansens
+
+   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 "obt/watch.h"
+#include <glib.h>
+
+void func(ObtWatch *w, const gchar *base_path,
+          const gchar *subpath, ObtWatchNotifyType type,
+          gpointer data)
+{
+    g_print("base path: %s subpath: %s type=%d\n", base_path, subpath, type);
+}
+
+gint main()
+{
+    ObtWatch *watch;
+    GMainLoop *loop;
+
+    watch = obt_watch_new();
+    obt_watch_add(watch, "/tmp/a", FALSE, func, NULL);
+
+    loop = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(loop);
+
+    return 0;
+}
diff --git a/obt/watch.c b/obt/watch.c
deleted file mode 100644 (file)
index c2f6487..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
-
-   obt/watch.c for the Openbox window manager
-   Copyright (c) 2010        Dana Jansens
-
-   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 "obt/watch.h"
-
-#ifdef HAVE_SYS_INOTIFY_H
-#  include <sys/inotify.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#  include <unistd.h>
-#endif
-#include <errno.h>
-
-struct _ObtWatch {
-    guint ref;
-    gint ino_fd;
-    guint ino_watch;
-    GHashTable *targets;
-
-#ifdef HAVE_SYS_INOTIFY_H
-    GHashTable *targets_by_wd;
-#endif
-};
-
-typedef struct _ObtWatchTarget {
-    ObtWatch *w;
-
-#ifdef HAVE_SYS_INOTIFY_H
-    gint wd;
-#endif
-
-    gchar *path;
-    ObtWatchFunc func;
-    gpointer data;
-} ObtWatchTarget;
-
-static void init_inot(ObtWatch *w);
-static gboolean read_inot(GIOChannel *s, GIOCondition cond, gpointer data);
-static gboolean add_inot(ObtWatch *w, ObtWatchTarget *t, const char *path,
-                         gboolean dir);
-static void rm_inot(ObtWatchTarget *t);
-static ObtWatchTarget* target_new(ObtWatch *w, const gchar *path,
-                                  ObtWatchFunc func, gpointer data);
-static void target_free(ObtWatchTarget *t);
-
-ObtWatch* obt_watch_new()
-{
-    ObtWatch *w;
-
-    w = g_slice_new(ObtWatch);
-    w->ref = 1;
-    w->ino_fd = -1;
-    w->targets = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                       NULL, (GDestroyNotify)target_free);
-#ifdef HAVE_SYS_INOTIFY_H
-    w->targets_by_wd = g_hash_table_new(g_int_hash, g_int_equal);
-#endif
-
-    init_inot(w);
-
-    return w;
-}
-void obt_watch_ref(ObtWatch *w)
-{
-    ++w->ref;
-}
-
-void obt_watch_unref(ObtWatch *w)
-{
-    if (--w->ref < 1) {
-        if (w->ino_fd >= 0 && w->ino_watch)
-            g_source_remove(w->ino_watch);
-
-        g_hash_table_destroy(w->targets);
-        g_hash_table_destroy(w->targets_by_wd);
-
-        g_slice_free(ObtWatch, w);
-    }
-}
-
-static void init_inot(ObtWatch *w)
-{
-#ifdef HAVE_SYS_INOTIFY_H
-    if (w->ino_fd >= 0) return;
-
-    w->ino_fd = inotify_init();
-    if (w->ino_fd >= 0) {
-        GIOChannel *ch;
-
-        ch = g_io_channel_unix_new(w->ino_fd);
-        w->ino_watch = g_io_add_watch(ch, G_IO_IN | G_IO_HUP | G_IO_ERR,
-                                      read_inot, w);
-        g_io_channel_unref(ch);
-    }
-#endif
-}
-
-static gboolean read_inot(GIOChannel *src, GIOCondition cond, gpointer data)
-{
-#ifdef HAVE_SYS_INOTIFY_H
-    ObtWatch *w = data;
-    ObtWatchTarget *t;
-    struct inotify_event s;
-    gint len;
-    guint ilen;
-    char *name;
-    
-    /* read the event */
-    for (ilen = 0; ilen < sizeof(s); ilen += len) {
-        len = read(w->ino_fd, ((char*)&s)+ilen, sizeof(s)-ilen);
-        if (len < 0 && errno != EINTR) return FALSE; /* error, don't repeat */
-        if (!len) return TRUE; /* nothing there */
-    }
-
-    name = g_new(char, s.len);
-
-    /* read the filename */
-    for (ilen = 0; ilen < s.len; ilen += len) {
-        len = read(w->ino_fd, name+ilen, s.len-ilen);
-        if (len < 0 && errno != EINTR) return FALSE; /* error, don't repeat */
-        if (!len) return TRUE; /* nothing there */
-    }
-
-    t = g_hash_table_lookup(w->targets, &s.wd);
-    if (t) t->func(w, name, t->data);
-
-    g_free(name);
-#endif
-    return TRUE; /* repeat */
-}
-
-static gboolean add_inot(ObtWatch *w, ObtWatchTarget *t, const char *path,
-                         gboolean dir)
-{
-#ifndef HAVE_SYS_INOTIFY_H
-    return FALSE;
-#else
-    gint mask;
-    if (w->ino_fd < 0) return FALSE;
-    if (dir) mask = IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE;
-    else g_assert_not_reached();
-    t->wd = inotify_add_watch(w->ino_fd, path, mask);
-    return TRUE;
-#endif
-}
-
-static void rm_inot(ObtWatchTarget *t)
-{
-#ifdef HAVE_SYS_INOTIFY_H
-    if (t->w->ino_fd < 0) return;
-    if (t->wd < 0) return;
-    inotify_rm_watch(t->w->ino_fd, t->wd);
-#endif
-}
-
-static ObtWatchTarget* target_new(ObtWatch *w, const gchar *path,
-                                  ObtWatchFunc func, gpointer data)
-{
-    ObtWatchTarget *t;
-
-    t = g_slice_new0(ObtWatchTarget);
-    t->w = w;
-    t->wd = -1;
-    t->path = g_strdup(path);
-    t->func = func;
-    t->data = data;
-
-    if (!add_inot(w, t, path, TRUE)) {
-        g_assert_not_reached(); /* XXX do something */
-    }
-
-#ifndef HAVE_SYS_INOTIFY_H
-#error need inotify for now
-#endif
-
-    return t;
-}
-
-static void target_free(ObtWatchTarget *t)
-{
-    rm_inot(t);
-
-    g_free(t->path);
-    g_slice_free(ObtWatchTarget, t);
-}
-
-void obt_paths_watch_dir(ObtWatch *w, const gchar *path,
-                         ObtWatchFunc func, gpointer data)
-{
-    ObtWatchTarget *t;
-
-    g_return_if_fail(w != NULL);
-    g_return_if_fail(path != NULL);
-    g_return_if_fail(data != NULL);
-
-    t = target_new(w, path, func, data);
-    g_hash_table_insert(w->targets, t->path, t);
-#ifdef HAVE_SYS_INOTIFY_H
-    g_hash_table_insert(w->targets_by_wd, &t->wd, t);
-#endif
-}
-
-void obt_paths_unwatch_dir(ObtWatch *w, const gchar *path)
-{
-    ObtWatchTarget *t;
-    
-    g_return_if_fail(w != NULL);
-    g_return_if_fail(path != NULL);
-
-    t = g_hash_table_lookup(w->targets, path);
-
-    if (t) {
-#ifdef HAVE_SYS_INOTIFY_H
-        g_hash_table_remove(w->targets_by_wd, &t->wd);
-#endif
-        g_hash_table_remove(w->targets, path);
-    }
-}
index ffff150..c872912 100644 (file)
--- a/obt/xml.c
+++ b/obt/xml.c
@@ -19,6 +19,7 @@
 #include "obt/xml.h"
 #include "obt/paths.h"
 
+#include <libxml/xinclude.h>
 #include <glib.h>
 
 #ifdef HAVE_STDLIB_H
@@ -136,6 +137,8 @@ static gboolean load_file(ObtXmlInst *i,
                with extra nodes in it. */
             i->doc = xmlReadFile(path, NULL, (XML_PARSE_NOBLANKS |
                                               XML_PARSE_RECOVER));
+            xmlXIncludeProcessFlags(i->doc, (XML_PARSE_NOBLANKS |
+                                             XML_PARSE_RECOVER));
             if (i->doc) {
                 i->root = xmlDocGetRootElement(i->doc);
                 if (!i->root) {
index 125084e..ee9d55f 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
 
-   actions.h for the Openbox window manager
+   actions.c for the Openbox window manager
    Copyright (c) 2007        Dana Jansens
 
    This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
 #include "event.h"
 #include "config.h"
 #include "client.h"
+#include "focus.h"
 #include "openbox.h"
 #include "debug.h"
 
@@ -300,6 +301,7 @@ void actions_run_acts(GSList *acts,
                       struct _ObClient *client)
 {
     GSList *it;
+    gboolean update_user_time;
 
     /* Don't allow saving the initial state when running things from the
        menu */
@@ -309,6 +311,7 @@ void actions_run_acts(GSList *acts,
     if (x < 0 && y < 0)
         screen_pointer_pos(&x, &y);
 
+    update_user_time = FALSE;
     for (it = acts; it; it = g_slist_next(it)) {
         ObActionsData data;
         ObActionsAct *act = it->data;
@@ -337,6 +340,8 @@ 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)
+                    update_user_time = TRUE;
             } else {
                 /* make sure its interactive if it returned TRUE */
                 g_assert(act->i_input);
@@ -346,6 +351,8 @@ void actions_run_acts(GSList *acts,
             }
         }
     }
+    if (update_user_time)
+        event_update_user_time();
 }
 
 gboolean actions_interactive_act_running(void)
index fdce77b..380ffa0 100644 (file)
@@ -260,7 +260,7 @@ static gboolean run_func(ObActionsData *data, gpointer options)
 
         if (o->sn) {
             if (!ok) sn_spawn_cancel();
-            unsetenv("DESKTOP_STARTUP_ID");
+            g_unsetenv("DESKTOP_STARTUP_ID");
         }
 
         g_free(program);
index 28010d3..0e055a9 100644 (file)
@@ -98,13 +98,16 @@ static gpointer setup_func(xmlNodePtr node)
             o->decor_on = TRUE;
     }
     if ((n = obt_xml_find_node(node, "desktop"))) {
-        gchar *s = obt_xml_node_string(n);
-        if (!g_ascii_strcasecmp(s, "current"))
-            o->desktop_current = TRUE;
-        if (!g_ascii_strcasecmp(s, "other"))
-            o->desktop_other = TRUE;
-        else
-            o->desktop_number = atoi(s);
+        gchar *s;
+        if ((s = obt_xml_node_string(n))) {
+          if (!g_ascii_strcasecmp(s, "current"))
+              o->desktop_current = TRUE;
+          if (!g_ascii_strcasecmp(s, "other"))
+              o->desktop_other = TRUE;
+          else
+              o->desktop_number = atoi(s);
+          g_free(s);
+        }
     }
     if ((n = obt_xml_find_node(node, "omnipresent"))) {
         if (obt_xml_node_bool(n))
index e666d58..1b010e4 100644 (file)
@@ -110,22 +110,14 @@ static void client_ping_event(ObClient *self, gboolean dead);
 static void client_prompt_kill(ObClient *self);
 static gboolean client_can_steal_focus(ObClient *self,
                                        gboolean allow_other_desktop,
+                                       gboolean request_from_user,
                                        Time steal_time, Time launch_time);
 
 void client_startup(gboolean reconfig)
 {
-    if ((client_default_icon = RrImageCacheFind(ob_rr_icons,
-                                                ob_rr_theme->def_win_icon,
-                                                ob_rr_theme->def_win_icon_w,
-                                                ob_rr_theme->def_win_icon_h)))
-        RrImageRef(client_default_icon);
-    else {
-        client_default_icon = RrImageNew(ob_rr_icons);
-        RrImageAddPicture(client_default_icon,
-                          ob_rr_theme->def_win_icon,
-                          ob_rr_theme->def_win_icon_w,
-                          ob_rr_theme->def_win_icon_h);
-    }
+    client_default_icon = RrImageNewFromData(
+        ob_rr_icons, ob_rr_theme->def_win_icon,
+        ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h);
 
     if (reconfig) return;
 
@@ -201,7 +193,8 @@ void client_manage(Window window, ObPrompt *prompt)
 {
     ObClient *self;
     XSetWindowAttributes attrib_set;
-    gboolean activate = FALSE;
+    gboolean try_activate = FALSE;
+    gboolean do_activate;
     ObAppSettings *settings;
     gboolean transient = FALSE;
     Rect place;
@@ -293,7 +286,7 @@ void client_manage(Window window, ObPrompt *prompt)
                            FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
                            settings->focus == 1))
     {
-        activate = TRUE;
+        try_activate = TRUE;
     }
 
     /* remove the client's border */
@@ -307,6 +300,16 @@ void client_manage(Window window, ObPrompt *prompt)
     /* where the frame was placed is where the window was originally */
     place = self->area;
 
+    ob_debug("Going to try activate new window? %s",
+             try_activate ? "yes" : "no");
+    if (try_activate)
+        do_activate = client_can_steal_focus(
+            self, settings->focus == 1,
+            (!!launch_time || settings->focus == 1),
+            event_time(), launch_time);
+    else
+        do_activate = FALSE;
+
     /* figure out placement for the window if the window is new */
     if (ob_state() == OB_STATE_RUNNING) {
         ob_debug("Positioned: %s @ %d %d",
@@ -325,7 +328,8 @@ void client_manage(Window window, ObPrompt *prompt)
                      "program + user specified" :
                      "BADNESS !?")))), place.width, place.height);
 
-        obplaced = place_client(self, &place.x, &place.y, settings);
+        obplaced = place_client(self, do_activate, &place.x, &place.y,
+                                settings);
 
         /* watch for buggy apps that ask to be placed at (0,0) when there is
            a strut there */
@@ -433,13 +437,34 @@ void client_manage(Window window, ObPrompt *prompt)
     client_apply_startup_state(self, place.x, place.y,
                                place.width, place.height);
 
-    ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
-                  activate ? "yes" : "no");
-    if (activate) {
-        activate = client_can_steal_focus(self, settings->focus,
-                                          event_time(), launch_time);
+    /* set the initial value of the desktop hint, when one wasn't requested
+       on map. */
+    OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
+    /* grab mouse bindings before showing the window */
+    mouse_grab_for_client(self, TRUE);
+
+    /* 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();
 
-        if (!activate) {
+        client_show(self);
+
+        if (!config_focus_under_mouse)
+            event_end_ignore_all_enters(ignore_start);
+    }
+
+    /* activate/hilight/raise the window */
+    if (try_activate) {
+        if (do_activate) {
+            gboolean stacked = client_restore_session_stacking(self);
+            client_present(self, FALSE, !stacked, TRUE);
+        }
+        else {
             /* if the client isn't stealing focus, then hilite it so the user
                knows it is there, but don't do this if we're restoring from a
                session */
@@ -459,27 +484,6 @@ void client_manage(Window window, ObPrompt *prompt)
             stacking_raise(CLIENT_AS_WINDOW(self));
     }
 
-    mouse_grab_for_client(self, TRUE);
-
-    /* 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);
-    }
-
-    if (activate) {
-        gboolean stacked = client_restore_session_stacking(self);
-        client_present(self, FALSE, !stacked, TRUE);
-    }
-
     /* add to client list/map */
     client_list = g_list_append(client_list, self);
     window_add(&self->window, CLIENT_AS_WINDOW(self));
@@ -697,105 +701,157 @@ void client_fake_unmanage(ObClient *self)
 
 static gboolean client_can_steal_focus(ObClient *self,
                                        gboolean allow_other_desktop,
+                                       gboolean request_from_user,
                                        Time steal_time,
                                        Time launch_time)
 {
     gboolean steal;
     gboolean relative_focused;
-    gboolean parent_focused;
 
     steal = TRUE;
 
-    parent_focused = (focus_client != NULL &&
-                      client_search_focus_parent(self));
     relative_focused = (focus_client != NULL &&
                         (client_search_focus_tree_full(self) != NULL ||
                          client_search_focus_group_full(self) != NULL));
 
     /* This is focus stealing prevention */
-    ob_debug_type(OB_DEBUG_FOCUS,
-                  "Want to focus window 0x%x at time %u "
-                  "launched at %u (last user interaction time %u)",
-                  self->window, steal_time, launch_time,
-                  event_last_user_time);
+    ob_debug("Want to focus window 0x%x at time %u "
+             "launched at %u (last user interaction time %u) "
+             "request from %s, allow other desktop: %s",
+             self->window, steal_time, launch_time,
+             event_last_user_time,
+             (request_from_user ? "user" : "other"),
+             (allow_other_desktop ? "yes" : "no"));
+
+    /*
+      if no launch time is provided for an application, make one up.
+
+      if the window is related to other existing windows
+        and one of those windows was the last used
+          then we will give it a launch time equal to the last user time,
+          which will end up giving the window focus probably.
+        else
+          the window is related to other windows, but you are not working in
+          them?
+          seems suspicious, so we will give it a launch time of
+          NOW - STEAL_INTERVAL,
+          so it will be given focus only if we didn't use something else
+          during the steal interval.
+      else
+        the window is all on its own, so we can't judge it.  give it a launch
+        time equal to the last user time, so it will probably take focus.
+
+      this way running things from a terminal will give them focus, but popups
+      without a launch time shouldn't steal focus so easily.
+    */
+
+    if (!launch_time) {
+        if (client_has_relative(self)) {
+            if (event_last_user_time && client_search_focus_group_full(self)) {
+                /* our relative is focused */
+                launch_time = event_last_user_time;
+                ob_debug("Unknown launch time, using %u - window in active "
+                         "group", launch_time);
+            }
+            else if (!request_from_user) {
+                /* has relatives which are not being used. suspicious */
+                launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
+                ob_debug("Unknown launch time, using %u - window in inactive "
+                         "group", launch_time);
+            }
+            else {
+                /* has relatives which are not being used, but the user seems
+                   to want to go there! */
+            launch_time = event_last_user_time;
+            ob_debug("Unknown launch time, using %u - user request",
+                     launch_time);
+            }
+        }
+        else {
+            /* the window is on its own, probably the user knows it is going
+               to appear */
+            launch_time = event_last_user_time;
+            ob_debug("Unknown launch time, using %u - independent window",
+                     launch_time);
+        }
+    }
 
-    /* if it's on another desktop... */
+    /* if it's on another desktop
+       then if allow_other_desktop is true, we don't want to let it steal
+       focus, unless it was launched after we changed desktops and the request
+       came from the user
+     */
     if (!(self->desktop == screen_desktop ||
           self->desktop == DESKTOP_ALL) &&
-        /* and (we dont know when it launched, and we don't want to allow
-           focus stealing from other desktops */
-        ((!launch_time && !allow_other_desktop) ||
-         /* or the timestamp is from before you changed desktops) */
-         (screen_desktop_user_time &&
+        (!allow_other_desktop ||
+         (request_from_user && screen_desktop_user_time &&
           !event_time_after(launch_time, screen_desktop_user_time))))
     {
         steal = FALSE;
-        ob_debug_type(OB_DEBUG_FOCUS,
-                      "Not focusing the window because its on another "
-                      "desktop\n");
+        ob_debug("Not focusing the window because its on another desktop\n");
     }
     /* If something is focused... */
     else if (focus_client) {
         /* If the user is working in another window right now, then don't
            steal focus */
-        if (!parent_focused &&
-            event_last_user_time && launch_time &&
-            event_time_after(event_last_user_time, launch_time) &&
-            event_last_user_time != launch_time &&
+        if (!relative_focused &&
+            event_last_user_time &&
+            /* last user time must be strictly > launch_time to block focus */
+            (event_time_after(event_last_user_time, launch_time) &&
+             event_last_user_time != launch_time) &&
             event_time_after(event_last_user_time,
                              steal_time - OB_EVENT_USER_TIME_DELAY))
         {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because the user is "
-                          "working in another window that is not "
-                          "its parent");
-        }
-        /* If the new window is a transient (and its relatives aren't
-           focused) */
-        else if (client_has_parent(self) && !relative_focused) {
-            steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because it is a "
-                          "transient, and its relatives aren't focused");
-        }
-        /* Don't steal focus from globally active clients.
-           I stole this idea from KWin. It seems nice.
-        */
-        else if (!(focus_client->can_focus ||
-                   focus_client->focus_notify))
-        {
-            steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because a globally "
-                          "active client has focus");
+            ob_debug("Not focusing the window because the user is "
+                     "working in another window that is not its relative");
         }
         /* Don't move focus if it's not going to go to this window
            anyway */
         else if (client_focus_target(self) != self) {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because another window "
-                          "would get the focus anyway");
+            ob_debug("Not focusing the window because another window "
+                     "would get the focus anyway");
         }
-        /* Don't move focus if the window is not visible on the current
-           desktop and none of its relatives are focused */
-        else if (!(self->desktop == screen_desktop ||
-                   self->desktop == DESKTOP_ALL) &&
-                 !relative_focused)
-        {
-            steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because it is on "
-                          "another desktop and no relatives are focused ");
+        /* For requests that don't come from the user */
+        else if (!request_from_user) {
+            /* If the new window is a transient (and its relatives aren't
+               focused) */
+            if (client_has_parent(self) && !relative_focused) {
+                steal = FALSE;
+                ob_debug("Not focusing the window because it is a "
+                         "transient, and its relatives aren't focused");
+            }
+            /* Don't steal focus from globally active clients.
+               I stole this idea from KWin. It seems nice.
+            */
+            else if (!(focus_client->can_focus || focus_client->focus_notify))
+            {
+                steal = FALSE;
+                ob_debug("Not focusing the window because a globally "
+                         "active client has focus");
+            }
+            /* Don't move focus if the window is not visible on the current
+               desktop and none of its relatives are focused */
+            else if (!allow_other_desktop &&
+                     !screen_compare_desktops(self->desktop, screen_desktop) &&
+                     !relative_focused)
+            {
+                steal = FALSE;
+                ob_debug("Not focusing the window because it is on "
+                         "another desktop and no relatives are focused ");
+            }
         }
     }
 
     if (!steal)
-        ob_debug_type(OB_DEBUG_FOCUS,
-                      "Focus stealing prevention activated for %s at "
-                      "time %u (last user interaction time %u)",
-                      self->title, steal_time, event_last_user_time);
+        ob_debug("Focus stealing prevention activated for %s at "
+                 "time %u (last user interaction time %u)",
+                 self->title, steal_time, event_last_user_time);
+    else
+        ob_debug("Allowing focus stealing for %s at time %u (last user "
+                 "interaction time %u)",
+                 self->title, steal_time, event_last_user_time);
     return steal;
 }
 
@@ -1154,11 +1210,10 @@ static void client_get_all(ObClient *self, gboolean real)
 
 static void client_get_startup_id(ObClient *self)
 {
-    if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
-                        &self->startup_id)))
+    if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
         if (self->group)
-            OBT_PROP_GETS(self->group->leader,
-                          NET_STARTUP_ID, utf8, &self->startup_id);
+            OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
+                               &self->startup_id);
 }
 
 static void client_get_area(ObClient *self)
@@ -1969,15 +2024,14 @@ void client_update_title(ObClient *self)
     g_free(self->original_title);
 
     /* try netwm */
-    if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
+    if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
         /* try old x stuff */
-        if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
-              || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
+        if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
             if (self->transient) {
-    /*
-    GNOME alert windows are not given titles:
-    http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
-    */
+   /*
+   GNOME alert windows are not given titles:
+   http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+   */
                 data = g_strdup("");
             } else
                 data = g_strdup(_("Unnamed Window"));
@@ -2000,7 +2054,7 @@ void client_update_title(ObClient *self)
         g_free(data);
     }
 
-    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
+    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
     self->title = visible;
 
     if (self->frame)
@@ -2011,10 +2065,9 @@ void client_update_title(ObClient *self)
     g_free(self->icon_title);
 
     /* try netwm */
-    if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
+    if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
         /* try old x stuff */
-        if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
-              OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
+        if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
             data = g_strdup(self->title);
 
     if (self->client_machine) {
@@ -2032,7 +2085,7 @@ void client_update_title(ObClient *self)
         g_free(data);
     }
 
-    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
+    OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
     self->icon_title = visible;
 }
 
@@ -2095,7 +2148,6 @@ void client_update_icons(ObClient *self)
     guint num;
     guint32 *data;
     guint w, h, i, j;
-    guint num_seen;  /* number of icons present */
     RrImage *img;
 
     img = NULL;
@@ -2108,13 +2160,15 @@ void client_update_icons(ObClient *self)
     if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
         /* figure out how many valid icons are in here */
         i = 0;
-        num_seen = 0;
         while (i + 2 < num) { /* +2 is to make sure there is a w and h */
             w = data[i++];
             h = data[i++];
             /* watch for the data being too small for the specified size,
                or for zero sized icons. */
-            if (i + w*h > num || w == 0 || h == 0) break;
+            if (i + w*h > num || w == 0 || h == 0) {
+                i += w*h;
+                continue;
+            }
 
             /* convert it to the right bit order for ObRender */
             for (j = 0; j < w*h; ++j)
@@ -2124,29 +2178,13 @@ void client_update_icons(ObClient *self)
                     (((data[i+j] >>  8) & 0xff) << RrDefaultGreenOffset) +
                     (((data[i+j] >>  0) & 0xff) << RrDefaultBlueOffset);
 
-            /* is it in the cache? */
-            img = RrImageCacheFind(ob_rr_icons, &data[i], w, h);
-            if (img) RrImageRef(img); /* own it */
+            /* add it to the image cache as an original */
+            if (!img)
+                img = RrImageNewFromData(ob_rr_icons, &data[i], w, h);
+            else
+                RrImageAddFromData(img, &data[i], w, h);
 
             i += w*h;
-            ++num_seen;
-
-            /* don't bother looping anymore if we already found it in the cache
-               since we'll just use that! */
-            if (img) break;
-        }
-
-        /* if it's not in the cache yet, then add it to the cache now.
-           we have already converted it to the correct bit order above */
-        if (!img && num_seen > 0) {
-            img = RrImageNew(ob_rr_icons);
-            i = 0;
-            for (j = 0; j < num_seen; ++j) {
-                w = data[i++];
-                h = data[i++];
-                RrImageAddPicture(img, &data[i], w, h);
-                i += w*h;
-            }
         }
 
         g_free(data);
@@ -2170,15 +2208,10 @@ void client_update_icons(ObClient *self)
 
                 if (xicon) {
                     if (w > 0 && h > 0) {
-                        /* is this icon in the cache yet? */
-                        img = RrImageCacheFind(ob_rr_icons, data, w, h);
-                        if (img) RrImageRef(img); /* own it */
-
-                        /* if not, then add it */
-                        if (!img) {
-                            img = RrImageNew(ob_rr_icons);
-                            RrImageAddPicture(img, data, w, h);
-                        }
+                        if (!img)
+                            img = RrImageNewFromData(ob_rr_icons, data, w, h);
+                        else
+                            RrImageAddFromData(img, data, w, h);
                     }
 
                     g_free(data);
@@ -2248,19 +2281,14 @@ static void client_get_session_ids(ObClient *self)
         leader = None;
 
     /* get the SM_CLIENT_ID */
-    got = FALSE;
-    if (leader)
-        got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
-    if (!got)
-        OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
+    if (leader && leader != self->window)
+        OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
+    else
+        OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
 
     /* get the WM_CLASS (name and class). make them "" if they are not
        provided */
-    got = FALSE;
-    if (leader)
-        got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
-    if (!got)
-        got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
+    got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
 
     if (got) {
         if (ss[0]) {
@@ -2275,11 +2303,7 @@ static void client_get_session_ids(ObClient *self)
     if (self->class == NULL) self->class = g_strdup("");
 
     /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
-    got = FALSE;
-    if (leader)
-        got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
-    if (!got)
-        got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
+    got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
 
     if (got)
         self->role = s;
@@ -2290,9 +2314,9 @@ static void client_get_session_ids(ObClient *self)
     got = FALSE;
 
     if (leader)
-        got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
+        got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
     if (!got)
-        got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
+        got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
 
     if (got) {
         /* merge/mash them all together */
@@ -2315,9 +2339,9 @@ static void client_get_session_ids(ObClient *self)
     /* get the WM_CLIENT_MACHINE */
     got = FALSE;
     if (leader)
-        got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
+        got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
     if (!got)
-        got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
+        got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
 
     if (got) {
         gchar localhost[128];
@@ -2344,10 +2368,10 @@ static void client_save_app_rule_values(ObClient *self)
 {
     const gchar *type;
 
-    OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role);
-    OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name);
-    OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class);
-    OBT_PROP_SETS(self->window, OB_APP_TITLE, utf8, self->original_title);
+    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_TITLE, self->original_title);
 
     switch (self->type) {
     case OB_CLIENT_TYPE_NORMAL:
@@ -2367,7 +2391,7 @@ static void client_save_app_rule_values(ObClient *self)
     case OB_CLIENT_TYPE_DOCK:
         type = "dock"; break;
     }
-    OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type);
+    OBT_PROP_SETS(self->window, OB_APP_TYPE, type);
 }
 
 static void client_change_wm_state(ObClient *self)
@@ -2484,6 +2508,11 @@ gboolean client_has_parent(ObClient *self)
     return self->parents != NULL;
 }
 
+gboolean client_has_children(ObClient *self)
+{
+    return self->transients != NULL;
+}
+
 gboolean client_is_oldfullscreen(const ObClient *self,
                                  const Rect *area)
 {
@@ -2767,9 +2796,6 @@ static void client_apply_startup_state(ObClient *self,
     self->area = oldarea;
     client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
 
-    /* set the desktop hint, to make sure that it always exists */
-    OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
-
     /* nothing to do for the other states:
        skip_taskbar
        skip_pager
@@ -2849,6 +2875,13 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
        the updated frame dimensions. */
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
 
+    /* cap any X windows at the size of an unsigned short */
+    *w = MIN(*w,
+             G_MAXUSHORT - self->frame->size.left - self->frame->size.right);
+    *h = MIN(*h,
+             G_MAXUSHORT - self->frame->size.top - self->frame->size.bottom);
+
+
     /* gets the frame's position */
     frame_client_gravity(self->frame, x, y);
 
@@ -3957,13 +3990,10 @@ void client_activate(ObClient *self, gboolean desktop,
                      gboolean here, gboolean raise,
                      gboolean unshade, gboolean user)
 {
-    if ((user && (desktop ||
-                  self->desktop == DESKTOP_ALL ||
-                  self->desktop == screen_desktop)) ||
-        client_can_steal_focus(self, desktop, event_time(), CurrentTime))
-    {
+    self = client_focus_target(self);
+
+    if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
         client_present(self, here, raise, unshade);
-    }
     else
         client_hilite(self, TRUE);
 }
@@ -4296,32 +4326,26 @@ void client_find_edge_directional(ObClient *self, ObDirection dir,
     }
 
     /* search for edges of clients */
-    if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) &&
-         !self->max_vert) ||
-        ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) &&
-         !self->max_horz))
-    {
-        for (it = client_list; it; it = g_list_next(it)) {
-            ObClient *cur = it->data;
+    for (it = client_list; it; it = g_list_next(it)) {
+        ObClient *cur = it->data;
 
-            /* skip windows to not bump into */
-            if (cur == self)
-                continue;
-            if (cur->iconic)
-                continue;
-            if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
-                cur->desktop != screen_desktop)
-                continue;
+        /* skip windows to not bump into */
+        if (cur == self)
+            continue;
+        if (cur->iconic)
+            continue;
+        if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+            cur->desktop != screen_desktop)
+            continue;
 
-            ob_debug("trying window %s", cur->title);
+        ob_debug("trying window %s", cur->title);
 
-            detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
-                        my_edge_size, dest, near_edge);
-        }
-        dock_get_area(&dock_area);
-        detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+        detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
                     my_edge_size, dest, near_edge);
     }
+    dock_get_area(&dock_area);
+    detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+                my_edge_size, dest, near_edge);
 
     g_slice_free(Rect, a);
 }
@@ -4508,6 +4532,13 @@ gboolean client_has_group_siblings(ObClient *self)
     return self->group && self->group->members->next;
 }
 
+gboolean client_has_relative(ObClient *self)
+{
+    return client_has_parent(self) ||
+        client_has_group_siblings(self) ||
+        client_has_children(self);
+}
+
 /*! Returns TRUE if the client is running on the same machine as Openbox */
 gboolean client_on_localhost(ObClient *self)
 {
index 47da397..b36bef5 100644 (file)
@@ -657,6 +657,10 @@ RrImage* client_icon(ObClient *self);
   transient for */
 gboolean client_has_parent(ObClient *self);
 
+/*! Return TRUE if the client has some transient children, and FALSE otherwise.
+*/
+gboolean client_has_children(ObClient *self);
+
 /*! Searches a client's immediate parents for a focused window. The function
   does not check for the passed client, only for *ONE LEVEL* of its parents.
   If no focused parent is found, NULL is returned.
@@ -741,6 +745,11 @@ ObClient* client_under_pointer(void);
 
 gboolean client_has_group_siblings(ObClient *self);
 
+/*! Returns TRUE if the client has a transient child, a parent, or a
+  group member.  Returns FALSE otherwise.
+*/
+gboolean client_has_relative(ObClient *self);
+
 /*! Returns TRUE if the client is running on the same machine as Openbox */
 gboolean client_on_localhost(ObClient *self);
 
index 025a683..debd9fb 100644 (file)
@@ -370,7 +370,8 @@ static void parse_per_app_settings(xmlNodePtr node, gpointer d)
             g_free(class);
             g_free(role);
             g_free(title);
-            name = class = role = title = NULL;
+            g_free(type_str);
+            name = class = role = title = type_str = NULL;
         }
 
         app = obt_xml_find_node(app->next, "application");
@@ -389,39 +390,44 @@ static void parse_per_app_settings(xmlNodePtr node, gpointer d)
 
 static void parse_key(xmlNodePtr node, GList *keylist)
 {
-    gchar *key;
+    gchar *keystring, **keys, **key;
     xmlNodePtr n;
     gboolean is_chroot = FALSE;
 
-    if (!obt_xml_attr_string(node, "key", &key))
+    if (!obt_xml_attr_string(node, "key", &keystring))
         return;
 
     obt_xml_attr_bool(node, "chroot", &is_chroot);
 
-    keylist = g_list_append(keylist, key);
+    keys = g_strsplit(keystring, " ", 0);
+    for (key = keys; *key; ++key) {
+        keylist = g_list_append(keylist, *key);
 
-    if ((n = obt_xml_find_node(node->children, "keybind"))) {
-        while (n) {
-            parse_key(n, keylist);
-            n = obt_xml_find_node(n->next, "keybind");
+        if ((n = obt_xml_find_node(node->children, "keybind"))) {
+            while (n) {
+                parse_key(n, keylist);
+                n = obt_xml_find_node(n->next, "keybind");
+            }
         }
-    }
-    else if ((n = obt_xml_find_node(node->children, "action"))) {
-        while (n) {
-            ObActionsAct *action;
-
-            action = actions_parse(n);
-            if (action)
-                keyboard_bind(keylist, action);
-            n = obt_xml_find_node(n->next, "action");
+        else if ((n = obt_xml_find_node(node->children, "action"))) {
+            while (n) {
+                ObActionsAct *action;
+
+                action = actions_parse(n);
+                if (action)
+                    keyboard_bind(keylist, action);
+                n = obt_xml_find_node(n->next, "action");
+            }
         }
-    }
 
-    if (is_chroot)
-        keyboard_chroot(keylist);
 
-    g_free(key);
-    keylist = g_list_delete_link(keylist, g_list_last(keylist));
+        if (is_chroot)
+            keyboard_chroot(keylist);
+        keylist = g_list_delete_link(keylist, g_list_last(keylist));
+    }
+
+    g_strfreev(keys);
+    g_free(keystring);
 }
 
 static void parse_keyboard(xmlNodePtr node, gpointer d)
@@ -576,6 +582,8 @@ static void parse_placement(xmlNodePtr node, gpointer d)
             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
         else if (obt_xml_node_contains(n, "mouse"))
             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
+        else if (obt_xml_node_contains(n, "any"))
+            config_place_monitor = OB_PLACE_MONITOR_ANY;
     }
     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
         config_primary_monitor_index = obt_xml_node_int(n);
@@ -997,7 +1005,7 @@ void config_startup(ObtXmlInst *i)
 
     config_place_policy = OB_PLACE_POLICY_SMART;
     config_place_center = TRUE;
-    config_place_monitor = OB_PLACE_MONITOR_ANY;
+    config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
 
     config_primary_monitor_index = 1;
     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
index 8423e8a..358b090 100644 (file)
@@ -135,7 +135,7 @@ static void prompt_handler(const gchar *log_domain, GLogLevelFlags log_level,
                            const gchar *message, gpointer data)
 {
     if (ob_state() == OB_STATE_RUNNING)
-        prompt_show_message(message, _("Openbox"), _("Close"));
+        prompt_show_message(message, "Openbox", _("Close"));
     else
         log_handler(log_domain, log_level, message, data);
 }
index 06d2280..c26eee6 100644 (file)
@@ -145,7 +145,7 @@ void dock_manage(Window icon_win, Window name_win)
     app->name_win = name_win;
     app->icon_win = icon_win;
 
-    if (OBT_PROP_GETSS(app->name_win, WM_CLASS, locale, &data)) {
+    if (OBT_PROP_GETSS_TYPE(app->name_win, WM_CLASS, STRING_NO_CC, &data)) {
         if (data[0]) {
             app->name = g_strdup(data[0]);
             if (data[1])
@@ -551,6 +551,8 @@ void dock_configure(void)
         dock->area.height += ob_rr_theme->obwidth * 2;
     }
 
+    /* screen_resize() depends on this function to call screen_update_areas(),
+       so if this changes, also update screen_resize(). */
     screen_update_areas();
 }
 
index b59707b..2dde132 100644 (file)
@@ -273,7 +273,7 @@ static void event_set_curtime(XEvent *e)
        which can happen if the clock goes backwards, we erase the last
        specified user_time */
     if (t && event_last_user_time && event_time_after(event_last_user_time, t))
-        event_last_user_time = CurrentTime;
+        event_reset_user_time();
 
     event_sourcetime = CurrentTime;
     event_curtime = t;
@@ -1103,7 +1103,9 @@ static void event_handle_client(ObClient *client, XEvent *e)
             if (grab_on_keyboard())
                 break;
             if (e->xcrossing.mode == NotifyGrab ||
-                e->xcrossing.mode == NotifyUngrab ||
+                (e->xcrossing.mode == NotifyUngrab &&
+                 /* ungrab enters are used when _under_ mouse is being used */
+                 !(config_focus_follow && config_focus_under_mouse)) ||
                 /*ignore enters when we're already in the window */
                 e->xcrossing.detail == NotifyInferior)
             {
@@ -1443,9 +1445,15 @@ static void event_handle_client(ObClient *client, XEvent *e)
                 ob_debug_type(OB_DEBUG_APP_BUGS,
                               "_NET_ACTIVE_WINDOW message for window %s is "
                               "missing source indication", client->title);
-            client_activate(client, FALSE, FALSE, TRUE, TRUE,
-                            (e->xclient.data.l[0] == 0 ||
-                             e->xclient.data.l[0] == 2));
+            /* TODO(danakj) This should use
+               (e->xclient.data.l[0] == 0 ||
+                e->xclient.data.l[0] == 2)
+               to determine if a user requested the activation, however GTK+
+               applications seem unable to make this distinction ever
+               (including panels such as xfce4-panel and gnome-panel).
+               So we are left just assuming all activations are from the user.
+            */
+            client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE);
         } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
             ob_debug("net_wm_moveresize for 0x%lx direction %d",
                      client->window, e->xclient.data.l[2]);
@@ -1695,10 +1703,12 @@ static void event_handle_client(ObClient *client, XEvent *e)
                         client->shaped = ((XShapeEvent*)e)->shaped;
                         kind = ShapeBounding;
                         break;
+#ifdef ShapeInput
                     case ShapeInput:
                         client->shaped_input = ((XShapeEvent*)e)->shaped;
                         kind = ShapeInput;
                         break;
+#endif
                     default:
                         g_assert_not_reached();
                 }
@@ -1792,8 +1802,9 @@ static gboolean event_handle_menu_input(XEvent *ev)
     if (ev->type == ButtonRelease || ev->type == ButtonPress) {
         ObMenuEntryFrame *e;
 
-        if (menu_hide_delay_reached() &&
-            (ev->xbutton.button < 4 || ev->xbutton.button > 5))
+        if ((ev->xbutton.button < 4 || ev->xbutton.button > 5) &&
+            ((ev->type == ButtonRelease && menu_hide_delay_reached()) ||
+             ev->type == ButtonPress))
         {
             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
                                             ev->xbutton.y_root)))
@@ -1804,23 +1815,11 @@ static gboolean event_handle_menu_input(XEvent *ev)
                 if (ev->type == ButtonRelease)
                     menu_entry_frame_execute(e, ev->xbutton.state);
             }
-            else if (ev->type == ButtonRelease)
+            else
                 menu_frame_hide_all();
         }
         ret = TRUE;
     }
-    else if (ev->type == MotionNotify) {
-        ObMenuFrame *f;
-        ObMenuEntryFrame *e;
-
-        if ((e = menu_entry_frame_under(ev->xmotion.x_root,
-                                        ev->xmotion.y_root)))
-            if (!(f = find_active_menu()) ||
-                f == e->frame ||
-                f->parent == e->frame ||
-                f->child == e->frame)
-                menu_frame_select(e->frame, e, FALSE);
-    }
     else if (ev->type == KeyPress || ev->type == KeyRelease) {
         guint mods;
         ObMenuFrame *frame;
@@ -1860,14 +1859,23 @@ static gboolean event_handle_menu_input(XEvent *ev)
                 ret = TRUE;
             }
 
-            else if (sym == XK_Right) {
-                /* Right goes to the selected submenu */
-                if (frame->selected &&
-                    frame->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
-                {
-                    /* make sure it is visible */
-                    menu_frame_select(frame, frame->selected, TRUE);
-                    menu_frame_select_next(frame->child);
+            else if (sym == XK_Right || sym == XK_Return || sym == XK_KP_Enter)
+            {
+                /* Right and enter goes to the selected submenu.
+                   Enter executes instead if it's not on a submenu. */
+
+                if (frame->selected) {
+                    const ObMenuEntryType t = frame->selected->entry->type;
+
+                    if (t == OB_MENU_ENTRY_TYPE_SUBMENU) {
+                        /* make sure it is visible */
+                        menu_frame_select(frame, frame->selected, TRUE);
+                        /* move focus to the child menu */
+                        menu_frame_select_next(frame->child);
+                    }
+                    else if (sym != XK_Right) {
+                        frame->press_doexec = TRUE;
+                    }
                 }
                 ret = TRUE;
             }
@@ -1892,11 +1900,6 @@ static gboolean event_handle_menu_input(XEvent *ev)
                 ret = TRUE;
             }
 
-            else if (sym == XK_Return || sym == XK_KP_Enter) {
-                frame->press_doexec = TRUE;
-                ret = TRUE;
-            }
-
             /* keyboard accelerator shortcuts. (if it was a valid key) */
             else if (frame->entries &&
                      (unikey =
@@ -1942,8 +1945,15 @@ static gboolean event_handle_menu_input(XEvent *ev)
                 if (found) {
                     menu_frame_select(frame, found, TRUE);
 
-                    if (num_found == 1)
-                        frame->press_doexec = TRUE;
+                    if (num_found == 1) {
+                        if (found->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+                            /* move focus to the child menu */
+                            menu_frame_select_next(frame->child);
+                        }
+                        else {
+                            frame->press_doexec = TRUE;
+                        }
+                    }
                     ret = TRUE;
                 }
             }
@@ -1958,9 +1968,7 @@ static gboolean event_handle_menu_input(XEvent *ev)
                 frame->got_press &&
                 frame->press_doexec)
             {
-                if (frame->child)
-                    menu_frame_select_next(frame->child);
-                else if (frame->selected)
+                if (frame->selected)
                     menu_entry_frame_execute(frame->selected, ev->xkey.state);
             }
         }
@@ -2213,7 +2221,7 @@ gboolean event_time_after(guint32 t1, guint32 t2)
 gboolean find_timestamp(XEvent *e, gpointer data)
 {
     const Time t = event_get_timestamp(e);
-    if (t > event_curtime) {
+    if (t && t >= event_curtime) {
         event_curtime = t;
         return TRUE;
     }
@@ -2257,3 +2265,13 @@ void event_reset_time(void)
 {
     next_time();
 }
+
+void event_update_user_time(void)
+{
+    event_last_user_time = event_time();
+}
+
+void event_reset_user_time(void)
+{
+    event_last_user_time = CurrentTime;
+}
index f0e2d39..4d9984e 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 (500) /* 0.5 seconds */
+#define OB_EVENT_USER_TIME_DELAY (1000) /* 1.0 seconds */
 
 /*! The last user-interaction time, as given by the clients */
 extern Time event_last_user_time;
@@ -76,7 +76,16 @@ void event_reset_time(void);
 /*! A time at which an event happened that caused this current event to be
   generated.  This is a user-provided time and not to be trusted.
   Returns CurrentTime if there was no source time provided.
- */
+*/
 Time event_source_time(void);
 
+/*! Update the timestamp for when the user has last used the focused window.
+  This updates the timestamp to the time of the last event, given by
+  event_time().
+*/
+void event_update_user_time(void);
+
+/*! Reset the timestamp for when the user has last used the focused window. */
+void event_reset_user_time(void);
+
 #endif
index 8c02361..a4626bf 100644 (file)
@@ -100,6 +100,10 @@ void focus_set_client(ObClient *client)
         active = client ? client->window : None;
         OBT_PROP_SET32(obt_root(ob_screen), NET_ACTIVE_WINDOW, WINDOW, active);
     }
+
+    /* when focus is moved to a new window, the last_user_time timestamp would
+       no longer be valid, as it applies for the focused window */
+    event_reset_user_time();
 }
 
 static ObClient* focus_fallback_target(gboolean allow_refocus,
index 39d704c..6d4cc2a 100644 (file)
@@ -93,7 +93,8 @@ 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, TRUE, TRUE, TRUE);
+                        TRUE, TRUE, OB_FOCUS_CYCLE_POPUP_MODE_NONE,
+                        TRUE, TRUE);
     }
 }
 
index 8a9a5a6..3aa3ab7 100644 (file)
@@ -276,9 +276,14 @@ void frame_adjust_shape_kind(ObFrame *self, int kind)
 {
     gint num;
     XRectangle xrect[2];
+    gboolean shaped;
 
-    if (!((kind == ShapeBounding && self->client->shaped) ||
-          (kind == ShapeInput && self->client->shaped_input))) {
+    shaped = (kind == ShapeBounding && self->client->shaped);
+#ifdef ShapeInput
+    shaped |= (kind == ShapeInput && self->client->shaped_input);
+#endif
+
+    if (!shaped) {
         /* clear the shape on the frame window */
         XShapeCombineMask(obt_display, self->window, kind,
                           self->size.left,
@@ -323,8 +328,10 @@ void frame_adjust_shape(ObFrame *self)
 {
 #ifdef SHAPE
   frame_adjust_shape_kind(self, ShapeBounding);
+#ifdef ShapeInput
   frame_adjust_shape_kind(self, ShapeInput);
 #endif
+#endif
 }
 
 void frame_adjust_area(ObFrame *self, gboolean moved,
@@ -953,7 +960,7 @@ void frame_adjust_state(ObFrame *self)
 void frame_adjust_focus(ObFrame *self, gboolean hilite)
 {
     ob_debug_type(OB_DEBUG_FOCUS,
-                  "Frame for 0x%x has focus: %d\n",
+                  "Frame for 0x%x has focus: %d",
                   self->client->window, hilite);
     self->focused = hilite;
     self->need_render = TRUE;
index 374aeec..daea0b6 100644 (file)
@@ -49,6 +49,7 @@ static GHashTable *menu_hash = NULL;
 static ObtXmlInst *menu_parse_inst;
 static ObMenuParseState menu_parse_state;
 static gboolean menu_can_hide = FALSE;
+static guint menu_timeout_id = 0;
 
 static void menu_destroy_hash_value(ObMenu *self);
 static void parse_menu_item(xmlNodePtr node, gpointer data);
@@ -293,19 +294,7 @@ static void parse_menu_item(xmlNodePtr node,  gpointer data)
             if (config_menu_show_icons &&
                 obt_xml_attr_string(node, "icon", &icon))
             {
-                RrImage *ic;
-
-                ic = RrImageCacheFindName(ob_rr_icons, icon);
-                if (ic)
-                    RrImageRef(ic);
-                else {
-                    ic = RrImageNew(ob_rr_icons);
-                    if (!RrImageAddPictureName(ic, icon)) {
-                        RrImageUnref(ic); /* no need to keep it around */
-                        ic = NULL;
-                    }
-                }
-                e->data.normal.icon = ic;
+                e->data.normal.icon = RrImageNewFromName(ob_rr_icons, icon);
 
                 if (e->data.normal.icon)
                     e->data.normal.icon_alpha = 0xff;
@@ -337,6 +326,8 @@ static void parse_menu(xmlNodePtr node, gpointer data)
     ObMenuParseState *state = data;
     gchar *name = NULL, *title = NULL, *script = NULL;
     ObMenu *menu;
+    ObMenuEntry *e;
+    gchar *icon;
 
     if (!obt_xml_attr_string(node, "id", &name))
         goto parse_menu_fail;
@@ -360,8 +351,20 @@ static void parse_menu(xmlNodePtr node, gpointer data)
         }
     }
 
-    if (state->parent)
-        menu_add_submenu(state->parent, -1, name);
+    if (state->parent) {
+        e = menu_add_submenu(state->parent, -1, name);
+
+        if (config_menu_show_icons &&
+            obt_xml_attr_string(node, "icon", &icon))
+        {
+            e->data.submenu.icon = RrImageNewFromName(ob_rr_icons, icon);
+
+            if (e->data.submenu.icon)
+                e->data.submenu.icon_alpha = 0xff;
+
+            g_free(icon);
+        }
+    }
 
 parse_menu_fail:
     g_free(name);
@@ -437,6 +440,7 @@ void menu_free(ObMenu *menu)
 static gboolean menu_hide_delay_func(gpointer data)
 {
     menu_can_hide = TRUE;
+    menu_timeout_id = 0;
     return FALSE; /* no repeat */
 }
 
@@ -486,10 +490,11 @@ void menu_show(gchar *name, gint x, gint y, gboolean mouse, ObClient *client)
             menu_can_hide = TRUE;
         else {
             menu_can_hide = FALSE;
-            g_timeout_add_full(G_PRIORITY_DEFAULT,
-                               config_menu_hide_delay,
-                               menu_hide_delay_func,
-                               NULL, NULL);
+            if (menu_timeout_id) g_source_remove(menu_timeout_id);
+            menu_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+                                                 config_menu_hide_delay,
+                                                 menu_hide_delay_func,
+                                                 NULL, NULL);
         }
     }
 }
@@ -543,6 +548,7 @@ void menu_entry_unref(ObMenuEntry *self)
             }
             break;
         case OB_MENU_ENTRY_TYPE_SUBMENU:
+            RrImageUnref(self->data.submenu.icon);
             g_free(self->data.submenu.name);
             break;
         case OB_MENU_ENTRY_TYPE_SEPARATOR:
index c0cc199..76cc238 100644 (file)
@@ -103,6 +103,10 @@ typedef enum
 } ObMenuEntryType;
 
 struct _ObNormalMenuEntry {
+    /* Icon stuff.  If you set this, make sure you RrImageRef() it too. */
+    RrImage *icon;
+    gint     icon_alpha;
+
     gchar *label;
     /*! The shortcut key that would be used to activate this menu entry */
     gunichar shortcut;
@@ -117,10 +121,6 @@ struct _ObNormalMenuEntry {
     /* List of ObActions */
     GSList *actions;
 
-    /* Icon stuff.  If you set this, make sure you RrImageRef() it too. */
-    RrImage *icon;
-    gint     icon_alpha;
-
     /* Mask icon */
     RrPixmapMask *mask;
     RrColor *mask_normal_color;
@@ -132,8 +132,13 @@ struct _ObNormalMenuEntry {
 };
 
 struct _ObSubmenuMenuEntry {
+    /* Icon stuff.  If you set this, make sure you RrImageRef() it too. */
+    RrImage *icon;
+    gint     icon_alpha;
+
     gchar *name;
     ObMenu *submenu;
+    
     guint show_from;
 };
 
index 5708cdf..6110045 100644 (file)
@@ -181,7 +181,8 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
     self->text = createWindow(self->window, 0, NULL);
     g_hash_table_insert(menu_frame_map, &self->window, self);
     g_hash_table_insert(menu_frame_map, &self->text, self);
-    if (entry->type == OB_MENU_ENTRY_TYPE_NORMAL) {
+    if ((entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+        (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
         self->icon = createWindow(self->window, 0, NULL);
         g_hash_table_insert(menu_frame_map, &self->icon, self);
     }
@@ -209,7 +210,8 @@ static void menu_entry_frame_free(ObMenuEntryFrame *self)
         XDestroyWindow(obt_display, self->window);
         g_hash_table_remove(menu_frame_map, &self->text);
         g_hash_table_remove(menu_frame_map, &self->window);
-        if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) {
+        if ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+            (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
             XDestroyWindow(obt_display, self->icon);
             g_hash_table_remove(menu_frame_map, &self->icon);
         }
@@ -324,11 +326,18 @@ void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y,
                                gint *dx, gint *dy)
 {
     const Rect *a = NULL;
-    gint pos, half;
+    Rect search = self->area;
+    gint pos, half, monitor;
 
     *dx = *dy = 0;
+    RECT_SET_POINT(search, x, y);
 
-    a = screen_physical_area_monitor(screen_find_monitor_point(x, y));
+    if (self->parent)
+        monitor = self->parent->monitor;
+    else
+        monitor = screen_find_monitor(&search);
+
+    a = screen_physical_area_monitor(monitor);
 
     half = g_list_length(self->entries) / 2;
     pos = g_list_index(self->entries, self->selected);
@@ -515,7 +524,8 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
         g_assert_not_reached();
     }
 
-    if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+    if (((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+         (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) &&
         self->entry->data.normal.icon)
     {
         RrAppearance *clear;
index 2f68395..3a98db3 100644 (file)
@@ -21,6 +21,7 @@
 #include "framerender.h"
 #include "screen.h"
 #include "client.h"
+#include "focus.h"
 #include "frame.h"
 #include "openbox.h"
 #include "resist.h"
@@ -63,8 +64,8 @@ static ObDirection edge_warp_dir = -1;
 static gboolean edge_warp_odd = FALSE;
 static guint edge_warp_timer = 0;
 static ObDirection key_resize_edge = -1;
-#ifdef SYNC
 static guint waiting_for_sync;
+#ifdef SYNC
 static guint sync_timer = 0;
 #endif
 
@@ -262,6 +263,7 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
     cur_h = start_ch;
 
     moveresize_in_progress = TRUE;
+    waiting_for_sync = 0;
 
 #ifdef SYNC
     if (config_resize_redraw && !moving && obt_display_extension_sync &&
@@ -300,8 +302,6 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
                                             XSyncCADelta |
                                             XSyncCAEvents,
                                             &aa);
-
-        waiting_for_sync = 0;
     }
 #endif
 }
@@ -1084,5 +1084,9 @@ gboolean moveresize_event(XEvent *e)
         used = TRUE;
     }
 #endif
+
+    if (used && moveresize_client == focus_client)
+        event_update_user_time();
+
     return used;
 }
index ae0ba8a..0782794 100644 (file)
@@ -196,7 +196,7 @@ gint main(gint argc, gchar **argv)
 
     /* set the DISPLAY environment variable for any lauched children, to the
        display we're using, so they open in the right place. */
-    setenv("DISPLAY", DisplayString(obt_display), TRUE);
+    g_setenv("DISPLAY", DisplayString(obt_display), TRUE);
 
     /* create available cursors */
     cursors[OB_CURSOR_NONE] = None;
@@ -258,8 +258,7 @@ gint main(gint argc, gchar **argv)
                     gchar *p = g_filename_to_utf8(config_file, -1,
                                                   NULL, NULL, NULL);
                     if (p)
-                        OBT_PROP_SETS(obt_root(ob_screen), OB_CONFIG_FILE,
-                                      utf8, p);
+                        OBT_PROP_SETS(obt_root(ob_screen), OB_CONFIG_FILE, p);
                     g_free(p);
                 }
                 else
@@ -286,8 +285,8 @@ gint main(gint argc, gchar **argv)
                 if (ob_rr_theme == NULL)
                     ob_exit_with_error(_("Unable to load a theme."));
 
-                OBT_PROP_SETS(obt_root(ob_screen),
-                              OB_THEME, utf8, ob_rr_theme->name);
+                OBT_PROP_SETS(obt_root(ob_screen), OB_THEME,
+                              ob_rr_theme->name);
             }
 
             if (reconfigure) {
@@ -356,11 +355,11 @@ gint main(gint argc, gchar **argv)
                 }
             }
 
-            reconfigure = FALSE;
-
             ob_set_state(OB_STATE_RUNNING);
 
-            if (startup_cmd) run_startup_cmd();
+            if (!reconfigure && startup_cmd) run_startup_cmd();
+
+            reconfigure = FALSE;
 
             /* look for parsing errors */
             {
@@ -590,12 +589,12 @@ static void parse_env(void)
     const gchar *id;
 
     /* unset this so we don't pass it on unknowingly */
-    unsetenv("DESKTOP_STARTUP_ID");
+    g_unsetenv("DESKTOP_STARTUP_ID");
 
     /* this is how gnome-session passes in a session client id */
     id = g_getenv("DESKTOP_AUTOSTART_ID");
     if (id) {
-        unsetenv("DESKTOP_AUTOSTART_ID");
+        g_unsetenv("DESKTOP_AUTOSTART_ID");
         if (ob_sm_id) g_free(ob_sm_id);
         ob_sm_id = g_strdup(id);
         ob_debug_type(OB_DEBUG_SM,
@@ -630,7 +629,7 @@ static void parse_args(gint *argc, gchar **argv)
         }
         else if (!strcmp(argv[i], "--startup")) {
             if (i == *argc - 1) /* no args left */
-                g_printerr(_("--startup requires an argument\n"));
+                g_printerr(_("%s requires an argument\n"), "--startup");
             else {
                 /* this will be in the current locale encoding, which is
                    what we want */
@@ -664,7 +663,7 @@ static void parse_args(gint *argc, gchar **argv)
         }
         else if (!strcmp(argv[i], "--config-file")) {
             if (i == *argc - 1) /* no args left */
-                g_printerr(_("--config-file requires an argument\n"));
+                g_printerr(_("%s requires an argument\n"), "--config-file");
             else {
                 /* this will be in the current locale encoding, which is
                    what we want */
index d9919d0..d56adfc 100644 (file)
 
 extern ObDock *dock;
 
-static void add_choice(guint *choice, guint mychoice)
+static Rect *pick_pointer_head(ObClient *c)
 {
-    guint i;
-    for (i = 0; i < screen_num_monitors; ++i) {
-        if (choice[i] == mychoice)
-            return;
-        else if (choice[i] == screen_num_monitors) {
-            choice[i] = mychoice;
-            return;
-        }
-    }
+    return screen_area(c->desktop, screen_monitor_pointer(), NULL);
 }
 
-static Rect *pick_pointer_head(ObClient *c)
+/* use the following priority lists for pick_head()
+
+   When a window is being placed in the FOREGROUND, use a monitor chosen in
+   the following order:
+   1. same monitor as parent
+   2. primary monitor if placement=PRIMARY
+      active monitor if placement=ACTIVE
+      pointer monitor if placement=MOUSE
+   3. primary monitor
+   4. other monitors where the window has group members on the same desktop
+   5. other monitors where the window has group members on other desktops
+   6. other monitors
+
+   When a window is being placed in the BACKGROUND, use a monitor chosen in the
+   following order:
+   1. same monitor as parent
+   2. other monitors where the window has group members on the same desktop
+    2a. primary monitor in this set
+    2b. other monitors in this set
+   3. other monitors where the window has group members on other desktops
+    3a. primary monitor in this set
+    3b. other monitors in this set
+   4. other monitors
+    4a. primary monitor in this set
+    4b. other monitors in this set
+*/
+
+/*! One for each possible head, used to sort them in order of precedence. */
+typedef struct {
+    guint monitor;
+    guint flags;
+} ObPlaceHead;
+
+/*! Flags for ObPlaceHead */
+enum {
+    HEAD_PARENT = 1 << 0, /* parent's monitor */
+    HEAD_PLACED = 1 << 1, /* chosen monitor by placement */
+    HEAD_PRIMARY = 1 << 2, /* primary monitor */
+    HEAD_GROUP_DESK = 1 << 3, /* has a group member on the same desktop */
+    HEAD_GROUP = 1 << 4, /* has a group member on another desktop */
+};
+
+gint cmp_foreground(const void *a, const void *b)
 {
-    return screen_area(c->desktop, screen_monitor_pointer(), NULL);
+    const ObPlaceHead *h1 = a;
+    const ObPlaceHead *h2 = b;
+    gint i = 0;
+
+    if (h1->monitor == h2->monitor) return 0;
+
+    if (h1->flags & HEAD_PARENT) --i;
+    if (h2->flags & HEAD_PARENT) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_PLACED) --i;
+    if (h2->flags & HEAD_PLACED) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_PRIMARY) --i;
+    if (h2->flags & HEAD_PRIMARY) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_GROUP_DESK) --i;
+    if (h2->flags & HEAD_GROUP_DESK) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_GROUP) --i;
+    if (h2->flags & HEAD_GROUP) ++i;
+    if (i) return i;
+
+    return h1->monitor - h2->monitor;
+}
+
+gint cmp_background(const void *a, const void *b)
+{
+    const ObPlaceHead *h1 = a;
+    const ObPlaceHead *h2 = b;
+    gint i = 0;
+
+    if (h1->monitor == h2->monitor) return 0;
+
+    if (h1->flags & HEAD_PARENT) --i;
+    if (h2->flags & HEAD_PARENT) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_GROUP_DESK || h2->flags & HEAD_GROUP_DESK) {
+        if (h1->flags & HEAD_GROUP_DESK) --i;
+        if (h2->flags & HEAD_GROUP_DESK) ++i;
+        if (i) return i;
+        if (h1->flags & HEAD_PRIMARY) --i;
+        if (h2->flags & HEAD_PRIMARY) ++i;
+        if (i) return i;
+    }
+
+    if (h1->flags & HEAD_GROUP || h2->flags & HEAD_GROUP) {
+        if (h1->flags & HEAD_GROUP) --i;
+        if (h2->flags & HEAD_GROUP) ++i;
+        if (i) return i;
+        if (h1->flags & HEAD_PRIMARY) --i;
+        if (h2->flags & HEAD_PRIMARY) ++i;
+        if (i) return i;
+    }
+
+    if (h1->flags & HEAD_PRIMARY) --i;
+    if (h2->flags & HEAD_PRIMARY) ++i;
+    if (i) return i;
+
+    return h1->monitor - h2->monitor;
 }
 
 /*! Pick a monitor to place a window on. */
-static Rect **pick_head(ObClient *c)
+static Rect *pick_head(ObClient *c, gboolean foreground)
 {
-    Rect **area;
-    guint *choice;
+    Rect *area;
+    ObPlaceHead *choice;
     guint i;
-    gint px, py;
     ObClient *p;
+    GSList *it;
 
-    area = g_new(Rect*, screen_num_monitors);
-    choice = g_new(guint, screen_num_monitors);
-    for (i = 0; i < screen_num_monitors; ++i)
-        choice[i] = screen_num_monitors; /* make them all invalid to start */
-
-    /* try direct parent first */
-    if ((p = client_direct_parent(c))) {
-        add_choice(choice, client_monitor(p));
-        ob_debug("placement adding choice %d for parent",
-                 client_monitor(p));
+    choice = g_new(ObPlaceHead, screen_num_monitors);
+    for (i = 0; i < screen_num_monitors; ++i) {
+        choice[i].monitor = i;
+        choice[i].flags = 0;
     }
 
-    /* more than one window in its group (more than just this window) */
-    if (client_has_group_siblings(c)) {
-        GSList *it;
-
-        /* try on the client's desktop */
-        for (it = c->group->members; it; it = g_slist_next(it)) {
-            ObClient *itc = it->data;
-            if (itc != c &&
-                (itc->desktop == c->desktop ||
-                 itc->desktop == DESKTOP_ALL || c->desktop == DESKTOP_ALL))
-            {
-                add_choice(choice, client_monitor(it->data));
-                ob_debug("placement adding choice %d for group sibling",
-                         client_monitor(it->data));
-            }
-        }
-
-        /* try on all desktops */
+    /* find monitors with group members */
+    if (c->group) {
         for (it = c->group->members; it; it = g_slist_next(it)) {
             ObClient *itc = it->data;
             if (itc != c) {
-                add_choice(choice, client_monitor(it->data));
-                ob_debug("placement adding choice %d for group sibling on "
-                         "another desktop", client_monitor(it->data));
+                guint m = client_monitor(itc);
+
+                if (m < screen_num_monitors) {
+                    if (screen_compare_desktops(itc->desktop, c->desktop))
+                        choice[m].flags |= HEAD_GROUP_DESK;
+                    else
+                        choice[m].flags |= HEAD_GROUP;
+                }
             }
         }
     }
 
-    /* skip this if placing by the mouse position */
-    if (focus_client && client_normal(focus_client) &&
-        config_place_monitor != OB_PLACE_MONITOR_MOUSE)
-    {
-        add_choice(choice, client_monitor(focus_client));
-        ob_debug("placement adding choice %d for normal focused window",
-                 client_monitor(focus_client));
+    i = screen_monitor_primary(FALSE);
+    if (i < screen_num_monitors) {
+        choice[i].flags |= HEAD_PRIMARY;
+        if (config_place_monitor == OB_PLACE_MONITOR_PRIMARY)
+            choice[i].flags |= HEAD_PLACED;
     }
 
-    screen_pointer_pos(&px, &py);
-
-    for (i = 0; i < screen_num_monitors; i++) {
-        const Rect *monitor = screen_physical_area_monitor(i);
-        gboolean contain = RECT_CONTAINS(*monitor, px, py);
-        if (contain) {
-            add_choice(choice, i);
-            ob_debug("placement adding choice %d for mouse pointer", i);
-            break;
-        }
+    /* direct parent takes highest precedence */
+    if ((p = client_direct_parent(c))) {
+        i = client_monitor(p);
+        if (i < screen_num_monitors)
+            choice[i].flags |= HEAD_PARENT;
     }
 
-    /* add any leftover choices */
-    for (i = 0; i < screen_num_monitors; ++i)
-        add_choice(choice, i);
+    qsort(choice, screen_num_monitors, sizeof(ObPlaceHead),
+          foreground ? cmp_foreground : cmp_background);
 
+    /* save the areas of the monitors in order of their being chosen */
     for (i = 0; i < screen_num_monitors; ++i)
-        area[i] = screen_area(c->desktop, choice[i], NULL);
+    {
+        ob_debug("placement choice %d is monitor %d", i, choice[i].monitor);
+        if (choice[i].flags & HEAD_PARENT)
+            ob_debug("  - parent on monitor");
+        if (choice[i].flags & HEAD_PLACED)
+            ob_debug("  - placement choice");
+        if (choice[i].flags & HEAD_PRIMARY)
+            ob_debug("  - primary monitor");
+        if (choice[i].flags & HEAD_GROUP_DESK)
+            ob_debug("  - group on same desktop");
+        if (choice[i].flags & HEAD_GROUP)
+            ob_debug("  - group on other desktop");
+    }
+
+    area = screen_area(c->desktop, choice[0].monitor, NULL);
 
     g_free(choice);
 
+    /* return the area for the chosen monitor */
     return area;
 }
 
-static gboolean place_random(ObClient *client, gint *x, gint *y)
+static gboolean place_random(ObClient *client, Rect *area, gint *x, gint *y)
 {
     gint l, r, t, b;
-    Rect **areas;
-    guint i;
 
-    areas = pick_head(client);
-    i = (config_place_monitor != OB_PLACE_MONITOR_ANY) ?
-        0 : g_random_int_range(0, screen_num_monitors);
+    ob_debug("placing randomly");
 
-    l = areas[i]->x;
-    t = areas[i]->y;
-    r = areas[i]->x + areas[i]->width - client->frame->area.width;
-    b = areas[i]->y + areas[i]->height - client->frame->area.height;
+    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 = areas[i]->x;
+    else       *x = area->x;
     if (b > t) *y = g_random_int_range(t, b + 1);
-    else       *y = areas[i]->y;
-
-    for (i = 0; i < screen_num_monitors; ++i)
-        g_slice_free(Rect, areas[i]);
-    g_free(areas);
+    else       *y = area->y;
 
     return TRUE;
 }
@@ -228,118 +308,108 @@ enum {
     IGNORE_END        = 7
 };
 
-static gboolean place_nooverlap(ObClient *c, gint *x, gint *y)
+static gboolean place_nooverlap(ObClient *c, Rect *area, gint *x, gint *y)
 {
-    Rect **areas;
     gint ignore;
     gboolean ret;
     gint maxsize;
     GSList *spaces = NULL, *sit, *maxit;
-    guint i;
 
-    areas = pick_head(c);
+    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++) {
-        /* try all monitors in order of preference, but only the first one
-           if config_place_monitor is MOUSE or ACTIVE */
-        for (i = 0; (i < (config_place_monitor != OB_PLACE_MONITOR_ANY ?
-                          1 : screen_num_monitors) && !ret); ++i)
-        {
-            GList *it;
-
-            /* add the whole monitor */
-            spaces = area_add(spaces, areas[i]);
-
-            /* 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);
+        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);
-            }
+        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;
+        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 (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;
+        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;
+            /* 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);
-            }
+        while (spaces) {
+            g_slice_free(Rect, spaces->data);
+            spaces = g_slist_delete_link(spaces, spaces);
         }
     }
 
-    for (i = 0; i < screen_num_monitors; ++i)
-        g_slice_free(Rect, areas[i]);
-    g_free(areas);
     return ret;
 }
 
@@ -349,6 +419,8 @@ static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
     gint px, py;
     Rect *area;
 
+    ob_debug("placing under mouse");
+
     if (!screen_pointer_pos(&px, &py))
         return FALSE;
     area = pick_pointer_head(client);
@@ -376,27 +448,17 @@ static gboolean place_per_app_setting(ObClient *client, gint *x, gint *y,
     if (!settings || (settings && !settings->pos_given))
         return FALSE;
 
+    ob_debug("placing by per-app settings");
+
     /* Find which head the pointer is on */
     if (settings->monitor == 0)
         /* this can return NULL */
         screen = pick_pointer_head(client);
-    else if (settings->monitor > 0 &&
-             (guint)settings->monitor <= screen_num_monitors)
-        screen = screen_area(client->desktop, (guint)settings->monitor - 1,
-                             NULL);
-
-    /* if we have't found a screen yet.. */
-    if (!screen) {
-        Rect **areas;
-        guint i;
-
-        areas = pick_head(client);
-        screen = areas[0];
-
-        /* don't free the first one, it's being set as "screen" */
-        for (i = 1; i < screen_num_monitors; ++i)
-            g_slice_free(Rect, areas[i]);
-        g_free(areas);
+    else {
+        guint m = settings->monitor;
+        if (m < 1 || m > screen_num_monitors)
+            m = screen_monitor_primary(TRUE) + 1;
+        screen = screen_area(client->desktop, m - 1, NULL);
     }
 
     if (settings->position.x.center)
@@ -423,12 +485,16 @@ static gboolean place_per_app_setting(ObClient *client, gint *x, gint *y,
     return TRUE;
 }
 
-static gboolean place_transient_splash(ObClient *client, gint *x, gint *y)
+static gboolean place_transient_splash(ObClient *client, Rect *area,
+                                       gint *x, gint *y)
 {
     if (client->type == OB_CLIENT_TYPE_DIALOG) {
         GSList *it;
         gboolean first = TRUE;
         gint l, r, t, b;
+
+        ob_debug("placing dialog");
+
         for (it = client->parents; it; it = g_slist_next(it)) {
             ObClient *m = it->data;
             if (!m->iconic) {
@@ -456,17 +522,10 @@ static gboolean place_transient_splash(ObClient *client, gint *x, gint *y)
     if (client->type == OB_CLIENT_TYPE_DIALOG ||
         client->type == OB_CLIENT_TYPE_SPLASH)
     {
-        Rect **areas;
-        guint i;
-
-        areas = pick_head(client);
+        ob_debug("placing dialog or splash");
 
-        *x = (areas[0]->width - client->frame->area.width) / 2 + areas[0]->x;
-        *y = (areas[0]->height - client->frame->area.height) / 2 + areas[0]->y;
-
-        for (i = 0; i < screen_num_monitors; ++i)
-            g_slice_free(Rect, areas[i]);
-        g_free(areas);
+        *x = (area->width - client->frame->area.width) / 2 + area->x;
+        *y = (area->height - client->frame->area.height) / 2 + area->y;
         return TRUE;
     }
 
@@ -475,9 +534,10 @@ static gboolean place_transient_splash(ObClient *client, gint *x, gint *y)
 
 /*! Return TRUE if openbox chose the position for the window, and FALSE if
   the application chose it */
-gboolean place_client(ObClient *client, gint *x, gint *y,
+gboolean place_client(ObClient *client, gboolean foreground, gint *x, gint *y,
                       ObAppSettings *settings)
 {
+    Rect *area;
     gboolean ret;
 
     /* per-app settings override program specified position
@@ -488,15 +548,19 @@ gboolean place_client(ObClient *client, gint *x, gint *y,
          !(settings && settings->pos_given)))
         return FALSE;
 
+    area = pick_head(client, foreground);
+
     /* try a number of methods */
     ret = place_per_app_setting(client, x, y, settings) ||
-        place_transient_splash(client, x, y) ||
+        place_transient_splash(client, area, x, y) ||
         (config_place_policy == OB_PLACE_POLICY_MOUSE &&
          place_under_mouse(client, x, y)) ||
-        place_nooverlap(client, x, y) ||
-        place_random(client, x, y);
+        place_nooverlap(client, area, x, y) ||
+        place_random(client, area, x, y);
     g_assert(ret);
 
+    g_slice_free(Rect, area);
+
     /* get where the client should be */
     frame_frame_gravity(client->frame, x, y);
     return TRUE;
index 6a9add4..94e2dc0 100644 (file)
@@ -35,10 +35,11 @@ typedef enum
 {
     OB_PLACE_MONITOR_ANY,
     OB_PLACE_MONITOR_ACTIVE,
-    OB_PLACE_MONITOR_MOUSE
+    OB_PLACE_MONITOR_MOUSE,
+    OB_PLACE_MONITOR_PRIMARY
 } ObPlaceMonitor;
 
-gboolean place_client(struct _ObClient *client, gint *x, gint *y,
-                      struct _ObAppSettings *settings);
+gboolean place_client(struct _ObClient *client, gboolean foreground,
+                      gint *x, gint *y, struct _ObAppSettings *settings);
 
 #endif
index ae7f38f..785b46c 100644 (file)
@@ -175,7 +175,7 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *title,
 
     /* set the window's title */
     if (title)
-        OBT_PROP_SETS(self->super.window, NET_WM_NAME, utf8, title);
+        OBT_PROP_SETS(self->super.window, NET_WM_NAME, title);
 
     /* listen for key presses on the window */
     self->event_mask = KeyPressMask;
index 353d2de..d368cab 100644 (file)
@@ -206,7 +206,7 @@ gboolean screen_annex(void)
                    NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
 
     /* set properties on the supporting window */
-    OBT_PROP_SETS(screen_support_win, NET_WM_NAME, utf8, "Openbox");
+    OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
     OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
                    WINDOW, screen_support_win);
 
@@ -311,7 +311,7 @@ gboolean screen_annex(void)
                     NET_SUPPORTED, ATOM, supported, num_support);
     g_free(supported);
 
-    OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION, utf8,
+    OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
                   OPENBOX_VERSION);
 
     screen_tell_ksplash();
@@ -375,7 +375,7 @@ void screen_startup(gboolean reconfig)
     screen_resize();
 
     /* have names already been set for the desktops? */
-    if (OBT_PROP_GETSS(obt_root(ob_screen), NET_DESKTOP_NAMES, utf8, &names)) {
+    if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
         g_strfreev(names);
         namesexist = TRUE;
     }
@@ -397,7 +397,7 @@ void screen_startup(gboolean reconfig)
 
         /* set the root window property */
         OBT_PROP_SETSS(obt_root(ob_screen),
-                       NET_DESKTOP_NAMES, utf8, (const gchar*const*)names);
+                       NET_DESKTOP_NAMES, (const gchar*const*)names);
 
         g_strfreev(names);
     }
@@ -500,11 +500,12 @@ void screen_resize(void)
     if (ob_state() != OB_STATE_RUNNING)
         return;
 
-    screen_update_areas();
+    /* this calls screen_update_areas(), which we need ! */
     dock_configure();
 
-    for (it = client_list; it; it = g_list_next(it))
-        client_move_onscreen(it->data, FALSE);
+    if (oldw)
+        for (it = client_list; it; it = g_list_next(it))
+            client_move_onscreen(it->data, FALSE);
 }
 
 void screen_set_num_desktops(guint num)
@@ -1182,7 +1183,7 @@ void screen_update_desktop_names(void)
     screen_desktop_names = NULL;
 
     if (OBT_PROP_GETSS(obt_root(ob_screen),
-                       NET_DESKTOP_NAMES, utf8, &screen_desktop_names))
+                       NET_DESKTOP_NAMES, &screen_desktop_names))
         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
     else
         i = 0;
@@ -1209,7 +1210,7 @@ void screen_update_desktop_names(void)
         /* if we changed any names, then set the root property so we can
            all agree on the names */
         OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
-                       utf8, (const gchar*const*)screen_desktop_names);
+                       (const gchar*const*)screen_desktop_names);
     }
 
     /* resize the pager for these names */
@@ -1458,10 +1459,8 @@ void screen_update_areas(void)
                     dims, 4 * screen_num_desktops);
 
     /* the area has changed, adjust all the windows if they need it */
-    for (it = onscreen; it; it = g_list_next(it)) {
-        client_move_onscreen(it->data, FALSE);
+    for (it = onscreen; it; it = g_list_next(it))
         client_reconfigure(it->data, FALSE);
-    }
 
     g_free(dims);
 }
@@ -1653,7 +1652,7 @@ guint screen_find_monitor(const Rect *search)
             }
         }
     }
-    return most;
+    return most < screen_num_monitors ? most : screen_monitor_primary(FALSE);
 }
 
 const Rect* screen_physical_area_all_monitors(void)
@@ -1754,3 +1753,12 @@ gboolean screen_pointer_pos(gint *x, gint *y)
     }
     return ret;
 }
+
+gboolean screen_compare_desktops(guint a, guint b)
+{
+    if (a == DESKTOP_ALL)
+        a = screen_desktop;
+    if (b == DESKTOP_ALL)
+        b = screen_desktop;
+    return a == b;
+}
index d15b352..a6a3995 100644 (file)
@@ -164,4 +164,12 @@ gboolean screen_pointer_pos(gint *x, gint *y);
 /*! Returns the monitor which contains the pointer device */
 guint screen_monitor_pointer(void);
 
+/*! Compare the desktop for two windows to see if they are considered on the
+  same desktop.
+  Windows that are on "all desktops" are treated like they are only on the
+  current desktop, so they are only in one place at a time.
+  @return TRUE if they are on the same desktop, FALSE otherwise.
+*/
+gboolean screen_compare_desktops(guint a, guint b);
+
 #endif
index 58a85ec..58551b5 100644 (file)
@@ -571,17 +571,16 @@ static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
 {
     GList *it;
     gboolean occluded = FALSE;
-    gboolean found = FALSE;
 
     /* no need for any looping in this case */
     if (sibling && client->layer != sibling->layer)
         return occluded;
 
-    for (it = stacking_list; it;
-         it = (found ? g_list_previous(it) :g_list_next(it)))
+    for (it = g_list_previous(g_list_find(stacking_list, client)); it;
+         it = g_list_previous(it))
         if (WINDOW_IS_CLIENT(it->data)) {
             ObClient *c = it->data;
-            if (found && !c->iconic &&
+            if (!c->iconic &&
                 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
                  c->desktop == client->desktop) &&
                 !client_search_transient(client, c))
@@ -602,8 +601,6 @@ static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
                         break; /* we past its layer */
                 }
             }
-            else if (c == client)
-                found = TRUE;
         }
     return occluded;
 }
@@ -615,16 +612,16 @@ static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
 {
     GList *it;
     gboolean occludes = FALSE;
-    gboolean found = FALSE;
 
     /* no need for any looping in this case */
     if (sibling && client->layer != sibling->layer)
         return occludes;
 
-    for (it = stacking_list; it; it = g_list_next(it))
+    for (it = g_list_next(g_list_find(stacking_list, client));
+         it; it = g_list_next(it))
         if (WINDOW_IS_CLIENT(it->data)) {
             ObClient *c = it->data;
-            if (found && !c->iconic &&
+            if (!c->iconic &&
                 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
                  c->desktop == client->desktop) &&
                 !client_search_transient(c, client))
@@ -645,8 +642,6 @@ static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
                         break; /* we past its layer */
                 }
             }
-            else if (c == client)
-                found = TRUE;
         }
     return occludes;
 }
index 16654cf..e249002 100644 (file)
@@ -263,7 +263,7 @@ void sn_setup_spawn_environment(const gchar *program, const gchar *name,
                        20 * 1000, sn_launch_wait_timeout, sn_launcher,
                        (GDestroyNotify)sn_launcher_context_unref);
 
-    setenv("DESKTOP_STARTUP_ID", id, TRUE);
+    g_setenv("DESKTOP_STARTUP_ID", id, TRUE);
 
     g_free(desc);
 }
index ad61294..51806f9 100644 (file)
@@ -26,6 +26,7 @@
 #include "prompt.h"
 #include "debug.h"
 #include "grab.h"
+#include "obt/prop.h"
 #include "obt/xqueue.h"
 
 static GHashTable *window_map;
@@ -186,6 +187,20 @@ void window_manage(Window win)
             }
             XFree(wmhints);
         }
+        /* This is a new method to declare that a window is a dockapp, being
+           implemented by Windowmaker, to alleviate pain in writing GTK+
+           dock apps.
+           http://thread.gmane.org/gmane.comp.window-managers.openbox/4881
+        */
+        if (!is_dockapp) {
+            gchar **ss;
+            if (OBT_PROP_GETSS_TYPE(win, WM_CLASS, STRING_NO_CC, &ss))
+            {
+                if (ss[0] && ss[1] && strcmp(ss[1], "DockApp") == 0)
+                    is_dockapp = TRUE;
+                g_strfreev(ss);
+            }
+        }
     }
 
     if (!no_manage) {
index 6ecf08e..a9701b6 100644 (file)
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,10 +7,11 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Openbox 3.4.3\n"
 "Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2008-11-15 22:28+0100\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
 "PO-Revision-Date: 2007-07-21 14:43+0300\n"
 "Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
 "Language-Team: Arabic <doc@arabeyes.org>\n"
+"Language: ar\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -18,223 +19,229 @@ msgstr ""
 "Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : n>=3 && n<=10 ? 2 : "
 "3\n"
 
-#: openbox/actions.c:149
+#: openbox/actions.c:198
 #, c-format
 msgid "Invalid action \"%s\" requested. No such action exists."
 msgstr ""
 
-#: openbox/actions/execute.c:128
+#: openbox/actions/execute.c:147
 msgid "No"
 msgstr ""
 
-#: openbox/actions/execute.c:129
+#: openbox/actions/execute.c:148
 msgid "Yes"
 msgstr ""
 
-#: openbox/actions/execute.c:133
+#: openbox/actions/execute.c:152
 msgid "Execute"
 msgstr ""
 
-#: openbox/actions/execute.c:142
+#: openbox/actions/execute.c:161
 #, c-format
 msgid "Failed to convert the path \"%s\" from utf8"
 msgstr "فشلت في تحويل المسار \"%s\" من utf8"
 
-#: openbox/actions/exit.c:52 openbox/actions/session.c:64
-#: openbox/client.c:3465
+#: openbox/actions/exit.c:69 openbox/client.c:3550
 msgid "Cancel"
 msgstr ""
 
-#: openbox/actions/exit.c:53
+#: openbox/actions/exit.c:70
 msgid "Exit"
 msgstr ""
 
-#: openbox/actions/exit.c:56
-msgid "Are you sure you want to exit Openbox?"
-msgstr ""
-
-#: openbox/actions/exit.c:57
-msgid "Exit Openbox"
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
 msgstr ""
 
-#. TRANSLATORS: Don't translate the word "SessionLogout" as it's the
-#. name of the action you write in rc.xml
-#: openbox/actions/session.c:43
-msgid ""
-"The SessionLogout action is not available since Openbox was built without "
-"session management support"
+#: openbox/actions/exit.c:75
+msgid "Log Out"
 msgstr ""
 
-#: openbox/actions/session.c:65 openbox/actions/session.c:70
-msgid "Log Out"
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
 msgstr ""
 
-#: openbox/actions/session.c:69
-msgid "Are you sure you want to log out?"
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
 msgstr ""
 
-#: openbox/client.c:2012
+#: openbox/client.c:2037
 msgid "Unnamed Window"
 msgstr ""
 
-#: openbox/client.c:2026 openbox/client.c:2058
+#: openbox/client.c:2051 openbox/client.c:2082
 msgid "Killing..."
 msgstr ""
 
-#: openbox/client.c:2028 openbox/client.c:2060
+#: openbox/client.c:2053 openbox/client.c:2084
 msgid "Not Responding"
 msgstr ""
 
-#: openbox/client.c:3454
+#: openbox/client.c:3539
 #, 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 ""
 
-#: openbox/client.c:3456
+#: openbox/client.c:3541
 msgid "End Process"
 msgstr ""
 
-#: openbox/client.c:3460
+#: openbox/client.c:3545
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to disconnect "
 "it from the X server?"
 msgstr ""
 
-#: openbox/client.c:3462
+#: openbox/client.c:3547
 msgid "Disconnect"
 msgstr ""
 
-#: openbox/client_list_combined_menu.c:87 openbox/client_list_menu.c:91
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
 msgid "Go there..."
 msgstr "اذهب هناك..."
 
-#: openbox/client_list_combined_menu.c:94
+#: openbox/client_list_combined_menu.c:100
 msgid "Manage desktops"
 msgstr "أدِر أسطح المكتب"
 
-#: openbox/client_list_combined_menu.c:95 openbox/client_list_menu.c:155
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
 msgid "_Add new desktop"
 msgstr "أضِف سطح مكتب جديد (_A)"
 
-#: openbox/client_list_combined_menu.c:96 openbox/client_list_menu.c:156
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
 msgid "_Remove last desktop"
 msgstr "احذف آخر سطح مكتب (_R)"
 
-#: openbox/client_list_combined_menu.c:149
+#: openbox/client_list_combined_menu.c:157
 msgid "Windows"
 msgstr "نوافذ"
 
-#: openbox/client_list_menu.c:203
+#: openbox/client_list_menu.c:214
 msgid "Desktops"
 msgstr "أسطح مكتب"
 
-#: openbox/client_menu.c:258
+#: openbox/client_menu.c:259
 msgid "All desktops"
 msgstr "كل أسطح المكتب"
 
-#: openbox/client_menu.c:370
+#: openbox/client_menu.c:371
 msgid "_Layer"
 msgstr "طبقة (_L)"
 
-#: openbox/client_menu.c:375
+#: openbox/client_menu.c:376
 msgid "Always on _top"
 msgstr "دائما على السطح (_T)"
 
-#: openbox/client_menu.c:376
+#: openbox/client_menu.c:377
 msgid "_Normal"
 msgstr "طبيعي (_N)"
 
-#: openbox/client_menu.c:377
+#: openbox/client_menu.c:378
 msgid "Always on _bottom"
 msgstr "دائما في القاع (_B)"
 
-#: openbox/client_menu.c:379
+#: openbox/client_menu.c:380
 msgid "_Send to desktop"
 msgstr "أرسِل إلى سطح المكتب (_S)"
 
-#: openbox/client_menu.c:383
+#: openbox/client_menu.c:384
 msgid "Client menu"
 msgstr "قائمة العميل"
 
-#: openbox/client_menu.c:393
+#: openbox/client_menu.c:394
 msgid "R_estore"
 msgstr "استعِد (_E)"
 
-#: openbox/client_menu.c:397
+#: openbox/client_menu.c:398
 msgid "_Move"
 msgstr "انقل (_M)"
 
-#: openbox/client_menu.c:399
+#: openbox/client_menu.c:400
 msgid "Resi_ze"
 msgstr "حجِّم (_Z)"
 
-#: openbox/client_menu.c:401
+#: openbox/client_menu.c:402
 msgid "Ico_nify"
 msgstr "صغّر (_N)"
 
-#: openbox/client_menu.c:405
+#: openbox/client_menu.c:406
 msgid "Ma_ximize"
 msgstr "كبّر (_X)"
 
-#: openbox/client_menu.c:409
+#: openbox/client_menu.c:410
 msgid "_Roll up/down"
 msgstr "لُف لأعلى/لأسفل (_R)"
 
-#: openbox/client_menu.c:411
+#: openbox/client_menu.c:414
 msgid "Un/_Decorate"
 msgstr "ضع/أزل الحواف (_D)"
 
-#: openbox/client_menu.c:415
+#: openbox/client_menu.c:418
 msgid "_Close"
 msgstr "أغلق (_C)"
 
-#: openbox/config.c:781
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "سياق غير صحيح \"%s\" في ارتباط الفأرة"
+
+#: openbox/config.c:857
 #, c-format
 msgid "Invalid button \"%s\" specified in config file"
 msgstr "زر غير صحيح \"%s\" محدد في ملف الإعدادات"
 
-#: openbox/keyboard.c:157
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "لم أستطِع إنشاء الدليل '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "أغلق"
+
+#: openbox/keyboard.c:161
 msgid "Conflict with key binding in config file"
 msgstr "يتعارض مع ارتباط المفاتيح في ملف الإعدادات"
 
-#: openbox/menu.c:102 openbox/menu.c:110
+#: openbox/menu.c:94 openbox/menu.c:106
 #, c-format
 msgid "Unable to find a valid menu file \"%s\""
 msgstr "لم أعثر على ملف قائمة سليم \"%s\""
 
-#: openbox/menu.c:170
+#: openbox/menu.c:158
 #, c-format
 msgid "Failed to execute command for pipe-menu \"%s\": %s"
 msgstr "فشل تنفيذ أمر ل pipe-menu \"%s\": %s"
 
-#: openbox/menu.c:184
+#: openbox/menu.c:172
 #, c-format
 msgid "Invalid output from pipe-menu \"%s\""
 msgstr "خرج غير سليم من pipe-menu \"%s\""
 
-#: openbox/menu.c:197
+#: openbox/menu.c:185
 #, c-format
 msgid "Attempted to access menu \"%s\" but it does not exist"
 msgstr "حاولت الوصول إلى القائمة \"%s\" لكنها غير موجودة"
 
-#: openbox/menu.c:367 openbox/menu.c:368
+#: openbox/menu.c:400 openbox/menu.c:401
 msgid "More..."
 msgstr "المزيد..."
 
-#: openbox/mouse.c:373
+#: openbox/mouse.c:376
 #, c-format
 msgid "Invalid button \"%s\" in mouse binding"
 msgstr "زر غير صحيح \"%s\" في ارتباط الفأرة"
 
-#: openbox/mouse.c:379
-#, c-format
-msgid "Invalid context \"%s\" in mouse binding"
-msgstr "سياق غير صحيح \"%s\" في ارتباط الفأرة"
-
-#: openbox/openbox.c:133
+#: openbox/openbox.c:137
 #, c-format
 msgid "Unable to change to home directory \"%s\": %s"
 msgstr "لم أستطع تغيير المجلد المنزلي \"%s\": %s"
@@ -243,27 +250,27 @@ msgstr "لم أستطع تغيير المجلد المنزلي \"%s\": %s"
 msgid "Failed to open the display from the DISPLAY environment variable."
 msgstr "تعذّر فتح العرض من متغير البيئة DISPLAY."
 
-#: openbox/openbox.c:183
+#: openbox/openbox.c:182
 msgid "Failed to initialize the obrender library."
 msgstr "تعذّر بدأ مكتبة obrender."
 
-#: openbox/openbox.c:194
+#: openbox/openbox.c:193
 msgid "X server does not support locale."
 msgstr "خادم إكس لا يدعم المحليّة."
 
-#: openbox/openbox.c:196
+#: openbox/openbox.c:195
 msgid "Cannot set locale modifiers for the X server."
 msgstr "لم أستطِع ضبط مُغيِّرات المحليّة لخادم إكس."
 
-#: openbox/openbox.c:263
+#: openbox/openbox.c:253
 msgid "Unable to find a valid config file, using some simple defaults"
 msgstr "لم أعثر على ملف إعدادات سليم، سأستخدم بعض الإفتراضيات البسيطة"
 
-#: openbox/openbox.c:297
+#: openbox/openbox.c:286
 msgid "Unable to load a theme."
 msgstr "لم أستطِع تحميل سِمة."
 
-#: openbox/openbox.c:377
+#: openbox/openbox.c:370
 #, c-format
 msgid ""
 "One or more XML syntax errors were found while parsing the Openbox "
@@ -271,28 +278,24 @@ msgid ""
 "was in file \"%s\" line %d, with message: %s"
 msgstr ""
 
-#: openbox/openbox.c:379
+#: openbox/openbox.c:372
 msgid "Openbox Syntax Error"
 msgstr ""
 
-#: openbox/openbox.c:379
-msgid "Close"
-msgstr "أغلق"
-
-#: openbox/openbox.c:448
+#: openbox/openbox.c:438
 #, c-format
 msgid "Restart failed to execute new executable \"%s\": %s"
 msgstr "فشلت إعادة التشغيل في تنفيذ مُنفّذ جديد \"%s\": %s"
 
-#: openbox/openbox.c:518 openbox/openbox.c:520
+#: openbox/openbox.c:517 openbox/openbox.c:519
 msgid "Copyright (c)"
 msgstr "حقوق النسخ"
 
-#: openbox/openbox.c:529
+#: openbox/openbox.c:528
 msgid "Syntax: openbox [options]\n"
 msgstr "الصيغة: openbox [options]\n"
 
-#: openbox/openbox.c:530
+#: openbox/openbox.c:529
 msgid ""
 "\n"
 "Options:\n"
@@ -300,30 +303,30 @@ msgstr ""
 "\n"
 "الخيارات:\n"
 
-#: openbox/openbox.c:531
+#: openbox/openbox.c:530
 msgid "  --help              Display this help and exit\n"
 msgstr "  --help              اعرض هذه المساعدة ثم اخرج\n"
 
-#: openbox/openbox.c:532
+#: openbox/openbox.c:531
 msgid "  --version           Display the version and exit\n"
 msgstr "  --version           اعرض النسخة ثم اخرج\n"
 
-#: openbox/openbox.c:533
+#: openbox/openbox.c:532
 msgid "  --replace           Replace the currently running window manager\n"
 msgstr "  --replace           استبدل مدير النوافذ الذي يعمل حاليا\n"
 
 #. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
 #. aligned still, if you have to, make a new line with \n and 22 spaces. It's
 #. fine to leave it as FILE though.
-#: openbox/openbox.c:537
+#: openbox/openbox.c:536
 msgid "  --config-file FILE  Specify the path to the config file to use\n"
 msgstr ""
 
-#: openbox/openbox.c:538
+#: openbox/openbox.c:537
 msgid "  --sm-disable        Disable connection to the session manager\n"
 msgstr "  --sm-disable        عطِّل الإتصال بمدير الجلسة\n"
 
-#: openbox/openbox.c:539
+#: openbox/openbox.c:538
 msgid ""
 "\n"
 "Passing messages to a running Openbox instance:\n"
@@ -331,19 +334,19 @@ msgstr ""
 "\n"
 "تمرير رسائل لمرّة تعمل من أوبن‌بوكس:\n"
 
-#: openbox/openbox.c:540
+#: openbox/openbox.c:539
 msgid "  --reconfigure       Reload Openbox's configuration\n"
 msgstr "  --reconfigure       أعِد تحميل إعدادات أوبن‌بوكس\n"
 
-#: openbox/openbox.c:541
+#: openbox/openbox.c:540
 msgid "  --restart           Restart Openbox\n"
 msgstr "  --restart           أعِد تشغيل أوبن‌بوكس\n"
 
-#: openbox/openbox.c:542
+#: openbox/openbox.c:541
 msgid "  --exit              Exit Openbox\n"
 msgstr ""
 
-#: openbox/openbox.c:543
+#: openbox/openbox.c:542
 msgid ""
 "\n"
 "Debugging options:\n"
@@ -351,10 +354,14 @@ msgstr ""
 "\n"
 "خيارات التنقيح:\n"
 
-#: openbox/openbox.c:544
+#: openbox/openbox.c:543
 msgid "  --sync              Run in synchronous mode\n"
 msgstr "  --sync              شغّل في النمط المزامن\n"
 
+#: openbox/openbox.c:544
+msgid "  --startup CMD       Run CMD after starting\n"
+msgstr ""
+
 #: openbox/openbox.c:545
 msgid "  --debug             Display debugging output\n"
 msgstr "  --debug             اعرض خرْج التنقيح\n"
@@ -364,10 +371,14 @@ msgid "  --debug-focus       Display debugging output for focus handling\n"
 msgstr "  --debug-focus       اعرض خرج التنقيح للتعامل مع البؤرة\n"
 
 #: openbox/openbox.c:547
+msgid "  --debug-session     Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
 msgid "  --debug-xinerama    Split the display into fake xinerama screens\n"
 msgstr "  --debug-xinerama    شق العرض إلى شاشات xinerama زائفة\n"
 
-#: openbox/openbox.c:548
+#: openbox/openbox.c:549
 #, c-format
 msgid ""
 "\n"
@@ -376,26 +387,27 @@ msgstr ""
 "\n"
 "من فضلك أبلغ عن العلل إلى %s\n"
 
-#: openbox/openbox.c:617
-msgid "--config-file requires an argument\n"
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
 msgstr ""
 
-#: openbox/openbox.c:660
+#: openbox/openbox.c:709
 #, c-format
 msgid "Invalid command line argument \"%s\"\n"
 msgstr "معامل سطر أوامر غير سليم \"%s\"\n"
 
-#: openbox/screen.c:102 openbox/screen.c:190
+#: openbox/screen.c:106 openbox/screen.c:191
 #, c-format
 msgid "A window manager is already running on screen %d"
 msgstr "يعمل مدير نوافذ بالفعل على الشاشة %Id"
 
-#: openbox/screen.c:124
+#: openbox/screen.c:127
 #, c-format
 msgid "Could not acquire window manager selection on screen %d"
 msgstr "تعذّر الحصول على انتقاء مدير النوافذ على الشاشة %Id"
 
-#: openbox/screen.c:145
+#: openbox/screen.c:150
 #, c-format
 msgid "The WM on screen %d is not exiting"
 msgstr "مدير النوافذ على الشاشة %Id لا وجود له"
@@ -404,7 +416,7 @@ msgstr "مدير النوافذ على الشاشة %Id لا وجود له"
 #. arguments, you can use %1$d for the first one and %2$d for the
 #. second one. For example,
 #. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
-#: openbox/screen.c:412
+#: openbox/screen.c:418
 #, c-format
 msgid ""
 "Openbox is configured for %d desktop, but the current session has %d.  "
@@ -417,31 +429,12 @@ msgstr[1] ""
 msgstr[2] ""
 msgstr[3] ""
 
-#: openbox/screen.c:1180
+#: openbox/screen.c:1205
 #, c-format
 msgid "desktop %i"
 msgstr "سطح المكتب %Ii"
 
-#: openbox/session.c:104
-#, c-format
-msgid "Unable to make directory \"%s\": %s"
-msgstr "لم أستطِع إنشاء الدليل \"%s\": %s"
-
-#: openbox/session.c:466
-#, c-format
-msgid "Unable to save the session to \"%s\": %s"
-msgstr "لم أستطِع حفظ الجلسة إلى \"%s\": %s"
-
-#: openbox/session.c:605
-#, c-format
-msgid "Error while saving the session to \"%s\": %s"
-msgstr "خطأ أثناء حفظ الجلسة إلى \"%s\": %s"
-
-#: openbox/session.c:842
-msgid "Not connected to a session manager"
-msgstr ""
-
-#: openbox/startupnotify.c:243
+#: openbox/startupnotify.c:241
 #, c-format
 msgid "Running %s"
 msgstr "تشغيل %s"
@@ -466,14 +459,18 @@ msgstr "اسم مفتاح \"%s\" غير سليم في ارتباط المفتا
 msgid "Requested key \"%s\" does not exist on the display"
 msgstr "المفتاح المطلوب \"%s\" لا وجود له في العرض"
 
-#: openbox/xerror.c:40
-#, c-format
-msgid "X Error: %s"
-msgstr "خطأ إكس: %s"
-
-#: openbox/prompt.c:200
+#: openbox/prompt.c:153
 msgid "OK"
 msgstr ""
 
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "لم أستطِع حفظ الجلسة إلى \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "خطأ أثناء حفظ الجلسة إلى \"%s\": %s"
+
+#~ msgid "X Error: %s"
+#~ msgstr "خطأ إكس: %s"
+
 #~ msgid "Failed to execute \"%s\": %s"
 #~ msgstr "فشلت في تنفيذ \"%s\": %s"
index b7c898c..b6a7d0a 100644 (file)
@@ -7,234 +7,241 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Openbox 3.4.2\n"
 "Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2008-11-15 22:28+0100\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
 "PO-Revision-Date: 2007-06-01 19:02+0530\n"
 "Last-Translator: Runa Bhattacharjee <runabh@gmail.com>\n"
 "Language-Team: Bengali (India) <en@li.org>\n"
+"Language: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: KBabel 1.11.4\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: openbox/actions.c:149
+#: openbox/actions.c:198
 #, c-format
 msgid "Invalid action \"%s\" requested. No such action exists."
 msgstr ""
 "অবৈধ কর্ম \"%s\"-র অনুরোধ জানানো হয়েছে। এই ধরনের কোনো কর্ম বর্তমানে উপস্থিত নেই।"
 
-#: openbox/actions/execute.c:128
+#: openbox/actions/execute.c:147
 msgid "No"
 msgstr ""
 
-#: openbox/actions/execute.c:129
+#: openbox/actions/execute.c:148
 msgid "Yes"
 msgstr ""
 
-#: openbox/actions/execute.c:133
+#: openbox/actions/execute.c:152
 msgid "Execute"
 msgstr ""
 
-#: openbox/actions/execute.c:142
+#: openbox/actions/execute.c:161
 #, c-format
 msgid "Failed to convert the path \"%s\" from utf8"
 msgstr "\"%s\" পাথটি utf8 থেকে রূপান্তর করতে ব্যর্থ"
 
-#: openbox/actions/exit.c:52 openbox/actions/session.c:64
-#: openbox/client.c:3465
+#: openbox/actions/exit.c:69 openbox/client.c:3550
 msgid "Cancel"
 msgstr ""
 
-#: openbox/actions/exit.c:53
+#: openbox/actions/exit.c:70
 msgid "Exit"
 msgstr ""
 
-#: openbox/actions/exit.c:56
-msgid "Are you sure you want to exit Openbox?"
-msgstr ""
-
-#: openbox/actions/exit.c:57
-msgid "Exit Openbox"
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
 msgstr ""
 
-#. TRANSLATORS: Don't translate the word "SessionLogout" as it's the
-#. name of the action you write in rc.xml
-#: openbox/actions/session.c:43
-msgid ""
-"The SessionLogout action is not available since Openbox was built without "
-"session management support"
+#: openbox/actions/exit.c:75
+msgid "Log Out"
 msgstr ""
 
-#: openbox/actions/session.c:65 openbox/actions/session.c:70
-msgid "Log Out"
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
 msgstr ""
 
-#: openbox/actions/session.c:69
-msgid "Are you sure you want to log out?"
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
 msgstr ""
 
-#: openbox/client.c:2012
+#: openbox/client.c:2037
 msgid "Unnamed Window"
 msgstr ""
 
-#: openbox/client.c:2026 openbox/client.c:2058
+#: openbox/client.c:2051 openbox/client.c:2082
 msgid "Killing..."
 msgstr ""
 
-#: openbox/client.c:2028 openbox/client.c:2060
+#: openbox/client.c:2053 openbox/client.c:2084
 msgid "Not Responding"
 msgstr ""
 
-#: openbox/client.c:3454
+#: openbox/client.c:3539
 #, 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 ""
 
-#: openbox/client.c:3456
+#: openbox/client.c:3541
 msgid "End Process"
 msgstr ""
 
-#: openbox/client.c:3460
+#: openbox/client.c:3545
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to disconnect "
 "it from the X server?"
 msgstr ""
 
-#: openbox/client.c:3462
+#: openbox/client.c:3547
 msgid "Disconnect"
 msgstr ""
 
-#: openbox/client_list_combined_menu.c:87 openbox/client_list_menu.c:91
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
 msgid "Go there..."
 msgstr "চিহ্নিত স্থানে চলুন..."
 
-#: openbox/client_list_combined_menu.c:94
+#: openbox/client_list_combined_menu.c:100
 msgid "Manage desktops"
 msgstr ""
 
-#: openbox/client_list_combined_menu.c:95 openbox/client_list_menu.c:155
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
 msgid "_Add new desktop"
 msgstr ""
 
-#: openbox/client_list_combined_menu.c:96 openbox/client_list_menu.c:156
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
 msgid "_Remove last desktop"
 msgstr ""
 
-#: openbox/client_list_combined_menu.c:149
+#: openbox/client_list_combined_menu.c:157
 msgid "Windows"
 msgstr "উইন্ডো"
 
-#: openbox/client_list_menu.c:203
+#: openbox/client_list_menu.c:214
 msgid "Desktops"
 msgstr "ডেস্কটপ"
 
-#: openbox/client_menu.c:258
+#: openbox/client_menu.c:259
 msgid "All desktops"
 msgstr "সর্বপ্রকার ডেস্কটপ"
 
-#: openbox/client_menu.c:370
+#: openbox/client_menu.c:371
 msgid "_Layer"
 msgstr "স্তর (_L)"
 
-#: openbox/client_menu.c:375
+#: openbox/client_menu.c:376
 msgid "Always on _top"
 msgstr "সর্বদা উপরে (_t)"
 
-#: openbox/client_menu.c:376
+#: openbox/client_menu.c:377
 msgid "_Normal"
 msgstr "স্বাভাবিক (_N)"
 
-#: openbox/client_menu.c:377
+#: openbox/client_menu.c:378
 msgid "Always on _bottom"
 msgstr "সর্বদা নীচে (_b)"
 
-#: openbox/client_menu.c:379
+#: openbox/client_menu.c:380
 msgid "_Send to desktop"
 msgstr "ডেস্কটপে পাঠানো হবে (_S)"
 
-#: openbox/client_menu.c:383
+#: openbox/client_menu.c:384
 msgid "Client menu"
 msgstr "ক্লায়েন্ট মেনু"
 
-#: openbox/client_menu.c:393
+#: openbox/client_menu.c:394
 msgid "R_estore"
 msgstr "পুনরুদ্ধার (_e)"
 
-#: openbox/client_menu.c:397
+#: openbox/client_menu.c:398
 msgid "_Move"
 msgstr "স্থানান্তরণ (_M)"
 
-#: openbox/client_menu.c:399
+#: openbox/client_menu.c:400
 msgid "Resi_ze"
 msgstr "মাপ পরিবর্তন (_z)"
 
-#: openbox/client_menu.c:401
+#: openbox/client_menu.c:402
 msgid "Ico_nify"
 msgstr "আইকন রূপে প্রদর্শন (_n)"
 
-#: openbox/client_menu.c:405
+#: openbox/client_menu.c:406
 msgid "Ma_ximize"
 msgstr "বড় করুন (_x)"
 
-#: openbox/client_menu.c:409
+#: openbox/client_menu.c:410
 msgid "_Roll up/down"
 msgstr "উপরে/নীচে গুটিয়ে নিন (_R)"
 
-#: openbox/client_menu.c:411
+#: openbox/client_menu.c:414
 msgid "Un/_Decorate"
 msgstr "বিন্যাস পরিবর্তন (_D)"
 
-#: openbox/client_menu.c:415
+#: openbox/client_menu.c:418
 msgid "_Close"
 msgstr "বন্ধ করুন (_C)"
 
-#: openbox/config.c:781
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "মাউস বাইন্ডিং সংক্রান্ত অবৈধ কনটেক্সট \"%s\""
+
+#: openbox/config.c:857
 #, c-format
 msgid "Invalid button \"%s\" specified in config file"
 msgstr "কনফিগ ফাইলে অবৈধ বাটন \"%s\" উল্লিখিত হয়েছে"
 
-#: openbox/keyboard.c:157
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "'%s' ডিরেক্টরি নির্মাণ করতে ব্যর্থ: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "বন্ধ করুন"
+
+#: openbox/keyboard.c:161
 msgid "Conflict with key binding in config file"
 msgstr "কনফিগ ফাইলে কি-বাইন্ডিং সংক্রান্ত দ্বন্দ্ব"
 
-#: openbox/menu.c:102 openbox/menu.c:110
+#: openbox/menu.c:94 openbox/menu.c:106
 #, c-format
 msgid "Unable to find a valid menu file \"%s\""
 msgstr "বৈধ মেনু ফাইল \"%s\" সনাক্ত করতে ব্যর্থ"
 
-#: openbox/menu.c:170
+#: openbox/menu.c:158
 #, c-format
 msgid "Failed to execute command for pipe-menu \"%s\": %s"
 msgstr "পাইপ-মেনু \"%s\"-র জন্য কমান্ড সঞ্চালন করতে ব্যর্থ: %s"
 
-#: openbox/menu.c:184
+#: openbox/menu.c:172
 #, c-format
 msgid "Invalid output from pipe-menu \"%s\""
 msgstr "পাইপ-মেনু \"%s\" থেকে অবৈধ ফলাফল প্রাপ্ত হয়েছে"
 
-#: openbox/menu.c:197
+#: openbox/menu.c:185
 #, c-format
 msgid "Attempted to access menu \"%s\" but it does not exist"
 msgstr "অনুপস্থিত মেনু \"%s\" ব্যবহারের প্রচেষ্টা হয়েছে"
 
-#: openbox/menu.c:367 openbox/menu.c:368
+#: openbox/menu.c:400 openbox/menu.c:401
 msgid "More..."
 msgstr "অতিরিক্ত..."
 
-#: openbox/mouse.c:373
+#: openbox/mouse.c:376
 #, c-format
 msgid "Invalid button \"%s\" in mouse binding"
 msgstr "মাউস বাইন্ডিং সংক্রান্ত অবৈধ বাটন \"%s\""
 
-#: openbox/mouse.c:379
-#, c-format
-msgid "Invalid context \"%s\" in mouse binding"
-msgstr "মাউস বাইন্ডিং সংক্রান্ত অবৈধ কনটেক্সট \"%s\""
-
-#: openbox/openbox.c:133
+#: openbox/openbox.c:137
 #, c-format
 msgid "Unable to change to home directory \"%s\": %s"
 msgstr "ব্যক্তিগত ডিরেক্টরি \"%s\"-তে পরিবর্তন করতে ব্যর্থ: %s"
@@ -243,27 +250,27 @@ msgstr "ব্যক্তিগত ডিরেক্টরি \"%s\"-তে 
 msgid "Failed to open the display from the DISPLAY environment variable."
 msgstr "DISPLAY এনভাশরনমেন্ট ভেরিয়েবলের মান প্রয়োগ করে প্রদর্শন আরম্ভ করতে ব্যর্থ।"
 
-#: openbox/openbox.c:183
+#: openbox/openbox.c:182
 msgid "Failed to initialize the obrender library."
 msgstr "obrender লাইব্রেরি আরম্ভ করতে ব্যর্থ।"
 
-#: openbox/openbox.c:194
+#: openbox/openbox.c:193
 msgid "X server does not support locale."
 msgstr "X সার্ভার দ্বারা লোকেইল সমর্থিতত হয় না।"
 
-#: openbox/openbox.c:196
+#: openbox/openbox.c:195
 msgid "Cannot set locale modifiers for the X server."
 msgstr "X সার্ভারের জন্য লোকেইল মডিফায়ার নির্ধারণ করতে ব্যর্থ।"
 
-#: openbox/openbox.c:263
+#: openbox/openbox.c:253
 msgid "Unable to find a valid config file, using some simple defaults"
 msgstr "বৈধ কনফিগ ফাইল সনাক্ত করতে ব্যর্থ, কয়েকটি সাধারণ ডিফল্ট মান প্রয়োগ করা হবে।"
 
-#: openbox/openbox.c:297
+#: openbox/openbox.c:286
 msgid "Unable to load a theme."
 msgstr "থিম লোড করতে ব্যর্থ।"
 
-#: openbox/openbox.c:377
+#: openbox/openbox.c:370
 #, c-format
 msgid ""
 "One or more XML syntax errors were found while parsing the Openbox "
@@ -271,28 +278,24 @@ msgid ""
 "was in file \"%s\" line %d, with message: %s"
 msgstr ""
 
-#: openbox/openbox.c:379
+#: openbox/openbox.c:372
 msgid "Openbox Syntax Error"
 msgstr ""
 
-#: openbox/openbox.c:379
-msgid "Close"
-msgstr "বন্ধ করুন"
-
-#: openbox/openbox.c:448
+#: openbox/openbox.c:438
 #, c-format
 msgid "Restart failed to execute new executable \"%s\": %s"
 msgstr "পুনরাম্ভের পরে নতুন এক্সেকিউটেবল \"%s\" সঞ্চালন করতে ব্যর্থ: %s"
 
-#: openbox/openbox.c:518 openbox/openbox.c:520
+#: openbox/openbox.c:517 openbox/openbox.c:519
 msgid "Copyright (c)"
 msgstr "স্বত্বাধিকার (c)"
 
-#: openbox/openbox.c:529
+#: openbox/openbox.c:528
 msgid "Syntax: openbox [options]\n"
 msgstr "ব্যবহারপ্রণালী: openbox [বিকল্প]\n"
 
-#: openbox/openbox.c:530
+#: openbox/openbox.c:529
 msgid ""
 "\n"
 "Options:\n"
@@ -300,15 +303,15 @@ msgstr ""
 "\n"
 "বিবিধ বিকল্প:\n"
 
-#: openbox/openbox.c:531
+#: openbox/openbox.c:530
 msgid "  --help              Display this help and exit\n"
 msgstr "  --help              সহায়তা বার্তা প্রদর্শন করে প্রস্থান\n"
 
-#: openbox/openbox.c:532
+#: openbox/openbox.c:531
 msgid "  --version           Display the version and exit\n"
 msgstr "  --version           সংস্করণ প্রদর্শন করে প্রস্থান\n"
 
-#: openbox/openbox.c:533
+#: openbox/openbox.c:532
 msgid "  --replace           Replace the currently running window manager\n"
 msgstr ""
 "  --replace           বর্তমানে চলমান উইন্ডো পরিচালন ব্যবস্থা পরিবর্তন করা হবে\n"
@@ -316,16 +319,16 @@ msgstr ""
 #. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
 #. aligned still, if you have to, make a new line with \n and 22 spaces. It's
 #. fine to leave it as FILE though.
-#: openbox/openbox.c:537
+#: openbox/openbox.c:536
 msgid "  --config-file FILE  Specify the path to the config file to use\n"
 msgstr ""
 
-#: openbox/openbox.c:538
+#: openbox/openbox.c:537
 msgid "  --sm-disable        Disable connection to the session manager\n"
 msgstr ""
 "  --sm-disable        সেশান পরিচালন ব্যবস্থার সাথে সংযোগ নিষ্ক্রিয় করা হবে\n"
 
-#: openbox/openbox.c:539
+#: openbox/openbox.c:538
 msgid ""
 "\n"
 "Passing messages to a running Openbox instance:\n"
@@ -333,19 +336,19 @@ msgstr ""
 "\n"
 "চলমান Openbox ইনস্ট্যান্সে বার্তা প্রেরণ:\n"
 
-#: openbox/openbox.c:540
+#: openbox/openbox.c:539
 msgid "  --reconfigure       Reload Openbox's configuration\n"
 msgstr "  --reconfigure       Openbox-র কনফিগারেশন পুনরায় লোড করে\n"
 
-#: openbox/openbox.c:541
+#: openbox/openbox.c:540
 msgid "  --restart           Restart Openbox\n"
 msgstr "  --restart           Openbox পুনরারম্ভ\n"
 
-#: openbox/openbox.c:542
+#: openbox/openbox.c:541
 msgid "  --exit              Exit Openbox\n"
 msgstr ""
 
-#: openbox/openbox.c:543
+#: openbox/openbox.c:542
 msgid ""
 "\n"
 "Debugging options:\n"
@@ -353,10 +356,14 @@ msgstr ""
 "\n"
 "ডিবাগ করার বিভিন্ন বিকল্প:\n"
 
-#: openbox/openbox.c:544
+#: openbox/openbox.c:543
 msgid "  --sync              Run in synchronous mode\n"
 msgstr "  --sync              সিঙ্ক্রোনাস মোডে সঞ্চালিত হবে\n"
 
+#: openbox/openbox.c:544
+msgid "  --startup CMD       Run CMD after starting\n"
+msgstr ""
+
 #: openbox/openbox.c:545
 msgid "  --debug             Display debugging output\n"
 msgstr "  --debug             ডিবাগ-এর ফলাফল প্রদর্শন করে\n"
@@ -367,10 +374,14 @@ msgstr ""
 "  --debug-focus       ফোকাস হ্যান্ডলিং সংক্রান্ত ডিবাগের ফলাফল প্রদর্শন করে\n"
 
 #: openbox/openbox.c:547
+msgid "  --debug-session     Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
 msgid "  --debug-xinerama    Split the display into fake xinerama screens\n"
 msgstr "  --debug-xinerama    প্রদর্শন ক্ষেত্রটি নকল xinerama পর্দায় ভাগ করা হবে\n"
 
-#: openbox/openbox.c:548
+#: openbox/openbox.c:549
 #, c-format
 msgid ""
 "\n"
@@ -379,26 +390,27 @@ msgstr ""
 "\n"
 "অনুগ্রহ করে %s-এ বাগ সংক্রান্ত সূচনা দায়ের করুন\n"
 
-#: openbox/openbox.c:617
-msgid "--config-file requires an argument\n"
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
 msgstr ""
 
-#: openbox/openbox.c:660
+#: openbox/openbox.c:709
 #, c-format
 msgid "Invalid command line argument \"%s\"\n"
 msgstr "অবৈধ কমান্ড-লাইন আর্গুমেন্ট \"%s\"\n"
 
-#: openbox/screen.c:102 openbox/screen.c:190
+#: openbox/screen.c:106 openbox/screen.c:191
 #, c-format
 msgid "A window manager is already running on screen %d"
 msgstr "একটি উইন্ডো পরিচালন ব্যবস্থা বর্তমানে %d-এ চলছে"
 
-#: openbox/screen.c:124
+#: openbox/screen.c:127
 #, c-format
 msgid "Could not acquire window manager selection on screen %d"
 msgstr "পর্দা %d-এ উইন্ডো পরিচালন ব্যবস্থার নির্বাচিত অংশ প্রাপ্ত করতে ব্যর্থ"
 
-#: openbox/screen.c:145
+#: openbox/screen.c:150
 #, c-format
 msgid "The WM on screen %d is not exiting"
 msgstr "পর্দা %d-র উপর চলমান উইন্ডো পরিচালন ব্যবস্থাটি বন্ধ করতে ব্যর্থ"
@@ -407,7 +419,7 @@ msgstr "পর্দা %d-র উপর চলমান উইন্ডো প
 #. arguments, you can use %1$d for the first one and %2$d for the
 #. second one. For example,
 #. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
-#: openbox/screen.c:412
+#: openbox/screen.c:418
 #, c-format
 msgid ""
 "Openbox is configured for %d desktop, but the current session has %d.  "
@@ -418,31 +430,12 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: openbox/screen.c:1180
+#: openbox/screen.c:1205
 #, c-format
 msgid "desktop %i"
 msgstr "desktop %i"
 
-#: openbox/session.c:104
-#, c-format
-msgid "Unable to make directory \"%s\": %s"
-msgstr "\"%s\" ডিরেক্টরি নির্মাণ করতে ব্যর্থ: %s"
-
-#: openbox/session.c:466
-#, c-format
-msgid "Unable to save the session to \"%s\": %s"
-msgstr "\"%s\"-র সেশান সংরক্ষণ করতে ব্যর্থ: %s"
-
-#: openbox/session.c:605
-#, c-format
-msgid "Error while saving the session to \"%s\": %s"
-msgstr "\"%s\"-এ সেশান সংরক্ষণকালে সমস্যা: %s"
-
-#: openbox/session.c:842
-msgid "Not connected to a session manager"
-msgstr ""
-
-#: openbox/startupnotify.c:243
+#: openbox/startupnotify.c:241
 #, c-format
 msgid "Running %s"
 msgstr "%s সঞ্চালিত হচ্ছে"
@@ -467,15 +460,19 @@ msgstr "কি-বাইন্ডিং-র মধ্যে অবৈধ কি-
 msgid "Requested key \"%s\" does not exist on the display"
 msgstr "অনুরোধ করা কি \"%s\", প্রদর্শন ক্ষেত্রে উপস্থিত নেই"
 
-#: openbox/xerror.c:40
-#, c-format
-msgid "X Error: %s"
-msgstr "X সংক্রান্ত ত্রুটি: %s"
-
-#: openbox/prompt.c:200
+#: openbox/prompt.c:153
 msgid "OK"
 msgstr ""
 
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "\"%s\"-র সেশান সংরক্ষণ করতে ব্যর্থ: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "\"%s\"-এ সেশান সংরক্ষণকালে সমস্যা: %s"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X সংক্রান্ত ত্রুটি: %s"
+
 #~ msgid "Failed to execute \"%s\": %s"
 #~ msgstr "\"%s\" সঞ্চালন করতে ব্যর্থ: %s"
 
index 287af4f..b892ebc 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -7,85 +7,75 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Openbox 3.4.7.2\n"
 "Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2008-11-15 22:28+0100\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
 "PO-Revision-Date: 2008-05-25 19:23+0200\n"
 "Last-Translator: David Majà Martínez <davidmaja@gmail.com>\n"
 "Language-Team: catalan\n"
+"Language: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: openbox/actions.c:149
+#: openbox/actions.c:198
 #, c-format
 msgid "Invalid action \"%s\" requested. No such action exists."
 msgstr "L'acció sollicitada \"%s\" no és vàlida. Aquesta acció no existeix."
 
-#: openbox/actions/execute.c:128
+#: openbox/actions/execute.c:147
 msgid "No"
 msgstr "No"
 
-#: openbox/actions/execute.c:129
+#: openbox/actions/execute.c:148
 msgid "Yes"
 msgstr "Sí"
 
-#: openbox/actions/execute.c:133
+#: openbox/actions/execute.c:152
 msgid "Execute"
 msgstr "Executa"
 
-#: openbox/actions/execute.c:142
+#: openbox/actions/execute.c:161
 #, c-format
 msgid "Failed to convert the path \"%s\" from utf8"
 msgstr "No s'ha pogut convertir el camí \"%s\" des de utf8"
 
-#: openbox/actions/exit.c:52 openbox/actions/session.c:64
-#: openbox/client.c:3465
+#: openbox/actions/exit.c:69 openbox/client.c:3550
 msgid "Cancel"
 msgstr "Cancel·la"
 
-#: openbox/actions/exit.c:53
+#: openbox/actions/exit.c:70
 msgid "Exit"
 msgstr "Surt"
 
-#: openbox/actions/exit.c:56
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Esteu segur de voler sortir?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Surt"
+
+#: openbox/actions/exit.c:78
 msgid "Are you sure you want to exit Openbox?"
 msgstr "Esteu segur de voler sortir de Openbox?"
 
-#: openbox/actions/exit.c:57
+#: openbox/actions/exit.c:79
 msgid "Exit Openbox"
 msgstr "Surt de Openbox"
 
-#. TRANSLATORS: Don't translate the word "SessionLogout" as it's the
-#. name of the action you write in rc.xml
-#: openbox/actions/session.c:43
-msgid ""
-"The SessionLogout action is not available since Openbox was built without "
-"session management support"
-msgstr ""
-"L'acció SessionLogout no està disponible ja que el Openbox s'ha compilat "
-"sense suport per a la gestió de sessions"
-
-#: openbox/actions/session.c:65 openbox/actions/session.c:70
-msgid "Log Out"
-msgstr "Surt"
-
-#: openbox/actions/session.c:69
-msgid "Are you sure you want to log out?"
-msgstr "Esteu segur de voler sortir?"
-
-#: openbox/client.c:2012
+#: openbox/client.c:2037
 msgid "Unnamed Window"
 msgstr "Finestra sense nom"
 
-#: openbox/client.c:2026 openbox/client.c:2058
+#: openbox/client.c:2051 openbox/client.c:2082
 msgid "Killing..."
 msgstr "S'està finalitzant..."
 
-#: openbox/client.c:2028 openbox/client.c:2060
+#: openbox/client.c:2053 openbox/client.c:2084
 msgid "Not Responding"
 msgstr "No està responent"
 
-#: openbox/client.c:3454
+#: openbox/client.c:3539
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to force it "
@@ -94,11 +84,11 @@ msgstr ""
 "Sembla que la finestra \"%s\" no està responent. Voleu forçar-la a "
 "finalitzar enviant el senyal %s?"
 
-#: openbox/client.c:3456
+#: openbox/client.c:3541
 msgid "End Process"
 msgstr "Finalitza el procés"
 
-#: openbox/client.c:3460
+#: openbox/client.c:3545
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to disconnect "
@@ -107,139 +97,154 @@ msgstr ""
 "Sembla que la finestra \"%s\" no està responent.  Voleu desconnectar-la del "
 "servidor d'X?"
 
-#: openbox/client.c:3462
+#: openbox/client.c:3547
 msgid "Disconnect"
 msgstr "Desconnecta"
 
-#: openbox/client_list_combined_menu.c:87 openbox/client_list_menu.c:91
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
 msgid "Go there..."
 msgstr "Vés aquí..."
 
-#: openbox/client_list_combined_menu.c:94
+#: openbox/client_list_combined_menu.c:100
 msgid "Manage desktops"
 msgstr "Gestiona els escriptoris"
 
-#: openbox/client_list_combined_menu.c:95 openbox/client_list_menu.c:155
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
 msgid "_Add new desktop"
 msgstr "_Afegeix un nou escriptori"
 
-#: openbox/client_list_combined_menu.c:96 openbox/client_list_menu.c:156
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
 msgid "_Remove last desktop"
 msgstr "_Suprimeix l'últim escriptori"
 
-#: openbox/client_list_combined_menu.c:149
+#: openbox/client_list_combined_menu.c:157
 msgid "Windows"
 msgstr "Finestres"
 
-#: openbox/client_list_menu.c:203
+#: openbox/client_list_menu.c:214
 msgid "Desktops"
 msgstr "Escriptoris"
 
-#: openbox/client_menu.c:258
+#: openbox/client_menu.c:259
 msgid "All desktops"
 msgstr "Tots els escriptoris"
 
-#: openbox/client_menu.c:370
+#: openbox/client_menu.c:371
 msgid "_Layer"
 msgstr "_Capa"
 
-#: openbox/client_menu.c:375
+#: openbox/client_menu.c:376
 msgid "Always on _top"
 msgstr "Sempre a so_bre"
 
-#: openbox/client_menu.c:376
+#: openbox/client_menu.c:377
 msgid "_Normal"
 msgstr "_Normal"
 
-#: openbox/client_menu.c:377
+#: openbox/client_menu.c:378
 msgid "Always on _bottom"
 msgstr "Sempre a so_ta"
 
-#: openbox/client_menu.c:379
+#: openbox/client_menu.c:380
 msgid "_Send to desktop"
 msgstr "A l'_escriptori"
 
-#: openbox/client_menu.c:383
+#: openbox/client_menu.c:384
 msgid "Client menu"
 msgstr "Menú del client"
 
-#: openbox/client_menu.c:393
+#: openbox/client_menu.c:394
 msgid "R_estore"
 msgstr "Restaur_a"
 
-#: openbox/client_menu.c:397
+#: openbox/client_menu.c:398
 msgid "_Move"
 msgstr "_Mou"
 
-#: openbox/client_menu.c:399
+#: openbox/client_menu.c:400
 msgid "Resi_ze"
 msgstr "Redimen_siona"
 
-#: openbox/client_menu.c:401
+#: openbox/client_menu.c:402
 msgid "Ico_nify"
 msgstr "Mi_nimitza"
 
-#: openbox/client_menu.c:405
+#: openbox/client_menu.c:406
 msgid "Ma_ximize"
 msgstr "Ma_ximitza"
 
-#: openbox/client_menu.c:409
+#: openbox/client_menu.c:410
 msgid "_Roll up/down"
 msgstr "En/Desen_rotlla"
 
-#: openbox/client_menu.c:411
+#: openbox/client_menu.c:414
 msgid "Un/_Decorate"
 msgstr "Sense/Amb _decoració"
 
-#: openbox/client_menu.c:415
+#: openbox/client_menu.c:418
 msgid "_Close"
 msgstr "_Tanca"
 
-#: openbox/config.c:781
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "El context \"%s\" no és vàlid en la vinculació del ratolí"
+
+#: openbox/config.c:857
 #, c-format
 msgid "Invalid button \"%s\" specified in config file"
 msgstr "El botó especificat al fitxer de configuració \"%s\" no és vàlid."
 
-#: openbox/keyboard.c:157
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "No és pot crear el directori '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Tanca"
+
+#: openbox/keyboard.c:161
 msgid "Conflict with key binding in config file"
 msgstr "Conflicte amb la tecla vinculada en el fitxer de configuració"
 
-#: openbox/menu.c:102 openbox/menu.c:110
+#: openbox/menu.c:94 openbox/menu.c:106
 #, c-format
 msgid "Unable to find a valid menu file \"%s\""
 msgstr "No s'ha pogut trobar un fitxer de menú \"%s\" vàlid"
 
-#: openbox/menu.c:170
+#: openbox/menu.c:158
 #, c-format
 msgid "Failed to execute command for pipe-menu \"%s\": %s"
 msgstr ""
 "S'ha produït un error en executar l'ordre per al menú de conducte \"%s\": %s"
 
-#: openbox/menu.c:184
+#: openbox/menu.c:172
 #, c-format
 msgid "Invalid output from pipe-menu \"%s\""
 msgstr "La sortida del menú de conducte \"%s\" no és vàlida"
 
-#: openbox/menu.c:197
+#: openbox/menu.c:185
 #, c-format
 msgid "Attempted to access menu \"%s\" but it does not exist"
 msgstr "S'ha intentat accedir al menú \"%s\" ja que no existeix"
 
-#: openbox/menu.c:367 openbox/menu.c:368
+#: openbox/menu.c:400 openbox/menu.c:401
 msgid "More..."
 msgstr "Més..."
 
-#: openbox/mouse.c:373
+#: openbox/mouse.c:376
 #, c-format
 msgid "Invalid button \"%s\" in mouse binding"
 msgstr "El botó \"%s\" no és vàlid en la vinculació del ratolí"
 
-#: openbox/mouse.c:379
-#, c-format
-msgid "Invalid context \"%s\" in mouse binding"
-msgstr "El context \"%s\" no és vàlid en la vinculació del ratolí"
-
-#: openbox/openbox.c:133
+#: openbox/openbox.c:137
 #, c-format
 msgid "Unable to change to home directory \"%s\": %s"
 msgstr "No s'ha pogut canviar al directori de l'usuari \"%s\": %s"
@@ -248,29 +253,29 @@ msgstr "No s'ha pogut canviar al directori de l'usuari \"%s\": %s"
 msgid "Failed to open the display from the DISPLAY environment variable."
 msgstr "No s'ha pogut obrir la pantalla des de la variable d'entorn DISPLAY"
 
-#: openbox/openbox.c:183
+#: openbox/openbox.c:182
 msgid "Failed to initialize the obrender library."
 msgstr "S'ha produït un error en inicialitza la llibreria obrender."
 
-#: openbox/openbox.c:194
+#: openbox/openbox.c:193
 msgid "X server does not support locale."
 msgstr "El servidor X no te suport per a idiomes"
 
-#: openbox/openbox.c:196
+#: openbox/openbox.c:195
 msgid "Cannot set locale modifiers for the X server."
 msgstr "No s'ha pogut assignar els modificadors del locale per al servidor X."
 
-#: openbox/openbox.c:263
+#: openbox/openbox.c:253
 msgid "Unable to find a valid config file, using some simple defaults"
 msgstr ""
 "No s'ha pogut trobat un fitxer de configuració vàlid, s'utilitzaran alguns "
 "valors predeterminats"
 
-#: openbox/openbox.c:297
+#: openbox/openbox.c:286
 msgid "Unable to load a theme."
 msgstr "No s'ha pogut carregar el tema."
 
-#: openbox/openbox.c:377
+#: openbox/openbox.c:370
 #, c-format
 msgid ""
 "One or more XML syntax errors were found while parsing the Openbox "
@@ -281,30 +286,26 @@ msgstr ""
 "configuració de Openbox. Per a més informació visualitza el stdout. L'últim "
 "error trobat estava al fitxer \"%s\" línia %d, amb el missatge: %s"
 
-#: openbox/openbox.c:379
+#: openbox/openbox.c:372
 msgid "Openbox Syntax Error"
 msgstr "Error de sintaxi de Openbox"
 
-#: openbox/openbox.c:379
-msgid "Close"
-msgstr "Tanca"
-
-#: openbox/openbox.c:448
+#: openbox/openbox.c:438
 #, c-format
 msgid "Restart failed to execute new executable \"%s\": %s"
 msgstr ""
 "S'ha produït un error en tornar a iniciar i executar el nou executable \"%s"
 "\": %s"
 
-#: openbox/openbox.c:518 openbox/openbox.c:520
+#: openbox/openbox.c:517 openbox/openbox.c:519
 msgid "Copyright (c)"
 msgstr "Copyright (c)"
 
-#: openbox/openbox.c:529
+#: openbox/openbox.c:528
 msgid "Syntax: openbox [options]\n"
 msgstr "Sintaxis: openbox [opcions]\n"
 
-#: openbox/openbox.c:530
+#: openbox/openbox.c:529
 msgid ""
 "\n"
 "Options:\n"
@@ -312,15 +313,15 @@ msgstr ""
 "\n"
 "Opcions:\n"
 
-#: openbox/openbox.c:531
+#: openbox/openbox.c:530
 msgid "  --help              Display this help and exit\n"
 msgstr "  --help              Visualitza aquesta ajuda i surt\n"
 
-#: openbox/openbox.c:532
+#: openbox/openbox.c:531
 msgid "  --version           Display the version and exit\n"
 msgstr "  --version           Visualitza la versió i surt\n"
 
-#: openbox/openbox.c:533
+#: openbox/openbox.c:532
 msgid "  --replace           Replace the currently running window manager\n"
 msgstr ""
 "  --replace           Reemplaça el gestor de finestres que s'està executant "
@@ -329,18 +330,18 @@ msgstr ""
 #. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
 #. aligned still, if you have to, make a new line with \n and 22 spaces. It's
 #. fine to leave it as FILE though.
-#: openbox/openbox.c:537
+#: openbox/openbox.c:536
 msgid "  --config-file FILE  Specify the path to the config file to use\n"
 msgstr ""
 "  --config-file FITXER\n"
 "                      Especifica el camí del fitxer de configuració a "
 "utilitzar\n"
 
-#: openbox/openbox.c:538
+#: openbox/openbox.c:537
 msgid "  --sm-disable        Disable connection to the session manager\n"
 msgstr "  --sm-disable        Inhabilita la connexió amb el gestor de sessió\n"
 
-#: openbox/openbox.c:539
+#: openbox/openbox.c:538
 msgid ""
 "\n"
 "Passing messages to a running Openbox instance:\n"
@@ -349,19 +350,19 @@ msgstr ""
 "S'està transferint missatges a la instància del Openbox que s'està "
 "executant:\n"
 
-#: openbox/openbox.c:540
+#: openbox/openbox.c:539
 msgid "  --reconfigure       Reload Openbox's configuration\n"
 msgstr "  --reconfigure       Torna a carregar la configuració de Openbox\n"
 
-#: openbox/openbox.c:541
+#: openbox/openbox.c:540
 msgid "  --restart           Restart Openbox\n"
 msgstr "  --restart           Torna a iniciar Openbox\n"
 
-#: openbox/openbox.c:542
+#: openbox/openbox.c:541
 msgid "  --exit              Exit Openbox\n"
 msgstr "  --exit              Surt de Openbox\n"
 
-#: openbox/openbox.c:543
+#: openbox/openbox.c:542
 msgid ""
 "\n"
 "Debugging options:\n"
@@ -369,10 +370,14 @@ msgstr ""
 "\n"
 "Opcions de depuració:\n"
 
-#: openbox/openbox.c:544
+#: openbox/openbox.c:543
 msgid "  --sync              Run in synchronous mode\n"
 msgstr "  --sync              Executa en mode sincronitzat\n"
 
+#: openbox/openbox.c:544
+msgid "  --startup CMD       Run CMD after starting\n"
+msgstr ""
+
 #: openbox/openbox.c:545
 msgid "  --debug             Display debugging output\n"
 msgstr "  --debug             Mostra la sortida de depuració\n"
@@ -384,12 +389,19 @@ msgstr ""
 "focus\n"
 
 #: openbox/openbox.c:547
+#, fuzzy
+msgid "  --debug-session     Display debugging output for session management\n"
+msgstr ""
+"  --debug-session     Mostra la sortida de depuració per a la gestió del "
+"session management\n"
+
+#: openbox/openbox.c:548
 msgid "  --debug-xinerama    Split the display into fake xinerama screens\n"
 msgstr ""
 "  --debug-xinerama    Divideix la visualització en pantalles xinerama "
 "falses\n"
 
-#: openbox/openbox.c:548
+#: openbox/openbox.c:549
 #, c-format
 msgid ""
 "\n"
@@ -398,27 +410,28 @@ msgstr ""
 "\n"
 "Informeu dels errors a %s\n"
 
-#: openbox/openbox.c:617
-msgid "--config-file requires an argument\n"
-msgstr "--config-file necessita un argument\n"
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s necessita un argument\n"
 
-#: openbox/openbox.c:660
+#: openbox/openbox.c:709
 #, c-format
 msgid "Invalid command line argument \"%s\"\n"
 msgstr "Opció \"%s\" no vàlida a la línia d'ordres\n"
 
-#: openbox/screen.c:102 openbox/screen.c:190
+#: openbox/screen.c:106 openbox/screen.c:191
 #, c-format
 msgid "A window manager is already running on screen %d"
 msgstr "Encara s'està executant un gestor de finestres a la pantalla %d"
 
-#: openbox/screen.c:124
+#: openbox/screen.c:127
 #, c-format
 msgid "Could not acquire window manager selection on screen %d"
 msgstr ""
 "No s'ha pogut adquirir la selecció del gestor de finestres en la pantalla %d"
 
-#: openbox/screen.c:145
+#: openbox/screen.c:150
 #, c-format
 msgid "The WM on screen %d is not exiting"
 msgstr "El gestor de finestres de la pantalla %d no està sortint"
@@ -427,7 +440,7 @@ msgstr "El gestor de finestres de la pantalla %d no està sortint"
 #. arguments, you can use %1$d for the first one and %2$d for the
 #. second one. For example,
 #. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
-#: openbox/screen.c:412
+#: openbox/screen.c:418
 #, c-format
 msgid ""
 "Openbox is configured for %d desktop, but the current session has %d.  "
@@ -436,37 +449,18 @@ msgid_plural ""
 "Openbox is configured for %d desktops, but the current session has %d.  "
 "Overriding the Openbox configuration."
 msgstr[0] ""
-"El Openbox està configurat per a %d escriptori, però la sessió actual en te %"
-"d.  S'està modificant la configuració del Openbox."
+"El Openbox està configurat per a %d escriptori, però la sessió actual en te "
+"%d.  S'està modificant la configuració del Openbox."
 msgstr[1] ""
 "El Openbox està configurat per a %d escriptoris, però la sessió actual en te "
 "%d.  S'està modificant la configuració del Openbox."
 
-#: openbox/screen.c:1180
+#: openbox/screen.c:1205
 #, c-format
 msgid "desktop %i"
 msgstr "escriptori %i"
 
-#: openbox/session.c:104
-#, c-format
-msgid "Unable to make directory \"%s\": %s"
-msgstr "No és pot crear el directori \"%s\": %s"
-
-#: openbox/session.c:466
-#, c-format
-msgid "Unable to save the session to \"%s\": %s"
-msgstr "No s'ha pogut desar la sessió a \"%s\": %s"
-
-#: openbox/session.c:605
-#, c-format
-msgid "Error while saving the session to \"%s\": %s"
-msgstr "S'ha produït un error mentre es desava la sessió a \"%s\": %s"
-
-#: openbox/session.c:842
-msgid "Not connected to a session manager"
-msgstr "No esteu connectats al gestor de sessions"
-
-#: openbox/startupnotify.c:243
+#: openbox/startupnotify.c:241
 #, c-format
 msgid "Running %s"
 msgstr "Executant %s"
@@ -492,15 +486,36 @@ msgstr "El nom de la tecla \"%s\" no és vàlid en la vinculació de tecles"
 msgid "Requested key \"%s\" does not exist on the display"
 msgstr "La tecla seleccionada \"%s\" no existeix a la pantalla"
 
-#: openbox/xerror.c:40
-#, c-format
-msgid "X Error: %s"
-msgstr "Error d'X: %s"
-
-#: openbox/prompt.c:200
+#: openbox/prompt.c:153
 msgid "OK"
 msgstr "D'acord"
 
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Surt de Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file necessita un argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "L'acció SessionLogout no està disponible ja que el Openbox s'ha compilat "
+#~ "sense suport per a la gestió de sessions"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "No s'ha pogut desar la sessió a \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "S'ha produït un error mentre es desava la sessió a \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "No esteu connectats al gestor de sessions"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Error d'X: %s"
+
 #~ msgid "Failed to execute \"%s\": %s"
 #~ msgstr "No s'ha pogut executar \"%s\": %s"
 
index 49ad300..79d84cf 100644 (file)
--- a/po/cs.po
+++ b/po/cs.po
@@ -7,233 +7,238 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Openbox 3.4.7\n"
 "Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2008-11-15 22:28+0100\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
 "PO-Revision-Date: 2008-03-17 17:00+0100\n"
 "Last-Translator: tezlo <tezlo@gmx.net>\n"
 "Language-Team: Czech <cs@li.org>\n"
+"Language: cs\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: openbox/actions.c:149
+#: openbox/actions.c:198
 #, c-format
 msgid "Invalid action \"%s\" requested. No such action exists."
 msgstr "Požadována neplatná akce \"%s\". Žádná taková akce neexistuje."
 
-#: openbox/actions/execute.c:128
+#: openbox/actions/execute.c:147
 msgid "No"
 msgstr "Ne"
 
-#: openbox/actions/execute.c:129
+#: openbox/actions/execute.c:148
 msgid "Yes"
 msgstr "Ano"
 
-#: openbox/actions/execute.c:133
+#: openbox/actions/execute.c:152
 msgid "Execute"
 msgstr "Spustit"
 
-#: openbox/actions/execute.c:142
+#: openbox/actions/execute.c:161
 #, c-format
 msgid "Failed to convert the path \"%s\" from utf8"
 msgstr "Nepodařilo se převést cestu \"%s\" z utf8"
 
-#: openbox/actions/exit.c:52 openbox/actions/session.c:64
-#: openbox/client.c:3465
+#: openbox/actions/exit.c:69 openbox/client.c:3550
 msgid "Cancel"
 msgstr "Zrušit"
 
-#: openbox/actions/exit.c:53
+#: openbox/actions/exit.c:70
 msgid "Exit"
 msgstr "Konec"
 
-#: openbox/actions/exit.c:56
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Určitě odhlásit?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Odhlásit"
+
+#: openbox/actions/exit.c:78
 msgid "Are you sure you want to exit Openbox?"
 msgstr "Určitě chcete ukončit Openbox?"
 
-#: openbox/actions/exit.c:57
+#: openbox/actions/exit.c:79
 msgid "Exit Openbox"
 msgstr "Ukončit Openbox"
 
-#. TRANSLATORS: Don't translate the word "SessionLogout" as it's the
-#. name of the action you write in rc.xml
-#: openbox/actions/session.c:43
-msgid ""
-"The SessionLogout action is not available since Openbox was built without "
-"session management support"
-msgstr ""
-"Akce SessionLogout není k dispozici jelikož byl Openbox zkompilován bez "
-"podpory session manageru"
-
-#: openbox/actions/session.c:65 openbox/actions/session.c:70
-msgid "Log Out"
-msgstr "Odhlásit"
-
-#: openbox/actions/session.c:69
-msgid "Are you sure you want to log out?"
-msgstr "Určitě odhlásit?"
-
-#: openbox/client.c:2012
+#: openbox/client.c:2037
 msgid "Unnamed Window"
 msgstr "Nepojmenované Okno"
 
-#: openbox/client.c:2026 openbox/client.c:2058
+#: openbox/client.c:2051 openbox/client.c:2082
 msgid "Killing..."
 msgstr "Ukončuji..."
 
-#: openbox/client.c:2028 openbox/client.c:2060
+#: openbox/client.c:2053 openbox/client.c:2084
 msgid "Not Responding"
 msgstr "Neodpovídá"
 
-#: openbox/client.c:3454
+#: openbox/client.c:3539
 #, 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 "Okno \"%s\" nedpovídá. Chcete jej ukončit signálem %s?"
 
-#: openbox/client.c:3456
+#: openbox/client.c:3541
 msgid "End Process"
 msgstr "Ukončit Proces"
 
-#: openbox/client.c:3460
+#: openbox/client.c:3545
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to disconnect "
 "it from the X server?"
 msgstr "Okno \"%s\" neodpovídá. Chcete jej odpojit od X serveru?"
 
-#: openbox/client.c:3462
+#: openbox/client.c:3547
 msgid "Disconnect"
 msgstr "Odpojit"
 
-#: openbox/client_list_combined_menu.c:87 openbox/client_list_menu.c:91
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
 msgid "Go there..."
 msgstr "Jdi tam..."
 
-#: openbox/client_list_combined_menu.c:94
+#: openbox/client_list_combined_menu.c:100
 msgid "Manage desktops"
 msgstr "Spravovat plochy"
 
-#: openbox/client_list_combined_menu.c:95 openbox/client_list_menu.c:155
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
 msgid "_Add new desktop"
 msgstr "_Přidat novou plochu"
 
-#: openbox/client_list_combined_menu.c:96 openbox/client_list_menu.c:156
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
 msgid "_Remove last desktop"
 msgstr "_Odstranit poslední plochu"
 
-#: openbox/client_list_combined_menu.c:149
+#: openbox/client_list_combined_menu.c:157
 msgid "Windows"
 msgstr "Okna"
 
-#: openbox/client_list_menu.c:203
+#: openbox/client_list_menu.c:214
 msgid "Desktops"
 msgstr "Plochy"
 
-#: openbox/client_menu.c:258
+#: openbox/client_menu.c:259
 msgid "All desktops"
 msgstr "Všechny plochy"
 
-#: openbox/client_menu.c:370
+#: openbox/client_menu.c:371
 msgid "_Layer"
 msgstr "Vrs_tva"
 
-#: openbox/client_menu.c:375
+#: openbox/client_menu.c:376
 msgid "Always on _top"
 msgstr "Vždy na_vrchu"
 
-#: openbox/client_menu.c:376
+#: openbox/client_menu.c:377
 msgid "_Normal"
 msgstr "_Normální"
 
-#: openbox/client_menu.c:377
+#: openbox/client_menu.c:378
 msgid "Always on _bottom"
 msgstr "Vždy ve_spodu"
 
-#: openbox/client_menu.c:379
+#: openbox/client_menu.c:380
 msgid "_Send to desktop"
 msgstr "_Poslat na plochu"
 
-#: openbox/client_menu.c:383
+#: openbox/client_menu.c:384
 msgid "Client menu"
 msgstr "Menu klienta"
 
-#: openbox/client_menu.c:393
+#: openbox/client_menu.c:394
 msgid "R_estore"
 msgstr "_Obnovit"
 
-#: openbox/client_menu.c:397
+#: openbox/client_menu.c:398
 msgid "_Move"
 msgstr "Přes_unout"
 
-#: openbox/client_menu.c:399
+#: openbox/client_menu.c:400
 msgid "Resi_ze"
 msgstr "Veli_kost"
 
-#: openbox/client_menu.c:401
+#: openbox/client_menu.c:402
 msgid "Ico_nify"
 msgstr "Mi_nimalizovat"
 
-#: openbox/client_menu.c:405
+#: openbox/client_menu.c:406
 msgid "Ma_ximize"
 msgstr "Ma_ximalizovat"
 
-#: openbox/client_menu.c:409
+#: openbox/client_menu.c:410
 msgid "_Roll up/down"
 msgstr "S_rolovat/Vyrolovat"
 
-#: openbox/client_menu.c:411
+#: openbox/client_menu.c:414
 msgid "Un/_Decorate"
 msgstr "Oz_dobit/Odzdobit"
 
-#: openbox/client_menu.c:415
+#: openbox/client_menu.c:418
 msgid "_Close"
 msgstr "_Zavřít"
 
-#: openbox/config.c:781
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Neplatný kontext \"%s\" v nastavení myši"
+
+#: openbox/config.c:857
 #, c-format
 msgid "Invalid button \"%s\" specified in config file"
 msgstr "Neplatné tlačítko \"%s\" v konfiguračním souboru"
 
-#: openbox/keyboard.c:157
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Nepodařilo se vytvořit adresář '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Zavřít"
+
+#: openbox/keyboard.c:161
 msgid "Conflict with key binding in config file"
 msgstr "Konflikt klávesových zkratek v konfiguračním souboru"
 
-#: openbox/menu.c:102 openbox/menu.c:110
+#: openbox/menu.c:94 openbox/menu.c:106
 #, c-format
 msgid "Unable to find a valid menu file \"%s\""
 msgstr "Nepodařilo se najít platný menu soubor \"%s\""
 
-#: openbox/menu.c:170
+#: openbox/menu.c:158
 #, c-format
 msgid "Failed to execute command for pipe-menu \"%s\": %s"
 msgstr "Nepodařilo se spustit příkaz pro pipe-menu \"%s\": %s"
 
-#: openbox/menu.c:184
+#: openbox/menu.c:172
 #, c-format
 msgid "Invalid output from pipe-menu \"%s\""
 msgstr "Neplatný výstup z pipe-menu \"%s\""
 
-#: openbox/menu.c:197
+#: openbox/menu.c:185
 #, c-format
 msgid "Attempted to access menu \"%s\" but it does not exist"
 msgstr "Pokus o přístup k menu \"%s\", ale ono neexistuje"
 
-#: openbox/menu.c:367 openbox/menu.c:368
+#: openbox/menu.c:400 openbox/menu.c:401
 msgid "More..."
 msgstr "Víc..."
 
-#: openbox/mouse.c:373
+#: openbox/mouse.c:376
 #, c-format
 msgid "Invalid button \"%s\" in mouse binding"
 msgstr "Neplatné tlačítko \"%s\" v nastavení myši"
 
-#: openbox/mouse.c:379
-#, c-format
-msgid "Invalid context \"%s\" in mouse binding"
-msgstr "Neplatný kontext \"%s\" v nastavení myši"
-
-#: openbox/openbox.c:133
+#: openbox/openbox.c:137
 #, c-format
 msgid "Unable to change to home directory \"%s\": %s"
 msgstr "Nepodařilo se přejít do domácího adresáře \"%s\": %s"
@@ -242,29 +247,29 @@ msgstr "Nepodařilo se přejít do domácího adresáře \"%s\": %s"
 msgid "Failed to open the display from the DISPLAY environment variable."
 msgstr "Nepodařilo se otevřít displej z proměnné prostředí DISPLAY."
 
-#: openbox/openbox.c:183
+#: openbox/openbox.c:182
 msgid "Failed to initialize the obrender library."
 msgstr "Nepodařilo se inicializovat knihovnu obrender."
 
-#: openbox/openbox.c:194
+#: openbox/openbox.c:193
 msgid "X server does not support locale."
 msgstr "X server nepodporuje lokalizaci."
 
-#: openbox/openbox.c:196
+#: openbox/openbox.c:195
 msgid "Cannot set locale modifiers for the X server."
 msgstr "Nelze nastavit modifikátory lokalizace pro X server."
 
-#: openbox/openbox.c:263
+#: openbox/openbox.c:253
 msgid "Unable to find a valid config file, using some simple defaults"
 msgstr ""
 "Nepodařilo se najít platný konfigurační soubor, pokračuji s výchozím "
 "nastavením"
 
-#: openbox/openbox.c:297
+#: openbox/openbox.c:286
 msgid "Unable to load a theme."
 msgstr "Nepodařilo se načíst motiv."
 
-#: openbox/openbox.c:377
+#: openbox/openbox.c:370
 #, c-format
 msgid ""
 "One or more XML syntax errors were found while parsing the Openbox "
@@ -275,28 +280,24 @@ msgstr ""
 "chyb, více informací na standartním výstupu. Poslední zaznamenaná chyba je v "
 "souboru \"%s\" na řádku %d, se zprávou: %s"
 
-#: openbox/openbox.c:379
+#: openbox/openbox.c:372
 msgid "Openbox Syntax Error"
 msgstr "Openbox Chyba Syntaxe"
 
-#: openbox/openbox.c:379
-msgid "Close"
-msgstr "Zavřít"
-
-#: openbox/openbox.c:448
+#: openbox/openbox.c:438
 #, c-format
 msgid "Restart failed to execute new executable \"%s\": %s"
 msgstr "Při restartu se nepodařilo spustit nový program \"%s\": %s"
 
-#: openbox/openbox.c:518 openbox/openbox.c:520
+#: openbox/openbox.c:517 openbox/openbox.c:519
 msgid "Copyright (c)"
 msgstr "Copyright (c)"
 
-#: openbox/openbox.c:529
+#: openbox/openbox.c:528
 msgid "Syntax: openbox [options]\n"
 msgstr "Syntaxe: openbox [přepínače]\n"
 
-#: openbox/openbox.c:530
+#: openbox/openbox.c:529
 msgid ""
 "\n"
 "Options:\n"
@@ -304,30 +305,30 @@ msgstr ""
 "\n"
 "Přepínače:\n"
 
-#: openbox/openbox.c:531
+#: openbox/openbox.c:530
 msgid "  --help              Display this help and exit\n"
 msgstr "  --help              Zobrazit tuto nápovědu a skončit\n"
 
-#: openbox/openbox.c:532
+#: openbox/openbox.c:531
 msgid "  --version           Display the version and exit\n"
 msgstr "  --version           Zobrazit verzi a skončit\n"
 
-#: openbox/openbox.c:533
+#: openbox/openbox.c:532
 msgid "  --replace           Replace the currently running window manager\n"
 msgstr "  --replace           Nahradit běžící window manager\n"
 
 #. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
 #. aligned still, if you have to, make a new line with \n and 22 spaces. It's
 #. fine to leave it as FILE though.
-#: openbox/openbox.c:537
+#: openbox/openbox.c:536
 msgid "  --config-file FILE  Specify the path to the config file to use\n"
 msgstr "  --config-file FILE  Cesta ke konfiguračnímu souboru\n"
 
-#: openbox/openbox.c:538
+#: openbox/openbox.c:537
 msgid "  --sm-disable        Disable connection to the session manager\n"
 msgstr "  --sm-disable        Nepřipojovat se k session manageru\n"
 
-#: openbox/openbox.c:539
+#: openbox/openbox.c:538
 msgid ""
 "\n"
 "Passing messages to a running Openbox instance:\n"
@@ -335,19 +336,19 @@ msgstr ""
 "\n"
 "Zasílání zpráv běžící instanci Openbox:\n"
 
-#: openbox/openbox.c:540
+#: openbox/openbox.c:539
 msgid "  --reconfigure       Reload Openbox's configuration\n"
 msgstr "  --reconfigure       Znovu načíst konfiguraci Openbox\n"
 
-#: openbox/openbox.c:541
+#: openbox/openbox.c:540
 msgid "  --restart           Restart Openbox\n"
 msgstr "  --restart           Restartovat Openbox\n"
 
-#: openbox/openbox.c:542
+#: openbox/openbox.c:541
 msgid "  --exit              Exit Openbox\n"
 msgstr "  --exit              Ukončit Openbox\n"
 
-#: openbox/openbox.c:543
+#: openbox/openbox.c:542
 msgid ""
 "\n"
 "Debugging options:\n"
@@ -355,10 +356,14 @@ msgstr ""
 "\n"
 "Ladící přepínače:\n"
 
-#: openbox/openbox.c:544
+#: openbox/openbox.c:543
 msgid "  --sync              Run in synchronous mode\n"
 msgstr "  --sync              Spustit v synchronním módu\n"
 
+#: openbox/openbox.c:544
+msgid "  --startup CMD       Run CMD after starting\n"
+msgstr ""
+
 #: openbox/openbox.c:545
 msgid "  --debug             Display debugging output\n"
 msgstr "  --debug             Zobrazit ladící výstup\n"
@@ -368,10 +373,14 @@ msgid "  --debug-focus       Display debugging output for focus handling\n"
 msgstr "  --debug-focus       Zobrazit ladící výstup pro správu oken\n"
 
 #: openbox/openbox.c:547
+msgid "  --debug-session     Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
 msgid "  --debug-xinerama    Split the display into fake xinerama screens\n"
 msgstr "  --debug-xinerama    Rozdělit displej na falešné obrazovky xinerama\n"
 
-#: openbox/openbox.c:548
+#: openbox/openbox.c:549
 #, c-format
 msgid ""
 "\n"
@@ -380,26 +389,27 @@ msgstr ""
 "\n"
 "Prosím hlašte chyby na %s\n"
 
-#: openbox/openbox.c:617
-msgid "--config-file requires an argument\n"
-msgstr "--config-file vyžaduje argument\n"
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s vyžaduje argument\n"
 
-#: openbox/openbox.c:660
+#: openbox/openbox.c:709
 #, c-format
 msgid "Invalid command line argument \"%s\"\n"
 msgstr "Neplatný argument příkazové řádky \"%s\"\n"
 
-#: openbox/screen.c:102 openbox/screen.c:190
+#: openbox/screen.c:106 openbox/screen.c:191
 #, c-format
 msgid "A window manager is already running on screen %d"
 msgstr "Na obrazovce %d již nějaký window manager běží"
 
-#: openbox/screen.c:124
+#: openbox/screen.c:127
 #, c-format
 msgid "Could not acquire window manager selection on screen %d"
 msgstr "Nepodařilo se získat výseč pro window manager na obrazovce %d"
 
-#: openbox/screen.c:145
+#: openbox/screen.c:150
 #, c-format
 msgid "The WM on screen %d is not exiting"
 msgstr "Window manager na obrazovce %d ne a ne skončit"
@@ -409,7 +419,7 @@ msgstr "Window manager na obrazovce %d ne a ne skončit"
 #. arguments, you can use %1$d for the first one and %2$d for the
 #. second one. For example,
 #. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
-#: openbox/screen.c:412
+#: openbox/screen.c:418
 #, fuzzy, c-format
 msgid ""
 "Openbox is configured for %d desktop, but the current session has %d.  "
@@ -424,31 +434,12 @@ msgstr[1] ""
 "Openbox je nakonfigurován pro %d ploch, ale současná session má %d. "
 "Konfigurace Openboxu bude změněna."
 
-#: openbox/screen.c:1180
+#: openbox/screen.c:1205
 #, c-format
 msgid "desktop %i"
 msgstr "plochu %i"
 
-#: openbox/session.c:104
-#, c-format
-msgid "Unable to make directory \"%s\": %s"
-msgstr "Nepodařilo se vytvořit adresář \"%s\": %s"
-
-#: openbox/session.c:466
-#, c-format
-msgid "Unable to save the session to \"%s\": %s"
-msgstr "Nepodařilo se uložit session do \"%s\": %s"
-
-#: openbox/session.c:605
-#, c-format
-msgid "Error while saving the session to \"%s\": %s"
-msgstr "Chyba během ukládání session do \"%s\": %s"
-
-#: openbox/session.c:842
-msgid "Not connected to a session manager"
-msgstr "Nepřipojen k session manageru"
-
-#: openbox/startupnotify.c:243
+#: openbox/startupnotify.c:241
 #, c-format
 msgid "Running %s"
 msgstr "Spouštím %s"
@@ -473,15 +464,36 @@ msgstr "Neplatné jméno klávesy \"%s\" v nastavení"
 msgid "Requested key \"%s\" does not exist on the display"
 msgstr "Požadovaná klávesa \"%s\" na displeji neexistuje"
 
-#: openbox/xerror.c:40
-#, c-format
-msgid "X Error: %s"
-msgstr "X Chyba: %s"
-
-#: openbox/prompt.c:200
+#: openbox/prompt.c:153
 msgid "OK"
 msgstr "OK"
 
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Ukončit Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file vyžaduje argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Akce SessionLogout není k dispozici jelikož byl Openbox zkompilován bez "
+#~ "podpory session manageru"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Nepodařilo se uložit session do \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Chyba během ukládání session do \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nepřipojen k session manageru"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X Chyba: %s"
+
 #~ msgid "Failed to execute \"%s\": %s"
 #~ msgstr "Nepodařilo se spustit \"%s\": %s"
 
index 3fa8ed9..9b5bc40 100644 (file)
--- a/po/da.po
+++ b/po/da.po
@@ -7,85 +7,75 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Openbox 3.4.7.2\n"
 "Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2008-11-15 22:28+0100\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
 "PO-Revision-Date: 2008-08-19 16:50+0100\n"
 "Last-Translator: Jesper Sander <sander.contrib@gmail.com>\n"
 "Language-Team: None\n"
+"Language: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: openbox/actions.c:149
+#: openbox/actions.c:198
 #, c-format
 msgid "Invalid action \"%s\" requested. No such action exists."
 msgstr "Ugyldig operation \"%s\" anmodet. Operationen findes ikke."
 
-#: openbox/actions/execute.c:128
+#: openbox/actions/execute.c:147
 msgid "No"
 msgstr "Nej"
 
-#: openbox/actions/execute.c:129
+#: openbox/actions/execute.c:148
 msgid "Yes"
 msgstr "Ja"
 
-#: openbox/actions/execute.c:133
+#: openbox/actions/execute.c:152
 msgid "Execute"
 msgstr "Udfør"
 
-#: openbox/actions/execute.c:142
+#: openbox/actions/execute.c:161
 #, c-format
 msgid "Failed to convert the path \"%s\" from utf8"
 msgstr "Fejl ved konvertering af stien \"%s\" fra utf8"
 
-#: openbox/actions/exit.c:52 openbox/actions/session.c:64
-#: openbox/client.c:3465
+#: openbox/actions/exit.c:69 openbox/client.c:3550
 msgid "Cancel"
 msgstr "Afbryd"
 
-#: openbox/actions/exit.c:53
+#: openbox/actions/exit.c:70
 msgid "Exit"
 msgstr "Afslut"
 
-#: openbox/actions/exit.c:56
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Er du sikker på at du vil logge ud?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Log Ud"
+
+#: openbox/actions/exit.c:78
 msgid "Are you sure you want to exit Openbox?"
 msgstr "Er du sikker på at du vil afslutte Openbox?"
 
-#: openbox/actions/exit.c:57
+#: openbox/actions/exit.c:79
 msgid "Exit Openbox"
 msgstr "Afslut Openbox"
 
-#. TRANSLATORS: Don't translate the word "SessionLogout" as it's the
-#. name of the action you write in rc.xml
-#: openbox/actions/session.c:43
-msgid ""
-"The SessionLogout action is not available since Openbox was built without "
-"session management support"
-msgstr ""
-"SessionLogout er ikke tilgænglig, fordi Openbox blev kompileret uden "
-"understøttelse for sessionsbehandling"
-
-#: openbox/actions/session.c:65 openbox/actions/session.c:70
-msgid "Log Out"
-msgstr "Log Ud"
-
-#: openbox/actions/session.c:69
-msgid "Are you sure you want to log out?"
-msgstr "Er du sikker på at du vil logge ud?"
-
-#: openbox/client.c:2012
+#: openbox/client.c:2037
 msgid "Unnamed Window"
 msgstr "Unavngivet vindue"
 
-#: openbox/client.c:2026 openbox/client.c:2058
+#: openbox/client.c:2051 openbox/client.c:2082
 msgid "Killing..."
 msgstr "Dræber..."
 
-#: openbox/client.c:2028 openbox/client.c:2060
+#: openbox/client.c:2053 openbox/client.c:2084
 msgid "Not Responding"
 msgstr "Svarer Ikke"
 
-#: openbox/client.c:3454
+#: openbox/client.c:3539
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to force it "
@@ -94,149 +84,164 @@ msgstr ""
 "Vinduet \"%s\" svarer ikke. Vil du udføre tvunget afslutning ved at sende %s "
 "signalet?"
 
-#: openbox/client.c:3456
+#: openbox/client.c:3541
 msgid "End Process"
 msgstr "Afslut proces"
 
-#: openbox/client.c:3460
+#: openbox/client.c:3545
 #, c-format
 msgid ""
 "The window \"%s\" does not seem to be responding.  Do you want to disconnect "
 "it from the X server?"
 msgstr "Vinduet \"%s\" svarer ikke. Vil du frakoble vinduet fra X-serveren?"
 
-#: openbox/client.c:3462
+#: openbox/client.c:3547
 msgid "Disconnect"
 msgstr "Frakoble"
 
-#: openbox/client_list_combined_menu.c:87 openbox/client_list_menu.c:91
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
 msgid "Go there..."
 msgstr "Gå der..."
 
-#: openbox/client_list_combined_menu.c:94
+#: openbox/client_list_combined_menu.c:100
 msgid "Manage desktops"
 msgstr "Håndter skrivebord"
 
-#: openbox/client_list_combined_menu.c:95 openbox/client_list_menu.c:155
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
 msgid "_Add new desktop"
 msgstr "_Nyt skrivebord"
 
-#: openbox/client_list_combined_menu.c:96 openbox/client_list_menu.c:156
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
 msgid "_Remove last desktop"
 msgstr "_Fjern sidste skrivebord"
 
-#: openbox/client_list_combined_menu.c:149
+#: openbox/client_list_combined_menu.c:157
 msgid "Windows"
 msgstr "Vinduer"
 
-#: openbox/client_list_menu.c:203
+#: openbox/client_list_menu.c:214
 msgid "Desktops"
 msgstr "Skrivebord"
 
-#: openbox/client_menu.c:258
+#: openbox/client_menu.c:259
 msgid "All desktops"
 msgstr "Alle skriveborde"
 
-#: openbox/client_menu.c:370
+#: openbox/client_menu.c:371
 msgid "_Layer"
 msgstr "La_g"
 
-#: openbox/client_menu.c:375
+#: openbox/client_menu.c:37