[Xfce4-commits] <xfce4-panel:master> Actions: Merge functionality of xfce4-session plugin.

Nick Schermer noreply at xfce.org
Sun Oct 23 18:34:01 CEST 2011


Updating branch refs/heads/master
         to 81a2d7523b779e2fbe93d632917074ff4d5613aa (commit)
       from ea0b61ae58fe484ebd1a5a5751998eadde92c12d (commit)

commit 81a2d7523b779e2fbe93d632917074ff4d5613aa
Author: Nick Schermer <nick at xfce.org>
Date:   Tue Jul 12 17:56:14 2011 +0200

    Actions: Merge functionality of xfce4-session plugin.
    
    Add support of the functionality of xfswitch and xfce4-session
    plugin in a single plugin.

 docs/README.gtkrc-2.0                |    5 +-
 plugins/actions/Makefile.am          |    2 +
 plugins/actions/actions-dialog.glade |  322 +++++++----
 plugins/actions/actions.c            | 1155 ++++++++++++++++++++++++++--------
 plugins/actions/actions.desktop.in   |    2 +-
 5 files changed, 1127 insertions(+), 359 deletions(-)

diff --git a/docs/README.gtkrc-2.0 b/docs/README.gtkrc-2.0
index 0f8b874..17d7276 100644
--- a/docs/README.gtkrc-2.0
+++ b/docs/README.gtkrc-2.0
@@ -27,8 +27,9 @@ class "XfcePanelWindow" style "xfce-panel-window-style"
 
 XfceActionsPlugin
 -----------------
-Special widget names in this plugin are actions-first-button and
-actions-second-button.
+You can set a custom icon size in gtk-icon-sizes with the name
+panel-actions-menu. The default icon size is 16px.
+Special widget name in this plugin is actions-button.
 
 
 
diff --git a/plugins/actions/Makefile.am b/plugins/actions/Makefile.am
index 1bd65c4..ddad990 100644
--- a/plugins/actions/Makefile.am
+++ b/plugins/actions/Makefile.am
@@ -23,6 +23,7 @@ libactions_la_CFLAGS = \
 	$(LIBXFCE4UI_CFLAGS) \
 	$(EXO_CFLAGS) \
 	$(XFCONF_CFLAGS) \
+	$(DBUS_CFLAGS) \
 	$(PLATFORM_CFLAGS)
 
 libactions_la_LDFLAGS = \
@@ -39,6 +40,7 @@ libactions_la_LIBADD = \
 	$(LIBXFCE4UTIL_LIBS) \
 	$(LIBXFCE4UI_LIBS) \
 	$(EXO_LIBS) \
+	$(DBUS_LIBS) \
 	$(XFCONF_LIBS)
 
 libactions_la_DEPENDENCIES = \
diff --git a/plugins/actions/actions-dialog.glade b/plugins/actions/actions-dialog.glade
index 384f522..c39d4bb 100644
--- a/plugins/actions/actions-dialog.glade
+++ b/plugins/actions/actions-dialog.glade
@@ -1,120 +1,24 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.14"/>
+  <requires lib="gtk+" version="2.20"/>
   <!-- interface-requires libxfce4ui 4.5 -->
   <!-- interface-naming-policy project-wide -->
   <object class="XfceTitledDialog" id="dialog">
+    <property name="can_focus">False</property>
     <property name="title" translatable="yes">Action Buttons</property>
+    <property name="default_width">300</property>
+    <property name="default_height">450</property>
     <property name="icon_name">gtk-properties</property>
     <property name="type_hint">normal</property>
-    <property name="has_separator">False</property>
     <child internal-child="vbox">
       <object class="GtkVBox" id="dialog-vbox1">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <property name="spacing">2</property>
-        <child>
-          <object class="GtkFrame" id="frame5">
-            <property name="visible">True</property>
-            <property name="border_width">6</property>
-            <property name="label_xalign">0</property>
-            <property name="shadow_type">none</property>
-            <child>
-              <object class="GtkAlignment" id="alignment5">
-                <property name="visible">True</property>
-                <property name="left_padding">12</property>
-                <child>
-                  <object class="GtkTable" id="table5">
-                    <property name="visible">True</property>
-                    <property name="border_width">6</property>
-                    <property name="n_rows">2</property>
-                    <property name="n_columns">2</property>
-                    <property name="column_spacing">12</property>
-                    <property name="row_spacing">6</property>
-                    <child>
-                      <object class="GtkLabel" id="label8">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_First button:</property>
-                        <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">first-action</property>
-                      </object>
-                      <packing>
-                        <property name="x_options">GTK_FILL</property>
-                        <property name="y_options">GTK_FILL</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label9">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Second button:</property>
-                        <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">second-action</property>
-                      </object>
-                      <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                        <property name="x_options">GTK_FILL</property>
-                        <property name="y_options">GTK_FILL</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkComboBox" id="first-action">
-                        <property name="visible">True</property>
-                        <property name="model">first-action-model</property>
-                        <child>
-                          <object class="GtkCellRendererText" id="cellrenderertext2"/>
-                          <attributes>
-                            <attribute name="text">0</attribute>
-                          </attributes>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkComboBox" id="second-action">
-                        <property name="visible">True</property>
-                        <property name="model">second-action-model</property>
-                        <child>
-                          <object class="GtkCellRendererText" id="cellrenderertext1"/>
-                          <attributes>
-                            <attribute name="text">0</attribute>
-                          </attributes>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                      </packing>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child type="label">
-              <object class="GtkLabel" id="label7">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Button Actions</property>
-                <attributes>
-                  <attribute name="weight" value="bold"/>
-                </attributes>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
         <child internal-child="action_area">
           <object class="GtkHButtonBox" id="dialog-action_area1">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="layout_style">end</property>
             <child>
               <object class="GtkButton" id="close-button">
@@ -122,6 +26,7 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -136,6 +41,7 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -148,10 +54,202 @@
           </object>
           <packing>
             <property name="expand">False</property>
+            <property name="fill">True</property>
             <property name="pack_type">end</property>
             <property name="position">0</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">6</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkTable" id="table1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="border_width">6</property>
+                        <property name="n_rows">3</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">12</property>
+                        <property name="row_spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label3">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Appeara_nce:</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">combo-mode</property>
+                          </object>
+                          <packing>
+                            <property name="x_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="invert-orientation">
+                            <property name="label" translatable="yes">Invert buttons _orientation</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBox" id="combo-mode">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">mode-store</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext2"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="confirmation-dialog">
+                            <property name="label" translatable="yes">_Show confirmation dialog</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Show a confirmation dialog with a 30 second timeout for some of the actions.</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">General</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="border_width">6</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="GtkTreeView" id="actions-treeview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="model">items-store</property>
+                            <property name="headers_clickable">False</property>
+                            <property name="reorderable">True</property>
+                            <property name="search_column">0</property>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                                <property name="title" translatable="yes">Visible</property>
+                                <child>
+                                  <object class="GtkCellRendererToggle" id="visible-toggle"/>
+                                  <attributes>
+                                    <attribute name="active">0</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+                                <property name="title" translatable="yes">Action</property>
+                                <child>
+                                  <object class="GtkCellRendererText" id="text-renderer"/>
+                                  <attributes>
+                                    <attribute name="markup">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">_Actions</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">actions-treeview</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
       </object>
     </child>
     <action-widgets>
