|
|
|
/*
|
|
|
|
* Copyright (C) 2008-2010 Nick Schermer <nick@xfce.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
|
|
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
#include <string.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include <exo/exo.h>
|
|
|
|
#include <libxfce4util/libxfce4util.h>
|
|
|
|
#include <libxfce4ui/libxfce4ui.h>
|
|
|
|
#include <garcon/garcon.h>
|
|
|
|
#include <xfconf/xfconf.h>
|
|
|
|
|
|
|
|
#include <libxfce4panel/libxfce4panel.h>
|
|
|
|
#include <common/panel-private.h>
|
|
|
|
#include <common/panel-xfconf.h>
|
|
|
|
#include <common/panel-utils.h>
|
|
|
|
|
|
|
|
#include "launcher.h"
|
|
|
|
#include "launcher-dialog.h"
|
|
|
|
|
|
|
|
#define ARROW_BUTTON_SIZE (12)
|
|
|
|
#define TOOLTIP_ICON_SIZE (32)
|
|
|
|
#define MENU_ICON_SIZE (32)
|
|
|
|
#define MENU_POPUP_DELAY (225)
|
|
|
|
#define NO_ARROW_INSIDE_BUTTON(plugin) ((plugin)->arrow_position != LAUNCHER_ARROW_INTERNAL \
|
|
|
|
|| LIST_HAS_ONE_OR_NO_ENTRIES ((plugin)->items))
|
|
|
|
#define ARROW_INSIDE_BUTTON(plugin) (!NO_ARROW_INSIDE_BUTTON (plugin))
|
|
|
|
#define RELATIVE_CONFIG_PATH PANEL_PLUGIN_RELATIVE_PATH G_DIR_SEPARATOR_S "%s-%d"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void launcher_plugin_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec);
|
|
|
|
static void launcher_plugin_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec);
|
|
|
|
static void launcher_plugin_construct (XfcePanelPlugin *panel_plugin);
|
|
|
|
static void launcher_plugin_free_data (XfcePanelPlugin *panel_plugin);
|
|
|
|
static void launcher_plugin_removed (XfcePanelPlugin *panel_plugin);
|
|
|
|
static gboolean launcher_plugin_remote_event (XfcePanelPlugin *panel_plugin,
|
|
|
|
const gchar *name,
|
|
|
|
const GValue *value);
|
|
|
|
static gboolean launcher_plugin_save_delayed_timeout (gpointer user_data);
|
|
|
|
static void launcher_plugin_save_delayed (LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_mode_changed (XfcePanelPlugin *panel_plugin,
|
|
|
|
XfcePanelPluginMode mode);
|
|
|
|
static gboolean launcher_plugin_size_changed (XfcePanelPlugin *panel_plugin,
|
|
|
|
gint size);
|
|
|
|
static void launcher_plugin_configure_plugin (XfcePanelPlugin *panel_plugin);
|
|
|
|
static void launcher_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin,
|
|
|
|
XfceScreenPosition position);
|
|
|
|
static void launcher_plugin_icon_theme_changed (GtkIconTheme *icon_theme,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static LauncherArrowType launcher_plugin_default_arrow_type (LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_pack_widgets (LauncherPlugin *plugin);
|
|
|
|
static GdkPixbuf *launcher_plugin_tooltip_pixbuf (GdkScreen *screen,
|
|
|
|
const gchar *icon_name);
|
|
|
|
static void launcher_plugin_menu_deactivate (GtkWidget *menu,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_menu_item_activate (GtkMenuItem *widget,
|
|
|
|
GarconMenuItem *item);
|
|
|
|
static void launcher_plugin_menu_item_drag_data_received (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
GtkSelectionData *data,
|
|
|
|
guint info,
|
|
|
|
guint drag_time,
|
|
|
|
GarconMenuItem *item);
|
|
|
|
static void launcher_plugin_menu_construct (LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_menu_popup_destroyed (gpointer user_data);
|
|
|
|
static gboolean launcher_plugin_menu_popup (gpointer user_data);
|
|
|
|
static void launcher_plugin_menu_destroy (LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_button_update (LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_button_state_changed (GtkWidget *button_a,
|
|
|
|
GtkStateType state,
|
|
|
|
GtkWidget *button_b);
|
|
|
|
static gboolean launcher_plugin_button_press_event (GtkWidget *button,
|
|
|
|
GdkEventButton *event,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_button_release_event (GtkWidget *button,
|
|
|
|
GdkEventButton *event,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_button_query_tooltip (GtkWidget *widget,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gboolean keyboard_mode,
|
|
|
|
GtkTooltip *tooltip,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_button_drag_data_received (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
GtkSelectionData *selection_data,
|
|
|
|
guint info,
|
|
|
|
guint drag_time,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_button_drag_motion (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guint drag_time,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_button_drag_drop (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guint drag_time,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_button_drag_leave (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
guint drag_time,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_button_expose_event (GtkWidget *widget,
|
|
|
|
GdkEventExpose *event,
|
|
|
|
LauncherPlugin *launcher);
|
|
|
|
static void launcher_plugin_arrow_visibility (LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_arrow_press_event (GtkWidget *button,
|
|
|
|
GdkEventButton *event,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_arrow_drag_motion (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guint drag_time,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static void launcher_plugin_arrow_drag_leave (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
guint drag_time,
|
|
|
|
LauncherPlugin *plugin);
|
|
|
|
static gboolean launcher_plugin_item_query_tooltip (GtkWidget *widget,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gboolean keyboard_mode,
|
|
|
|
GtkTooltip *tooltip,
|
|
|
|
GarconMenuItem *item);
|
|
|
|
static gboolean launcher_plugin_item_exec_on_screen (GarconMenuItem *item,
|
|
|
|
guint32 event_time,
|
|
|
|
GdkScreen *screen,
|
|
|
|
GSList *uri_list);
|
|
|
|
static void launcher_plugin_item_exec (GarconMenuItem *item,
|
|
|
|
guint32 event_time,
|
|
|
|
GdkScreen *screen,
|
|
|
|
GSList *uri_list);
|
|
|
|
static void launcher_plugin_item_exec_from_clipboard (GarconMenuItem *item,
|
|
|
|
guint32 event_time,
|
|
|
|
GdkScreen *screen);
|
|
|
|
static void launcher_plugin_exec_append_quoted (GString *string,
|
|
|
|
const gchar *unquoted);
|
|
|
|
static gboolean launcher_plugin_exec_parse (GarconMenuItem *item,
|
|
|
|
GSList *uri_list,
|
|
|
|
gchar ***argv,
|
|
|
|
GError **error);
|
|
|
|
static GSList *launcher_plugin_uri_list_extract (GtkSelectionData *data);
|
|
|
|
static void launcher_plugin_uri_list_free (GSList *uri_list);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct _LauncherPluginClass
|
|
|
|
{
|
|
|
|
XfcePanelPluginClass __parent__;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _LauncherPlugin
|
|
|
|
{
|
|
|
|
XfcePanelPlugin __parent__;
|
|
|
|
|
|
|
|
GtkWidget *box;
|
|
|
|
GtkWidget *button;
|
|
|
|
GtkWidget *arrow;
|
|
|
|
GtkWidget *child;
|
|
|
|
GtkWidget *menu;
|
|
|
|
|
|
|
|
GSList *items;
|
|
|
|
|
|
|
|
GdkPixbuf *tooltip_cache;
|
|
|
|
|
|
|
|
gulong theme_change_id;
|
|
|
|
|
|
|
|
guint menu_timeout_id;
|
|
|
|
|
|
|
|
guint disable_tooltips : 1;
|
|
|
|
guint move_first : 1;
|
|
|
|
guint show_label : 1;
|
|
|
|
LauncherArrowType arrow_position;
|
|
|
|
|
|
|
|
GFile *config_directory;
|
|
|
|
GFileMonitor *config_monitor;
|
|
|
|
|
|
|
|
guint save_timeout_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
PROP_ITEMS,
|
|
|
|
PROP_DISABLE_TOOLTIPS,
|
|
|
|
PROP_MOVE_FIRST,
|
|
|
|
PROP_SHOW_LABEL,
|
|
|
|
PROP_ARROW_POSITION
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
ITEMS_CHANGED,
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* define the plugin */
|
|
|
|
XFCE_PANEL_DEFINE_PLUGIN_RESIDENT (LauncherPlugin, launcher_plugin)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* quark to attach the plugin to menu items */
|
|
|
|
static GQuark launcher_plugin_quark = 0;
|
|
|
|
static guint launcher_signals[LAST_SIGNAL];
|
|
|
|
static GtkIconSize launcher_menu_icon_size = GTK_ICON_SIZE_INVALID;
|
|
|
|
static GtkIconSize launcher_tooltip_icon_size = GTK_ICON_SIZE_INVALID;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* target types for dropping in the launcher plugin */
|
|
|
|
static const GtkTargetEntry drop_targets[] =
|
|
|
|
{
|
|
|
|
{ "text/uri-list", 0, 0, },
|
|
|
|
{ "STRING", 0, 0 },
|
|
|
|
{ "UTF8_STRING", 0, 0 },
|
|
|
|
{ "text/plain", 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_class_init (LauncherPluginClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class;
|
|
|
|
XfcePanelPluginClass *plugin_class;
|
|
|
|
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->get_property = launcher_plugin_get_property;
|
|
|
|
gobject_class->set_property = launcher_plugin_set_property;
|
|
|
|
|
|
|
|
plugin_class = XFCE_PANEL_PLUGIN_CLASS (klass);
|
|
|
|
plugin_class->construct = launcher_plugin_construct;
|
|
|
|
plugin_class->free_data = launcher_plugin_free_data;
|
|
|
|
plugin_class->mode_changed = launcher_plugin_mode_changed;
|
|
|
|
plugin_class->size_changed = launcher_plugin_size_changed;
|
|
|
|
plugin_class->configure_plugin = launcher_plugin_configure_plugin;
|
|
|
|
plugin_class->screen_position_changed = launcher_plugin_screen_position_changed;
|
|
|
|
plugin_class->removed = launcher_plugin_removed;
|
|
|
|
plugin_class->remote_event = launcher_plugin_remote_event;
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
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_DISABLE_TOOLTIPS,
|
|
|
|
g_param_spec_boolean ("disable-tooltips",
|
|
|
|
NULL, NULL,
|
|
|
|
FALSE,
|
|
|
|
EXO_PARAM_READWRITE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_MOVE_FIRST,
|
|
|
|
g_param_spec_boolean ("move-first",
|
|
|
|
NULL, NULL,
|
|
|
|
FALSE,
|
|
|
|
EXO_PARAM_READWRITE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_SHOW_LABEL,
|
|
|
|
g_param_spec_boolean ("show-label",
|
|
|
|
NULL, NULL,
|
|
|
|
FALSE,
|
|
|
|
EXO_PARAM_READWRITE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_ARROW_POSITION,
|
|
|
|
g_param_spec_uint ("arrow-position",
|
|
|
|
NULL, NULL,
|
|
|
|
LAUNCHER_ARROW_DEFAULT,
|
|
|
|
LAUNCHER_ARROW_INTERNAL,
|
|
|
|
LAUNCHER_ARROW_DEFAULT,
|
|
|
|
EXO_PARAM_READWRITE));
|
|
|
|
|
|
|
|
launcher_signals[ITEMS_CHANGED] =
|
|
|
|
g_signal_new (g_intern_static_string ("items-changed"),
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_FIRST,
|
|
|
|
0, NULL, NULL,
|
|
|
|
g_cclosure_marshal_VOID__VOID,
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
|
|
|
/* initialize the quark */
|
|
|
|
launcher_plugin_quark = g_quark_from_static_string ("xfce-launcher-plugin");
|
|
|
|
|
|
|
|
launcher_menu_icon_size = gtk_icon_size_from_name ("panel-launcher-menu");
|
|
|
|
if (launcher_menu_icon_size == GTK_ICON_SIZE_INVALID)
|
|
|
|
launcher_menu_icon_size = gtk_icon_size_register ("panel-launcher-menu",
|
|
|
|
MENU_ICON_SIZE,
|
|
|
|
MENU_ICON_SIZE);
|
|
|
|
|
|
|
|
launcher_tooltip_icon_size = gtk_icon_size_from_name ("panel-launcher-tooltip");
|
|
|
|
if (launcher_tooltip_icon_size == GTK_ICON_SIZE_INVALID)
|
|
|
|
launcher_tooltip_icon_size = gtk_icon_size_register ("panel-launcher-tooltip",
|
|
|
|
TOOLTIP_ICON_SIZE,
|
|
|
|
TOOLTIP_ICON_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_init (LauncherPlugin *plugin)
|
|
|
|
{
|
|
|
|
GtkIconTheme *icon_theme;
|
|
|
|
|
|
|
|
plugin->disable_tooltips = FALSE;
|
|
|
|
plugin->move_first = FALSE;
|
|
|
|
plugin->show_label = FALSE;
|
|
|
|
plugin->arrow_position = LAUNCHER_ARROW_DEFAULT;
|
|
|
|
plugin->menu = NULL;
|
|
|
|
plugin->items = NULL;
|
|
|
|
plugin->child = NULL;
|
|
|
|
plugin->tooltip_cache = NULL;
|
|
|
|
plugin->menu_timeout_id = 0;
|
|
|
|
plugin->save_timeout_id = 0;
|
|
|
|
|
|
|
|
/* monitor the default icon theme for changes */
|
|
|
|
icon_theme = gtk_icon_theme_get_default ();
|
|
|
|
plugin->theme_change_id = g_signal_connect (G_OBJECT (icon_theme), "changed",
|
|
|
|
G_CALLBACK (launcher_plugin_icon_theme_changed), plugin);
|
|
|
|
|
|
|
|
/* create the panel widgets */
|
|
|
|
plugin->box = xfce_hvbox_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
|
|
|
|
gtk_container_add (GTK_CONTAINER (plugin), plugin->box);
|
|
|
|
|
|
|
|
plugin->button = xfce_panel_create_button ();
|
|
|
|
gtk_box_pack_start (GTK_BOX (plugin->box), plugin->button, TRUE, TRUE, 0);
|
|
|
|
xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->button);
|
|
|
|
gtk_widget_set_has_tooltip (plugin->button, TRUE);
|
|
|
|
gtk_widget_set_name (plugin->button, "launcher-button");
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "button-press-event",
|
|
|
|
G_CALLBACK (launcher_plugin_button_press_event), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "button-release-event",
|
|
|
|
G_CALLBACK (launcher_plugin_button_release_event), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "query-tooltip",
|
|
|
|
G_CALLBACK (launcher_plugin_button_query_tooltip), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "drag-data-received",
|
|
|
|
G_CALLBACK (launcher_plugin_button_drag_data_received), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "drag-motion",
|
|
|
|
G_CALLBACK (launcher_plugin_button_drag_motion), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "drag-drop",
|
|
|
|
G_CALLBACK (launcher_plugin_button_drag_drop), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "drag-leave",
|
|
|
|
G_CALLBACK (launcher_plugin_button_drag_leave), plugin);
|
|
|
|
g_signal_connect_after (G_OBJECT (plugin->button), "expose-event",
|
|
|
|
G_CALLBACK (launcher_plugin_button_expose_event), plugin);
|
|
|
|
|
|
|
|
plugin->child = xfce_panel_image_new ();
|
|
|
|
gtk_container_add (GTK_CONTAINER (plugin->button), plugin->child);
|
|
|
|
|
|
|
|
plugin->arrow = xfce_arrow_button_new (GTK_ARROW_UP);
|
|
|
|
gtk_box_pack_start (GTK_BOX (plugin->box), plugin->arrow, FALSE, FALSE, 0);
|
|
|
|
xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->arrow);
|
|
|
|
gtk_button_set_relief (GTK_BUTTON (plugin->arrow), GTK_RELIEF_NONE);
|
|
|
|
gtk_widget_set_name (plugin->button, "launcher-arrow");
|
|
|
|
g_signal_connect (G_OBJECT (plugin->arrow), "button-press-event",
|
|
|
|
G_CALLBACK (launcher_plugin_arrow_press_event), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->arrow), "drag-motion",
|
|
|
|
G_CALLBACK (launcher_plugin_arrow_drag_motion), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "drag-drop",
|
|
|
|
G_CALLBACK (launcher_plugin_button_drag_drop), plugin);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->arrow), "drag-leave",
|
|
|
|
G_CALLBACK (launcher_plugin_arrow_drag_leave), plugin);
|
|
|
|
|
|
|
|
panel_utils_set_atk_info (plugin->arrow, _("Open launcher menu"), NULL);
|
|
|
|
|
|
|
|
/* accept all sorts of drag data, but filter in drag-drop, so we can
|
|
|
|
* send other sorts of drops to parent widgets */
|
|
|
|
gtk_drag_dest_set (plugin->button, 0, NULL, 0, 0);
|
|
|
|
gtk_drag_dest_set (plugin->arrow, 0, NULL, 0, 0);
|
|
|
|
|
|
|
|
/* sync button states */
|
|
|
|
g_signal_connect (G_OBJECT (plugin->button), "state-changed",
|
|
|
|
G_CALLBACK (launcher_plugin_button_state_changed), plugin->arrow);
|
|
|
|
g_signal_connect (G_OBJECT (plugin->arrow), "state-changed",
|
|
|
|
G_CALLBACK (launcher_plugin_button_state_changed), plugin->button);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (object);
|
|
|
|
GPtrArray *array;
|
|
|
|
GValue *tmp;
|
|
|
|
GSList *li;
|
|
|
|
GFile *item_file;
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_ITEMS:
|
|
|
|
array = g_ptr_array_new ();
|
|
|
|
for (li = plugin->items; li != NULL; li = li->next)
|
|
|
|
{
|
|
|
|
tmp = g_new0 (GValue, 1);
|
|
|
|
g_value_init (tmp, G_TYPE_STRING);
|
|
|
|
panel_return_if_fail (GARCON_IS_MENU_ITEM (li->data));
|
|
|
|
item_file = garcon_menu_item_get_file (li->data);
|
|
|
|
if (g_file_has_prefix (item_file, plugin->config_directory))
|
|
|
|
g_value_take_string (tmp, g_file_get_basename (item_file));
|
|
|
|
else
|
|
|
|
g_value_take_string (tmp, g_file_get_uri (item_file));
|
|
|
|
g_object_unref (G_OBJECT (item_file));
|
|
|
|
g_ptr_array_add (array, tmp);
|
|
|
|
}
|
|
|
|
g_value_set_boxed (value, array);
|
|
|
|
xfconf_array_free (array);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_DISABLE_TOOLTIPS:
|
|
|
|
g_value_set_boolean (value, plugin->disable_tooltips);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_MOVE_FIRST:
|
|
|
|
g_value_set_boolean (value, plugin->move_first);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_SHOW_LABEL:
|
|
|
|
g_value_set_boolean (value, plugin->show_label);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_ARROW_POSITION:
|
|
|
|
g_value_set_uint (value, plugin->arrow_position);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_item_changed (GarconMenuItem *item,
|
|
|
|
LauncherPlugin *plugin)
|
|
|
|
{
|
|
|
|
GSList *li;
|
|
|
|
|
|
|
|
panel_return_if_fail (GARCON_IS_MENU_ITEM (item));
|
|
|
|
panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
|
|
|
|
|
|
|
|
/* find the item */
|
|
|
|
li = g_slist_find (plugin->items, item);
|
|
|
|
if (G_LIKELY (li != NULL))
|
|
|
|
{
|
|
|
|
/* update the button or destroy the menu */
|
|
|
|
if (plugin->items == li)
|
|
|
|
launcher_plugin_button_update (plugin);
|
|
|
|
else
|
|
|
|
launcher_plugin_menu_destroy (plugin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
panel_assert_not_reached ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
launcher_plugin_item_duplicate (GFile *src_file,
|
|
|
|
GFile *dst_file,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GKeyFile *key_file;
|
|
|
|
gchar *contents = NULL;
|
|
|
|
gsize length;
|
|
|
|
gboolean result = FALSE;
|
|
|
|
gchar *uri;
|
|
|
|
|
|
|
|
panel_return_val_if_fail (G_IS_FILE (src_file), FALSE);
|
|
|
|
panel_return_val_if_fail (G_IS_FILE (dst_file), FALSE);
|
|
|
|
panel_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
|
|
|
|
if (!g_file_load_contents (src_file, NULL, &contents, &length, NULL, error))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* note that we don't load the key file with preserving the translations
|
|
|
|
* and comments, this way we save a small desktop file in the user's language */
|
|
|
|
key_file = g_key_file_new ();
|
|
|
|
if (!g_key_file_load_from_data (key_file, contents, length, 0, error))
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
/* store the source uri in the desktop file for restore purposes */
|
|
|
|
uri = g_file_get_uri (src_file);
|
|
|
|
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-XFCE-Source", uri);
|
|
|
|
g_free (uri);
|
|
|
|
|
|
|
|
contents = g_key_file_to_data (key_file, &length, error);
|
|
|
|
if (contents == NULL)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
result = g_file_replace_contents (dst_file, contents, length, NULL, FALSE,
|
|
|
|
#if GLIB_CHECK_VERSION (2, 20, 0)
|
|
|
|
G_FILE_CREATE_REPLACE_DESTINATION,
|
|
|
|
#else
|
|
|
|
G_FILE_CREATE_NONE,
|
|
|
|
#endif
|
|
|
|
NULL, NULL, error);
|
|
|
|
|
|
|
|
err1:
|
|
|
|
g_free (contents);
|
|
|
|
g_key_file_free (key_file);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static GarconMenuItem *
|
|
|
|
launcher_plugin_item_load (LauncherPlugin *plugin,
|
|
|
|
const gchar *str,
|
|
|
|
gboolean *desktop_id_return,
|
|
|
|
gboolean *location_changed)
|
|
|
|
{
|
|
|
|
GFile *src_file, *dst_file;
|
|
|
|
gchar *src_path, *dst_path;
|
|
|
|
GSList *li, *lnext;
|
|
|
|
GarconMenuItem *item = NULL;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), NULL);
|
|
|
|
panel_return_val_if_fail (str != NULL, NULL);
|
|
|
|
panel_return_val_if_fail (G_IS_FILE (plugin->config_directory), NULL);
|
|
|
|
|
|
|
|
if (G_UNLIKELY (g_path_is_absolute (str) || exo_str_looks_like_an_uri (str)))
|
|
|
|
{
|
|
|
|
src_file = g_file_new_for_commandline_arg (str);
|
|
|
|
if (g_file_has_prefix (src_file, plugin->config_directory))
|
|
|
|
{
|
|
|
|
/* nothing, we use the file below */
|
|
|
|
}
|
|
|
|
else if (g_file_query_exists (src_file, NULL))
|
|
|
|
{
|
|
|
|
/* create a unique file in the config directory */
|
|
|
|
dst_path = launcher_plugin_unique_filename (plugin);
|
|
|
|
dst_file = g_file_new_for_path (dst_path);
|
|
|
|
|
|
|
|
/* create a duplicate in the config directory */
|
|
|
|
if (launcher_plugin_item_duplicate (src_file, dst_file, &error))
|
|
|
|
{
|
|
|
|
/* use the new file */
|
|
|
|
g_object_unref (G_OBJECT (src_file));
|
|
|
|
src_file = dst_file;
|
|
|
|
|
|
|
|
if (G_LIKELY (location_changed != NULL))
|
|
|
|
*location_changed = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
src_path = g_file_get_parse_name (src_file);
|
|
|
|
g_warning ("Failed to create duplicate of desktop file \"%s\" "
|
|
|
|
"to \"%s\": %s", src_path, dst_path, error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
g_free (src_path);
|
|
|
|
|
|
|
|
/* continue using the source file, the user won't be able to
|
|
|
|
* edit the item, but atleast we have something that works in
|
|
|
|
* the panel */
|
|
|
|
g_object_unref (G_OBJECT (dst_file));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (dst_path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* nothing we can do with this file */
|
|
|
|
src_path = g_file_get_parse_name (src_file);
|
|
|
|
g_warning ("Failed to load desktop file \"%s\". It will be removed "
|
|
|
|
"from the configuration", src_path);
|
|
|
|
g_free (src_path);
|
|
|
|
g_object_unref (G_OBJECT (src_file));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* assume the file is a child in the config directory */
|
|
|
|
src_file = g_file_get_child (plugin->config_directory, str);
|
|
|
|
|
|
|
|
/* str might also be a global desktop id */
|
|
|
|
if (G_LIKELY (desktop_id_return != NULL))
|
|
|
|
*desktop_id_return = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
panel_assert (G_IS_FILE (src_file));
|
|
|
|
|
|
|
|
/* maybe we have this file in the launcher configuration, then we don't
|
|
|
|
* have to load it again from the harddisk */
|
|
|
|
for (li = plugin->items; item == NULL && li != NULL; li = lnext)
|
|
|
|
{
|
|
|
|
lnext = li->next;
|
|
|
|
dst_file = garcon_menu_item_get_file (GARCON_MENU_ITEM (li->data));
|
|
|
|
if (g_file_equal (src_file, dst_file))
|
|
|
|
{
|
|
|
|
item = GARCON_MENU_ITEM (li->data);
|
|
|
|
plugin->items = g_slist_delete_link (plugin->items, li);
|
|
|
|
}
|
|
|
|
g_object_unref (G_OBJECT (dst_file));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* load the file from the disk */
|
|
|
|
if (item == NULL)
|
|
|
|
item = garcon_menu_item_new (src_file);
|
|
|
|
|
|
|
|
g_object_unref (G_OBJECT (src_file));
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_items_delete_configs (LauncherPlugin *plugin)
|
|
|
|
{
|
|
|
|
GSList *li;
|
|
|
|
GFile *file;
|
|
|
|
gboolean succeed = TRUE;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
panel_return_if_fail (G_IS_FILE (plugin->config_directory));
|
|
|
|
|
|
|
|
/* cleanup desktop files in the config dir */
|
|
|
|
for (li = plugin->items; succeed && li != NULL; li = li->next)
|
|
|
|
{
|
|
|
|
file = garcon_menu_item_get_file (li->data);
|
|
|
|
if (g_file_has_prefix (file, plugin->config_directory))
|
|
|
|
succeed = g_file_delete (file, NULL, &error);
|
|
|
|
g_object_unref (G_OBJECT (file));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!succeed)
|
|
|
|
{
|
|
|
|
g_message ("launcher-%d: Failed to cleanup the configuration: %s",
|
|
|
|
xfce_panel_plugin_get_unique_id (XFCE_PANEL_PLUGIN (plugin)),
|
|
|
|
error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_items_free (LauncherPlugin *plugin)
|
|
|
|
{
|
|
|
|
if (G_LIKELY (plugin->items != NULL))
|
|
|
|
{
|
|
|
|
g_slist_foreach (plugin->items, (GFunc) g_object_unref, NULL);
|
|
|
|
g_slist_free (plugin->items);
|
|
|
|
plugin->items = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_items_load (LauncherPlugin *plugin,
|
|
|
|
GPtrArray *array)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
const GValue *value;
|
|
|
|
const gchar *str;
|
|
|
|
GarconMenuItem *item;
|
|
|
|
GarconMenuItem *pool_item;
|
|
|
|
GSList *items = NULL;
|
|
|
|
GHashTable *pool = NULL;
|
|
|
|
gboolean desktop_id;
|
|
|
|
gchar *uri;
|
|
|
|
gboolean items_modified = FALSE;
|
|
|
|
gboolean location_changed;
|
|
|
|
|
|
|
|
panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
|
|
|
|
panel_return_if_fail (array != NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < array->len; i++)
|
|
|
|
{
|
|
|
|
value = g_ptr_array_index (array, i);
|
|
|
|
panel_assert (G_VALUE_HOLDS_STRING (value));
|
|
|
|
str = g_value_get_string (value);
|
|
|
|
|
|
|
|
/* only accept desktop files */
|
|
|
|
if (str == NULL || !g_str_has_suffix (str, ".desktop"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* try to load the item */
|
|
|
|
desktop_id = FALSE;
|
|
|
|
location_changed = FALSE;
|
|
|
|
item = launcher_plugin_item_load (plugin, str, &desktop_id, &location_changed);
|
|
|
|
if (G_LIKELY (item == NULL))
|
|
|
|
{
|
|
|
|
/* str did not look like a desktop-id, so no need to look
|
|
|
|
* for it in the application pool */
|
|
|
|
if (!desktop_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* we are going to load an desktop_id from the item pool,
|
|
|
|
* even if this failes, save the new item list, so we don't
|
|
|
|
* try this again in the future */
|
|
|
|
items_modified = TRUE;
|
|
|
|
|
|
|
|
/* load the pool with desktop items */
|
|
|
|
if (pool == NULL)
|
|
|
|
pool = launcher_plugin_garcon_menu_pool ();
|
|
|
|
|
|
|
|
/* lookup the item in the item pool */
|
|
|
|
pool_item = g_hash_table_lookup (pool, str);
|
|
|
|
if (pool_item != NULL)
|
|
|
|
{
|
|
|
|
/* we want an editable file, so try to make a copy */
|
|
|
|
uri = garcon_menu_item_get_uri (pool_item);
|
|
|
|
item = launcher_plugin_item_load (plugin, uri, NULL, NULL);
|
|
|
|
g_free (uri);
|
|
|
|
|
|
|
|
/* if something failed, use the pool item, but this one
|
|
|
|
* won't be editable in the dialog */
|
|
|
|
if (G_UNLIKELY (item == NULL))
|
|
|
|
item = g_object_ref (G_OBJECT (pool_item));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip this item if still not found */
|
|
|
|
if (item == NULL)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (location_changed)
|
|
|
|
{
|
|
|
|
items_modified = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add the item to the list */
|
|
|
|
panel_assert (GARCON_IS_MENU_ITEM (item));
|
|
|
|
items = g_slist_append (items, item);
|
|
|
|
g_signal_connect (G_OBJECT (item), "changed",
|
|
|
|
G_CALLBACK (launcher_plugin_item_changed), plugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (G_UNLIKELY (pool != NULL))
|
|
|
|
g_hash_table_destroy (pool);
|
|
|
|
|
|
|
|
/* remove config files of items not in the new config */
|
|
|
|
launcher_plugin_items_delete_configs (plugin);
|
|
|
|
|
|
|
|
/* release the old menu items and set new one */
|
|
|
|
launcher_plugin_items_free (plugin);
|
|
|
|
plugin->items = items;
|
|
|
|
|
|
|
|
/* store the new item list */
|
|
|
|
if (items_modified)
|
|
|
|
launcher_plugin_save_delayed (plugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
launcher_plugin_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (object);
|
|
|
|
GPtrArray *array;
|
|
|
|
|
|
|
|
panel_return_if_fail (G_IS_FILE (plugin->config_directory));
|
|
|
|
|
|
|
|
/* destroy the menu, all the setting changes need this */
|
|
|
|
launcher_plugin_menu_destroy (plugin);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_ITEMS:
|
|
|
|
/* load new items from the array */
|
|
|
|
array = g_value_get_boxed (value);
|
|
|
|
if (G_LIKELY (array != NULL))
|
|
|
|
{
|
|
|
|
launcher_plugin_items_load (plugin, array);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
launcher_plugin_items_delete_configs (plugin);
|
|
|
|
launcher_plugin_items_free (plugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* emit signal */
|
|
|
|
g_signal_emit (G_OBJECT (plugin), launcher_signals[ITEMS_CHANGED], 0);
|
|
|
|
|
|
|
|
/* update the button */
|
|
|
|
launcher_plugin_button_update (plugin);
|
|
|
|
|
|
|
|
/* update the widget packing */
|
|
|
|
goto update_arrow;
|
|
|
|
break;
|
|
|