[gnome-software/wip/folders: 5/5] Add ui for folders
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/folders: 5/5] Add ui for folders
- Date: Mon, 4 Nov 2013 03:48:56 +0000 (UTC)
commit 27c20e35a429fe02aad61aa5a482c607ec1fd820
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Nov 3 22:42:49 2013 -0500
Add ui for folders
This adds most of the UI for organizing applications
in folders. The folders are not written back to
the menu files yet.
src/Makefile.am | 3 +
src/app-folder-dialog.ui | 87 ++++++++++
src/gnome-software.gresource.xml | 1 +
src/gnome-software.ui | 91 +++++++++--
src/gs-app-folder-dialog.c | 337 ++++++++++++++++++++++++++++++++++++++
src/gs-app-folder-dialog.h | 59 +++++++
src/gs-app-widget.c | 3 +
src/gs-app.c | 14 ++
src/gs-shell-installed.c | 122 +++++++++++++-
src/gtk-style.css | 4 +-
10 files changed, 702 insertions(+), 19 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index e41459f..d4c4834 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,7 @@ desktop_in_files = \
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
UI_FILES = \
+ app-folder-dialog.ui \
app-menu.ui \
app-tile.ui \
app-widget.ui \
@@ -108,6 +109,8 @@ gnome_software_service_SOURCES = \
gs-category-tile.h \
gs-app-tile.c \
gs-app-tile.h \
+ gs-app-folder-dialog.c \
+ gs-app-folder-dialog.h \
gs-box.h \
gs-box.c \
gs-plugin.c \
diff --git a/src/app-folder-dialog.ui b/src/app-folder-dialog.ui
new file mode 100644
index 0000000..8cf612b
--- /dev/null
+++ b/src/app-folder-dialog.ui
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.10 -->
+ <template class="GsAppFolderDialog" parent="GtkWindow">
+ <property name="title" translatable="yes">Application Folders</property>
+ <property name="type-hint">dialog</property>
+ <property name="modal">True</property>
+ <property name="resizable">False</property>
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="header">
+ <property name="visible">True</property>
+ <child type="title">
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Application Folders</property>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="label" translatable="True">_Cancel</property>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="done_button">
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="label" translatable="True">_Done</property>
+ <property name="receives_default">True</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="content">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="margin">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="visible">True</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">50</property>
+ <property name="max_width_chars">55</property>
+ <property name="label">Choose a folder for Firefox, XChat and GIMP. Your application folder can
be found in the Activities Overview.</property>
+ </object>
+ <packing>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkListBox" id="app_folder_list">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="fill">True</property>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="new_folder_button">
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="label" translatable="yes">_New Folder</property>
+ </object>
+ <packing>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 1f0ece8..278ba0e 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -8,6 +8,7 @@
<file preprocess="xml-stripblanks">category-tile.ui</file>
<file preprocess="xml-stripblanks">app-tile.ui</file>
<file preprocess="xml-stripblanks">app-widget.ui</file>
+ <file preprocess="xml-stripblanks">app-folder-dialog.ui</file>
<file preprocess="xml-stripblanks">screenshot-image.ui</file>
<file preprocess="xml-stripblanks">gs-star-widget.ui</file>
<file>gtk-style.css</file>
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 48ef35e..1693daf 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -2,10 +2,20 @@
<!-- Generated with glade 3.15.2 on Thu Aug 15 17:13:59 2013 -->
<interface>
<!-- interface-requires gtk+ 3.10 -->
- <object class="GtkImage" id="button_select_image">
+ <object class="GtkMenu" id="header_selection_menu">
<property name="visible">True</property>
- <property name="icon_name">object-select-symbolic</property>
- <property name="icon_size">0</property>
+ <child>
+ <object class="GtkMenuItem" id="select_all_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Select All</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_none_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Select None</property>
+ </object>
+ </child>
</object>
<object class="GtkApplicationWindow" id="window_software">
<property name="can_focus">False</property>
@@ -130,6 +140,36 @@
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkMenuButton" id="header_selection_menu_button">
+ <property name="popup">header_selection_menu</property>
+ <style>
+ <class name="selection-menu"/>
+ </style>
+ <child>
+ <object class="GtkBox" id="header_selection_box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="header_selection_label">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkArrow" id="header_selection_arrow">
+ <property name="visible">True</property>
+ <property name="arrow_type">down</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
</object>
</child>
<child>
@@ -188,7 +228,7 @@
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="button_select">
+ <object class="GtkButton" id="button_select">
<property name="image">button_select_image</property>
<property name="can_focus">True</property>
</object>
@@ -509,20 +549,43 @@
</packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow_install">
+ <object class="GtkBox" id="box_install">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">none</property>
- <style>
- <class name="main-scrolled-software"/>
- </style>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkListBox" id="list_box_install">
+ <object class="GtkScrolledWindow" id="scrolledwindow_install">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="selection_mode">none</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">none</property>
+ <style>
+ <class name="main-scrolled-software"/>
+ </style>
+ <child>
+ <object class="GtkListBox" id="list_box_install">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="selection_mode">none</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="bottom_install">
+ <property name="visible">True</property>
+ <property name="transition-type">4</property>
+ <child>
+ <object class="GtkButton" id="button_folder_install">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Application _Folders</property>
+ <property name="use_underline">True</property>
+ <property name="hexpand">True</property>
+ <property name="halign">center</property>
+ <property name="margin">10</property>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/src/gs-app-folder-dialog.c b/src/gs-app-folder-dialog.c
new file mode 100644
index 0000000..4fa90e2
--- /dev/null
+++ b/src/gs-app-folder-dialog.c
@@ -0,0 +1,337 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-app-folder-dialog.h"
+
+typedef struct _GsAppFolderDialogPrivate GsAppFolderDialogPrivate;
+struct _GsAppFolderDialogPrivate
+{
+ GList *apps;
+ GtkWidget *header;
+ GtkWidget *cancel_button;
+ GtkWidget *done_button;
+ GtkWidget *description_label;
+ GtkWidget *app_folder_list;
+ GtkWidget *new_folder_button;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsAppFolderDialog, gs_app_folder_dialog, GTK_TYPE_WINDOW)
+
+#define PRIVATE(o) (gs_app_folder_dialog_get_instance_private (o))
+
+static void
+gs_app_folder_dialog_destroy (GtkWidget *widget)
+{
+ GsAppFolderDialog *dialog = GS_APP_FOLDER_DIALOG (widget);
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+
+ g_list_free (priv->apps);
+ priv->apps = NULL;
+
+ GTK_WIDGET_CLASS (gs_app_folder_dialog_parent_class)->destroy (widget);
+}
+
+static void
+cancel_cb (GsAppFolderDialog *dialog)
+{
+ gtk_window_close (GTK_WINDOW (dialog));
+}
+
+static void
+apply_changes (GsAppFolderDialog *dialog)
+{
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+ GtkListBoxRow *row;
+ const gchar *folder;
+ GList *l;
+
+ row = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->app_folder_list));
+ if (row == NULL)
+ folder = NULL;
+ else
+ folder = (const gchar *)g_object_get_data (G_OBJECT (row), "folder");
+ for (l = priv->apps; l; l = l->next) {
+ GsApp *app = l->data;
+ gs_app_set_folder (app, folder);
+ }
+
+ /* TODO here is where we need to write out a menu file */
+}
+
+static void
+done_cb (GsAppFolderDialog *dialog)
+{
+ apply_changes (dialog);
+ gtk_window_close (GTK_WINDOW (dialog));
+}
+
+static void
+delete_row (GtkButton *button, GtkWidget *row)
+{
+ GtkWidget *parent;
+
+ parent = gtk_widget_get_parent (row);
+ gtk_container_remove (GTK_CONTAINER (parent), row);
+}
+
+static void
+done_editing (GtkEntry *entry, GtkWidget *row)
+{
+ gchar *folder;
+ GtkWidget *label;
+ GtkWidget *parent;
+
+ if (gtk_entry_get_text_length (entry) == 0) {
+ delete_row (NULL, row);
+ return;
+ }
+ folder = g_strdup (gtk_entry_get_text (entry));
+ parent = gtk_widget_get_parent (GTK_WIDGET (entry));
+ gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (entry));
+
+ label = gtk_label_new (folder);
+ gtk_widget_show (label);
+ gtk_widget_set_margin_left (label, 10);
+ gtk_widget_set_margin_right (label, 10);
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ gtk_box_pack_start (GTK_BOX (parent), label, TRUE, TRUE, 0);
+ gtk_box_reorder_child (GTK_BOX (parent), label, 0);
+
+ g_object_set_data_full (G_OBJECT (row), "folder", folder, g_free);
+}
+
+static void
+new_folder_cb (GsAppFolderDialog *dialog)
+{
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+ GtkWidget *row;
+ GtkWidget *box;
+ GtkWidget *entry;
+ GtkWidget *button;
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ entry = gtk_entry_new ();
+ gtk_widget_set_margin_left (entry, 10);
+ gtk_widget_set_margin_right (entry, 10);
+ gtk_widget_set_halign (entry, GTK_ALIGN_START);
+ gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 0);
+
+ button = gtk_button_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+ row = gtk_list_box_row_new ();
+ gtk_container_add (GTK_CONTAINER (row), box);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (delete_row), row);
+ g_signal_connect (entry, "activate",
+ G_CALLBACK (done_editing), row);
+
+ gtk_widget_show_all (row);
+
+ gtk_list_box_insert (GTK_LIST_BOX (priv->app_folder_list), row, -1);
+
+ gtk_widget_grab_focus (entry);
+}
+
+static void
+update_header_func (GtkListBoxRow *row,
+ GtkListBoxRow *before,
+ gpointer user_data)
+{
+ GtkWidget *current;
+
+ if (before == NULL)
+ return;
+
+ current = gtk_list_box_row_get_header (row);
+ if (current == NULL)
+ {
+ current = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_widget_show (current);
+ gtk_list_box_row_set_header (row, current);
+ }
+}
+
+static void
+gs_app_folder_dialog_init (GsAppFolderDialog *dialog)
+{
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+
+ g_signal_connect_swapped (priv->cancel_button, "clicked",
+ G_CALLBACK (cancel_cb), dialog);
+ g_signal_connect_swapped (priv->done_button, "clicked",
+ G_CALLBACK (done_cb), dialog);
+ g_signal_connect_swapped (priv->new_folder_button, "clicked",
+ G_CALLBACK (new_folder_cb), dialog);
+
+ gtk_list_box_set_header_func (GTK_LIST_BOX (priv->app_folder_list),
+ update_header_func, NULL, NULL);
+}
+
+static void
+gs_app_folder_dialog_class_init (GsAppFolderDialogClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->destroy = gs_app_folder_dialog_destroy;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/software/app-folder-dialog.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, GsAppFolderDialog, header);
+ gtk_widget_class_bind_template_child_private (widget_class, GsAppFolderDialog, cancel_button);
+ gtk_widget_class_bind_template_child_private (widget_class, GsAppFolderDialog, done_button);
+ gtk_widget_class_bind_template_child_private (widget_class, GsAppFolderDialog, description_label);
+ gtk_widget_class_bind_template_child_private (widget_class, GsAppFolderDialog, app_folder_list);
+ gtk_widget_class_bind_template_child_private (widget_class, GsAppFolderDialog, new_folder_button);
+}
+
+static GtkWidget *
+create_row (GsAppFolderDialog *dialog, const gchar *folder)
+{
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+ GtkWidget *row;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *button;
+ GString *s;
+ GList *l;
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ label = gtk_label_new (folder);
+ gtk_widget_set_margin_left (label, 10);
+ gtk_widget_set_margin_right (label, 10);
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+
+ s = g_string_new ("");
+ for (l = priv->apps; l; l = l->next) {
+ GsApp *app = l->data;
+ if (g_strcmp0 (folder, gs_app_get_folder (app)) == 0) {
+ if (s->len > 0)
+ g_string_append (s, ", ");
+ g_string_append (s, gs_app_get_name (app));
+ }
+ }
+ if (s->len > 0) {
+ label = gtk_label_new (s->str);
+ gtk_widget_set_margin_left (label, 10);
+ gtk_widget_set_margin_right (label, 10);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
+ gtk_widget_set_halign (label, GTK_ALIGN_END);
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+ }
+ g_string_free (s, TRUE);
+
+ button = gtk_button_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+ row = gtk_list_box_row_new ();
+ gtk_container_add (GTK_CONTAINER (row), box);
+
+ gtk_widget_show_all (row);
+
+ g_object_set_data_full (G_OBJECT (row), "folder", g_strdup (folder), g_free);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (delete_row), row);
+
+ return row;
+}
+
+static void
+populate_list (GsAppFolderDialog *dialog)
+{
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+ gtk_list_box_insert (GTK_LIST_BOX (priv->app_folder_list), create_row (dialog, _("Games")), -1);
+ gtk_list_box_insert (GTK_LIST_BOX (priv->app_folder_list), create_row (dialog, _("Graphics")), -1);
+ gtk_list_box_insert (GTK_LIST_BOX (priv->app_folder_list), create_row (dialog, _("Office")), -1);
+ gtk_list_box_insert (GTK_LIST_BOX (priv->app_folder_list), create_row (dialog, _("Sundry")), -1);
+
+ /* TODO: here is where we need to load existing folders */
+}
+
+static void
+gs_app_folder_dialog_set_apps (GsAppFolderDialog *dialog,
+ GList *apps)
+{
+ GsAppFolderDialogPrivate *priv = PRIVATE (dialog);
+ gchar *label;
+ const gchar *app1, *app2, *app3;
+
+ priv->apps = g_list_copy (apps);
+
+ switch (g_list_length (priv->apps)) {
+ case 0:
+ label = g_strdup (_("Add or remove folders. Your application folders can be found in the
Activities Overview."));
+ break;
+ case 1:
+ app1 = gs_app_get_name (GS_APP (priv->apps->data));
+ label = g_strdup_printf (_("Choose a folder for %s. Your application folders can be found in
the Activities Overview."), app1);
+ break;
+ case 2:
+ app1 = gs_app_get_name (GS_APP (priv->apps->data));
+ app2 = gs_app_get_name (GS_APP (priv->apps->next->data));
+ label = g_strdup_printf (_("Choose a folder for %s and %s. Your application folders can be
found in the Activities Overview."), app1, app2);
+ break;
+ case 3:
+ app1 = gs_app_get_name (GS_APP (priv->apps->data));
+ app2 = gs_app_get_name (GS_APP (priv->apps->next->data));
+ app3 = gs_app_get_name (GS_APP (priv->apps->next->next->data));
+ label = g_strdup_printf (_("Choose a folder for %s, %s and %s. Your application folders can
be found in the Activities Overview."), app1, app2, app3);
+ break;
+ default:
+ label = g_strdup (_("Choose a folder for the selected applications. Your application folders
can be found in the Activities Overview."));
+ break;
+ }
+ gtk_label_set_label (GTK_LABEL (priv->description_label), label);
+ g_free (label);
+}
+
+GtkWidget *
+gs_app_folder_dialog_new (GtkWindow *parent, GList *apps)
+{
+ GsAppFolderDialog *dialog;
+
+ dialog = g_object_new (GS_TYPE_APP_FOLDER_DIALOG,
+ "transient-for", parent,
+ NULL);
+ gs_app_folder_dialog_set_apps (dialog, apps);
+ populate_list (dialog);
+
+ return GTK_WIDGET (dialog);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-app-folder-dialog.h b/src/gs-app-folder-dialog.h
new file mode 100644
index 0000000..4f6dcae
--- /dev/null
+++ b/src/gs-app-folder-dialog.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_APP_FOLDER_DIALOG_H
+#define GS_APP_FOLDER_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#include "gs-app.h"
+
+#define GS_TYPE_APP_FOLDER_DIALOG (gs_app_folder_dialog_get_type())
+#define GS_APP_FOLDER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GS_TYPE_APP_FOLDER_DIALOG,
GsAppFolderDialog))
+#define GS_APP_FOLDER_DIALOG_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls),
GS_TYPE_APP_FOLDER_DIALOG, GsAppFolderDialogClass))
+#define GS_IS_APP_FOLDER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GS_TYPE_APP_FOLDER_DIALOG))
+#define GS_IS_APP_FOLDER_DIALOG_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), GS_TYPE_APP_FOLDER_DIALOG))
+#define GS_APP_FOLDER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GS_TYPE_APP_FOLDER_DIALOG,
GsAppFolderDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GsAppFolderDialog GsAppFolderDialog;
+typedef struct _GsAppFolderDialogClass GsAppFolderDialogClass;
+
+struct _GsAppFolderDialog
+{
+ GtkWindow parent;
+};
+
+struct _GsAppFolderDialogClass
+{
+ GtkWindowClass parent_class;
+};
+
+GType gs_app_folder_dialog_get_type (void);
+GtkWidget *gs_app_folder_dialog_new (GtkWindow *parent,
+ GList *apps);
+
+G_END_DECLS
+
+#endif /* GS_APP_FOLDER_DIALOG_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-app-widget.c b/src/gs-app-widget.c
index a6a13e8..ca60325 100644
--- a/src/gs-app-widget.c
+++ b/src/gs-app-widget.c
@@ -225,6 +225,9 @@ gs_app_widget_set_app (GsAppWidget *app_widget, GsApp *app)
g_signal_connect_object (app_widget->priv->app, "notify::state",
G_CALLBACK (gs_app_widget_notify_props_changed_cb),
app_widget, 0);
+ g_signal_connect_object (app_widget->priv->app, "notify::folder",
+ G_CALLBACK (gs_app_widget_notify_props_changed_cb),
+ app_widget, 0);
gs_app_widget_refresh (app_widget);
}
diff --git a/src/gs-app.c b/src/gs-app.c
index 7518934..beb0022 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -102,6 +102,7 @@ enum {
PROP_KIND,
PROP_STATE,
PROP_INSTALL_DATE,
+ PROP_FOLDER,
PROP_LAST
};
@@ -1396,6 +1397,8 @@ gs_app_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *
case PROP_INSTALL_DATE:
g_value_set_uint64 (value, priv->install_date);
break;
+ case PROP_FOLDER:
+ g_value_set_string (value, priv->folder);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1438,6 +1441,9 @@ gs_app_set_property (GObject *object, guint prop_id, const GValue *value, GParam
case PROP_INSTALL_DATE:
gs_app_set_install_date (app, g_value_get_uint64 (value));
break;
+ case PROP_FOLDER:
+ gs_app_set_folder (app, g_value_get_string (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1527,6 +1533,11 @@ gs_app_class_init (GsAppClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_class_install_property (object_class, PROP_INSTALL_DATE, pspec);
+ pspec = g_param_spec_string ("folder", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_FOLDER, pspec);
+
g_type_class_add_private (klass, sizeof (GsAppPrivate));
}
@@ -1627,8 +1638,11 @@ gs_app_set_folder (GsApp *app,
{
GsAppPrivate *priv = app->priv;
+
g_free (priv->folder);
priv->folder = g_strdup (folder);
+
+ g_object_notify (G_OBJECT (app), "folder");
}
diff --git a/src/gs-shell-installed.c b/src/gs-shell-installed.c
index ad272bf..0dd95ba 100644
--- a/src/gs-shell-installed.c
+++ b/src/gs-shell-installed.c
@@ -30,6 +30,7 @@
#include "gs-app.h"
#include "gs-utils.h"
#include "gs-app-widget.h"
+#include "gs-app-folder-dialog.h"
#define INSTALL_DATE_QUEUED (G_MAXUINT - 1)
#define INSTALL_DATE_INSTALLING (G_MAXUINT - 2)
@@ -46,6 +47,7 @@ struct GsShellInstalledPrivate
GtkBuilder *builder;
GCancellable *cancellable;
GtkListBox *list_box_installed;
+ GtkRevealer *bottom_bar;
GtkSizeGroup *sizegroup_image;
GtkSizeGroup *sizegroup_name;
gboolean cache_valid;
@@ -558,14 +560,45 @@ gs_shell_installed_pending_apps_changed_cb (GsPluginLoader *plugin_loader,
}
static void
-selection_mode_cb (GtkToggleButton *button, GsShellInstalled *shell_installed)
+selection_mode_cb (GtkButton *button, GsShellInstalled *shell_installed)
{
GsShellInstalledPrivate *priv = shell_installed->priv;
GList *children, *l;
GtkWidget *row;
GtkWidget *app_widget;
+ GtkWidget *header;
+ GtkWidget *widget;
+ GtkStyleContext *context;
- priv->selection_mode = gtk_toggle_button_get_active (button);
+ priv->selection_mode = !priv->selection_mode;
+
+ header = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header"));
+ context = gtk_widget_get_style_context (header);
+ if (priv->selection_mode) {
+ gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header), FALSE);
+ gtk_style_context_add_class (context, "selection-mode");
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_select"));
+ gtk_button_set_image (GTK_BUTTON (widget), NULL);
+ gtk_button_set_label (GTK_BUTTON (widget), _("Cancel"));
+ gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "buttonbox_main"));
+ gtk_widget_hide (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_selection_menu_button"));
+ gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_selection_label"));
+ gtk_label_set_label (GTK_LABEL (widget), _("Click on items to select them"));
+ } else {
+ gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header), TRUE);
+ gtk_style_context_remove_class (context, "selection-mode");
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_select"));
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new_from_icon_name
("object-select-symbolic", GTK_ICON_SIZE_MENU));
+ gtk_button_set_label (GTK_BUTTON (widget), NULL);
+ gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "buttonbox_main"));
+ gtk_widget_show (widget);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_selection_menu_button"));
+ gtk_widget_hide (widget);
+ }
children = gtk_container_get_children (GTK_CONTAINER (priv->list_box_installed));
for (l = children; l; l = l->next) {
@@ -575,6 +608,72 @@ selection_mode_cb (GtkToggleButton *button, GsShellInstalled *shell_installed)
priv->selection_mode);
}
g_list_free (children);
+
+ gtk_revealer_set_reveal_child (priv->bottom_bar, priv->selection_mode);
+}
+
+static GList *
+get_selected_apps (GsShellInstalled *shell_installed)
+{
+ GsShellInstalledPrivate *priv = shell_installed->priv;
+ GList *children, *l, *list;
+
+ list = NULL;
+ children = gtk_container_get_children (GTK_CONTAINER (priv->list_box_installed));
+ for (l = children; l; l = l->next) {
+ GtkListBoxRow *row = l->data;
+ GsAppWidget *app_widget = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (row)));
+ if (gs_app_widget_get_selected (app_widget)) {
+ list = g_list_prepend (list, gs_app_widget_get_app (app_widget));
+ }
+ }
+ g_list_free (children);
+
+ return list;
+}
+
+static void
+show_folder_dialog (GtkButton *button, GsShellInstalled *shell_installed)
+{
+ GtkWidget *toplevel;
+ GtkWidget *dialog;
+ GList *apps;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ apps = get_selected_apps (shell_installed);
+ dialog = gs_app_folder_dialog_new (GTK_WINDOW (toplevel), apps);
+ g_list_free (apps);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+select_all_cb (GtkMenuItem *item, GsShellInstalled *shell_installed)
+{
+ GsShellInstalledPrivate *priv = shell_installed->priv;
+ GList *children, *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (priv->list_box_installed));
+ for (l = children; l; l = l->next) {
+ GtkListBoxRow *row = l->data;
+ GsAppWidget *app_widget = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (row)));
+ gs_app_widget_set_selected (app_widget, TRUE);
+ }
+ g_list_free (children);
+}
+
+static void
+select_none_cb (GtkMenuItem *item, GsShellInstalled *shell_installed)
+{
+ GsShellInstalledPrivate *priv = shell_installed->priv;
+ GList *children, *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (priv->list_box_installed));
+ for (l = children; l; l = l->next) {
+ GtkListBoxRow *row = l->data;
+ GsAppWidget *app_widget = GS_APP_WIDGET (gtk_bin_get_child (GTK_BIN (row)));
+ gs_app_widget_set_selected (app_widget, FALSE);
+ }
+ g_list_free (children);
}
/**
@@ -611,10 +710,27 @@ gs_shell_installed_setup (GsShellInstalled *shell_installed,
gtk_list_box_set_sort_func (priv->list_box_installed,
gs_shell_installed_sort_func,
shell_installed, NULL);
+
+ priv->bottom_bar = GTK_REVEALER (gtk_builder_get_object (priv->builder, "bottom_install"));
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_folder_install"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (show_folder_dialog), shell_installed);
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_select"));
- g_signal_connect (widget, "toggled",
+ g_signal_connect (widget, "clicked",
G_CALLBACK (selection_mode_cb), shell_installed);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_select"));
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new_from_icon_name ("object-select-symbolic",
GTK_ICON_SIZE_MENU));
+ gtk_button_set_label (GTK_BUTTON (widget), NULL);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "select_all_menuitem"));
+ g_signal_connect (widget, "activate",
+ G_CALLBACK (select_all_cb), shell_installed);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "select_none_menuitem"));
+ g_signal_connect (widget, "activate",
+ G_CALLBACK (select_none_cb), shell_installed);
}
/**
diff --git a/src/gtk-style.css b/src/gtk-style.css
index aef3909..39f0ac1 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -63,8 +63,8 @@ GtkNotebook.main-notebook-software > GtkScrolledWindow {
.folder-label {
background-color: #999999;
- border-radius: 5;
- padding: 5;
+ border-radius: 5px;
+ padding: 5px;
color: white;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]