@@ -159,16 +257,28 @@
       <action-widget response="0">help-button</action-widget>
     </action-widgets>
   </object>
-  <object class="GtkListStore" id="first-action-model">
+  <object class="GtkListStore" id="items-store">
     <columns>
-      <!-- column-name title -->
+      <!-- column-name hidden -->
+      <column type="gboolean"/>
+      <!-- column-name name -->
       <column type="gchararray"/>
+      <!-- column-name type -->
+      <column type="guint"/>
     </columns>
   </object>
-  <object class="GtkListStore" id="second-action-model">
+  <object class="GtkListStore" id="mode-store">
     <columns>
       <!-- column-name title -->
       <column type="gchararray"/>
     </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">Action Buttons</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Session Menu</col>
+      </row>
+    </data>
   </object>
 </interface>
diff --git a/plugins/actions/actions.c b/plugins/actions/actions.c
index 2e58540..6b50486 100644
--- a/plugins/actions/actions.c
+++ b/plugins/actions/actions.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2009-2010 Nick Schermer <nick at xfce.org>
+ * Copyright (C) 2009-2011 Nick Schermer <nick at xfce.org>
+ * Copyright (c) 2009      Brian Tarricone <brian at tarricone.org>
  *
  * This library 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
@@ -25,6 +26,7 @@
 #include <libxfce4util/libxfce4util.h>
 #include <libxfce4ui/libxfce4ui.h>
 #include <exo/exo.h>
+#include <dbus/dbus-glib.h>
 
 #include <common/panel-private.h>
 #include <common/panel-xfconf.h>
@@ -35,45 +37,55 @@
 
 
 
-static void     actions_plugin_get_property        (GObject               *object,
-                                                    guint                  prop_id,
-                                                    GValue                *value,
-                                                    GParamSpec            *pspec);
-static void     actions_plugin_set_property        (GObject               *object,
-                                                    guint                  prop_id,
-                                                    const GValue          *value,
-                                                    GParamSpec            *pspec);
-static void     actions_plugin_construct           (XfcePanelPlugin       *panel_plugin);
-static gboolean actions_plugin_size_changed        (XfcePanelPlugin       *panel_plugin,
-                                                    gint                   size);
-static void     actions_plugin_configure_plugin    (XfcePanelPlugin       *panel_plugin);
-static void     actions_plugin_orientation_changed (XfcePanelPlugin       *panel_plugin,
-                                                    GtkOrientation         orientation);
-static void     actions_plugin_button_clicked      (GtkWidget             *button,
-                                                    ActionsPlugin         *plugin);
+#define DEFAULT_ICON_SIZE (16)
+#define DEFAULT_TIMEOUT   (30)
+
+
+
+static void       actions_plugin_get_property        (GObject               *object,
+                                                      guint                  prop_id,
+                                                      GValue                *value,
+                                                      GParamSpec            *pspec);
+static void       actions_plugin_set_property        (GObject               *object,
+                                                      guint                  prop_id,
+                                                      const GValue          *value,
+                                                      GParamSpec            *pspec);
+static void       actions_plugin_construct           (XfcePanelPlugin       *panel_plugin);
+static void       actions_plugin_free_data           (XfcePanelPlugin       *panel_plugin);
+static gboolean   actions_plugin_size_changed        (XfcePanelPlugin       *panel_plugin,
+                                                      gint                   size);
+static void       actions_plugin_configure_plugin    (XfcePanelPlugin       *panel_plugin);
+static void       actions_plugin_orientation_changed (XfcePanelPlugin       *panel_plugin,
+                                                      GtkOrientation         orientation);
+static void       actions_plugin_pack                (ActionsPlugin         *plugin);
+static GPtrArray *actions_plugin_default_array       (void);
+static void       actions_plugin_menu                (GtkWidget             *button,
+                                                      ActionsPlugin         *plugin);
 
 
 
 typedef enum
 {
-  ACTION_DISABLED = 0,
-  ACTION_LOG_OUT_DIALOG,
-  ACTION_LOG_OUT,
-  ACTION_LOCK_SCREEN,
-  ACTION_SHUT_DOWN,
-  ACTION_RESTART,
-  ACTION_SUSPEND,
-  ACTION_HIBERNATE
+  APPEARANCE_TYPE_BUTTONS,
+  APPEARANCE_TYPE_MENU
 }
-ActionType;
+AppearanceType;
 
