Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial import

  • Loading branch information...
commit 65b1f79f56802c9346656028e892add3398c1cc0 1 parent efab353
Ross Burton authored
View
1  AUTHORS
@@ -0,0 +1 @@
+Ross Burton <ross@burtonini.com>
View
62 ChangeLog
@@ -0,0 +1,62 @@
+2002-11-10 Ross Burton <ross@burtonini.com>
+
+ * configure.in: Bump version up to 0.2.4. Also check for the
+ regular expression POSIX headers, for...
+
+ * src/devilspie-matcher-windowname.gob: Use regular expression
+ matching instead of literal string compares. Thanks to Patrick
+ Aussems <zyk@bugfactory.org> for this kick-arse patch.
+
+2002-10-30 Ross Burton <ross@burtonini.com>
+
+ * src/devilspie-action-savegeometry.gob: Connect to the
+ window_closed signal of the WnckScreen in the constructor.
+
+ * README: Documentation is always a good thing. I was bored so I
+ fleshed this out a little.
+
+2002-10-24 Ross Burton <ross@burtonini.com>
+
+ * src/devilspie-action-debug.gob: Print out the window Role.
+
+ * src/devilspie-action-savegeometry.gob: Started on a Save Window
+ Geometry action. Doesn't do anything at the moment apart from
+ printing messages at the right moments.
+
+ * src/devilspie-matcher-windowname.gob: Re-wrote the return logic
+ to be much clearer, and started on a window role (WM_WINDOW_ROLE)
+ matcher, which also doesn't work.
+
+2002-10-17 Ross Burton <ross@burtonini.com>
+
+ * src/devilspie-action-layer.gob: Added an Action which can set
+ the ABOVE and BELOW layer hints on windows. Panel-below heaven is
+ here! :)
+
+ * src/devilspie.c: Parse the command line arguments
+ correctly. Open the file specified, if no file is specified try to
+ open ~/.devilspie.xml.
+
+ * configure.in: Bump version to 0.2.2.
+
+2002-09-08 Ross Burton <ross@burtonini.com>
+
+ * src/devilspie.c (main): Actually check the number of arguments,
+ so that we don't coredump if no configuration file is
+ specified. Fixes a segfault pointed out by jdub. As usual, IRC is
+ a great way of finding bugs in code.
+
+ * configure.in: Bump version to 0.2.1.
+
+2002-09-03 Ross Burton <ross@burtonini.com>
+
+ * src/*: Many, many changes. In fact this entry is totally
+ redundant, except for...
+
+ * configure.in: Bump version up to 0.2.
+
+2002-08-14 Ross Burton <ross@burtonini.com>
+
+ * src/*: Finally managed to make enough work for an alpha
+ release. Release 0.1!
+
View
15 Makefile.am
@@ -0,0 +1,15 @@
+SUBDIRS = po src
+
+man_MANS = devilspie.1
+
+EXTRA_DIST = \
+ debian \
+ autogen.sh \
+ intltool-extract.in \
+ intltool-merge.in \
+ intltool-update.in \
+ sample-config.xml \
+ devilspie.dtd \
+ devilspie.glade \
+ devilspie.gladep \
+ $(man_MANS)
View
26 NEWS
@@ -0,0 +1,26 @@
+Devil's Pie 0.2.4
+
+* Use regular expressions instead of literal string compares when
+ matching window names, application names, or roles.
+* More documentation!
+
+Devil's Pie 0.2.3
+
+* Re-generated the .c and .h files using GOB 2.0.3, fixing a terrible bug
+ with devilspie when using GCC 3.
+
+Devil's Pie 0.2.2
+
+* Have decent command line argument parsing
+* Added a layer action so that windows can be below or above the
+ normal windows
+
+Devil's Pie 0.2.1
+
+* Check the number of command line arguments, so we don't segfault any
+ more.
+
+Devil's Pie 0.2
+
+* The first real release -- 0.1 was actually released, but I didn't
+ really like it so I didn't tell many people...
View
28 TODO
@@ -0,0 +1,28 @@
+TODO for Devil's Pie
+
+A nearly-ordered list:
+* More matchers (can't think of any though)
+* More actions (set size, set position)
+* Debian packages
+* SGML catalogue entry
+* Catch events on window move and resize too
+* Clean up and order the souce tree. devilspie and the editor should
+ be seperate directories, and the sample configuration file (etc)
+ should not be in the top-level directory.
+* Add ability to insert self into session on startup
+* GUI builder
+* RedHat packages
+* Remove dependency on gnome-common, as using it breaks builds on RH8
+ (they forgot to package it) and this program only requires GTK+ and
+ libwnck.
+* Use gnome-vfs to monitor the configuration file, and re-read it when
+ it is changed. This is fairly trivial, props to the gnome-vfs guys.
+
+The position of GUI builder is not a reflection of how much I want
+one, but a reflection of how long I think a good config tool will take
+to write.
+
+My previous GUI builders have been rather advanced... maybe it should
+be more wizard like. Allow the user to select one of a series of
+common options, such as 'make this window not appear in the task list'
+or 'move this window to workspace "Foo"'.
View
20 autogen.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="devilspie"
+
+(test -f $srcdir/configure.in) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+}
+
+which gnome-autogen.sh || {
+ echo "You need to install gnome-common from the GNOME CVS"
+ exit 1
+}
+
+USE_GNOME2_MACROS=1 . gnome-autogen.sh
View
41 configure.in
@@ -0,0 +1,41 @@
+dnl Initial blurb
+AC_PREREQ(2.52)
+AC_INIT(devilspie, 0.2.4, http://www.burtonini.com/)
+AC_CONFIG_SRCDIR(src/devilspie.c)
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+AM_CONFIG_HEADER(config.h)
+
+AM_MAINTAINER_MODE
+
+dnl Do the gettext/i18n stuff
+AC_DEFINE(PACKAGE, AC_PACKAGE_NAME)
+AC_DEFINE(VERSION, AC_PACKAGE_VERSION)
+AC_DEFINE(GETTEXT_PACKAGE, "AC_PACKAGE_NAME", [foo])
+GETTEXT_PACKAGE=AC_PACKAGE_NAME
+AC_SUBST(GETTEXT_PACKAGE)
+AC_PROG_INTLTOOL([0.20])
+ALL_LINGUAS=""
+AM_GLIB_GNU_GETTEXT
+
+dnl Check for the POSIX regexpr functions and headers
+AC_CHECK_FUNCS([regcomp regexec regfree])
+AC_CHECK_HEADERS([sys/types.h regex.h])
+
+dnl Check that we actually have the libraries required to
+dnl build. Versioned dependancy on libwnck as essential API additions
+dnl were made in 0.17.
+PKG_CHECK_MODULES(EXTRA_GNOME,
+ gtk+-2.0
+ libwnck-1.0 >= 0.17
+ libglade-2.0)
+AC_SUBST(EXTRA_GNOME_CFLAGS)
+AC_SUBST(EXTRA_GNOME_LIBS)
+
+dnl We really need gob2 to build.
+GOB2_CHECK(2.0.3)
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+po/Makefile.in
+])
View
17 devilspie.1
@@ -0,0 +1,17 @@
+.TH devilspie 1
+.SH NAME
+devilspie \- perform actions on windows as they are created
+.SH SYNOPSIS
+.B devilspie
+.I "configuration-file"
+.SH "DESCRIPTION"
+.BR devilspie
+is a program which can detect windows as they are created, and perform
+actions on them if they match as set of criteria.
+
+The configuration file is a XML file. For now, the documentation for
+this file in in the example configuration file, and the relevant DTD.
+.SH BUGS
+This manpage is useless, as it was thrown together on a train.
+.SH AUTHOR
+This manual page was written by Ross Burton <ross@burtonini.com>.
View
28 devilspie.dtd
@@ -0,0 +1,28 @@
+<!--
+This is the DTD for Devil's Pie configuration files
+Author: Ross Burton <ross@burtonini.com>
+-->
+
+<!--
+Root element
+-->
+
+<!ELEMENT devilspie (flurb)*>
+
+<!ELEMENT flurb (matchers, actions)>
+<!ATTLIST flurb name CDATA #IMPLIED>
+
+<!ELEMENT matchers (matcher)*>
+<!ELEMENT actions (action)*>
+
+<!ELEMENT matcher (property)*>
+<!ATTLIST matcher name CDATA #REQUIRED>
+
+<!ELEMENT action (property)*>
+<!ATTLIST action name CDATA #REQUIRED>
+
+<!ELEMENT property EMPTY>
+<!ATTLIST property
+ name CDATA #REQUIRED
+ value CDATA #REQUIRED
+>
View
900 devilspie.glade
@@ -0,0 +1,900 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="main_dialog">
+ <property name="title" translatable="yes">Window Actions</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">300</property>
+ <property name="default_height">300</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="help_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-11</property>
+ <signal name="clicked" handler="on_actions_help_clicked" last_modification_time="Fri, 30 Aug 2002 07:32:28 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="close_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-7</property>
+ <signal name="clicked" handler="on_actions_close_clicked" last_modification_time="Fri, 30 Aug 2002 07:32:40 GMT"/>
+ <signal name="clicked" handler="gtk_main_quit" last_modification_time="Fri, 30 Aug 2002 07:51:38 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Window _Actions:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="flurb_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <signal name="button_press_event" handler="on_treeview_button_press_event" last_modification_time="Thu, 29 Aug 2002 07:27:07 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">18</property>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="add_flurb">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add a new window action</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_add_flurb_clicked" last_modification_time="Fri, 30 Aug 2002 16:38:10 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-add</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Add...</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="remove_flurb">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Remove the selected window action</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_remove_flurb_clicked" last_modification_time="Fri, 30 Aug 2002 16:38:36 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="edit_flurb">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Edit the selected window action</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_edit_flurb_clicked" last_modification_time="Fri, 30 Aug 2002 16:38:58 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-properties</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit...</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="edit_dialog_old">
+ <property name="title" translatable="yes">Edit Window Action</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="helpbutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-11</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Name:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="mnemonic_widget">entry1</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="entry1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview_matchers">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <accessibility>
+ <atkproperty name="AtkObject::accessible_name" translatable="yes">treeview_matchers</atkproperty>
+ </accessibility>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox1">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="button_edit_matcher_add">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_button_edit_matcher_add_clicked" last_modification_time="Mon, 19 Aug 2002 08:36:04 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_edit_matcher_remove">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_button_edit_matcher_remove_clicked" last_modification_time="Mon, 19 Aug 2002 08:36:15 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_edit_matcher_properties">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-properties</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_button_edit_matcher_properties_clicked" last_modification_time="Mon, 19 Aug 2002 08:36:22 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_edit_matcher_about">
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gnome-stock-about</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_button_edit_matcher_about_clicked" last_modification_time="Mon, 19 Aug 2002 08:36:28 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Matchers (rename)</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <accessibility>
+ <atkrelation target="treeview_matchers" type="label-for"/>
+ </accessibility>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <placeholder/>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Actions (rename)</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="edit_dialog">
+ <property name="title" translatable="yes">Edit Window Action</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="edit_help_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-11</property>
+ <signal name="clicked" handler="on_edit_help_button_clicked" last_modification_time="Mon, 02 Sep 2002 18:11:20 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="edit_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-6</property>
+ <signal name="clicked" handler="on_edit_cancel_button_clicked" last_modification_time="Mon, 02 Sep 2002 18:11:12 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="edit_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-5</property>
+ <signal name="clicked" handler="on_edit_ok_button_clicked" last_modification_time="Mon, 02 Sep 2002 18:11:05 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Name:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">entry2</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="entry2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Matchers:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">matchers_treeview</property>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="matchers_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Actions:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">actions_treeview</property>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="actions_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
View
8 devilspie.gladep
@@ -0,0 +1,8 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>devilspie</name>
+ <program_name>devilspie</program_name>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>
View
72 sample-config.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<!DOCTYPE devilspie SYSTEM "devilspie.dtd">
+
+<!-- The root element is devilspie -->
+<devilspie>
+
+ <!--
+ This starts a new flurb, which is a set of matchers and actions.
+ This flurb matches all windows, and prints out the window name and application name
+ -->
+ <flurb name="Print Window Names">
+ <matchers>
+ <!-- this is a matcher which always returns true. The full class name must be used -->
+ <matcher name="DevilsPieMatcherAlways"/>
+ </matchers>
+ <actions>
+ <!-- this action prints out the window and application name -->
+ <action name="DevilsPieActionDebug"/>
+ <action name="DevilsPieActionSaveGeometry"/>
+ </actions>
+ </flurb>
+
+ <!--
+ This flurb is actually useful. Match any XChat window, and pin it so that it
+ appears on all workspaces.
+ -->
+ <flurb name="X-Chat on all Desktops">
+ <matchers>
+ <matcher name="DevilsPieMatcherWindowName">
+ <property name="application_name" value="Xchat"/>
+ </matcher>
+ </matchers>
+ <actions>
+ <action name="DevilsPieActionSetWorkspace">
+ <property name="pinned" value="TRUE"/>
+ </action>
+ </actions>
+ </flurb>
+
+ <flurb>
+ <matchers>
+ <matcher name="DevilsPieMatcherWindowName">
+ <property name="window_title" value="gnome-panel"/>
+ </matcher>
+ </matchers>
+ <actions>
+ <action name="DevilsPieActionLayer">
+ <property name="above" value="FALSE"/>
+ </action>
+ </actions>
+ </flurb>
+ <!--
+ This will put all GNOME Calculators on the second workspace,
+ maxmized. Note that the workspace number is a 1-based index, not a
+ 0-based index.
+ -->
+ <flurb>
+ <matchers>
+ <matcher name="DevilsPieMatcherWindowName">
+ <property name="application_name" value="Gnome-calculator"/>
+ </matcher>
+ </matchers>
+ <actions>
+ <action name="DevilsPieActionSetWorkspace">
+ <property name="workspace_index" value="2"/>
+ </action>
+ <action name="DevilsPieActionHide">
+ <property name="skip_tasklist" value="TRUE"/>
+ </action>
+ </actions>
+ </flurb>
+</devilspie>
View
94 src/Makefile.am
@@ -0,0 +1,94 @@
+# We only install devilspie at the moment
+bin_PROGRAMS = devilspie
+
+# Don't install the parser test and the (incomplete/not working) editor
+noinst_PROGRAMS = parser-test editor
+
+devilspie_SOURCES = \
+ ${BUILT_SOURCES} \
+ config-parser.c \
+ flurb.h \
+ flurb.c \
+ devilspie.h \
+ devilspie.c
+
+parser_test_SOURCES = \
+ ${BUILT_SOURCES} \
+ config-parser.c \
+ flurb.h \
+ flurb.c \
+ devilspie.h \
+ parser-test.c
+
+editor_SOURCES = \
+ ${BUILT_SOURCES} \
+ config-parser.c \
+ flurb.h \
+ flurb.c \
+ devilspie.h \
+ editor.c
+
+CFLAGS = -Wall -g
+INCLUDES = \
+ $(EXTRA_GNOME_CFLAGS) \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \
+ -DGTK_DISABLE_DEPRECATED -DGNOME_DISABLE_DEPRECATED
+
+LDADD = ${EXTRA_GNOME_LIBS}
+# The editor needs this flag so that libglade can do its
+# auto-connection magic.
+editor_LDFLAGS = -export-dynamic
+
+# The long list of sources which are generated from .gob files.
+BUILT_SOURCES = \
+ tristate-dummy.c \
+ tristate-dummy.h \
+ devilspie-matcher.c \
+ devilspie-matcher.h \
+ devilspie-matcher-windowname.c \
+ devilspie-matcher-windowname.h \
+ devilspie-matcher-windowname-private.h \
+ devilspie-matcher-always.c \
+ devilspie-matcher-always.h \
+ devilspie-action.c \
+ devilspie-action.h \
+ devilspie-action-debug.c \
+ devilspie-action-debug.h \
+ devilspie-action-layer.c \
+ devilspie-action-layer.h \
+ devilspie-action-resize.c \
+ devilspie-action-resize.h \
+ devilspie-action-savegeometry.c \
+ devilspie-action-savegeometry.h \
+ devilspie-action-setworkspace.c \
+ devilspie-action-setworkspace.h \
+ devilspie-action-hide.c \
+ devilspie-action-hide.h
+
+# Leaving this list in as a reminder that some day most of this file
+# should involve $(addprefix) functions, if only automake suported
+# them.
+devilspie_GOBS = \
+ tristate-dummy \
+ devilspie-matcher \
+ devilspie-matcher-windowname \
+ devilspie-matcher-always \
+ devilspie-action \
+ devilspie-action-debug \
+ devilspie-action-layer \
+ devilspie-action-savegeometry \
+ devilspie-action-setworkspace \
+ devilspie-action-resize \
+ devilspie-action-hide
+
+# Must distribute the gob files!
+EXTRA_DIST = $(addsuffix .gob, ${devilspie_GOBS})
+
+CLEANFILES = ${BUILT_SOURCES}
+
+SUFFIXES = .gob
+
+%.c %.h %-private.h: %.gob
+ @GOB2@ $<
View
614 src/config-parser.c
@@ -0,0 +1,614 @@
+#include <stdlib.h>
+#include <string.h>
+#include "glib.h"
+#include "glib-object.h"
+#include "devilspie.h"
+#include "devilspie-matcher.h"
+#include "devilspie-action.h"
+#include "tristate-dummy.h"
+#include "flurb.h"
+#define _(x) x
+
+extern GList *flurbs;
+
+/*
+ * BIG TODO:
+ * Use this GError thing we hear so much about... :)
+ */
+
+typedef enum {
+ STATE_START,
+ STATE_DEVILSPIE,
+ STATE_FLURB,
+ STATE_MATCHERS,
+ STATE_MATCHER,
+ STATE_MATCHER_PROPERTY,
+ STATE_ACTIONS,
+ STATE_ACTION,
+ STATE_ACTION_PROPERTY
+} ParseState;
+
+typedef struct {
+ /* The current state of the parse */
+ GSList *states;
+ /* A list of flurbs defined */
+ GList *flurbs;
+ /* A Flurb being worked on */
+ Flurb *flurb;
+ /* The DevilsPie::Matcher being worked on */
+ DevilsPieMatcher *matcher;
+ /* The DevilsPie::Action being worked on */
+ DevilsPieAction *action;
+} ParseInfo;
+
+static void set_error (GError **err,
+ GMarkupParseContext *context,
+ int error_domain,
+ int error_code,
+ const char *format,
+ ...) G_GNUC_PRINTF (5, 6);
+
+static void push_state (ParseInfo *info,
+ ParseState state);
+static void pop_state (ParseInfo *info);
+static ParseState peek_state (ParseInfo *info);
+
+static void start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+static void end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+static void text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+
+static GMarkupParser devilspie_config_parser = {
+ start_element_handler,
+ end_element_handler,
+ text_handler,
+ NULL,
+ NULL
+};
+
+/* Stolen from Metacity */
+static gboolean
+all_whitespace (const char *text, int text_len) {
+ const char *p, *end;
+ p = text;
+ end = text + text_len;
+ while (p != end) {
+ if (!g_ascii_isspace (*p)) return FALSE;
+ p = g_utf8_next_char (p);
+ }
+ return TRUE;
+}
+
+typedef struct
+{
+ const char *name;
+ const char **retloc;
+} LocateAttr;
+
+static gboolean
+locate_attributes (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ GError **error,
+ const char *first_attribute_name,
+ const char **first_attribute_retloc,
+ ...)
+{
+ va_list args;
+ const char *name;
+ const char **retloc;
+ int n_attrs;
+#define MAX_ATTRS 24
+ LocateAttr attrs[MAX_ATTRS];
+ gboolean retval;
+ int i;
+
+ g_return_val_if_fail (first_attribute_name != NULL, FALSE);
+ g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
+
+ retval = TRUE;
+
+ n_attrs = 1;
+ attrs[0].name = first_attribute_name;
+ attrs[0].retloc = first_attribute_retloc;
+ *first_attribute_retloc = NULL;
+
+ va_start (args, first_attribute_retloc);
+
+ name = va_arg (args, const char*);
+ retloc = va_arg (args, const char**);
+
+ while (name != NULL)
+ {
+ g_return_val_if_fail (retloc != NULL, FALSE);
+
+ g_assert (n_attrs < MAX_ATTRS);
+
+ attrs[n_attrs].name = name;
+ attrs[n_attrs].retloc = retloc;
+ n_attrs += 1;
+ *retloc = NULL;
+
+ name = va_arg (args, const char*);
+ retloc = va_arg (args, const char**);
+ }
+
+ va_end (args);
+
+ if (!retval)
+ return retval;
+
+ i = 0;
+ while (attribute_names[i])
+ {
+ int j;
+ gboolean found;
+
+ found = FALSE;
+ j = 0;
+ while (j < n_attrs)
+ {
+ if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+ {
+ retloc = attrs[j].retloc;
+
+ if (*retloc != NULL)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Attribute \"%s\" repeated twice on the same <%s> element"),
+ attrs[j].name, element_name);
+ retval = FALSE;
+ goto out;
+ }
+
+ *retloc = attribute_values[i];
+ found = TRUE;
+ }
+
+ ++j;
+ }
+
+ if (!found)
+ {
+ set_error (error, context,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_PARSE,
+ _("Attribute \"%s\" is invalid on <%s> element in this context"),
+ attribute_names[i], element_name);
+ retval = FALSE;
+ goto out;
+ }
+
+ ++i;
+ }
+
+ out:
+ return retval;
+}
+
+static void
+set_error (GError **err, GMarkupParseContext *context, int error_domain,
+ int error_code, const char *format, ...) {
+ int line, ch;
+ va_list args;
+ char *str;
+
+ g_markup_parse_context_get_position (context, &line, &ch);
+
+ va_start (args, format);
+ str = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ g_set_error (err, error_domain, error_code,_("Line %d character %d: %s"),
+ line, ch, str);
+
+ g_free (str);
+}
+
+static void
+parse_info_init(ParseInfo *info) {
+ info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
+ info->flurbs = NULL;
+}
+
+static void
+parse_info_free (ParseInfo *info) {
+ g_slist_free (info->states);
+ /* We don't free the contents of the list as ownership has been
+ passed on */
+ /* g_list_free (info->flurbs); */
+}
+
+static void
+push_state (ParseInfo *info, ParseState state) {
+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
+}
+
+static void
+pop_state (ParseInfo *info) {
+ g_return_if_fail (info->states != NULL);
+ info->states = g_slist_remove (info->states, info->states->data);
+}
+
+static ParseState
+peek_state (ParseInfo *info) {
+ g_return_val_if_fail (info->states != NULL, STATE_START);
+ return GPOINTER_TO_INT (info->states->data);
+}
+
+static void
+set_property_with_data(GObject *object, const char *property_name, const char *data)
+{
+ GParamSpec *property_spec;
+ GValue *value;
+
+ property_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name);
+ if (property_spec == NULL) {
+ g_message("fuck");
+ /* TODO: output something sane */
+ }
+ value = g_new0(GValue, 1);
+ if (g_type_is_a(property_spec->value_type, G_TYPE_STRING)) {
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string(value, data);
+ g_object_set_property(object, property_name, value);
+ } else if (g_type_is_a(property_spec->value_type, G_TYPE_INT)) {
+ int i;
+ g_value_init (value, G_TYPE_INT);
+ i = strtol(data, (char**)NULL, 10); /* if I put 0 is it clever? */
+ g_value_set_int(value, i);
+ g_object_set_property(object, property_name, value);
+ } else if (g_type_is_a(property_spec->value_type, G_TYPE_BOOLEAN)) {
+ /*
+ * TODO: decide whether we want a 0/1 representation of boolean,
+ * or a 'true'/'false' representation.
+ */
+ int i;
+ g_value_init (value, G_TYPE_BOOLEAN);
+ i = strtol(data, (char**)NULL, 10); /* if I put 0 is it clever? */
+ g_value_set_boolean(value, (gboolean)i);
+ g_object_set_property(object, property_name, value);
+ } else if (g_type_is_a(property_spec->value_type, DEVILSPIE_TYPE_TRISTATE)) {
+ DevilsPieTriState tri;
+ g_value_init (value, DEVILSPIE_TYPE_TRISTATE);
+ if (!strcmp(data, "TRUE")) {
+ tri = TRI_TRUE;
+ } else if (!strcmp(data, "FALSE")) {
+ tri = TRI_FALSE;
+ } else {
+ tri = TRI_UNSET;
+ }
+ g_value_set_enum(value, tri);
+ g_object_set_property(object, property_name, value);
+ } else {
+ g_assert_not_reached();
+ }
+ g_free(value);
+}
+
+static void
+parse_property (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ const char *name;
+ const char *value;
+
+ if (!locate_attributes (context, element_name,
+ attribute_names, attribute_values, error,
+ "name", &name,
+ "value", &value,
+ NULL)) return;
+ if (name == NULL) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return;
+ }
+ if (value == NULL) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"value\" attribute on element <%s>"), element_name);
+ return;
+ }
+ switch (peek_state(info)) {
+ case STATE_MATCHER_PROPERTY:
+ set_property_with_data (G_OBJECT (info->matcher), name, value);
+ break;
+ case STATE_ACTION_PROPERTY:
+ set_property_with_data (G_OBJECT (info->action), name, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static DevilsPieMatcher*
+create_matcher (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ const char *type_name;
+ GType matcher_type;
+ DevilsPieMatcher *matcher;
+
+ if (!locate_attributes (context, element_name,
+ attribute_names, attribute_values, error,
+ "name", &type_name,
+ NULL)) return NULL;
+
+ if (type_name == NULL) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return NULL;
+ }
+
+ matcher_type = g_type_from_name (type_name);
+ if (!g_type_is_a(matcher_type, devilspie_matcher_get_type())) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Requested type %s is not a known Matcher"), type_name);
+ return NULL;
+ }
+
+ matcher = g_object_new (matcher_type, NULL);
+ return matcher;
+}
+
+static DevilsPieAction*
+create_action (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ const char *type_name;
+ GType action_type;
+ DevilsPieAction *action;
+
+ if (!locate_attributes (context, element_name,
+ attribute_names, attribute_values, error,
+ "name", &type_name,
+ NULL)) {
+ if (!error)
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Could not parse attributes of <%s>"), element_name);
+ return NULL;
+ }
+
+ if (type_name == NULL) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No \"name\" attribute on element <%s>"), element_name);
+ return NULL;
+ }
+
+ action_type = g_type_from_name (type_name);
+ if (!g_type_is_a(action_type, devilspie_action_get_type())) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Requested type %s is not a known Action"), type_name);
+ return NULL;
+ }
+
+ action = g_object_new (action_type, NULL);
+ return action;
+}
+
+static void
+start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info;
+ info = (ParseInfo*)user_data;
+
+ switch (peek_state(info)) {
+ case STATE_START:
+ if (strcmp (element_name, "devilspie") == 0) {
+ push_state(info, STATE_DEVILSPIE);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Outermost element in config files must be <devilspie> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_DEVILSPIE:
+ if (strcmp (element_name, "flurb") == 0) {
+ const char* name;
+ push_state(info, STATE_FLURB);
+ if (!locate_attributes (context, element_name,
+ attribute_names, attribute_values, error,
+ "name", &name, NULL)) return;
+ info->flurb = g_new0 (Flurb, 1);
+ info->flurb->name = g_strdup(name);
+ info->flurbs = g_list_append(info->flurbs, info->flurb);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Only <flurb> elements are allowed inside <devilspie> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_FLURB:
+ if (strcmp (element_name, "matchers") == 0) {
+ push_state(info, STATE_MATCHERS);
+ } else if (strcmp (element_name, "actions") == 0) {
+ push_state(info, STATE_ACTIONS);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Only <matchers> and <actions> elements are allowed inside <blurb> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_MATCHERS:
+ if (strcmp (element_name, "matcher") == 0) {
+ push_state(info, STATE_MATCHER);
+ info->matcher = create_matcher (context, element_name, attribute_names,
+ attribute_values, info, error);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Only <matcher> elements are allowed inside <matchers> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_MATCHER:
+ if (strcmp (element_name, "property") == 0) {
+ push_state(info, STATE_MATCHER_PROPERTY);
+ parse_property (context, element_name, attribute_names,
+ attribute_values, info, error);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Only <property> elements are allowed inside <matcher> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_MATCHER_PROPERTY:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside <property>"),
+ element_name);
+ break;
+ case STATE_ACTIONS:
+ if (strcmp (element_name, "action") == 0) {
+ push_state(info, STATE_ACTION);
+ info->action = create_action (context, element_name, attribute_names,
+ attribute_values, info, error);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Only <action> elements are allowed inside <actions> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_ACTION:
+ if (strcmp (element_name, "property") == 0) {
+ push_state(info, STATE_ACTION_PROPERTY);
+ parse_property (context, element_name, attribute_names,
+ attribute_values, info, error);
+ } else {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Only <property> elements are allowed inside <action> not <%s>"),
+ element_name);
+ }
+ break;
+ case STATE_ACTION_PROPERTY:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Element <%s> is not allowed inside <property>"),
+ element_name);
+ break;
+ }
+}
+
+static void end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo*)user_data;
+ switch(peek_state (info)) {
+ case STATE_START:
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("Can not close element <%s> outside of the root element"),
+ element_name);
+ break;
+ case STATE_DEVILSPIE:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_START);
+ break;
+ case STATE_FLURB:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_DEVILSPIE);
+ break;
+ case STATE_MATCHERS:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_FLURB);
+ break;
+ case STATE_MATCHER:
+ pop_state (info);
+ info->flurb->matchers = g_list_append(info->flurb->matchers, info->matcher);
+ g_assert (peek_state (info) == STATE_MATCHERS);
+ break;
+ case STATE_MATCHER_PROPERTY:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_MATCHER);
+ break;
+ case STATE_ACTIONS:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_FLURB);
+ break;
+ case STATE_ACTION:
+ pop_state (info);
+ info->flurb->actions = g_list_append(info->flurb->actions, info->action);
+ g_assert (peek_state (info) == STATE_ACTIONS);
+ break;
+ case STATE_ACTION_PROPERTY:
+ pop_state (info);
+ g_assert (peek_state (info) == STATE_ACTION);
+ break;
+ }
+}
+
+static void text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ /* TODO: decode the state enum into a name to output nicely */
+ if (!all_whitespace(text, text_len)) {
+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+ _("No text is allowed inside element <%d>"),
+ peek_state(user_data));
+ }
+}
+
+void load_configuration(const char *filename) {
+ char *text;
+ unsigned int length;
+ GError *error;
+ GMarkupParseContext *context;
+ ParseInfo info;
+
+ error = NULL;
+ if (!g_file_get_contents(filename, &text, &length, &error)) {
+ g_message("Could not load theme: %s", error->message);
+ exit(1);
+ }
+ /* Sanity check */
+ g_assert(text);
+
+ parse_info_init (&info);
+ context = g_markup_parse_context_new (&devilspie_config_parser, 0, &info, NULL);
+ error = NULL;
+ if (g_markup_parse_context_parse (context, text, length, &error))
+ if (g_markup_parse_context_end_parse (context, &error))
+ g_markup_parse_context_free (context);
+
+ g_free (text);
+
+ if (error) {
+ g_message("Could not parse configuration: %s", error->message);
+ exit(1);
+ }
+
+ flurbs = info.flurbs;
+
+ parse_info_free (&info);
+}
View
26 src/devilspie-action-debug.gob
@@ -0,0 +1,26 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/application.h"
+#include "libwnck/window.h"
+#include "devilspie-action.h"
+#include <stdio.h>
+/* Borrow this function from libwnck */
+#include "X11/X.h"
+Atom _wnck_atom_get (const char *atom_name);
+char* _wnck_get_string_property_latin1 (Window xwindow, Atom atom);
+%}
+
+class DevilsPie:Action:Debug from DevilsPie:Action {
+
+ override (DevilsPie:Action) gboolean run(DevilsPie:Action *self (check null type), Wnck:Window *window (check null type)) {
+ printf("Window title: \"%s\"; Application name: \"%s\"; Role: \"%s\"\n",
+ wnck_window_get_name(window),
+ wnck_application_get_name(wnck_window_get_application(window)),
+ _wnck_get_string_property_latin1 (wnck_window_get_xid (window), _wnck_atom_get("WM_WINDOW_ROLE")));
+ return TRUE;
+ }
+
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
38 src/devilspie-action-hide.gob
@@ -0,0 +1,38 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/window.h"
+#include "libwnck/screen.h"
+#include "tristate-dummy.h"
+#include "devilspie-action.h"
+#include "devilspie.h"
+
+/* TODO: remove when gettext is used */
+#define _(x) x
+%}
+
+class DevilsPie:Action:Hide from DevilsPie:Action {
+
+ public DevilsPieTriState skip_pager;
+ property ENUM skip_pager
+ (nick = _("Skip Pager"),
+ blurb = _("Window does not appear in the pager"),
+ enum_type = DevilsPie:TriState, default_value = TRI_UNSET, link);
+
+ public DevilsPieTriState skip_tasklist;
+ property ENUM skip_tasklist
+ (nick = _("Skip Task List"),
+ blurb = _("Window does not appear in the task list"),
+ enum_type = DevilsPie:TriState, default_value = TRI_UNSET, link);
+
+ override (DevilsPie:Action) gboolean run(DevilsPie:Action *self (check null type), Wnck:Window *window (check null type)) {
+ DevilsPieActionHide *a = (DevilsPieActionHide*)self;
+ /* Assume that a true/false tristate in a boolean context is correct */
+ if (a->skip_pager != TRI_UNSET) wnck_window_set_skip_pager(window, a->skip_pager);
+ if (a->skip_tasklist != TRI_UNSET) wnck_window_set_skip_tasklist(window, a->skip_tasklist);
+ return TRUE;
+ }
+
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
54 src/devilspie-action-layer.gob
@@ -0,0 +1,54 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/window.h"
+#include "libwnck/screen.h"
+#include "tristate-dummy.h"
+#include "devilspie-action.h"
+#include "devilspie.h"
+
+/* TODO: remove when gettext is used */
+#define _(x) x
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+Screen *_wnck_screen_get_xscreen (WnckScreen *screen);
+Atom _wnck_atom_get (const char *atom_name);
+void _wnck_change_state (Screen *screen,
+ Window xwindow,
+ gboolean add,
+ Atom state1,
+ Atom state2);
+%}
+
+class DevilsPie:Action:Layer from DevilsPie:Action {
+
+ public DevilsPieTriState above;
+ property ENUM above
+ (nick = _("Above windows?"),
+ blurb = _("Window is above all others"),
+ enum_type = DevilsPie:TriState, default_value = TRI_UNSET, link);
+
+ override (DevilsPie:Action) gboolean run(DevilsPie:Action *self (check null type), Wnck:Window *window (check null type)) {
+ DevilsPieActionLayer *a = (DevilsPieActionLayer*)self;
+ /* TRUE = above, FALSE = below */
+ if (a->above == TRI_TRUE) {
+ _wnck_change_state (_wnck_screen_get_xscreen(wnck_window_get_screen(window)),
+ wnck_window_get_xid(window),
+ TRUE,
+ _wnck_atom_get ("_NET_WM_STATE_ABOVE"),
+ 0);
+ }
+ if (a->above == TRI_FALSE) {
+ _wnck_change_state (_wnck_screen_get_xscreen(wnck_window_get_screen(window)),
+ wnck_window_get_xid(window),
+ TRUE,
+ _wnck_atom_get ("_NET_WM_STATE_BELOW"),
+ 0);
+ }
+ return TRUE;
+ }
+
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
50 src/devilspie-action-resize.gob
@@ -0,0 +1,50 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/window.h"
+#include "tristate-dummy.h"
+#include "devilspie-action.h"
+#include "devilspie.h"
+
+/* TODO: remove when gettext is used */
+#define _(x) x
+%}
+
+class DevilsPie:Action:Resize from DevilsPie:Action {
+
+ public DevilsPieTriState maximized;
+ property ENUM maximized
+ (nick = _("Maximized"),
+ blurb = _("Window is maximized on the screen"),
+ enum_type = DevilsPie:TriState, default_value = TRI_UNSET, link);
+
+ public DevilsPieTriState minimized;
+ property ENUM minimized
+ (nick = _("Minimized"),
+ blurb = _("Window is minimized"),
+ enum_type = DevilsPie:TriState, default_value = TRI_UNSET, link);
+
+ override (DevilsPie:Action) gboolean run(DevilsPie:Action *self (check null type), Wnck:Window *window (check null type)) {
+ DevilsPieActionResize *a = (DevilsPieActionResize*)self;
+ /* Assume that a true/false tristate in a boolean context is correct */
+ /* Assume the user is not totally stupid and has not set both
+ maximize and minimize */
+
+ /* Handle maximized */
+ if (a->maximized == TRI_TRUE)
+ wnck_window_maximize(window);
+ else if (a->maximized == TRI_FALSE)
+ wnck_window_unmaximize(window);
+
+ /* Handle minimized */
+ if (a->minimized == TRI_TRUE)
+ wnck_window_minimize(window);
+ else if (a->minimized == TRI_FALSE)
+ wnck_window_unminimize(window);
+
+ return TRUE;
+ }
+
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
56 src/devilspie-action-savegeometry.gob
@@ -0,0 +1,56 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/window.h"
+#include "devilspie-action.h"
+#include <stdio.h>
+%}
+
+/**
+ * This action saves and restores window geometry. It uses the same
+ * algorithm as the session manager, i.e. window with the same title
+ * and role are identical.
+ *
+ * Unfortunately, there is no easy way to get X Atoms via libwnck.
+ * /me pesters Havoc
+ * So I have to go down to the raw X interfaces.
+ */
+class DevilsPie:Action:SaveGeometry from DevilsPie:Action {
+
+ /**
+ * This callback is called when the geometry of the window changes.
+ */
+ private void geometry_changed_cb (WnckWindow *window) {
+ g_message ("Window %s geometry changed", wnck_window_get_name (window));
+ }
+
+ /**
+ * This callback is called when a window on a screen is closed.
+ */
+ private void window_closed_cb (WnckScreen *screen, WnckWindow *window) {
+ g_message ("Window %s closed", wnck_window_get_name (window));
+ }
+
+ override (DevilsPie:Action) gboolean run(DevilsPie:Action *self (check null type), Wnck:Window *window (check null type)) {
+ puts("Starting SaveGeometry action");
+ /* Find the window in the geometry maps, and apply if found */
+ /* TODO */
+ /* Hook up the geometry changes signal so we can store future changes */
+ g_signal_connect (window, "geometry_changed", G_CALLBACK(self_geometry_changed_cb), NULL);
+ return TRUE;
+ }
+
+ /**
+ * The constructor. Sets up a callback when windows are closed so we
+ * can track that and persist the state.
+ */
+ init (self) {
+ WnckScreen *screen;
+ screen = wnck_screen_get_default ();
+ /* Hook up to the window_closing signal so we can persist the settings */
+ g_signal_connect (screen, "window_closed", G_CALLBACK(self_window_closed_cb), NULL);
+
+ }
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
56 src/devilspie-action-setworkspace.gob
@@ -0,0 +1,56 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/screen.h"
+#include "libwnck/window.h"
+#include "libwnck/workspace.h"
+#include "tristate-dummy.h"
+#include "devilspie-action.h"
+#include "devilspie.h"
+
+/* Sucky. Use gettext */
+#define _(x) x
+%}
+
+class DevilsPie:Action:SetWorkspace from DevilsPie:Action {
+
+ public DevilsPieTriState pinned;
+ property ENUM pinned
+ (nick = _("Pinned"),
+ blurb = _("Window is pinned on all workspaces, aka sticky"),
+ enum_type = DevilsPie:TriState, default_value = TRI_UNSET, link);
+
+ public int workspace_index = -1;
+ property INT workspace_index
+ (nick = _("Workspace"),
+ blurb = _("Which workspace to move this window to (numbered)"),
+ default_value = -1, link);
+
+ override (DevilsPie:Action) gboolean run(DevilsPie:Action *self (check null type), Wnck:Window *window (check null type)) {
+ DevilsPieActionSetWorkspace *a = (DevilsPieActionSetWorkspace*)self;
+
+ /* Move the window to the correct workspace */
+ if (a->workspace_index != -1) {
+ WnckScreen *screen;
+ WnckWorkspace *workspace;
+ screen = wnck_window_get_screen(window);
+ /* Adjust for 0-offset in workspaces list */
+ workspace = wnck_screen_get_workspace(screen, a->workspace_index-1);
+ if (!workspace) {
+ g_warning(_("Workspace number %d does not exist"), a->workspace_index);
+ }
+ wnck_window_move_to_workspace(window, workspace);
+ }
+
+ /* Pin the window */
+ if (a->pinned == TRI_TRUE)
+ wnck_window_pin(window);
+ else if (a->pinned == TRI_FALSE)
+ wnck_window_unpin(window);
+
+ return TRUE;
+ }
+
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
24 src/devilspie-action.gob
@@ -0,0 +1,24 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/window.h"
+
+/* Little bit hacky. Using GType here would be nice */
+typedef enum {
+ DebugActionType,
+ ExecuteActionType,
+ WorkspaceActionType
+} ActionType;
+%}
+
+/* TODO: make this an interface when GOB supports it */
+class DevilsPie:Action from G:Object {
+
+ public ActionType type;
+
+ /* TODO: gboolean or void? */
+ virtual gboolean run(self, WnckWindow *window) defreturn TRUE;
+
+ public GObject *new(void) {
+ return (GObject*)GET_NEW;
+ }
+}
View
23 src/devilspie-matcher-always.gob
@@ -0,0 +1,23 @@
+%h{
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include "libwnck/window.h"
+#include "devilspie-matcher.h"
+%}
+
+/**
+ * This is a dummy matcher which is always true