You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1538 lines
50 KiB

/* $Id$
*
* Copyright (c) 2005-2007 Jasper Huijsmans <jasper@xfce.org>
* Copyright (c) 2006-2007 Nick Schermer <nick@xfce.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <libxfce4util/libxfce4util.h>
#include <libxfce4panel/xfce-arrow-button.h>
#include <libxfce4panel/xfce-panel-convenience.h>
#include <libxfce4panel/xfce-hvbox.h>
#include "launcher.h"
#include "launcher-exec.h"
#include "launcher-dialog.h"
/* for 4.4 settings migration */
static const gchar *icon_category_map[] = {
"applications-other",
"accessories-text-editor",
"system-file-manager",
"applications-accessories",
"applications-games",
"help-browser",
"applications-multimedia",
"applications-internet",
"applications-graphics",
"printer",
"office-calendar",
"applications-office",
"audio-card",
"utilities-terminal",
"applications-development",
"preferences-desktop",
"applications-system",
"applications-other",
"applications-accessories"
};
/* prototypes */
static void launcher_utility_icon_theme_changed (GtkIconTheme *icon_theme,
LauncherPlugin *launcher);
static gboolean launcher_icon_button_expose_event (GtkWidget *widget,
GdkEventExpose *event,
LauncherPlugin *launcher);
static void launcher_icon_button_set_icon (LauncherPlugin *launcher);
#if LAUNCHER_NEW_TOOLTIP_API
static gboolean launcher_icon_button_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_mode,
GtkTooltip *tooltip,
LauncherPlugin *launcher);
#else
static void launcher_icon_button_set_tooltip (LauncherPlugin *launcher);
#endif
static gboolean launcher_icon_button_pressed (GtkWidget *button,
GdkEventButton *event,
LauncherPlugin *launcher);
static gboolean launcher_icon_button_released (GtkWidget *button,
GdkEventButton *event,
LauncherPlugin *launcher);
static void launcher_icon_button_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time_,
LauncherPlugin *launcher);
static gboolean launcher_arrow_button_pressed (GtkWidget *button,
GdkEventButton *event,
LauncherPlugin *launcher);
static void launcher_button_state_changed (GtkWidget *button_a,
GtkStateType state,
GtkWidget *button_b);
static gboolean launcher_menu_item_released (GtkWidget *mi,
GdkEventButton *event,
LauncherPlugin *launcher);
static void launcher_menu_popup_destroyed (gpointer user_data);
static gboolean launcher_menu_popup (gpointer user_data);
static void launcher_menu_deactivated (LauncherPlugin *launcher);
static void launcher_menu_destroy (LauncherPlugin *launcher);
static void launcher_menu_rebuild (LauncherPlugin *launcher);
static LauncherPlugin *launcher_plugin_new (XfcePanelPlugin *plugin) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
static void launcher_plugin_pack_buttons (LauncherPlugin *launcher);
static gchar *launcher_plugin_read_entry (XfceRc *rc,
const gchar *name) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
static void launcher_plugin_screen_position_changed (LauncherPlugin *launcher);
static void launcher_plugin_orientation_changed (LauncherPlugin *launcher);
static gboolean launcher_plugin_set_size (LauncherPlugin *launcher,
guint size);
static void launcher_plugin_free (LauncherPlugin *launcher);
static void launcher_plugin_construct (XfcePanelPlugin *plugin);
/* register the plugin */
XFCE_PANEL_PLUGIN_REGISTER_INTERNAL (launcher_plugin_construct);
/**
* Utility Functions
**/
static void
launcher_utility_icon_theme_changed (GtkIconTheme *icon_theme,
LauncherPlugin *launcher)
{
/* update the button icon */
launcher_icon_button_set_icon (launcher);
/* destroy the menu */
launcher_menu_destroy (launcher);
}
GSList *
launcher_utility_filenames_from_selection_data (GtkSelectionData *selection_data)
{
gchar **uri_list;
GSList *filenames = NULL;
gchar *filename;
gboolean is_uri = TRUE;
guint i;
/* check whether the retrieval worked */
if (G_LIKELY (selection_data->length > 0))
{
if (selection_data->target == gdk_atom_intern_static_string ("text/uri-list"))
{
/* split the received uri list */
uri_list = g_uri_list_extract_uris ((gchar *) selection_data->data);
}
else
{
/* split input by \n, \r or \r\n, this might result in empty elements, we sort
* them out below */
uri_list = g_strsplit_set ((gchar *) selection_data->data, "\n\r", 0);
is_uri = FALSE;
}
if (G_LIKELY (uri_list))
{
/* walk through the list */
for (i = 0; uri_list[i] != NULL; i++)
{
if (! uri_list[i][0]) /* skip emtpy elements */
continue;
/* convert the uri to a filename */
if (is_uri)
filename = g_filename_from_uri (uri_list[i], NULL, NULL);
else
filename = g_strdup (uri_list[i]);
/* prepend the filename */
if (G_LIKELY (filename))
filenames = g_slist_prepend (filenames, filename);
}
/* cleanup */
g_strfreev (uri_list);
/* reverse the list */
filenames = g_slist_reverse (filenames);
}
}
return filenames;
}
GdkPixbuf *
launcher_utility_load_pixbuf (GdkScreen *screen,
const gchar *name,
guint size)
{
GdkPixbuf *pixbuf = NULL;
GdkPixbuf *scaled;
GtkIconTheme *theme;
if (G_LIKELY (name))
{
if (g_path_is_absolute (name))
{
/* load the icon from the file */
pixbuf = exo_gdk_pixbuf_new_from_file_at_max_size (name, size, size, TRUE, NULL);
}
else
{
/* determine the appropriate icon theme */
if (G_LIKELY (screen))
theme = gtk_icon_theme_get_for_screen (screen);
else
theme = gtk_icon_theme_get_default ();
/* try to load the named icon */
pixbuf = gtk_icon_theme_load_icon (theme, name, size, 0, NULL);
if (G_LIKELY (pixbuf))
{
/* scale down the icon if required */
scaled = exo_gdk_pixbuf_scale_down (pixbuf, TRUE, size, size);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = scaled;
}
}
}
return pixbuf;
}
#if LAUNCHER_NEW_TOOLTIP_API
static gboolean
launcher_utility_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_mode,
GtkTooltip *tooltip,
LauncherEntry *entry)
{
gchar *string;
/* create tooltip text */
if (G_LIKELY (entry && entry->name))
{
if (entry->comment)
{
string = g_strdup_printf ("<b>%s</b>\n%s", entry->name, entry->comment);
gtk_tooltip_set_markup (tooltip, string);
g_free (string);
}
else
{
gtk_tooltip_set_text (tooltip, entry->name);
}
if (G_LIKELY (entry->icon))
{
/* load the cached pixbuf */
if (entry->tooltip_cache == NULL)
entry->tooltip_cache = launcher_utility_load_pixbuf (gtk_widget_get_screen (widget),
entry->icon,
LAUNCHER_TOOLTIP_SIZE);
/* set the tooltip icon */
if (G_LIKELY (entry->tooltip_cache))
gtk_tooltip_set_icon (tooltip, entry->tooltip_cache);
}
/* show the tooltip */
return TRUE;
}
/* nothing to show */
return FALSE;
}
#endif
/**
* Icon Button Functions
**/
static gboolean
launcher_icon_button_expose_event (GtkWidget *widget,
GdkEventExpose *event,
LauncherPlugin *launcher)
{
gint x, y, w;
GtkArrowType arrow_type;
/* only paint the arrow when the arrow button is hidden */
if (launcher->arrow_position == LAUNCHER_ARROW_INSIDE_BUTTON)
{
/* calculate the width of the arrow */
w = widget->allocation.width / 3;
/* get the arrow type */
arrow_type = xfce_arrow_button_get_arrow_type (XFCE_ARROW_BUTTON (launcher->arrow_button));
/* start coordinates */
x = widget->allocation.x;
y = widget->allocation.y;
/* calculate the position based on the arrow type */
switch (arrow_type)
{
case GTK_ARROW_UP:
/* north east */
x += (widget->allocation.width - w);
break;
case GTK_ARROW_DOWN:
/* south west */
y += (widget->allocation.height - w);
break;
case GTK_ARROW_RIGHT:
/* south east */
x += (widget->allocation.width - w);
y += (widget->allocation.height - w);
break;
default:
/* north west */
break;
}
/* paint the arrow */
gtk_paint_arrow (widget->style, widget->window,
GTK_WIDGET_STATE (widget), GTK_SHADOW_IN,
&(event->area), widget, "launcher_button",
arrow_type, TRUE, x, y, w, w);
}
return FALSE;
}
static void
launcher_icon_button_set_icon (LauncherPlugin *launcher)
{
GdkPixbuf *pixbuf;
LauncherEntry *entry;
GdkScreen *screen;
GList *first = g_list_first (launcher->entries);
/* leave when there is no list item */
if (G_UNLIKELY (first == NULL))
return;
/* get the first entry in the list */
entry = first->data;
if (G_UNLIKELY (entry == NULL))
return;
/* get widget screen */
screen = gtk_widget_get_screen (launcher->image);
/* try to load the file */
pixbuf = launcher_utility_load_pixbuf (screen, entry->icon, launcher->image_size);
if (G_LIKELY (pixbuf))
{
/* set the image and release the pixbuf */
gtk_image_set_from_pixbuf (GTK_IMAGE (launcher->image), pixbuf);
g_object_unref (G_OBJECT (pixbuf));
}
else
{
/* clear the image */
gtk_image_clear (GTK_IMAGE (launcher->image));
}
}
#if LAUNCHER_NEW_TOOLTIP_API
static gboolean
launcher_icon_button_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_mode,
GtkTooltip *tooltip,
LauncherPlugin *launcher)
{
GList *first = g_list_first (launcher->entries);
/* don't show tooltips on a menu button */
if (launcher->arrow_position == LAUNCHER_ARROW_INSIDE_BUTTON
|| first == NULL)
return FALSE;
return launcher_utility_query_tooltip (widget, x, y, keyboard_mode, tooltip,
first->data);
}
#else
static void
launcher_icon_button_set_tooltip (LauncherPlugin *launcher)
{
LauncherEntry *entry;
gchar *string = NULL;
GList *first = g_list_first (launcher->entries);
/* leave when there is no entry */
if (G_UNLIKELY (first == NULL))
return;
/* get first entry */
entry = first->data;
/* create tooltip text */
if (entry != NULL
&& entry->name
&& launcher->arrow_position != LAUNCHER_ARROW_INSIDE_BUTTON)
{
if (entry->comment)
string = g_strdup_printf ("%s\n%s", entry->name, entry->comment);
else
string = g_strdup_printf ("%s", entry->name);
}
/* set the tooltip */
gtk_tooltips_set_tip (launcher->tips, launcher->icon_button, string, NULL);
/* cleanup */
g_free (string);
}
#endif
static gboolean
launcher_icon_button_pressed (GtkWidget *button,
GdkEventButton *event,
LauncherPlugin *launcher)
{
guint modifiers;
/* get the default accelerator modifier mask */
modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
/* exit if another button then 1 is pressed or control is hold */
if (event->button != 1 || modifiers == GDK_CONTROL_MASK)
return FALSE;
/* popup the menu or start the popup timeout */
if (launcher->arrow_position == LAUNCHER_ARROW_INSIDE_BUTTON)
{
launcher_menu_popup (launcher);
}
else if (launcher->popup_timeout_id == 0 && g_list_length (launcher->entries) > 1)
{
launcher->popup_timeout_id =
g_timeout_add_full (G_PRIORITY_DEFAULT, LAUNCHER_POPUP_DELAY, launcher_menu_popup,
launcher, launcher_menu_popup_destroyed);
}
return FALSE;
}
static gboolean
launcher_icon_button_released (GtkWidget *button,
GdkEventButton *event,
LauncherPlugin *launcher)
{
LauncherEntry *entry;
GdkScreen *screen;
GList *first;
/* remove the timeout */
if (G_LIKELY (launcher->popup_timeout_id > 0))
g_source_remove (launcher->popup_timeout_id);
/* only accept click in the button and don't respond on multiple clicks */
if (GTK_BUTTON (button)->in_button && launcher->arrow_position != LAUNCHER_ARROW_INSIDE_BUTTON)
{
/* get the first launcher entry */
first = g_list_first (launcher->entries);
if (G_UNLIKELY (first == NULL))
return FALSE;
entry = first->data;
/* get the widget screen */
screen = gtk_widget_get_screen (button);
/* execute the command on button 1 and 2 */
if (event->button == 1)
launcher_execute (screen, entry, NULL, event->time);
else if (event->button == 2)
launcher_execute_from_clipboard (screen, entry, event->time);
}
return FALSE;
}
static void
launcher_icon_button_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time_,
LauncherPlugin *launcher)
{
GSList *filenames;
LauncherEntry *entry;
GList *first;
/* execute */
if (launcher->arrow_position != LAUNCHER_ARROW_INSIDE_BUTTON)
{
/* create filenames list from all the uris */
filenames = launcher_utility_filenames_from_selection_data (selection_data);
if (G_LIKELY (filenames))
{
/* get entry */
first = g_list_first (launcher->entries);
if (G_UNLIKELY (first == NULL))
return;
entry = first->data;
/* execute the entry with the filenames */
launcher_execute (gtk_widget_get_screen (widget), entry, filenames, time_);
/* cleanup */
launcher_free_filenames (filenames);
}
}
/* finish drag */
gtk_drag_finish (context, TRUE, FALSE, time_);
}
/**
* Arrow Button Functions
**/
static gboolean
launcher_arrow_button_pressed (GtkWidget *button,
GdkEventButton *event,
LauncherPlugin *launcher)
{
/* only popup on 1st button */
if (event->button == 1)
launcher_menu_popup (launcher);
return FALSE;
}
static gboolean
launcher_arrow_button_drag_motion (GtkWidget *widget,
GdkDragContext *drag_context,
gint x,
gint y,
guint drag_time,
LauncherPlugin *launcher)
{
/* do not try to popup if the arrow button is used and the signal
* is comming from the icon button */
if (launcher->arrow_position != LAUNCHER_ARROW_INSIDE_BUTTON
&& launcher->arrow_button != widget)
return TRUE;
if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (launcher->arrow_button)))
{
/* make the toggle button active */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), TRUE);
launcher->popup_timeout_id =
g_timeout_add_full (G_PRIORITY_DEFAULT, LAUNCHER_POPUP_DELAY, launcher_menu_popup,
launcher, launcher_menu_popup_destroyed);
}
return TRUE;
}
static gboolean
launcher_arrow_button_drag_leave_timeout (gpointer user_data)
{
LauncherPlugin *launcher = user_data;
GdkScreen *screen;
GdkDisplay *display;
gint x, y;
gint menu_x, menu_y, menu_w, menu_h;
if (launcher->menu == NULL)
return FALSE;
g_return_val_if_fail (GDK_IS_WINDOW (launcher->menu->window), FALSE);
/* get the current pointer position */
screen = gtk_widget_get_screen (launcher->arrow_button);
display = gdk_screen_get_display (screen);
gdk_display_get_pointer (display, NULL, &x, &y, NULL);
/* get the menu size and postion */
gdk_window_get_root_origin (launcher->menu->window, &menu_x, &menu_y);
gdk_drawable_get_size (GDK_DRAWABLE (launcher->menu->window), &menu_w, &menu_h);
/* check if we should hide the menu */
if (x < menu_x || x > menu_x + menu_w
|| y < menu_y || y > menu_y + menu_h)
{
/* hide the menu */
gtk_widget_hide (GTK_MENU (launcher->menu)->toplevel);
gtk_widget_hide (launcher->menu);
/* inactive the toggle button */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), FALSE);
}
return FALSE;
}
static void
launcher_arrow_button_drag_leave (GtkWidget *widget,
GdkDragContext *drag_context,
guint drag_time,
LauncherPlugin *launcher)
{
if (launcher->popup_timeout_id != 0)
{
/* destroy the timeout */
g_source_remove (launcher->popup_timeout_id);
/* restore button */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), FALSE);
}
else
{
/* start a timeout to give the user some time to drag to the menu */
g_timeout_add (100, launcher_arrow_button_drag_leave_timeout, launcher);
}
}
/**
* Global Button Functions
**/
static void
launcher_button_state_changed (GtkWidget *button_a,
GtkStateType state,
GtkWidget *button_b)
{
if (GTK_WIDGET_STATE (button_b) != GTK_WIDGET_STATE (button_a)
&& GTK_WIDGET_STATE (button_a) != GTK_STATE_INSENSITIVE)
{
/* sync the button states */
gtk_widget_set_state (button_b, GTK_WIDGET_STATE (button_a));
}
}
/**
* Menu Functions
**/
static gboolean
launcher_menu_item_released (GtkWidget *mi,
GdkEventButton *event,
LauncherPlugin *launcher)
{
GdkScreen *screen;
LauncherEntry *entry;
/* get the widget screen */
screen = gtk_widget_get_screen (mi);
/* get the item number */
entry = g_object_get_data (G_OBJECT (mi), I_("entry"));
if (G_LIKELY (entry))
{
if (event->button == 1)
launcher_execute (screen, entry, NULL, event->time);
else if (event->button == 2)
launcher_execute_from_clipboard (screen, entry, event->time);
/* move the item to the first position in the list */
if (launcher->move_first
&& launcher->entries != NULL
&& launcher->entries->data != entry)
{
/* remove from the list */
launcher->entries = g_list_remove (launcher->entries, entry);
/* insert in first position */
launcher->entries = g_list_prepend (launcher->entries, entry);
/* destroy the menu */
launcher_menu_destroy (launcher);
/* rebuild the icon button */
launcher_icon_button_set_icon (launcher);
#if !LAUNCHER_NEW_TOOLTIP_API
launcher_icon_button_set_tooltip (launcher);
#endif
}
}
return FALSE;
}
static void
launcher_menu_item_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time_,
LauncherPlugin *launcher)
{
GSList *filenames;
LauncherEntry *entry;
entry = g_object_get_data (G_OBJECT (widget), I_("entry"));
if (G_LIKELY (entry != NULL))
{
/* create filenames list from all the uris */
filenames = launcher_utility_filenames_from_selection_data (selection_data);
if (G_LIKELY (filenames))
{
/* execute the entry with the filenames */
launcher_execute (gtk_widget_get_screen (widget), entry, filenames, time_);
/* cleanup */
launcher_free_filenames (filenames);
}
/* finish drag */
gtk_drag_finish (context, TRUE, FALSE, time_);
}
/* hide the menu */
gtk_widget_hide (GTK_MENU (launcher->menu)->toplevel);
gtk_widget_hide (launcher->menu);
/* inactive the toggle button */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), FALSE);
}
static void
launcher_menu_popup_destroyed (gpointer user_data)
{
LauncherPlugin *launcher = user_data;
launcher->popup_timeout_id = 0;
}
static gboolean
launcher_menu_popup (gpointer user_data)
{
LauncherPlugin *launcher = user_data;
gint x, y;
GDK_THREADS_ENTER ();
/* check if the menu exists, if not, rebuild it */
if (launcher->menu == NULL)
launcher_menu_rebuild (launcher);
/* toggle the arrow button */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), TRUE);
/* popup menu */
gtk_menu_popup (GTK_MENU (launcher->menu), NULL, NULL,
xfce_panel_plugin_position_menu,
launcher->panel_plugin,
1, gtk_get_current_event_time ());
if (!GTK_WIDGET_VISIBLE (launcher->menu))
{
/* make sure the size is allocated */
if (!GTK_WIDGET_REALIZED (launcher->menu))
gtk_widget_realize (launcher->menu);
/* use the widget position function to get the coordinates */
xfce_panel_plugin_position_widget (launcher->panel_plugin, launcher->menu,
NULL, &x, &y);
/* ugly, but it works most of the time... */
gtk_widget_show (launcher->menu);
gtk_window_move (GTK_WINDOW (GTK_MENU (launcher->menu)->toplevel), x, y);
gtk_widget_show (GTK_MENU (launcher->menu)->toplevel);
}
GDK_THREADS_LEAVE ();
return FALSE;
}
static void
launcher_menu_deactivated (LauncherPlugin *launcher)
{
/* deactivate arrow button */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), FALSE);
}
static void
launcher_menu_destroy (LauncherPlugin *launcher)
{
if (launcher->menu != NULL)
{
/* destroy the menu and null the variable */
gtk_widget_destroy (launcher->menu);
launcher->menu = NULL;
/* deactivate arrow button */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (launcher->arrow_button), FALSE);
}
/* set the visibility of the arrow button */
if (launcher->arrow_position == LAUNCHER_ARROW_INSIDE_BUTTON ||
g_list_length (launcher->entries) < 2)
{
gtk_widget_hide (launcher->arrow_button);
}
else
{
gtk_widget_show (launcher->arrow_button);
}
}
static void
launcher_menu_rebuild (LauncherPlugin *launcher)
{
GdkScreen *screen;
GList *li;
guint n = 0;
LauncherEntry *entry;
GtkWidget *mi, *image;
GdkPixbuf *pixbuf;
GtkArrowType arrow_type;
/* destroy the old menu */
if (G_UNLIKELY (launcher->menu))
launcher_menu_destroy (launcher);
/* create new menu */
launcher->menu = gtk_menu_new ();
/* get the plugin screen */
screen = gtk_widget_get_screen (GTK_WIDGET (launcher->panel_plugin));
/* set the menu screen */
gtk_menu_set_screen (GTK_MENU (launcher->menu), screen);
/* get the arrow direction of the button */
arrow_type = xfce_arrow_button_get_arrow_type (XFCE_ARROW_BUTTON (launcher->arrow_button));
/* walk through the entries */
for (li = launcher->entries; li != NULL; li = li->next, n++)
{
/* skip the first entry when the arrow is visible */
if (n == 0 && launcher->arrow_position != LAUNCHER_ARROW_INSIDE_BUTTON)
continue;
entry = li->data;
mi = gtk_image_menu_item_new_with_label (entry->name ? entry->name : _("New Item"));
gtk_widget_show (mi);
/* fix menu order when it's on the top or bottom of the screen */
if (arrow_type == GTK_ARROW_DOWN)
gtk_menu_shell_append (GTK_MENU_SHELL (launcher->menu), mi);
else
gtk_menu_shell_prepend (GTK_MENU_SHELL (launcher->menu), mi);
/* try to set an image */
if (G_LIKELY (entry->icon))
{
/* load pixbuf */
pixbuf = launcher_utility_load_pixbuf (screen, entry->icon, LAUNCHER_MENU_SIZE);
if (G_LIKELY (pixbuf))
{
/* set image */
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
gtk_widget_show (image);
/* release reference */
g_object_unref (G_OBJECT (pixbuf));
}
}
/* set entry */
g_object_set_data (G_OBJECT (mi), I_("entry"), entry);
/* dnd support */
gtk_drag_dest_set (mi, GTK_DEST_DEFAULT_ALL, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_COPY);
/* connect signals */
g_signal_connect (G_OBJECT (mi), "button-release-event", G_CALLBACK (launcher_menu_item_released), launcher);
g_signal_connect (G_OBJECT (mi), "drag-data-received", G_CALLBACK (launcher_menu_item_drag_data_received), launcher);
g_signal_connect (G_OBJECT (mi), "drag-leave", G_CALLBACK (launcher_arrow_button_drag_leave), launcher);
#if LAUNCHER_NEW_TOOLTIP_API
gtk_widget_set_has_tooltip (mi, TRUE);
g_signal_connect (G_OBJECT (mi), "query-tooltip", G_CALLBACK (launcher_utility_query_tooltip), entry);
#endif
#if !LAUNCHER_NEW_TOOLTIP_API
/* set tooltip */
if (entry->comment)
gtk_tooltips_set_tip (launcher->tips, mi, entry->comment, NULL);
#endif
}
/* connect deactivate signal */
g_signal_connect_swapped (G_OBJECT (launcher->menu), "deactivate", G_CALLBACK (launcher_menu_deactivated), launcher);
}
/**
* Entry Functions
**/
LauncherEntry *
launcher_entry_new (void)
{
LauncherEntry *entry;
/* allocate structure */
entry = panel_slice_new0 (LauncherEntry);
/* TRANSLATORS: Name of a newly created launcher */
entry->name = g_strdup (_("New Item"));
entry->comment = NULL;
entry->icon = g_strdup ("applications-other");
/* fill others */
entry->exec = NULL;
entry->path = NULL;
entry->terminal = FALSE;
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
entry->startup = FALSE;
#endif
#if LAUNCHER_NEW_TOOLTIP_API
entry->tooltip_cache = NULL;
#endif
return entry;
}
void
launcher_entry_free (LauncherEntry *entry,
LauncherPlugin *launcher)
{
/* remove from the list */
if (G_LIKELY (launcher))
launcher->entries = g_list_remove (launcher->entries, entry);
/* free variables */
g_free (entry->name);
g_free (entry->comment);
g_free (entry->path);
g_free (entry->icon);
g_free (entry->exec);
#if LAUNCHER_NEW_TOOLTIP_API
/* release cached tooltip icon */
if (entry->tooltip_cache)
g_object_unref (G_OBJECT (entry->tooltip_cache));
#endif
/* free structure */
panel_slice_free (LauncherEntry, entry);
}
/**
* Panel Plugin Functions
**/
static LauncherPlugin*
launcher_plugin_new (XfcePanelPlugin *plugin)
{
LauncherPlugin *launcher;
GtkIconTheme *icon_theme;
/* create launcher structure */
launcher = panel_slice_new0 (LauncherPlugin);
/* init */
launcher->panel_plugin = plugin;
launcher->menu = NULL;
launcher->plugin_can_save = TRUE;
#if !LAUNCHER_NEW_TOOLTIP_API
/* create tooltips */
launcher->tips = gtk_tooltips_new ();
exo_gtk_object_ref_sink (GTK_OBJECT (launcher->tips));
#endif
/* create widgets */
launcher->box = xfce_hvbox_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
gtk_container_add (GTK_CONTAINER (plugin), launcher->box);
gtk_widget_show (launcher->box);
launcher->icon_button = xfce_create_panel_button ();
gtk_box_pack_start (GTK_BOX (launcher->box), launcher->icon_button, TRUE, TRUE, 0);
gtk_widget_show (launcher->icon_button);
launcher->image = gtk_image_new ();
gtk_container_add (GTK_CONTAINER (launcher->icon_button), launcher->image);
gtk_widget_show (launcher->image);
launcher->arrow_button = xfce_arrow_button_new (GTK_ARROW_UP);
GTK_WIDGET_UNSET_FLAGS (launcher->arrow_button, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
gtk_box_pack_start (GTK_BOX (launcher->box), launcher->arrow_button, FALSE, FALSE, 0);
gtk_button_set_relief (GTK_BUTTON (launcher->arrow_button), GTK_RELIEF_NONE);
gtk_button_set_focus_on_click (GTK_BUTTON (launcher->arrow_button), FALSE);
/* hook for icon themes changes */
icon_theme = gtk_icon_theme_get_default ();
launcher->icon_theme_change_handler =
g_signal_connect (G_OBJECT (icon_theme), "changed",
G_CALLBACK (launcher_utility_icon_theme_changed), launcher);
/* icon button signals */
g_signal_connect (G_OBJECT (launcher->icon_button), "state-changed",
G_CALLBACK (launcher_button_state_changed), launcher->arrow_button);
g_signal_connect (G_OBJECT (launcher->icon_button), "button-press-event",
G_CALLBACK (launcher_icon_button_pressed), launcher);
g_signal_connect (G_OBJECT (launcher->icon_button), "button-release-event",
G_CALLBACK (launcher_icon_button_released), launcher);
g_signal_connect (G_OBJECT (launcher->icon_button), "drag-data-received",
G_CALLBACK (launcher_icon_button_drag_data_received), launcher);
g_signal_connect_after (G_OBJECT (launcher->image), "expose-event",
G_CALLBACK (launcher_icon_button_expose_event), launcher);
g_signal_connect (G_OBJECT (launcher->icon_button), "drag-motion",
G_CALLBACK (launcher_arrow_button_drag_motion), launcher);
g_signal_connect (G_OBJECT (launcher->icon_button), "drag-leave",
G_CALLBACK (launcher_arrow_button_drag_leave), launcher);
#if LAUNCHER_NEW_TOOLTIP_API
gtk_widget_set_has_tooltip (launcher->icon_button, TRUE);
g_signal_connect (G_OBJECT (launcher->icon_button), "query-tooltip",
G_CALLBACK (launcher_icon_button_query_tooltip), launcher);
#endif
/* arrow button signals */
g_signal_connect (G_OBJECT (launcher->arrow_button), "state-changed",
G_CALLBACK (launcher_button_state_changed), launcher->icon_button);
g_signal_connect (G_OBJECT (launcher->arrow_button), "button-press-event",
G_CALLBACK (launcher_arrow_button_pressed), launcher);
g_signal_connect (G_OBJECT (launcher->arrow_button), "drag-motion",
G_CALLBACK (launcher_arrow_button_drag_motion), launcher);
g_signal_connect (G_OBJECT (launcher->arrow_button), "drag-leave",
G_CALLBACK (launcher_arrow_button_drag_leave), launcher);
/* set drag destinations */
gtk_drag_dest_set (launcher->icon_button, GTK_DEST_DEFAULT_ALL,
drop_targets, G_N_ELEMENTS (drop_targets),
GDK_ACTION_COPY);
gtk_drag_dest_set (launcher->arrow_button, GTK_DEST_DEFAULT_ALL,
drop_targets, G_N_ELEMENTS (drop_targets),
GDK_ACTION_COPY);
/* read the user settings */
launcher_plugin_read (launcher);
/* add new entry if the list is empty */
if (G_UNLIKELY (g_list_length (launcher->entries) == 0))
launcher->entries = g_list_prepend (launcher->entries, launcher_entry_new ());
/* set the arrow direction */
launcher_plugin_screen_position_changed (launcher);
/* set the buttons in the correct position */
launcher_plugin_pack_buttons (launcher);
/* change the visiblity of the arrow button */
launcher_menu_destroy (launcher);
#if !LAUNCHER_NEW_TOOLTIP_API
/* set the button tooltip */
launcher_icon_button_set_tooltip (launcher);
#endif
return launcher;
}
void
launcher_plugin_rebuild (LauncherPlugin *launcher,
gboolean update_icon)
{
/* pack buttons again */
launcher_plugin_pack_buttons (launcher);
/* size */
launcher_plugin_set_size (launcher, xfce_panel_plugin_get_size (launcher->panel_plugin));
#if !LAUNCHER_NEW_TOOLTIP_API
/* update tooltip */
launcher_icon_button_set_tooltip (launcher);
#endif
if (update_icon)
launcher_icon_button_set_icon (launcher);
/* destroy menu */
launcher_menu_destroy (launcher);
}
static void
launcher_plugin_pack_buttons (LauncherPlugin *launcher)
{
GtkOrientation orientation;
guint position = launcher->arrow_position;
gint box_position, width = -1, height = -1;
if (position == LAUNCHER_ARROW_DEFAULT)
{
/* get the current panel orientation */
orientation = xfce_panel_plugin_get_orientation (launcher->panel_plugin);
/* get the arrow position in the default layout */
if (orientation == GTK_ORIENTATION_HORIZONTAL)
position = LAUNCHER_ARROW_RIGHT;
else
position = LAUNCHER_ARROW_BOTTOM;
}
else if (position == LAUNCHER_ARROW_INSIDE_BUTTON)
{
/* nothing to arrange */
return;
}
/* set the arrow button position in the box */
box_position = (position == LAUNCHER_ARROW_LEFT || position == LAUNCHER_ARROW_TOP) ? 0 : -1;
gtk_box_reorder_child (GTK_BOX (launcher->box), launcher->arrow_button, box_position);
/* set the arrow button size and box orientation */
if (position == LAUNCHER_ARROW_LEFT || position == LAUNCHER_ARROW_RIGHT)
{
orientation = GTK_ORIENTATION_HORIZONTAL;
width = LAUNCHER_ARROW_SIZE;
}
else
{
orientation = GTK_ORIENTATION_VERTICAL;
height = LAUNCHER_ARROW_SIZE;
}
gtk_widget_set_size_request (launcher->arrow_button, width, height);
xfce_hvbox_set_orientation (XFCE_HVBOX (launcher->box), orientation);
/* set the visibility of the arrow button */
if (g_list_length (launcher->entries) > 1)
gtk_widget_show (launcher->arrow_button);
else
gtk_widget_hide (launcher->arrow_button);
}
static gchar *
launcher_plugin_read_entry (XfceRc *rc,
const gchar *name)
{
const gchar *temp;
gchar *value = NULL;
temp = xfce_rc_read_entry (rc, name, NULL);
if (G_LIKELY (temp != NULL && *temp != '\0'))
value = g_strdup (temp);
return value;
}
void
launcher_plugin_read (LauncherPlugin *launcher)
{
gchar *file;
gchar group[10];
XfceRc *rc;
guint i;
LauncherEntry *entry;
gint icon_category;
/* get rc file name, create it if needed */
file = xfce_panel_plugin_lookup_rc_file ( launcher->panel_plugin);
if (G_LIKELY (file))
{
/* open config file, read-only */
rc = xfce_rc_simple_open (file, TRUE);
/* cleanup */
g_free (file);
if (G_LIKELY (rc))
{
/* read global settings */
xfce_rc_set_group (rc, "Global");
launcher->move_first = xfce_rc_read_bool_entry (rc, "MoveFirst", FALSE);
launcher->arrow_position = xfce_rc_read_int_entry (rc, "ArrowPosition", 0);
for (i = 0; i < 100 /* arbitrary */; ++i)
{
/* create group name */
g_snprintf (group, sizeof (group), "Entry %d", i);
/* break if no more entries found */
if (xfce_rc_has_group (rc, group) == FALSE)
break;
/* set the group */
xfce_rc_set_group (rc, group);
/* create new entry structure */
entry = panel_slice_new0 (LauncherEntry);
/* read all the entry settings */
entry->name = launcher_plugin_read_entry (rc, "Name");
entry->comment = launcher_plugin_read_entry (rc, "Comment");
entry->icon = launcher_plugin_read_entry (rc, "Icon");
entry->exec = launcher_plugin_read_entry (rc, "Exec");
entry->path = launcher_plugin_read_entry (rc, "Path");
entry->terminal = xfce_rc_read_bool_entry (rc, "Terminal", FALSE);
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
entry->startup = xfce_rc_read_bool_entry (rc, "StartupNotify", FALSE);
#endif
/* 4.4 category icon migration */
if (G_UNLIKELY (entry->icon == NULL))
{
icon_category = xfce_rc_read_int_entry (rc, "X-XFCE-IconCategory", -1);
if (icon_category >= 0
&& icon_category <= (gint) G_N_ELEMENTS (icon_category_map) - 1)
entry->icon = g_strdup (icon_category_map[icon_category]);
}
/* prepend the entry */
launcher->entries = g_list_prepend (launcher->entries, entry);
}
/* reverse the list */
launcher->entries = g_list_reverse (launcher->entries);
/* close the rc file */
xfce_rc_close (rc);
}
}
}
void
launcher_plugin_save (LauncherPlugin *launcher)
{
gchar *file;
gchar **groups;
gchar group[10];
XfceRc *rc;
GList *li;
guint i;
LauncherEntry *entry;
/* check if it's allowed to save */
if (G_UNLIKELY (launcher->plugin_can_save == FALSE))
return;
/* get rc file name, create it if needed */
file = xfce_panel_plugin_save_location ( launcher->panel_plugin, TRUE);
if (G_LIKELY (file))
{
/* open the config file, writable */
rc = xfce_rc_simple_open (file, FALSE);
/* cleanup */
g_free (file);
if (G_LIKELY (rc))
{
/* retreive all the groups in the config file */
groups = xfce_rc_get_groups (rc);
if (G_LIKELY (groups))
{
/* remove all the groups */
for (i = 0; groups[i] != NULL; i++)
xfce_rc_delete_group (rc, groups[i], TRUE);
/* cleanup */
g_strfreev (groups);
}
/* save global launcher settings */
xfce_rc_set_group (rc, "Global");
xfce_rc_write_bool_entry (rc, "MoveFirst", launcher->move_first);
xfce_rc_write_int_entry (rc, "ArrowPosition", launcher->arrow_position);
/* save all the entries */
for (li = launcher->entries, i = 0; li != NULL; li = li->next, i++)
{
entry = li->data;
/* create group name */
g_snprintf (group, sizeof (group), "Entry %d", i);
/* set entry group */
xfce_rc_set_group (rc, group);
/* write entry settings */
if (G_LIKELY (entry->name))
xfce_rc_write_entry (rc, "Name", entry->name);
if (G_LIKELY (entry->comment))
xfce_rc_write_entry (rc, "Comment", entry->comment);
if (G_LIKELY (entry->icon))
xfce_rc_write_entry (rc, "Icon", entry->icon);
if (G_LIKELY (entry->exec))
xfce_rc_write_entry (rc, "Exec", entry->exec);
if (G_LIKELY (entry->path))
xfce_rc_write_entry (rc, "Path", entry->path);
xfce_rc_write_bool_entry (rc, "Terminal", entry->terminal);
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
xfce_rc_write_bool_entry (rc, "StartupNotify", entry->startup);
#endif
}
/* close the rc file */
xfce_rc_close (rc);
}
}
}
static void
launcher_plugin_screen_position_changed (LauncherPlugin *launcher)
{
GtkArrowType arrow_type;
/* get the arrow type */
arrow_type = xfce_panel_plugin_arrow_type (launcher->panel_plugin);
/* set the arrow direction */
xfce_arrow_button_set_arrow_type (XFCE_ARROW_BUTTON (launcher->arrow_button), arrow_type);
/* destroy the menu, so menu items appear in the correct direction */
launcher_menu_destroy (launcher);
}
static void
launcher_plugin_orientation_changed (LauncherPlugin *launcher)
{
/* set the arrow direction */
launcher_plugin_screen_position_changed (launcher);
/* reorder the boxes again */
launcher_plugin_pack_buttons (launcher);
}
static gboolean
launcher_plugin_set_size (LauncherPlugin *launcher,
guint size)
{
gint width = size, height = size;
GtkOrientation orientation;
GtkWidget *widget = launcher->icon_button;
if (g_list_length (launcher->entries) > 1)
{
/* get the orientation of the panel */
orientation = xfce_panel_plugin_get_orientation (launcher->panel_plugin);
switch (launcher->arrow_position)
{
case LAUNCHER_ARROW_DEFAULT:
if (orientation == GTK_ORIENTATION_HORIZONTAL)
width += LAUNCHER_ARROW_SIZE;
else
height += LAUNCHER_ARROW_SIZE;
break;
case LAUNCHER_ARROW_LEFT:
case LAUNCHER_ARROW_RIGHT:
if (orientation == GTK_ORIENTATION_HORIZONTAL)
width += LAUNCHER_ARROW_SIZE;
else
height -= LAUNCHER_ARROW_SIZE;
break;
case LAUNCHER_ARROW_TOP:
case LAUNCHER_ARROW_BOTTOM:
if (orientation == GTK_ORIENTATION_HORIZONTAL)
width -= LAUNCHER_ARROW_SIZE;
else
height += LAUNCHER_ARROW_SIZE;
break;
default:
/* nothing to do for a hidden arrow button */
break;
}
}
/* calculate the image size inside the button */
launcher->image_size = MIN (width, height);
launcher->image_size -= 2 + 2 * MAX (widget->style->xthickness, widget->style->ythickness);
/* set the plugin size */
gtk_widget_set_size_request (GTK_WIDGET (launcher->panel_plugin), width, height);
/* update the icon button */
launcher_icon_button_set_icon (launcher);
/* we handled the size */
return TRUE;
}
static void
launcher_plugin_free (LauncherPlugin *launcher)
{
GtkWidget *dialog;
GtkIconTheme *icon_theme;
/* check if we still need to destroy the properties dialog */
dialog = g_object_get_data (G_OBJECT (launcher->panel_plugin), I_("launcher-dialog"));
if (G_UNLIKELY (dialog != NULL))
gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
/* disconnect icon theme change handler */
icon_theme = gtk_icon_theme_get_default ();
if (launcher->icon_theme_change_handler != 0)
g_signal_handler_disconnect (G_OBJECT (icon_theme),
launcher->icon_theme_change_handler);
/* stop timeout */
if (G_UNLIKELY (launcher->popup_timeout_id))
g_source_remove (launcher->popup_timeout_id);
/* destroy the popup menu */
if (launcher->menu)
gtk_widget_destroy (launcher->menu);
/* remove the entries */
g_list_foreach (launcher->entries, (GFunc) launcher_entry_free, launcher);
g_list_free (launcher->entries);
#if !LAUNCHER_NEW_TOOLTIP_API
/* release the tooltips */
g_object_unref (G_OBJECT (launcher->tips));
#endif
/* free launcher structure */
panel_slice_free (LauncherPlugin, launcher);
}
static void
launcher_plugin_construct (XfcePanelPlugin *plugin)
{
LauncherPlugin *launcher;
/* create the plugin */
launcher = launcher_plugin_new (plugin);
/* set the action widgets and show configure */
xfce_panel_plugin_add_action_widget (plugin, launcher->icon_button);
xfce_panel_plugin_add_action_widget (plugin, launcher->arrow_button);
xfce_panel_plugin_menu_show_configure (plugin);
/* connect signals */
g_signal_connect_swapped (G_OBJECT (plugin), "screen-position-changed",
G_CALLBACK (launcher_plugin_screen_position_changed), launcher);
g_signal_connect_swapped (G_OBJECT (plugin), "orientation-changed",
G_CALLBACK (launcher_plugin_orientation_changed), launcher);
g_signal_connect_swapped (G_OBJECT (plugin), "size-changed",
G_CALLBACK (launcher_plugin_set_size), launcher);
g_signal_connect_swapped (G_OBJECT (plugin), "save",
G_CALLBACK (launcher_plugin_save), launcher);
g_signal_connect_swapped (G_OBJECT (plugin), "free-data",
G_CALLBACK (launcher_plugin_free), launcher);
g_signal_connect_swapped (G_OBJECT (plugin), "configure-plugin",
G_CALLBACK (launcher_dialog_show), launcher);
}