-typedef struct
+enum
 {
-  ActionType   type;
-  const gchar *title;
-  const gchar *icon_name;
-}
-ActionEntry;
+  PROP_0,
+  PROP_ITEMS,
+  PROP_APPEARANCE,
+  PROP_INVERT_ORIENTATION,
+  PROP_ASK_CONFIRMATION
+};
+
+enum
+{
+  COLUMN_VISIBLE,
+  COLUMN_NAME,
+  COLUMN_TYPE
+};
 
 struct _ActionsPluginClass
 {
@@ -84,35 +96,118 @@ struct _ActionsPlugin
 {
   XfcePanelPlugin __parent__;
 
-  /* widgets */
-  GtkWidget     *box;
-  GtkWidget     *first_button;
-  GtkWidget     *first_image;
-  GtkWidget     *second_button;
-  GtkWidget     *second_image;
-
-  /* settings */
-  ActionType     first_action;
-  ActionType     second_action;
+  AppearanceType  type;
+  GPtrArray      *items;
+  GtkWidget      *menu;
+  guint           invert_orientation : 1;
+  guint           ask_confirmation : 1;
+  guint           pack_idle_id;
 };
 
-enum
+typedef enum
 {
-  PROP_0,
-  PROP_FIRST_ACTION,
-  PROP_SECOND_ACTION
-};
+  ACTION_TYPE_0,
+  ACTION_TYPE_SEPARATOR,
+  ACTION_TYPE_LOGOUT,
+  ACTION_TYPE_LOGOUT_DIALOG,
+  ACTION_TYPE_SWITCH_USER,
+  ACTION_TYPE_LOCK_SCREEN,
+  ACTION_TYPE_HIBERNATE,
+  ACTION_TYPE_SUSPEND,
+  ACTION_TYPE_RESTART,
+  ACTION_TYPE_SHUTDOWN,
+  N_ACTION_TYPES
+}
+ActionType;
+
+/* copied from xfce4-session/shutdown.h -- ORDER MATTERS.
+ * The numbers correspond to the 'type' parameter of
+ * org.xfce.Session.Manager.Shutdown */
+typedef enum
+{
+  ACTION_SHUTDOWN_ASK = 0,
+  ACTION_SHUTDOWN_LOGOUT,
+  ACTION_SHUTDOWN_HALT,
+  ACTION_SHUTDOWN_REBOOT,
+  ACTION_SHUTDOWN_SUSPEND,
+  ACTION_SHUTDOWN_HIBERNATE,
+} ActionShutdownType;
+
+typedef struct
+{
+  ActionType   type;
+  const gchar *name;
+  const gchar *name_mnemonic;
+  const gchar *question;
+  const gchar *status;
+  const gchar *icon_name;
+}
+ActionEntry;
+
+typedef struct
+{
+  ActionEntry *entry;
+  GtkWidget   *dialog;
+  gint         time_left;
+  guint        unattended : 1;
+}
+ActionTimeout;
 
 static ActionEntry action_entries[] =
 {
-  { ACTION_DISABLED, N_("Disabled"), NULL },
-  { ACTION_LOG_OUT_DIALOG, N_("Log Out Dialog"), "system-log-out" },
-  { ACTION_LOG_OUT, N_("Log Out"), "system-log-out" },
-  { ACTION_LOCK_SCREEN, N_("Lock Screen"), "system-lock-screen" },
-  { ACTION_SHUT_DOWN, N_("Shut Down"), "system-shutdown"},
-  { ACTION_RESTART, N_("Restart"), "xfsm-reboot" },
-  { ACTION_SUSPEND, N_("Suspend"), "system-suspend" },
-  { ACTION_HIBERNATE, N_("Hibernate"), "system-hibernate" }
+  { ACTION_TYPE_LOGOUT,
+    N_("Log Out"),
+    N_("_Log Out"),
+    N_("Are you sure you want to log out?"),
+    N_("Logging out in %d seconds."),
+    "system-log-out"
+  },
+  { ACTION_TYPE_LOGOUT_DIALOG,
+    N_("Log Out..."),
+    N_("Log _Out..."),
+    NULL, NULL, /* already shows a dialog */
+    "system-log-out"
+  },
+  { ACTION_TYPE_SWITCH_USER,
+    N_("Switch User"),
+    N_("_Switch User"),
+    NULL, NULL, /* not needed */
+    "system-users"
+  },
+  { ACTION_TYPE_LOCK_SCREEN,
+    N_("Lock Screen"),
+    N_("L_ock Screen"),
+    NULL, NULL, /* not needed */
+    "system-lock-screen"
+  },
+  { ACTION_TYPE_HIBERNATE,
+    N_("Hibernate"),
+    N_("_Hibernate"),
+    N_("Do you want to suspend to disk?"),
+    N_("Hibernating computer in %d seconds."),
+    "system-hibernate"
+  },
+  { ACTION_TYPE_SUSPEND,
+    N_("Suspend"),
+    N_("Sus_pend"),
+    N_("Do you want to suspend to RAM?"),
+    N_("Suspending computer in %d seconds."),
+    "system-suspend"
+  },
+  { ACTION_TYPE_RESTART,
+    N_("Restart"),
+    N_("_Restart"),
+    N_("Are you sure you want to restart?"),
+    N_("Restarting computer in %d seconds."),
+    "xfsm-reboot"
+  },
+  { ACTION_TYPE_SHUTDOWN,
+    N_("Shut Down"),
+    N_("Shut _Down"),
+    N_("Are you sure you want to shut down?"),
+    N_("Turning off computer in %d seconds."),
+    "system-shutdown"
+  }
 };
 
 
@@ -122,6 +217,11 @@ XFCE_PANEL_DEFINE_PLUGIN (ActionsPlugin, actions_plugin)
 
 
 
+static GtkIconSize menu_icon_size = GTK_ICON_SIZE_INVALID;
+static GQuark      action_quark = 0;
+
+
+
 static void
 actions_plugin_class_init (ActionsPluginClass *klass)
 {
@@ -134,27 +234,48 @@ actions_plugin_class_init (ActionsPluginClass *klass)
 
   plugin_class = XFCE_PANEL_PLUGIN_CLASS (klass);
   plugin_class->construct = actions_plugin_construct;
+  plugin_class->free_data = actions_plugin_free_data;
   plugin_class->size_changed = actions_plugin_size_changed;
   plugin_class->configure_plugin = actions_plugin_configure_plugin;
   plugin_class->orientation_changed = actions_plugin_orientation_changed;
 
   g_object_class_install_property (gobject_class,
-                                   PROP_FIRST_ACTION,
-                                   g_param_spec_uint ("first-action",
-                                                      NULL, NULL,
-                                                      ACTION_DISABLED,
-                                                      ACTION_HIBERNATE - 1,
-                                                      ACTION_LOG_OUT_DIALOG,
-                                                      EXO_PARAM_READWRITE));
+                                   PROP_ITEMS,
+                                   g_param_spec_boxed ("items",
+                                                       NULL, NULL,
+                                                       PANEL_PROPERTIES_TYPE_VALUE_ARRAY,
+                                                       EXO_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
-                                   PROP_SECOND_ACTION,
-                                   g_param_spec_uint ("second-action",
+                                   PROP_APPEARANCE,
+                                   g_param_spec_uint ("appearance",
                                                       NULL, NULL,
-                                                      ACTION_DISABLED,
-                                                      ACTION_HIBERNATE,
-                                                      ACTION_DISABLED,
+                                                      APPEARANCE_TYPE_BUTTONS,
+                                                      APPEARANCE_TYPE_MENU,
+                                                      APPEARANCE_TYPE_MENU,
                                                       EXO_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_INVERT_ORIENTATION,
+                                   g_param_spec_boolean ("invert-orientation",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         EXO_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_ASK_CONFIRMATION,
+                                   g_param_spec_boolean ("ask-confirmation",
+                                                         NULL, NULL,
+                                                         TRUE,
+                                                         EXO_PARAM_READWRITE));
+
+  menu_icon_size = gtk_icon_size_from_name ("panel-actions-menu");
+  if (menu_icon_size == GTK_ICON_SIZE_INVALID)
+    menu_icon_size = gtk_icon_size_register ("panel-actions-menu",
+                                             DEFAULT_ICON_SIZE,
+                                             DEFAULT_ICON_SIZE);
+
+  action_quark = g_quark_from_string ("panel-action-quark");
 }
 
 
@@ -162,40 +283,9 @@ actions_plugin_class_init (ActionsPluginClass *klass)
 static void
 actions_plugin_init (ActionsPlugin *plugin)
 {
-  GtkWidget   *widget;
-  ActionEntry *entry = &action_entries[ACTION_LOG_OUT_DIALOG];
-
-  plugin->first_action = ACTION_LOG_OUT_DIALOG;
-  plugin->second_action = ACTION_DISABLED;
-
-  plugin->box = xfce_hvbox_new (GTK_ORIENTATION_HORIZONTAL, TRUE, 0);
-  gtk_container_add (GTK_CONTAINER (plugin), plugin->box);
-
-  plugin->first_button = widget = xfce_panel_create_button ();
-  gtk_box_pack_start (GTK_BOX (plugin->box), widget, TRUE, TRUE, 0);
-  gtk_widget_set_name (widget, "actions-first-button");
-  g_signal_connect (G_OBJECT (widget), "clicked",
-      G_CALLBACK (actions_plugin_button_clicked), plugin);
-  gtk_widget_set_tooltip_text (widget, _(entry->title));
-  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), widget);
-  gtk_widget_show (widget);
-
-  panel_utils_set_atk_info (widget, _(entry->title), NULL);
-
-  plugin->first_image = xfce_panel_image_new_from_source (entry->icon_name);
-  gtk_container_add (GTK_CONTAINER (widget), plugin->first_image);
-  gtk_widget_show (plugin->first_image);
-
-  plugin->second_button = widget = xfce_panel_create_button ();
-  gtk_box_pack_start (GTK_BOX (plugin->box), widget, TRUE, TRUE, 0);
-  gtk_widget_set_name (widget, "actions-second-button");
-  g_signal_connect (G_OBJECT (widget), "clicked",
-      G_CALLBACK (actions_plugin_button_clicked), plugin);
-  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), widget);
-
-  plugin->second_image = xfce_panel_image_new ();
-  gtk_container_add (GTK_CONTAINER (widget), plugin->second_image);
-  gtk_widget_show (plugin->second_image);
+  plugin->type = APPEARANCE_TYPE_MENU;
+  plugin->invert_orientation = FALSE;
+  plugin->ask_confirmation = TRUE;
 }
 
 
