Commit 1e9c2b48 authored by Sean Davis's avatar Sean Davis 🕶

Add systray items list view

parent 8201ea0b
Pipeline #1111 passed with stages
in 4 minutes and 42 seconds
......@@ -116,6 +116,7 @@ enum
CONFIGURATION_CHANGED,
ITEM_LIST_CHANGED,
COLLECT_KNOWN_ITEMS,
KNOWN_ITEM_LIST_CHANGED,
LAST_SIGNAL
};
......@@ -256,6 +257,14 @@ sn_config_class_init (SnConfigClass *klass)
0, NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE, 1, G_TYPE_POINTER);
sn_config_signals[KNOWN_ITEM_LIST_CHANGED] =
g_signal_new (g_intern_static_string ("known-items-list-changed"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
......@@ -476,6 +485,7 @@ sn_config_set_property (GObject *object,
{
config->mode_whitelist = val;
g_signal_emit (G_OBJECT (config), sn_config_signals[ITEM_LIST_CHANGED], 0);
g_signal_emit (G_OBJECT (config), sn_config_signals[KNOWN_ITEM_LIST_CHANGED], 0);
}
break;
......@@ -526,7 +536,7 @@ sn_config_set_property (GObject *object,
config->known_legacy_items = g_list_append (config->known_legacy_items, name);
}
}
g_signal_emit (G_OBJECT (config), sn_config_signals[ITEM_LIST_CHANGED], 0);
g_signal_emit (G_OBJECT (config), sn_config_signals[KNOWN_ITEM_LIST_CHANGED], 0);
break;
case PROP_HIDDEN_LEGACY_ITEMS:
......@@ -542,7 +552,7 @@ sn_config_set_property (GObject *object,
g_hash_table_replace (config->hidden_legacy_items, name, name);
}
}
g_signal_emit (G_OBJECT (config), sn_config_signals[ITEM_LIST_CHANGED], 0);
g_signal_emit (G_OBJECT (config), sn_config_signals[KNOWN_ITEM_LIST_CHANGED], 0);
break;
default:
......@@ -762,7 +772,7 @@ sn_config_set_legacy_hidden (SnConfig *config,
g_hash_table_remove (config->hidden_legacy_items, name);
}
g_object_notify (G_OBJECT (config), "hidden-legacy-items");
g_signal_emit (G_OBJECT (config), sn_config_signals[ITEM_LIST_CHANGED], 0);
g_signal_emit (G_OBJECT (config), sn_config_signals[KNOWN_ITEM_LIST_CHANGED], 0);
}
......@@ -869,7 +879,7 @@ sn_config_add_known_legacy_item (SnConfig *config,
}
g_object_notify (G_OBJECT (config), "known-legacy-items");
g_signal_emit (G_OBJECT (config), sn_config_signals[ITEM_LIST_CHANGED], 0);
g_signal_emit (G_OBJECT (config), sn_config_signals[KNOWN_ITEM_LIST_CHANGED], 0);
}
......@@ -913,6 +923,44 @@ sn_config_swap_known_items (SnConfig *config,
void sn_config_swap_known_legacy_items(SnConfig *config,
const gchar *name1,
const gchar *name2)
{
GList *li, *li_tmp;
g_return_if_fail(XFCE_IS_SN_CONFIG(config));
for (li = config->known_legacy_items; li != NULL; li = li->next)
if (g_strcmp0(li->data, name1) == 0)
break;
/* make sure that the list contains name1 followed by name2 */
if (li == NULL || li->next == NULL || g_strcmp0(li->next->data, name2) != 0)
{
g_debug("Couldn't swap items: %s and %s", name1, name2);
return;
}
/* li_tmp will contain only the removed element (name2) */
li_tmp = li->next;
config->known_legacy_items = g_list_remove_link(config->known_legacy_items, li_tmp);
/* not strictly necessary (in testing the list contents was preserved)
* but searching for the index again should be safer */
for (li = config->known_legacy_items; li != NULL; li = li->next)
if (g_strcmp0(li->data, name1) == 0)
break;
config->known_legacy_items = g_list_insert_before(config->known_legacy_items, li, li_tmp->data);
g_list_free(li_tmp);
g_object_notify(G_OBJECT(config), "known-legacy-items");
g_signal_emit(G_OBJECT(config), sn_config_signals[KNOWN_ITEM_LIST_CHANGED], 0);
}
static gboolean
sn_config_items_clear_callback (gpointer key,
gpointer value,
......
......@@ -92,6 +92,9 @@ void sn_config_add_known_legacy_item (SnConfig
void sn_config_swap_known_items (SnConfig *config,
const gchar *name1,
const gchar *name2);
void sn_config_swap_known_legacy_items (SnConfig *config,
const gchar *name1,
const gchar *name2);
gboolean sn_config_items_clear (SnConfig *config);
......
......@@ -53,6 +53,7 @@ struct _SnDialog
GtkBuilder *builder;
GtkWidget *dialog;
GObject *store;
GObject *legacy_store;
SnConfig *config;
};
......@@ -74,8 +75,24 @@ enum
static const gchar *known_applications[][3] =
{
/* application name, icon-name, understandable name */
{ "nm-applet", "network-workgroup", "Network Manager Applet" },
{ "blueman", "blueman", "Blueman Applet" },
{ "nm-applet", "network-workgroup", "Network Manager Applet" },
};
static const gchar *known_legacy_applications[][3] =
{
/* application name, icon-name, understandable name */
{ "audacious2", "audacious", "Audacious" },
{ "drop-down terminal", "utilities-terminal", "Xfce Dropdown Terminal" },
{ "networkmanager applet", "network-workgroup", "Network Manager Applet" },
{ "parole", "parole", "Parole Media Player" },
{ "task manager", "utilities-system-monitor", "Xfce Taskmanager" },
{ "thunar", "Thunar", "Thunar Progress Dialog" },
{ "wicd-client.py", "wicd-gtk", "Wicd" },
{ "workrave tray icon", NULL, "Workrave Applet" },
{ "workrave", NULL, "Workrave" },
{ "xfce terminal", "utilities-terminal", "Xfce Terminal" },
{ "xfce4-power-manager", "xfpm-ac-adapter", "Xfce Power Manager" },
};
......@@ -97,6 +114,7 @@ sn_dialog_init (SnDialog *dialog)
dialog->builder = NULL;
dialog->dialog = NULL;
dialog->store = NULL;
dialog->legacy_store = NULL;
dialog->config = NULL;
}
......@@ -127,6 +145,31 @@ sn_dialog_add_item (SnDialog *dialog,
static void
sn_dialog_add_legacy_item(SnDialog *dialog,
GdkPixbuf *pixbuf,
const gchar *name,
const gchar *title,
gboolean hidden)
{
GtkTreeIter iter;
g_return_if_fail(XFCE_IS_SN_DIALOG(dialog));
g_return_if_fail(GTK_IS_LIST_STORE(dialog->legacy_store));
g_return_if_fail(name == NULL || g_utf8_validate(name, -1, NULL));
/* insert in the store */
gtk_list_store_append(GTK_LIST_STORE(dialog->legacy_store), &iter);
gtk_list_store_set(GTK_LIST_STORE(dialog->legacy_store), &iter,
COLUMN_PIXBUF, pixbuf,
COLUMN_TITLE, title,
COLUMN_HIDDEN, hidden,
COLUMN_TIP, name,
-1);
}
static void
sn_dialog_update_names (SnDialog *dialog)
{
......@@ -171,6 +214,50 @@ sn_dialog_update_names (SnDialog *dialog)
static void
sn_dialog_update_legacy_names(SnDialog *dialog)
{
GList *li;
const gchar *name;
const gchar *title;
const gchar *icon_name;
GdkPixbuf *pixbuf;
guint i;
g_return_if_fail(XFCE_IS_SN_DIALOG(dialog));
g_return_if_fail(XFCE_IS_SN_CONFIG(dialog->config));
g_return_if_fail(GTK_IS_LIST_STORE(dialog->legacy_store));
for (li = sn_config_get_known_legacy_items(dialog->config); li != NULL; li = li->next)
{
name = li->data;
title = name;
icon_name = name;
/* check if we have a better name for the application */
for (i = 0; i < G_N_ELEMENTS(known_legacy_applications); i++)
{
if (strcmp(name, known_legacy_applications[i][0]) == 0)
{
icon_name = known_legacy_applications[i][1];
title = known_legacy_applications[i][2];
break;
}
}
pixbuf = xfce_panel_pixbuf_from_source(icon_name, NULL, 22);
/* insert item in the store */
sn_dialog_add_legacy_item(dialog, pixbuf, name, title,
sn_config_is_legacy_hidden(dialog->config, name));
if (pixbuf != NULL)
g_object_unref(G_OBJECT(pixbuf));
}
}
static void
sn_dialog_selection_changed (GtkTreeSelection *selection,
SnDialog *dialog)
......@@ -210,6 +297,45 @@ sn_dialog_selection_changed (GtkTreeSelection *selection,
static void
sn_dialog_legacy_selection_changed (GtkTreeSelection *selection,
SnDialog *dialog)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
gint *indices;
gint count = 0, position = -1, depth;
gboolean item_up_sensitive, item_down_sensitive;
GObject *object;
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
path = gtk_tree_model_get_path (model, &iter);
indices = gtk_tree_path_get_indices_with_depth (path, &depth);
if (indices != NULL && depth > 0)
position = indices[0];
count = gtk_tree_model_iter_n_children (model, NULL);
gtk_tree_path_free (path);
}
item_up_sensitive = position > 0;
item_down_sensitive = position + 1 < count;
object = gtk_builder_get_object (dialog->builder, "item-up");
if (GTK_IS_BUTTON (object))
gtk_widget_set_sensitive (GTK_WIDGET (object), item_up_sensitive);
object = gtk_builder_get_object (dialog->builder, "item-down");
if (GTK_IS_BUTTON (object))
gtk_widget_set_sensitive (GTK_WIDGET (object), item_down_sensitive);
}
static void
sn_dialog_hidden_toggled (GtkCellRendererToggle *renderer,
const gchar *path_string,
......@@ -242,6 +368,38 @@ sn_dialog_hidden_toggled (GtkCellRendererToggle *renderer,
static void
sn_dialog_legacy_hidden_toggled (GtkCellRendererToggle *renderer,
const gchar *path_string,
SnDialog *dialog)
{
GtkTreeIter iter;
gboolean hidden;
gchar *name;
g_return_if_fail (XFCE_IS_SN_DIALOG (dialog));
g_return_if_fail (XFCE_IS_SN_CONFIG (dialog->config));
g_return_if_fail (GTK_IS_LIST_STORE (dialog->store));
if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->legacy_store), &iter, path_string))
{
gtk_tree_model_get (GTK_TREE_MODEL (dialog->legacy_store), &iter,
COLUMN_HIDDEN, &hidden,
COLUMN_TIP, &name, -1);
/* insert value (we need to update it) */
hidden = !hidden;
/* update box and store with new state */
sn_config_set_legacy_hidden (dialog->config, name, hidden);
gtk_list_store_set (GTK_LIST_STORE (dialog->legacy_store), &iter, COLUMN_HIDDEN, hidden, -1);
g_free (name);
}
}
static void
sn_dialog_swap_rows (SnDialog *dialog,
GtkTreeIter *iter_prev,
......@@ -283,6 +441,47 @@ sn_dialog_swap_rows (SnDialog *dialog,
static void
sn_dialog_legacy_swap_rows(SnDialog *dialog,
GtkTreeIter *iter_prev,
GtkTreeIter *iter)
{
GdkPixbuf *pixbuf1, *pixbuf2;
gchar *title1, *title2;
gboolean hidden1, hidden2;
gchar *tip1, *tip2;
g_return_if_fail(XFCE_IS_SN_DIALOG(dialog));
g_return_if_fail(XFCE_IS_SN_CONFIG(dialog->config));
g_return_if_fail(GTK_IS_LIST_STORE(dialog->legacy_store));
gtk_tree_model_get(GTK_TREE_MODEL(dialog->legacy_store), iter_prev,
COLUMN_PIXBUF, &pixbuf1,
COLUMN_TITLE, &title1,
COLUMN_HIDDEN, &hidden1,
COLUMN_TIP, &tip1, -1);
gtk_tree_model_get(GTK_TREE_MODEL(dialog->legacy_store), iter,
COLUMN_PIXBUF, &pixbuf2,
COLUMN_TITLE, &title2,
COLUMN_HIDDEN, &hidden2,
COLUMN_TIP, &tip2, -1);
gtk_list_store_set(GTK_LIST_STORE(dialog->legacy_store), iter_prev,
COLUMN_PIXBUF, pixbuf2,
COLUMN_TITLE, title2,
COLUMN_HIDDEN, hidden2,
COLUMN_TIP, tip2, -1);
gtk_list_store_set(GTK_LIST_STORE(dialog->legacy_store), iter,
COLUMN_PIXBUF, pixbuf1,
COLUMN_TITLE, title1,
COLUMN_HIDDEN, hidden1,
COLUMN_TIP, tip1, -1);
/* do a matching operation on SnConfig */
sn_config_swap_known_legacy_items(dialog->config, tip1, tip2);
}
static gboolean
sn_dialog_iter_equal (GtkTreeIter *iter1,
GtkTreeIter *iter2)
......@@ -331,6 +530,43 @@ sn_dialog_item_up_clicked (GtkWidget *button,
static void
sn_dialog_legacy_item_up_clicked(GtkWidget *button,
SnDialog *dialog)
{
GObject *treeview;
GtkTreeSelection *selection;
GtkTreeIter iter, iter_prev, iter_tmp;
g_return_if_fail(XFCE_IS_SN_DIALOG(dialog));
g_return_if_fail(GTK_IS_LIST_STORE(dialog->legacy_store));
treeview = gtk_builder_get_object(dialog->builder, "legacy-items-treeview");
g_return_if_fail(GTK_IS_TREE_VIEW(treeview));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
return;
/* gtk_tree_model_iter_previous available from Gtk3 */
/* so we have to search for it starting from the first iter */
if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->legacy_store), &iter_prev))
return;
iter_tmp = iter_prev;
while (!sn_dialog_iter_equal(&iter_tmp, &iter))
{
iter_prev = iter_tmp;
if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog->legacy_store), &iter_tmp))
return;
}
sn_dialog_legacy_swap_rows(dialog, &iter_prev, &iter);
gtk_tree_selection_select_iter(selection, &iter_prev);
}
static void
sn_dialog_item_down_clicked (GtkWidget *button,
SnDialog *dialog)
......@@ -358,6 +594,33 @@ sn_dialog_item_down_clicked (GtkWidget *button,
static void
sn_dialog_legacy_item_down_clicked(GtkWidget *button,
SnDialog *dialog)
{
GObject *treeview;
GtkTreeSelection *selection;
GtkTreeIter iter, iter_next;
g_return_if_fail(XFCE_IS_SN_DIALOG(dialog));
treeview = gtk_builder_get_object(dialog->builder, "legacy-items-treeview");
g_return_if_fail(GTK_IS_TREE_VIEW(treeview));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
return;
iter_next = iter;
if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog->legacy_store), &iter_next))
return;
sn_dialog_legacy_swap_rows(dialog, &iter, &iter_next);
gtk_tree_selection_select_iter(selection, &iter_next);
}
static void
sn_dialog_clear_clicked (GtkWidget *button,
SnDialog *dialog)
......@@ -479,6 +742,34 @@ sn_dialog_build (SnDialog *dialog)
g_signal_connect (G_OBJECT (object), "clicked",
G_CALLBACK (sn_dialog_item_down_clicked), dialog);
dialog->legacy_store = gtk_builder_get_object (dialog->builder, "legacy-items-store");
g_return_val_if_fail (GTK_IS_LIST_STORE (dialog->legacy_store), FALSE);
sn_dialog_update_legacy_names (dialog);
object = gtk_builder_get_object (dialog->builder, "legacy-items-treeview");
g_return_val_if_fail (GTK_IS_TREE_VIEW (object), FALSE);
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (object), COLUMN_TIP);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (object));
g_signal_connect (G_OBJECT (selection), "changed",
G_CALLBACK (sn_dialog_legacy_selection_changed), dialog);
sn_dialog_selection_changed (selection, dialog);
object = gtk_builder_get_object (dialog->builder, "legacy-hidden-toggle");
g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (object), FALSE);
g_signal_connect (G_OBJECT (object), "toggled",
G_CALLBACK (sn_dialog_legacy_hidden_toggled), dialog);
object = gtk_builder_get_object (dialog->builder, "legacy-item-up");
g_return_val_if_fail (GTK_IS_BUTTON (object), FALSE);
g_signal_connect (G_OBJECT (object), "clicked",
G_CALLBACK (sn_dialog_legacy_item_up_clicked), dialog);
object = gtk_builder_get_object (dialog->builder, "legacy-item-down");
g_return_val_if_fail (GTK_IS_BUTTON (object), FALSE);
g_signal_connect (G_OBJECT (object), "clicked",
G_CALLBACK (sn_dialog_legacy_item_down_clicked), dialog);
object = gtk_builder_get_object (dialog->builder, "items-clear");
g_return_val_if_fail (GTK_IS_BUTTON (object), FALSE);
g_signal_connect (G_OBJECT (object), "clicked",
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.18"/>
<requires lib="libxfce4ui-2" version="4.12"/>
......@@ -25,6 +25,18 @@
<column type="gchararray"/>
</columns>
</object>
<object class="GtkListStore" id="legacy-items-store">
<columns>
<!-- column-name icon -->
<column type="GdkPixbuf"/>
<!-- column-name title -->
<column type="gchararray"/>
<!-- column-name hidden -->
<column type="gboolean"/>
<!-- column-name tooltip -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkAdjustment" id="size-adjustment">
<property name="lower">12</property>
<property name="upper">64</property>
......@@ -269,7 +281,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="top_padding">6</property>
<property name="left_padding">12</property>
<child>
<object class="GtkBox" id="vbox4">
<property name="visible">True</property>
......@@ -277,13 +288,12 @@
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="checkbutton-whitelist">
<property name="label" translatable="yes">Hide items by default</property>
<object class="GtkStackSwitcher" id="items_stack_switcher">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">When enabled, all new items will be marked as "Hidden"</property>
<property name="draw_indicator">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="stack">items_stack</property>
</object>
<packing>
<property name="expand">False</property>
......@@ -292,108 +302,250 @@
</packing>
</child>
<child>
<object class="GtkBox" id="hbox2">
<object class="GtkStack" id="items_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="transition_type">slide-left-right</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<object class="GtkBox" id="hbox2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkTreeView" id="items-treeview">
<object class="GtkScrolledWindow" id="scrolledwindow1">
<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="rules_hint">True</property>
<property name="enable_search">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn-icon">
<property name="min_width">24</property>
<object class="GtkTreeView" id="items-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="rules_hint">True</property>
<property name="enable_search">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn-icon">
<property name="min_width">24</property>
<child>
<object class="GtkCellRendererPixbuf" id="cellrendererpixbuf1"/>
<attributes>
<attribute name="pixbuf">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn-name">
<property name="title" translatable="yes">Item</property>
<property name="expand">True</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkCellRendererPixbuf" id="cellrendererpixbuf1"/>
<attributes>
<attribute name="pixbuf">0</attribute>
</attributes>
<object class="GtkTreeViewColumn" id="treeviewcolumn-hidden">
<property name="title" translatable="yes">Hidden</property>
<child>
<object class="GtkCellRendererToggle" id="hidden-toggle"/>
<attributes>
<attribute name="active">2</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn-name">
<property name="title" translatable="yes">Item</property>
<property name="expand">True</property>
<object class="GtkButton" id="item-up">
<property name="visible">True</property>