]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkappchooserbutton.c
Add gtk_app_chooser_button_get/set_heading
[~andy/gtk] / gtk / gtkappchooserbutton.c
index cccd76096d0f11f8f24ed9bde3d3d27d43f800a9..3fe38b95df5d8ea0d0f8f8fab17bc0fb01ec27e7 100644 (file)
  * Authors: Cosimo Cecchi <ccecchi@redhat.com>
  */
 
+/**
+ * SECTION:gtkappchooserbutton
+ * @Title: GtkAppChooserButton
+ * @Short_description: A button to launch an application chooser dialog
+ *
+ * The #GtkAppChooserButton is a widget that lets the user select
+ * an application. It implements the #GtkAppChooser interface.
+ */
 #include "config.h"
 
 #include "gtkappchooserbutton.h"
@@ -39,6 +47,7 @@
 enum {
   PROP_CONTENT_TYPE = 1,
   PROP_SHOW_DIALOG_ITEM,
+  PROP_HEADING
 };
 
 enum {
@@ -80,8 +89,10 @@ G_DEFINE_TYPE_WITH_CODE (GtkAppChooserButton, gtk_app_chooser_button, GTK_TYPE_C
 struct _GtkAppChooserButtonPrivate {
   GtkListStore *store;
 
+  int last_active;
   gchar *content_type;
   gboolean show_dialog_item;
+  gchar *heading;
 
   GHashTable *custom_item_names;
 };
@@ -192,7 +203,7 @@ other_application_dialog_response_cb (GtkDialog *dialog,
       /* reset the active item, otherwise we are stuck on
        * 'Other application...'
        */
-      gtk_combo_box_set_active (GTK_COMBO_BOX (self), 0);
+      gtk_combo_box_set_active (GTK_COMBO_BOX (self), self->priv->last_active);
       gtk_widget_destroy (GTK_WIDGET (dialog));
       return;
     }
@@ -215,6 +226,11 @@ other_application_item_activated_cb (GtkAppChooserButton *self)
   toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
   dialog = gtk_app_chooser_dialog_new_for_content_type (toplevel, GTK_DIALOG_DESTROY_WITH_PARENT,
                                                         self->priv->content_type);
+
+  gtk_window_set_modal (GTK_WINDOW (dialog), gtk_window_get_modal (toplevel));
+  gtk_app_chooser_dialog_set_heading (GTK_APP_CHOOSER_DIALOG (dialog),
+                                      self->priv->heading);
+
   widget = gtk_app_chooser_dialog_get_widget (GTK_APP_CHOOSER_DIALOG (dialog));
   g_object_set (widget,
                 "show-fallback", TRUE,
@@ -230,24 +246,23 @@ static void
 gtk_app_chooser_button_ensure_dialog_item (GtkAppChooserButton *self,
                                            GtkTreeIter *prev_iter)
 {
-  GIcon *icon;
-  GtkTreeIter iter;
+  GtkTreeIter iter, iter2;
 
   if (!self->priv->show_dialog_item)
     return;
 
-  icon = g_themed_icon_new ("application-x-executable");
+  if (prev_iter == NULL)
+    gtk_list_store_append (self->priv->store, &iter);
+  else
+    gtk_list_store_insert_after (self->priv->store, &iter, prev_iter);
 
-  gtk_list_store_insert_after (self->priv->store, &iter, prev_iter);
   real_insert_separator (self, FALSE, &iter);
-  *prev_iter = iter;
+  iter2 = iter;
 
-  gtk_list_store_insert_after (self->priv->store, &iter, prev_iter);
+  gtk_list_store_insert_after (self->priv->store, &iter, &iter2);
   real_insert_custom_item (self, CUSTOM_ITEM_OTHER_APP,
-                           _("Other application..."), icon,
+                           _("Other application..."), NULL,
                            FALSE, &iter);
-
-  g_object_unref (icon);
 }
 
 static void
@@ -257,10 +272,12 @@ gtk_app_chooser_button_populate (GtkAppChooserButton *self)
   GAppInfo *app;
   GtkTreeIter iter, iter2;
   GIcon *icon;
-  gboolean first;
+  gboolean cycled_recommended;
 
+#ifndef G_OS_WIN32
   recommended_apps = g_app_info_get_recommended_for_type (self->priv->content_type);
-  first = TRUE;
+#endif
+  cycled_recommended = FALSE;
 
   for (l = recommended_apps; l != NULL; l = l->next)
     {
@@ -273,20 +290,20 @@ gtk_app_chooser_button_populate (GtkAppChooserButton *self)
       else
         g_object_ref (icon);
 
-      if (first)
+      if (cycled_recommended)
         {
-          get_first_iter (self->priv->store, &iter);
-          first = FALSE;
+          gtk_list_store_insert_after (self->priv->store, &iter2, &iter);
+          iter = iter2;
         }
       else
         {
-          gtk_list_store_insert_after (self->priv->store, &iter2, &iter);
-          iter = iter2;
+          get_first_iter (self->priv->store, &iter);
+          cycled_recommended = TRUE;
         }
 
       gtk_list_store_set (self->priv->store, &iter,
                           COLUMN_APP_INFO, app,
-                          COLUMN_LABEL, g_app_info_get_display_name (app),
+                          COLUMN_LABEL, g_app_info_get_name (app),
                           COLUMN_ICON, icon,
                           COLUMN_CUSTOM, FALSE,
                           -1);
@@ -294,7 +311,11 @@ gtk_app_chooser_button_populate (GtkAppChooserButton *self)
       g_object_unref (icon);
     }
 
-  gtk_app_chooser_button_ensure_dialog_item (self, &iter);
+  if (!cycled_recommended)
+    gtk_app_chooser_button_ensure_dialog_item (self, NULL);
+  else
+    gtk_app_chooser_button_ensure_dialog_item (self, &iter);
+
   gtk_combo_box_set_active (GTK_COMBO_BOX (self), 0);
 }
 
@@ -302,6 +323,7 @@ static void
 gtk_app_chooser_button_build_ui (GtkAppChooserButton *self)
 {
   GtkCellRenderer *cell;
+  GtkCellArea *area;
 
   self->priv->store = gtk_list_store_new (NUM_COLUMNS,
                                           G_TYPE_APP_INFO,
@@ -314,23 +336,29 @@ gtk_app_chooser_button_build_ui (GtkAppChooserButton *self)
   gtk_combo_box_set_model (GTK_COMBO_BOX (self),
                            GTK_TREE_MODEL (self->priv->store));
 
+  area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (self));
+
   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (self),
                                         row_separator_func, NULL, NULL);
 
   cell = gtk_cell_renderer_pixbuf_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, FALSE);
+  gtk_cell_area_add_with_properties (area, cell,
+                                     "align", FALSE,
+                                     "expand", FALSE,
+                                     "fixed-size", FALSE,
+                                     NULL);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), cell,
                                   "gicon", COLUMN_ICON,
                                   NULL);
 
   cell = gtk_cell_renderer_text_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, TRUE);