@@ -210,12 +300,20 @@ actions_plugin_get_property (GObject    *object,
 
   switch (prop_id)
     {
-    case PROP_FIRST_ACTION:
-      g_value_set_uint (value, plugin->first_action - 1);
+    case PROP_ITEMS:
+      g_value_set_boxed (value, plugin->items);
       break;
 
-    case PROP_SECOND_ACTION:
-      g_value_set_uint (value, plugin->second_action);
+    case PROP_APPEARANCE:
+      g_value_set_uint (value, plugin->type);
+      break;
+
+    case PROP_INVERT_ORIENTATION:
+      g_value_set_boolean (value, plugin->invert_orientation);
+      break;
+
+    case PROP_ASK_CONFIRMATION:
+      g_value_set_boolean (value, plugin->ask_confirmation);
       break;
 
     default:
@@ -233,49 +331,28 @@ actions_plugin_set_property (GObject      *object,
                              GParamSpec   *pspec)
 {
   ActionsPlugin *plugin = XFCE_ACTIONS_PLUGIN (object);
-  ActionType     action;
 
   switch (prop_id)
     {
-    case PROP_FIRST_ACTION:
-      /* set new value and update icon */
-      action = plugin->first_action = g_value_get_uint (value) + 1;
-      gtk_widget_set_tooltip_text (plugin->first_button,
-          _(action_entries[action].title));
-      xfce_panel_image_set_from_source (
-          XFCE_PANEL_IMAGE (plugin->first_image),
-          action_entries[action].icon_name);
-
-      panel_utils_set_atk_info (plugin->first_button,
-          _(action_entries[action].title), NULL);
+    case PROP_ITEMS:
+      if (plugin->items != NULL)
+        xfconf_array_free (plugin->items);
+      plugin->items = g_value_dup_boxed (value);
+      actions_plugin_pack (plugin);
       break;
 
-    case PROP_SECOND_ACTION:
-      /* set new value */
-      action = plugin->second_action = g_value_get_uint (value);
-
-      /* update button visibility and icon */
-      if (action == ACTION_DISABLED)
-        {
-          gtk_widget_hide (plugin->second_button);
-        }
-      else
-        {
-          gtk_widget_show (plugin->second_button);
-          gtk_widget_set_tooltip_text (plugin->second_button,
-              _(action_entries[action].title));
-          xfce_panel_image_set_from_source (
-              XFCE_PANEL_IMAGE (plugin->second_image),
-              action_entries[action].icon_name);
-
-          panel_utils_set_atk_info (plugin->second_button,
-              _(action_entries[action].title), NULL);
-        }
+    case PROP_APPEARANCE:
+      plugin->type = g_value_get_uint (value);
+      actions_plugin_pack (plugin);
+      break;
 
-      /* update plugin size */
-      actions_plugin_size_changed (XFCE_PANEL_PLUGIN (plugin),
-          xfce_panel_plugin_get_size (XFCE_PANEL_PLUGIN (plugin)));
+    case PROP_INVERT_ORIENTATION:
+      plugin->invert_orientation = g_value_get_boolean (value);
+      actions_plugin_pack (plugin);
+      break;
 
+    case PROP_ASK_CONFIRMATION:
+      plugin->ask_confirmation = g_value_get_boolean (value);
       break;
 
     default:
@@ -292,8 +369,10 @@ actions_plugin_construct (XfcePanelPlugin *panel_plugin)
   ActionsPlugin       *plugin = XFCE_ACTIONS_PLUGIN (panel_plugin);
   const PanelProperty  properties[] =
   {
-    { "first-action", G_TYPE_UINT },
-    { "second-action", G_TYPE_UINT },
+    { "items", PANEL_PROPERTIES_TYPE_VALUE_ARRAY },
+    { "appearance", G_TYPE_UINT },
+    { "invert-orientation", G_TYPE_BOOLEAN },
+    { "ask-confirmation", G_TYPE_BOOLEAN },
     { NULL }
   };
 
@@ -305,12 +384,40 @@ actions_plugin_construct (XfcePanelPlugin *panel_plugin)
                          xfce_panel_plugin_get_property_base (panel_plugin),
                          properties, FALSE);
 
+  actions_plugin_pack (plugin);
+
   /* set orientation and size */
   actions_plugin_orientation_changed (panel_plugin,
       xfce_panel_plugin_get_orientation (panel_plugin));
+}
 
-  /* show the plugin */
-  gtk_widget_show (plugin->box);
+
+
+static void
+actions_plugin_free_data (XfcePanelPlugin *panel_plugin)
+{
+  ActionsPlugin *plugin = XFCE_ACTIONS_PLUGIN (panel_plugin);
+
+  if (plugin->pack_idle_id != 0)
+    g_source_remove (plugin->pack_idle_id);
+
+  if (plugin->items != NULL)
+    xfconf_array_free (plugin->items);
+
+  if (plugin->menu != NULL)
+    gtk_widget_destroy (plugin->menu);
+}
+
+
+
+static void
+actions_plugin_size_changed_child (GtkWidget *child,
+                                   gpointer   data)
+{
+  gint size = GPOINTER_TO_INT (data);
+
+  if (!GTK_IS_SEPARATOR (child))
+    gtk_widget_set_size_request (child, size, size);
 }
 
 
@@ -320,27 +427,124 @@ actions_plugin_size_changed (XfcePanelPlugin *panel_plugin,
                              gint             size)
 {
   ActionsPlugin *plugin = XFCE_ACTIONS_PLUGIN (panel_plugin);
-  gint           width = size;
-  gint           height = size;
+  GtkWidget     *box;
+  GList         *children, *li;
+  gint           n_children;
+  gint           child_size;
 
-  if (plugin->second_action != ACTION_DISABLED)
+  if (plugin->type == APPEARANCE_TYPE_BUTTONS)
     {
-      if (xfce_panel_plugin_get_orientation (panel_plugin) ==
-          GTK_ORIENTATION_HORIZONTAL)
-        width /= 2;
-      else
-        height /= 2;
+      box = gtk_bin_get_child (GTK_BIN (plugin));
+      if (box != NULL)
+        {
+          if (plugin->invert_orientation)
+            {
+              children = gtk_container_get_children (GTK_CONTAINER (box));
+              if (G_UNLIKELY (children == NULL))
+                return TRUE;
+              n_children = g_list_length (children);
+
+              for (li = children; li != NULL; li = li->next)
+                {
+                  child_size = size / n_children--;
+                  size -= child_size;
+
+                  gtk_widget_set_size_request (GTK_WIDGET (li->data),
+                                               child_size, child_size);
+                }
+            }
+          else
+            {
+              gtk_container_foreach (GTK_CONTAINER (box),
+                  actions_plugin_size_changed_child,
+                  GINT_TO_POINTER (size));
+            }
+        }
     }
 
-  /* set the plugin size */
-  gtk_widget_set_size_request (GTK_WIDGET (panel_plugin),
-                               width, height);
-
   return TRUE;
 }
 
 
 
+static gboolean
+actions_plugin_configure_store (gpointer data)
+{
+  ActionsPlugin *plugin = XFCE_ACTIONS_PLUGIN (data);
+  GtkTreeModel  *model;
+  GtkTreeIter    iter;
+  GPtrArray     *array;
+  gboolean       visible;
+  guint          type;
+  GValue        *val;
+
+  model = g_object_get_data (G_OBJECT (plugin), "items-store");
+  panel_return_val_if_fail (GTK_IS_LIST_STORE (model), FALSE);
+
+  array = g_ptr_array_new ();
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      for (;;)
+        {
+          gtk_tree_model_get (model, &iter,
+                              COLUMN_VISIBLE, &visible,
+                              COLUMN_TYPE, &type, -1);
+
+          val = g_new0 (GValue, 1);
+          g_value_init (val, G_TYPE_INT);
+          g_value_set_int (val, visible ? type : -type);
+          g_ptr_array_add (array, val);
+
+          if (!gtk_tree_model_iter_next (model, &iter))
+            break;
+        }
+    }
+
+  /* Store the new array */
+  if (plugin->items != NULL)
+    xfconf_array_free (plugin->items);
+  plugin->items = array;
+  g_object_notify (G_OBJECT (plugin), "items");
+
+  return FALSE;
+}
+
+
+
+static void
+actions_plugin_configure_store_idle (ActionsPlugin *plugin)
+{
+  g_idle_add (actions_plugin_configure_store, plugin);
+}
+
+
+
+static void
+actions_plugin_configure_visible_toggled (GtkCellRendererToggle *renderer,
+                                          const gchar           *path_string,
+                                          ActionsPlugin         *plugin)
+{
+  GtkTreeIter   iter;
+  gboolean      visible;
+  GtkTreeModel *model;
+
+  panel_return_if_fail (XFCE_IS_ACTIONS_PLUGIN (plugin));
+
+  model = g_object_get_data (G_OBJECT (plugin), "items-store");
+  panel_return_if_fail (GTK_IS_LIST_STORE (model));
+  if (gtk_tree_model_get_iter_from_string (model, &iter, path_string))
+    {
+      gtk_tree_model_get (model, &iter, COLUMN_VISIBLE, &visible, -1);
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                          COLUMN_VISIBLE, !visible, -1);
+
+      actions_plugin_configure_store (plugin);
+    }
+}
+
+
+
 static void
 actions_plugin_configure_plugin (XfcePanelPlugin *panel_plugin)
 {
@@ -348,9 +552,20 @@ actions_plugin_configure_plugin (XfcePanelPlugin *panel_plugin)
   GtkBuilder    *builder;
   GObject       *dialog;
   GObject       *object;
+  GObject       *combo;
+  ActionEntry   *entry;
   guint          i;
+  const GValue  *val;
+  gint           type;
+  ActionType     real_type;
+  guint          n;
+  GObject       *store;
+  gboolean       found;
+  GtkTreeIter    iter;
+  gchar         *sep_str;
 
   panel_return_if_fail (XFCE_IS_ACTIONS_PLUGIN (plugin));
+  panel_return_if_fail (plugin->items != NULL);
 
   /* setup the dialog */
   PANEL_UTILS_LINK_4UI
@@ -359,25 +574,106 @@ actions_plugin_configure_plugin (XfcePanelPlugin *panel_plugin)
   if (G_UNLIKELY (builder == NULL))
     return;
 
-  /* populate the first store */
-  object = gtk_builder_get_object (builder, "first-action-model");
-  for (i = 1; i < G_N_ELEMENTS (action_entries); i++)
-    gtk_list_store_insert_with_values (GTK_LIST_STORE (object), NULL, i - 1,
-                                       0, _(action_entries[i].title), -1);
+  combo = gtk_builder_get_object (builder, "combo-mode");
+  exo_mutual_binding_new (G_OBJECT (plugin), "appearance",
+                          G_OBJECT (combo), "active");
+
+  object = gtk_builder_get_object (builder, "invert-orientation");
+  exo_mutual_binding_new (G_OBJECT (plugin), "invert-orientation",
+                          G_OBJECT (object), "active");
+  exo_binding_new_with_negation (G_OBJECT (combo), "active",
+                                 G_OBJECT (object), "sensitive");
 
-  object = gtk_builder_get_object (builder, "first-action");
-  exo_mutual_binding_new (G_OBJECT (plugin), "first-action",
+  object = gtk_builder_get_object (builder, "confirmation-dialog");
+  exo_mutual_binding_new (G_OBJECT (plugin), "ask-confirmation",
                           G_OBJECT (object), "active");
 
-  /* populate the second store */
-  object = gtk_builder_get_object (builder, "second-action-model");
+  store = gtk_builder_get_object (builder, "items-store");
+  panel_return_if_fail (GTK_IS_LIST_STORE (store));
+  g_object_set_data (G_OBJECT (plugin), "items-store", store);
+
+  object = gtk_builder_get_object (builder, "visible-toggle");
+  panel_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (object));
+  g_signal_connect (G_OBJECT (object), "toggled",
+      G_CALLBACK (actions_plugin_configure_visible_toggled), plugin);
+
+  sep_str = g_markup_printf_escaped ("<span color='grey' style='italic'>%s</span>", _("Separator"));
+
+  /* add items from the settings */
+  for (i = 0; i < plugin->items->len; i++)
+    {
+      /* get the value and check if it is within range */
+      val = g_ptr_array_index (plugin->items, i);
+      type = g_value_get_int (val);
+      real_type = ABS (type);
+      if (type == 0 || real_type > N_ACTION_TYPES)
+        continue;
+
+      /* find the entry in the available actions */
+      entry = NULL;
+      for (n = 0; n < G_N_ELEMENTS (action_entries); n++)
+        {
+          if (action_entries[n].type == real_type)
+            {
+              entry = &action_entries[n];
+              break;
+            }
+        }
+
+      if (entry == NULL)
+        {
+          if (real_type == ACTION_TYPE_SEPARATOR)
+            {
+              gtk_list_store_insert_with_values (GTK_LIST_STORE (store), NULL, i,
+                                                 COLUMN_VISIBLE, type > 0,
+                                                 COLUMN_NAME, sep_str,
+                                                 COLUMN_TYPE, ACTION_TYPE_SEPARATOR,
+                                                 -1);
+            }
+        }
+      else
+        {
+          gtk_list_store_insert_with_values (GTK_LIST_STORE (store), NULL, i,
+                                             COLUMN_VISIBLE, type > 0,
+                                             COLUMN_NAME, _(entry->name),
+                                             COLUMN_TYPE, entry->type,
+                                             -1);
+        }
+    }
+
+  g_free (sep_str);
+
+  /* check if there are known actions not in the settings */
   for (i = 0; i < G_N_ELEMENTS (action_entries); i++)
-    gtk_list_store_insert_with_values (GTK_LIST_STORE (object), NULL, i,
-                                       0, _(action_entries[i].title), -1);
+    {
+      entry = &action_entries[i];
+      found = FALSE;
 
-  object = gtk_builder_get_object (builder, "second-action");
-  exo_mutual_binding_new (G_OBJECT (plugin), "second-action",
-                          G_OBJECT (object), "active");
+      for (n = 0; n < plugin->items->len; n++)
+        {
+          val = g_ptr_array_index (plugin->items, n);
+          type = g_value_get_int (val);
+          if (ABS (type) == (gint) entry->type)
+            {
+              found = TRUE;
+              break;
+            }
+        }
+
+      if (!found)
+        {
+          gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+          gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+                              COLUMN_VISIBLE, FALSE,
+                              COLUMN_NAME, _(entry->name),
+                              COLUMN_TYPE, entry->type,
+                              -1);
+        }
+    }
+
+  /* save on dnd changes */
+  g_signal_connect_swapped (G_OBJECT (store), "row-inserted",
+      G_CALLBACK (actions_plugin_configure_store_idle), plugin);
 
   gtk_widget_show (GTK_WIDGET (dialog));
 }
@@ -388,120 +684,479 @@ static void
 actions_plugin_orientation_changed (XfcePanelPlugin *panel_plugin,
                                     GtkOrientation   orientation)
 {
-  ActionsPlugin  *plugin = XFCE_ACTIONS_PLUGIN (panel_plugin);
-  GtkOrientation  box_orientation;
+  actions_plugin_pack (XFCE_ACTIONS_PLUGIN (panel_plugin));
+}
 
-  /* box orientation is opposite to the panel orientation */
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    box_orientation = GTK_ORIENTATION_VERTICAL;
-  else
-    box_orientation = GTK_ORIENTATION_HORIZONTAL;
 
-  /* set orientation */
-  xfce_hvbox_set_orientation (XFCE_HVBOX (plugin->box), box_orientation);
 
-  /* update the plugin size */
-  actions_plugin_size_changed (panel_plugin,
-      xfce_panel_plugin_get_size (panel_plugin));
+static gboolean
+actions_plugin_action_confirmation_time (gpointer data)
+{
+  ActionTimeout *timeout = data;
+
+  panel_return_val_if_fail (timeout->entry != NULL, FALSE);
+
+  if (timeout->time_left == 0)
+    {
+      /* unattended shutdown, so don't allow apps to cancel shutdown */
+      timeout->unattended = TRUE;
+
+      gtk_dialog_response (GTK_DIALOG (timeout->dialog),
+                           GTK_RESPONSE_ACCEPT);
+    }
+  else
+    {
+      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (timeout->dialog),
+                                                timeout->entry->status, timeout->time_left);
+    }
+
+  return --timeout->time_left >= 0;
 }
 
 
 