+  gtk_cell_area_add_with_properties (area, cell,
+                                     "align", FALSE,
+                                     "expand", TRUE,
+                                     NULL);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), cell,
                                   "text", COLUMN_LABEL,
                                   NULL);
-  g_object_set (cell,
-                "xpad", 6,
-                NULL);
 
   gtk_app_chooser_button_populate (self);
 }
@@ -381,6 +409,7 @@ gtk_app_chooser_button_changed (GtkComboBox *object)
         {
           name_quark = g_quark_from_string (name);
           g_signal_emit (self, signals[SIGNAL_CUSTOM_ITEM_ACTIVATED], name_quark, name);
+          self->priv->last_active = gtk_combo_box_get_active (object);
         }
       else
         {
@@ -390,6 +419,8 @@ gtk_app_chooser_button_changed (GtkComboBox *object)
 
       g_free (name);
     }
+  else
+    self->priv->last_active = gtk_combo_box_get_active (object);
 }
 
 static void
@@ -447,6 +478,9 @@ gtk_app_chooser_button_set_property (GObject      *obj,
     case PROP_SHOW_DIALOG_ITEM:
       gtk_app_chooser_button_set_show_dialog_item (self, g_value_get_boolean (value));
       break;
+    case PROP_HEADING:
+      gtk_app_chooser_button_set_heading (self, g_value_get_string (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
       break;
@@ -469,6 +503,9 @@ gtk_app_chooser_button_get_property (GObject    *obj,
     case PROP_SHOW_DIALOG_ITEM:
       g_value_set_boolean (value, self->priv->show_dialog_item);
       break;
+    case PROP_HEADING:
+      g_value_set_string (value, self->priv->heading);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
       break;
@@ -482,6 +519,7 @@ gtk_app_chooser_button_finalize (GObject *obj)
 
   g_hash_table_destroy (self->priv->custom_item_names);
   g_free (self->priv->content_type);
+  g_free (self->priv->heading);
 
   G_OBJECT_CLASS (gtk_app_chooser_button_parent_class)->finalize (obj);
 }
@@ -512,7 +550,7 @@ gtk_app_chooser_button_class_init (GtkAppChooserButtonClass *klass)
   /**
    * GtkAppChooserButton:show-dialog-item:
    *
-   * The ::show-dialog-item property determines whether the dropdown menu
+   * The #GtkAppChooserButton:show-dialog-item property determines whether the dropdown menu
    * should show an item that triggers a #GtkAppChooserDialog when clicked.
    */
   pspec =
@@ -523,6 +561,29 @@ gtk_app_chooser_button_class_init (GtkAppChooserButtonClass *klass)
                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (oclass, PROP_SHOW_DIALOG_ITEM, pspec);
 
+  /**
+   * GtkAppChooserButton:heading:
+   *
+   * The text to show at the top of the dialog that can be
+   * opened from the button. The string may contain Pango markup.
+   */
+  pspec = g_param_spec_string ("heading",
+                               P_("Heading"),
+                               P_("The text to show at the top of the dialog"),
+                               NULL,
+                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (oclass, PROP_HEADING, pspec);
+
+
+  /**
+   * GtkAppChooserButton::custom-item-activated:
+   * @self: the object which received the signal
+   * @item_name: the name of the activated item
+   *
+   * Emitted when a custom item, previously added with
+   * gtk_app_chooser_button_append_custom_item(), is activated from the
+   * dropdown menu.
+   */
   signals[SIGNAL_CUSTOM_ITEM_ACTIVATED] =
     g_signal_new ("custom-item-activated",
                   GTK_TYPE_APP_CHOOSER_BUTTON,
@@ -546,6 +607,37 @@ gtk_app_chooser_button_init (GtkAppChooserButton *self)
                            g_free, NULL);
 }
 
+static gboolean
+app_chooser_button_iter_from_custom_name (GtkAppChooserButton *self,
+                                          const gchar *name,
+                                          GtkTreeIter *set_me)
+{
+  GtkTreeIter iter;
+  gchar *custom_name = NULL;
+
+  if (!gtk_tree_model_get_iter_first
+      (GTK_TREE_MODEL (self->priv->store), &iter))
+    return FALSE;
+
+  do {
+    gtk_tree_model_get (GTK_TREE_MODEL (self->priv->store), &iter,
+                        COLUMN_NAME, &custom_name,
+                        -1);
+
+    if (g_strcmp0 (custom_name, name) == 0)
+      {
+        g_free (custom_name);
+        *set_me = iter;
+
+        return TRUE;
+      }
+
+    g_free (custom_name);
+  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->store), &iter));
+
+  return FALSE;
+}
+
 static void
 real_insert_custom_item (GtkAppChooserButton *self,
                          const gchar *name,
@@ -660,6 +752,38 @@ gtk_app_chooser_button_append_custom_item (GtkAppChooserButton *self,
   real_insert_custom_item (self, name, label, icon, TRUE, &iter);
 }
 
+/**
+ * gtk_app_chooser_button_select_custom_item:
+ * @self: a #GtkAppChooserButton
+ * @name: the name of the custom item
+ *
+ * Selects a custom item previously added with
+ * gtk_app_chooser_button_append_custom_item().
+ * Use gtk_app_chooser_refresh() to bring the selection to its initial
+ * state.
+ *
+ * Since: 3.0
+ */
+void
+gtk_app_chooser_button_set_active_custom_item (GtkAppChooserButton *self,
+                                               const gchar         *name)
+{
+  GtkTreeIter iter;
+
+  g_return_if_fail (GTK_IS_APP_CHOOSER_BUTTON (self));
+  g_return_if_fail (name != NULL);
+
+  if (g_hash_table_lookup (self->priv->custom_item_names, name) == NULL ||
+      !app_chooser_button_iter_from_custom_name (self, name, &iter))
+    {
+      g_warning ("Can't find the item named %s in the app chooser.",
+                 name);
+      return;
+    }
+
+  gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), &iter);
+}
+
 /**
  * gtk_app_chooser_button_get_show_dialog_item:
  * @self: a #GtkAppChooserButton
@@ -680,7 +804,7 @@ gtk_app_chooser_button_get_show_dialog_item (GtkAppChooserButton *self)
 }
 
 /**
- * gtk_app_chooser_button_get_show_dialog_item:
+ * gtk_app_chooser_button_set_show_dialog_item:
  * @self: a #GtkAppChooserButton
  * @setting: the new value for #GtkAppChooserButton:show-dialog-item
  *
@@ -702,3 +826,40 @@ gtk_app_chooser_button_set_show_dialog_item (GtkAppChooserButton *self,
       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
     }
 }
+
+/**
+ * gtk_app_chooser_button_set_heading:
+ * @self: a #GtkAppChooserButton
+ * @heading: a string containing Pango markup
+ *
+ * Sets the text to display at the top of the dialog.
+ * If the heading is not set, the dialog displays a default text.
+ */
+void
+gtk_app_chooser_button_set_heading (GtkAppChooserButton *self,
+                                    const gchar         *heading)
+{
+  g_return_if_fail (GTK_IS_APP_CHOOSER_BUTTON (self));
+
+  g_free (self->priv->heading);
+  self->priv->heading = g_strdup (heading);
+
+  g_object_notify (G_OBJECT (self), "heading");
+}
+
+/**
+ * gtk_app_chooser_button_get_heading:
+ * @self: a #GtkAppChooserButton
+ *
+ * Returns the text to display at the top of the dialog.
+ *
+ * Returns: the text to display at the top of the dialog, or %NULL, in which
+ *     case a default text is displayed
+ */
+const gchar *
+gtk_app_chooser_button_get_heading (GtkAppChooserButton *self)
+{
+  g_return_val_if_fail (GTK_IS_APP_CHOOSER_BUTTON (self), NULL);
+
+  return self->priv->heading;
+}