-static void
-actions_plugin_button_spawn_command (const gchar *command)
+static gboolean
+actions_plugin_action_confirmation (ActionsPlugin *plugin,
+                                    ActionEntry   *entry,
+                                    gboolean      *unattended)
 {
-  GError *error = NULL;
+  GtkWidget     *dialog;
+  GtkWidget     *button;
+  gint           result;
+  GtkWidget     *image;
+  ActionTimeout *timeout;
+  guint          timeout_id;
+
+  panel_return_val_if_fail (entry->question != NULL, FALSE);
+  panel_return_val_if_fail (entry->status != NULL, FALSE);
+
+  dialog = gtk_message_dialog_new (NULL, 0,
+                                   GTK_MESSAGE_QUESTION, GTK_BUTTONS_CANCEL,
+                                   "%s", entry->question);
+  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
+  gtk_window_stick (GTK_WINDOW (dialog));
+  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
+  gtk_window_set_title (GTK_WINDOW (dialog), _(entry->name));
+
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog), _(entry->name_mnemonic), GTK_RESPONSE_ACCEPT);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+
+  image = gtk_image_new_from_icon_name (entry->icon_name, GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (button), image);
+
+  timeout = g_slice_new0 (ActionTimeout);
+  timeout->entry = entry;
+  timeout->time_left = DEFAULT_TIMEOUT;
+  timeout->dialog = dialog;
+  timeout->unattended = FALSE;
+
+  /* first second looks out of sync with a second timer */
+  timeout_id = g_timeout_add (1000, actions_plugin_action_confirmation_time, timeout);
+  actions_plugin_action_confirmation_time (timeout);
+
+  result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  if (unattended != NULL)
+    *unattended = timeout->unattended;
+
+  g_source_remove (timeout_id);
+  gtk_widget_destroy (dialog);
+  g_slice_free (ActionTimeout, timeout);
+
+  return result == GTK_RESPONSE_ACCEPT;
+}
+
 
-  if (g_getenv ("SESSION_MANAGER") == NULL)
-    {
-      /* TRANSLATORS: no session manager is launched, so avoid any
-       * problems and ask the user to quit the panel so users without
-       * xfce4-session can still close the xserver */
-      if (xfce_dialog_confirm (NULL, GTK_STOCK_QUIT, NULL,
-          _("You have started X without session manager. Clicking Quit will close the X server."),
-          _("Are you sure you want to quit the panel?")))
-         command = "xfce4-panel --quit";
-       else
-         return;
-    }
 
-  if (!g_spawn_command_line_async (command, &error))
+static gboolean
+actions_plugin_action_activate_dbus (ActionShutdownType   type,
+                                     gboolean             allow_save,
+                                     GError             **error)
+{
+  DBusGConnection *connection;
+  DBusGProxy      *proxy;
+  gboolean         retval;
+
+  connection = dbus_g_bus_get (DBUS_BUS_SESSION, error);
+  if (connection == NULL)
+    return FALSE;
+
+  proxy = dbus_g_proxy_new_for_name (connection,
+                                     "org.xfce.SessionManager",
+                                     "/org/xfce/SessionManager",
+                                     "org.xfce.Session.Manager");
+  if (G_LIKELY (proxy != NULL))
     {
-      xfce_dialog_show_error (NULL, error, _("Failed to execute command \"%s\""), command);
-      g_error_free (error);
+      retval = dbus_g_proxy_call (proxy, "Shutdown", error,
+                                  G_TYPE_UINT, type,
+                                  G_TYPE_BOOLEAN, allow_save,
+                                  G_TYPE_INVALID, G_TYPE_INVALID);
+
+      g_object_unref (G_OBJECT (proxy));
     }
+
+  return retval;
 }
 
 
 
 static void
-actions_plugin_button_clicked (GtkWidget     *button,
-                               ActionsPlugin *plugin)
+actions_plugin_action_activate (GtkWidget      *widget,
+                                ActionsPlugin  *plugin)
 {
-  ActionType    action;
-  XfceSMClient *sm_client = NULL;
-
-  panel_return_if_fail (XFCE_IS_ACTIONS_PLUGIN (plugin));
-
-  /* get the action to execute */
-  if (button == plugin->first_button)
-    action = plugin->first_action;
-  else
-    action = plugin->second_action;
-
-#if 0
-  /* get the active session */
-  /* TODO this is not implemented in XfceSMClient */
-  sm_client = xfce_sm_client_get ();
-  if (!xfce_sm_client_is_connected (sm_client))
-    sm_client = NULL;
-#endif
+  ActionEntry *entry;
+  gboolean     unattended = FALSE;
+  GError      *error = NULL;
+  gboolean     succeed = FALSE;
+
+  entry = g_object_get_qdata (G_OBJECT (widget), action_quark);
+  panel_return_if_fail (entry != NULL);
+
+  if (plugin->ask_confirmation
+      && entry->question != NULL
+      && entry->status != NULL
+      && !actions_plugin_action_confirmation (plugin, entry, &unattended))
+    return;
 
-  switch (action)
+  switch (entry->type)
     {
-    case ACTION_DISABLED:
-      /* foo */
+    case ACTION_TYPE_LOGOUT:
+      succeed = actions_plugin_action_activate_dbus (ACTION_SHUTDOWN_LOGOUT,
+                                                     unattended, &error);
       break;
 
-    case ACTION_LOG_OUT_DIALOG:
-      if (G_LIKELY (sm_client != NULL))
-        xfce_sm_client_request_shutdown (sm_client, XFCE_SM_CLIENT_SHUTDOWN_HINT_ASK);
-      else
-        actions_plugin_button_spawn_command ("xfce4-session-logout");
+    case ACTION_TYPE_LOGOUT_DIALOG:
+      succeed = actions_plugin_action_activate_dbus (ACTION_SHUTDOWN_ASK,
+                                                     unattended, &error);
       break;
 
-    case ACTION_LOG_OUT:
-      if (G_LIKELY (sm_client != NULL))
-        xfce_sm_client_request_shutdown (sm_client, XFCE_SM_CLIENT_SHUTDOWN_HINT_LOGOUT);
-      else
-        actions_plugin_button_spawn_command ("xfce4-session-logout --logout");
+    case ACTION_TYPE_SWITCH_USER:
+      succeed = g_spawn_command_line_async ("gdmflexiserver", &error);
       break;
 
-    case ACTION_SHUT_DOWN:
-      if (G_LIKELY (sm_client != NULL))
-        xfce_sm_client_request_shutdown (sm_client, XFCE_SM_CLIENT_SHUTDOWN_HINT_HALT);
-      else
-        actions_plugin_button_spawn_command ("xfce4-session-logout --halt");
+    case ACTION_TYPE_LOCK_SCREEN:
+      succeed = g_spawn_command_line_async ("xflock4", &error);
       break;
 
-    case ACTION_RESTART:
-      if (G_LIKELY (sm_client != NULL))
-        xfce_sm_client_request_shutdown (sm_client, XFCE_SM_CLIENT_SHUTDOWN_HINT_REBOOT);
-      else
-        actions_plugin_button_spawn_command ("xfce4-session-logout --reboot");
+    case ACTION_TYPE_HIBERNATE:
+      succeed = actions_plugin_action_activate_dbus (ACTION_SHUTDOWN_HIBERNATE,
+                                                     unattended, &error);
       break;
 
-    case ACTION_LOCK_SCREEN:
-      actions_plugin_button_spawn_command ("xflock4");
+    case ACTION_TYPE_SUSPEND:
+      succeed = actions_plugin_action_activate_dbus (ACTION_SHUTDOWN_SUSPEND,
+                                                     unattended, &error);
       break;
 
-    case ACTION_SUSPEND:
-      actions_plugin_button_spawn_command ("xfce4-session-logout --suspend");
+    case ACTION_TYPE_RESTART:
+      succeed = actions_plugin_action_activate_dbus (ACTION_SHUTDOWN_REBOOT,
+                                                     unattended, &error);
       break;
 
-    case ACTION_HIBERNATE:
-      actions_plugin_button_spawn_command ("xfce4-session-logout --hibernate");
+    case ACTION_TYPE_SHUTDOWN:
+      succeed = actions_plugin_action_activate_dbus (ACTION_SHUTDOWN_HALT,
+                                                     unattended, &error);
       break;
+
+    default:
+      panel_assert_not_reached ();
+      return;
+    }
+
+  if (!succeed)
+    {
+      xfce_dialog_show_error (NULL, error,
+          _("Failed to run action \"%s\""),
+          _(entry->name));
+    }
+}
+
+
+
+static GtkWidget *
+actions_plugin_action_button (ActionsPlugin  *plugin,
+                              guint           type,
+                              GtkOrientation  orientation)
+{
+  GtkWidget   *widget;
+  GtkWidget   *image;
+  guint        i;
+  ActionEntry *entry = NULL;
+
+  if (type == ACTION_TYPE_SEPARATOR)
+    {
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        widget = gtk_vseparator_new ();
+      else
+        widget = gtk_hseparator_new ();
     }
+  else
+    {
+      /* lookup the action entry */
+      for (i = 0; i < G_N_ELEMENTS (action_entries); i++)
+        if (action_entries[i].type == type)
+           entry = &action_entries[i];
+      if (entry == NULL)
+        return NULL;
+
+      widget = xfce_panel_create_button ();
+      gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
+      g_object_set_qdata (G_OBJECT (widget), action_quark, entry);
+      gtk_widget_set_tooltip_text (widget, _(entry->name));
+      g_signal_connect (G_OBJECT (widget), "clicked",
+          G_CALLBACK (actions_plugin_action_activate), plugin);
+
+      image = xfce_panel_image_new_from_source (entry->icon_name);
+      gtk_container_add (GTK_CONTAINER (widget), image);
+      gtk_widget_show (image);
+    }
+
+  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), widget);
+  return widget;
 }
 
+
+
+static GtkWidget *
+actions_plugin_action_menu_item (ActionsPlugin *plugin,
+                                 guint          type,
+                                 gint           size)
+{
+  GtkWidget   *mi;
+  GtkWidget   *image;
+  guint        i;
+  ActionEntry *entry = NULL;
+
+  if (type == ACTION_TYPE_SEPARATOR)
+    return gtk_separator_menu_item_new ();
+
+  /* lookup the action entry */
+  for (i = 0; i < G_N_ELEMENTS (action_entries); i++)
+    if (action_entries[i].type == type)
+       entry = &action_entries[i];
+  if (entry == NULL)
+    return NULL;
+
+  mi = gtk_image_menu_item_new_with_mnemonic (_(entry->name_mnemonic));
+  g_object_set_qdata (G_OBJECT (mi), action_quark, entry);
+  g_signal_connect (G_OBJECT (mi), "activate",
+      G_CALLBACK (actions_plugin_action_activate), plugin);
+
+  if (size > 0)
+    {
+      image = xfce_panel_image_new_from_source (entry->icon_name);
+      xfce_panel_image_set_size (XFCE_PANEL_IMAGE (image), size);
+      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
+      gtk_widget_show (image);
+    }
+
+  return mi;
+}
+
+
+
+static gboolean
+actions_plugin_pack_idle (gpointer data)
+{
+  ActionsPlugin  *plugin = XFCE_ACTIONS_PLUGIN (data);
+  GtkWidget      *label;
+  GtkWidget      *button;
+  GtkWidget      *widget;
+  const gchar    *username;
+  GtkWidget      *child;
+  GtkWidget      *box;
+  guint           i;
+  const GValue   *val;
+  gint            type;
+  GtkOrientation  orientation;
+
+  child = gtk_bin_get_child (GTK_BIN (plugin));
+  if (child != NULL)
+    gtk_widget_destroy (child);
+
+  if (plugin->menu != NULL)
+    gtk_widget_destroy (plugin->menu);
+
+  if (plugin->items == NULL)
+    plugin->items = actions_plugin_default_array ();
+
+  orientation = xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin));
+
+  if (plugin->type == APPEARANCE_TYPE_BUTTONS)
+    {
+      if (plugin->invert_orientation)
+        orientation = !orientation;
+      box = xfce_hvbox_new (orientation, FALSE, 0);
+      gtk_container_add (GTK_CONTAINER (plugin), box);
+      gtk_widget_show (box);
+
+      for (i = 0; i < plugin->items->len; i++)
+        {
+          val = g_ptr_array_index (plugin->items, i);
+          type = g_value_get_int (val);
+          if (type <= 0)
+            continue;
+
+          /* skip separators when packing buttons in the opposite
+           * orientation */
+          if (plugin->invert_orientation
+              && type == ACTION_TYPE_SEPARATOR)
+            continue;
+
+          widget = actions_plugin_action_button (plugin, type, orientation);
+          if (widget != NULL)
+            {
+              gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+              gtk_widget_show (widget);
+            }
+        }
+
+      actions_plugin_size_changed (XFCE_PANEL_PLUGIN (plugin),
+          xfce_panel_plugin_get_size (XFCE_PANEL_PLUGIN (plugin)));
+    }
+  else
+    {
+      /* get a decent username, not the glib defaults */
+      username = g_get_real_name ();
+      if (exo_str_is_empty (username)
+          || strcmp (username, "Unknown") == 0)
+        {
+          username = g_get_user_name ();
+          if (exo_str_is_empty (username)
+              || strcmp (username, "somebody") == 0)
+            username = _("John Doo");
+        }
+
+      button = xfce_arrow_button_new (GTK_ARROW_NONE);
+      gtk_widget_set_name (button, "actions-button");
+      gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+      xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), button);
+      gtk_container_add (GTK_CONTAINER (plugin), button);
+      g_signal_connect (G_OBJECT (button), "toggled",
+          G_CALLBACK (actions_plugin_menu), plugin);
+      gtk_widget_show (button);
+
+      label = gtk_label_new (username);
+      gtk_container_add (GTK_CONTAINER (button), label);
+      gtk_label_set_angle (GTK_LABEL (label),
+          orientation == GTK_ORIENTATION_HORIZONTAL ? 0 : 270);
+      gtk_widget_show (label);
+    }
+
+  return FALSE;
+}
+
+
+
+static void
+actions_plugin_pack_idle_destoyed (gpointer data)
+{
+  XFCE_ACTIONS_PLUGIN (data)->pack_idle_id = 0;
+}
+
+
+
+static void
+actions_plugin_pack (ActionsPlugin *plugin)
+{
+  if (plugin->pack_idle_id == 0)
+    {
+      plugin->pack_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, actions_plugin_pack_idle,
+                                              plugin, actions_plugin_pack_idle_destoyed);
+    }
+}
+
+
+
+static GPtrArray *
+actions_plugin_default_array (void)
+{
+  GPtrArray  *array;
+  GValue     *val;
+  guint       i;
+  gint        defaults[] =
+    {
+      ACTION_TYPE_LOCK_SCREEN,
+      ACTION_TYPE_SWITCH_USER,
+      ACTION_TYPE_SEPARATOR,
+      ACTION_TYPE_SUSPEND,
+      -ACTION_TYPE_HIBERNATE, /*hidden */
+      -ACTION_TYPE_SEPARATOR, /*hidden */
+      ACTION_TYPE_SHUTDOWN,
+      -ACTION_TYPE_RESTART,
+      ACTION_TYPE_SEPARATOR, /*hidden */
+      ACTION_TYPE_LOGOUT
+    };
+
+  array = g_ptr_array_sized_new (G_N_ELEMENTS (defaults));
+  for (i = 0; i < G_N_ELEMENTS (defaults); i++)
+    {
+      val = g_new0 (GValue, 1);
+      g_value_init (val, G_TYPE_INT);
+      g_value_set_int (val, defaults[i]);
+      g_ptr_array_add (array, val);
+    }
+
+  return array;
+}
+
+
+
+static void
+actions_plugin_menu_deactivate (GtkWidget *menu,
+                                GtkWidget *button)
+{
+  panel_return_if_fail (button == NULL || GTK_IS_TOGGLE_BUTTON (button));
+  panel_return_if_fail (GTK_IS_MENU (menu));
+
+  /* button is NULL when we popup the menu under the cursor position */
+  if (button != NULL)
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
+
+  gtk_menu_popdown (GTK_MENU (menu));
+}
+
+
+
+static void
+actions_plugin_menu (GtkWidget     *button,
+                     ActionsPlugin *plugin)
+{
+  guint         i;
+  const GValue *val;
+  gint          type;
+  GtkWidget    *mi;
+  gint          w, h, size;
+
+  panel_return_if_fail (XFCE_IS_ACTIONS_PLUGIN (plugin));
+
+  if (plugin->menu == NULL)
+    {
+      plugin->menu = gtk_menu_new ();
+      g_signal_connect (G_OBJECT (plugin->menu), "selection-done",
+          G_CALLBACK (actions_plugin_menu_deactivate), button);
+      g_object_add_weak_pointer (G_OBJECT (plugin->menu), (gpointer) &plugin->menu);
+
+      if (gtk_icon_size_lookup (menu_icon_size, &w, &h))
+        size = MIN (w, h);
+
+      for (i = 0; i < plugin->items->len; i++)
+        {
+          val = g_ptr_array_index (plugin->items, i);
+          type = g_value_get_int (val);
+          if (type <= 0)
+            continue;
+
+          mi = actions_plugin_action_menu_item (plugin, type, size);
+          if (mi != NULL)
+            {
+              gtk_menu_shell_append (GTK_MENU_SHELL (plugin->menu), mi);
+              gtk_widget_show (mi);
+            }
+        }
+    }
+
+  gtk_menu_popup (GTK_MENU (plugin->menu), NULL, NULL,
+                  button != NULL ? xfce_panel_plugin_position_menu : NULL,
+                  plugin, 1, gtk_get_current_event_time ());
+}
diff --git a/plugins/actions/actions.desktop.in b/plugins/actions/actions.desktop.in
index 52e83ad..158b024 100644
--- a/plugins/actions/actions.desktop.in
+++ b/plugins/actions/actions.desktop.in
@@ -4,4 +4,4 @@ _Name=Action Buttons
 _Comment=Log out, lock or other system actions
 Icon=system-log-out
 X-XFCE-Module=actions
-X-XFCE-Internal=TRUE
+X-XFCE-Internal=FALSE


More information about the Xfce4-commits mailing list