gtkappchooserwidget.c 43.6 KB
Newer Older
1
/*
2
 * gtkappchooserwidget.c: an app-chooser widget
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Copyright (C) 2004 Novell, Inc.
 * Copyright (C) 2007, 2010 Red Hat, Inc.
 *
 * This library 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 library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
18
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 20 21 22 23 24
 *
 * Authors: Dave Camp <dave@novell.com>
 *          Alexander Larsson <alexl@redhat.com>
 *          Cosimo Cecchi <ccecchi@redhat.com>
 */

Matthias Clasen's avatar
Matthias Clasen committed
25
#include "config.h"
26

27
#include "gtkappchooserwidget.h"
28 29 30

#include "gtkintl.h"
#include "gtkmarshalers.h"
Matthias Clasen's avatar
Matthias Clasen committed
31
#include "gtkappchooserwidget.h"
32
#include "gtkappchooserprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
33 34 35 36 37 38 39 40
#include "gtkliststore.h"
#include "gtkcellrenderertext.h"
#include "gtkcellrendererpixbuf.h"
#include "gtktreeview.h"
#include "gtktreeselection.h"
#include "gtktreemodelsort.h"
#include "gtkorientable.h"
#include "gtkscrolledwindow.h"
41
#include "gtklabel.h"
42 43 44 45 46

#include <string.h>
#include <glib/gi18n-lib.h>
#include <gio/gio.h>

Matthias Clasen's avatar
Matthias Clasen committed
47 48 49 50 51 52 53 54 55
/**
 * SECTION:gtkappchooserwidget
 * @Title: GtkAppChooserWidget
 * @Short_description: Application chooser widget that can be embedded in other widgets
 *
 * #GtkAppChooserWidget is a widget for selecting applications.
 * It is the main building block for #GtkAppChooserDialog. Most
 * applications only need to use the latter; but you can use
 * this widget as part of a larger widget if you have special needs.
56 57 58 59 60 61 62 63 64 65 66 67 68
 *
 * #GtkAppChooserWidget offers detailed control over what applications
 * are shown, using the
 * #GtkAppChooserWidget:show-default,
 * #GtkAppChooserWidget:show-recommended,
 * #GtkAppChooserWidget:show-fallback,
 * #GtkAppChooserWidget:show-other and
 * #GtkAppChooserWidget:show-all
 * properties. See the #GtkAppChooser documentation for more information
 * about these groups of applications.
 *
 * To keep track of the selected application, use the
 * #GtkAppChooserWidget::application-selected and #GtkAppChooserWidget::application-activated signals.
69 70 71 72
 *
 * # CSS nodes
 *
 * GtkAppChooserWidget has a single CSS node with name appchooser.
Matthias Clasen's avatar
Matthias Clasen committed
73 74
 */

75
struct _GtkAppChooserWidgetPrivate {
76 77
  GAppInfo *selected_app_info;

78 79
  gchar *content_type;
  gchar *default_text;
80 81 82 83 84 85

  guint show_default     : 1;
  guint show_recommended : 1;
  guint show_fallback    : 1;
  guint show_other       : 1;
  guint show_all         : 1;
86 87 88

  GtkWidget *program_list;
  GtkListStore *program_list_store;
89 90
  GtkWidget *no_apps_label;
  GtkWidget *no_apps;
91

92
  GtkTreeViewColumn *column;
93
  GtkCellRenderer *padding_renderer;
94
  GtkCellRenderer *secondary_padding;
95 96

  GAppInfoMonitor *monitor;
97 98

  GtkWidget *popup_menu;
99 100 101 102 103 104
};

enum {
  COLUMN_APP_INFO,
  COLUMN_GICON,
  COLUMN_NAME,
105
  COLUMN_DESC,
106
  COLUMN_EXEC,
107
  COLUMN_DEFAULT,
108 109 110
  COLUMN_HEADING,
  COLUMN_HEADING_TEXT,
  COLUMN_RECOMMENDED,
111
  COLUMN_FALLBACK,
112 113 114 115 116 117 118
  NUM_COLUMNS
};


enum {
  PROP_CONTENT_TYPE = 1,
  PROP_GFILE,
119
  PROP_SHOW_DEFAULT,
120 121 122 123
  PROP_SHOW_RECOMMENDED,
  PROP_SHOW_FALLBACK,
  PROP_SHOW_OTHER,
  PROP_SHOW_ALL,
124
  PROP_DEFAULT_TEXT,
125 126 127 128 129 130
  N_PROPERTIES
};

enum {
  SIGNAL_APPLICATION_SELECTED,
  SIGNAL_APPLICATION_ACTIVATED,
131
  SIGNAL_POPULATE_POPUP,
132 133 134 135 136
  N_SIGNALS
};

static guint signals[N_SIGNALS] = { 0, };

137
static void gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface);
138

139
G_DEFINE_TYPE_WITH_CODE (GtkAppChooserWidget, gtk_app_chooser_widget, GTK_TYPE_BOX,
140
                         G_ADD_PRIVATE (GtkAppChooserWidget)
Matthias Clasen's avatar
Matthias Clasen committed
141 142
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER,
                                                gtk_app_chooser_widget_iface_init));
143 144

static void
145
refresh_and_emit_app_selected (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
146
                               GtkTreeSelection    *selection)
147 148 149 150 151 152 153
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  GAppInfo *info = NULL;
  gboolean should_emit = FALSE;

  if (gtk_tree_selection_get_selected (selection, &model, &iter))
Matthias Clasen's avatar
Matthias Clasen committed
154
    gtk_tree_model_get (model, &iter, COLUMN_APP_INFO, &info, -1);
155 156 157 158

  if (info == NULL)
    return;

159
  if (self->priv->selected_app_info)
160 161
    {
      if (!g_app_info_equal (self->priv->selected_app_info, info))
Matthias Clasen's avatar
Matthias Clasen committed
162 163
        {
          should_emit = TRUE;
164
          g_set_object (&self->priv->selected_app_info, info);
Matthias Clasen's avatar
Matthias Clasen committed
165
        }
166 167 168 169
    }
  else
    {
      should_emit = TRUE;
170
      g_set_object (&self->priv->selected_app_info, info);
171 172
    }

173 174
  g_object_unref (info);

175 176
  if (should_emit)
    g_signal_emit (self, signals[SIGNAL_APPLICATION_SELECTED], 0,
Matthias Clasen's avatar
Matthias Clasen committed
177
                   self->priv->selected_app_info);
178 179
}

180 181
static GAppInfo *
get_app_info_for_event (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
182
                        GdkEventButton      *event)
183 184 185 186 187 188 189 190
{
  GtkTreePath *path = NULL;
  GtkTreeIter iter;
  GtkTreeModel *model;
  GAppInfo *info;
  gboolean recommended;

  if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (self->priv->program_list),
Matthias Clasen's avatar
Matthias Clasen committed
191 192 193
                                      event->x, event->y,
                                      &path,
                                      NULL, NULL, NULL))
194 195 196 197 198 199 200 201 202 203 204 205
    return NULL;

  model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));

  if (!gtk_tree_model_get_iter (model, &iter, path))
    {
      gtk_tree_path_free (path);
      return NULL;
    }

  /* we only allow interaction with recommended applications */
  gtk_tree_model_get (model, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
206 207 208
                      COLUMN_APP_INFO, &info,
                      COLUMN_RECOMMENDED, &recommended,
                      -1);
209 210 211 212 213 214 215

  if (!recommended)
    g_clear_object (&info);

  return info;
}

216 217 218 219 220 221 222 223 224
static void
popup_menu_detach (GtkWidget *attach_widget,
                   GtkMenu   *menu)
{
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (attach_widget);

  self->priv->popup_menu = NULL;
}

225
static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
226 227 228
widget_button_press_event_cb (GtkWidget      *widget,
                              GdkEventButton *event,
                              gpointer        user_data)
229 230 231
{
  GtkAppChooserWidget *self = user_data;

232
  if (event->button == GDK_BUTTON_SECONDARY && event->type == GDK_BUTTON_PRESS)
233 234 235 236 237 238 239 240 241
    {
      GAppInfo *info;
      GtkWidget *menu;
      GList *children;
      gint n_children;

      info = get_app_info_for_event (self, event);

      if (info == NULL)
Matthias Clasen's avatar
Matthias Clasen committed
242
        return FALSE;
243

244 245 246 247
      if (self->priv->popup_menu)
        gtk_widget_destroy (self->priv->popup_menu);

      self->priv->popup_menu = menu = gtk_menu_new ();
Matthias Clasen's avatar
Matthias Clasen committed
248
      gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self), popup_menu_detach);
249

250
      g_signal_emit (self, signals[SIGNAL_POPULATE_POPUP], 0, menu, info);
251 252 253 254 255 256 257 258

      g_object_unref (info);

      /* see if clients added menu items to this container */
      children = gtk_container_get_children (GTK_CONTAINER (menu));
      n_children = g_list_length (children);

      if (n_children > 0)
259
        /* actually popup the menu */
260
        gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
261 262 263 264 265 266 267

      g_list_free (children);
    }

  return FALSE;
}

268 269
static gboolean
path_is_heading (GtkTreeView *view,
Matthias Clasen's avatar
Matthias Clasen committed
270
                 GtkTreePath *path)
271 272 273 274 275 276 277 278
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  gboolean res;

  model = gtk_tree_view_get_model (view);
  gtk_tree_model_get_iter (model, &iter, path);
  gtk_tree_model_get (model, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
279 280
                      COLUMN_HEADING, &res,
                      -1);
281 282 283 284

  return res;
}

285
static void
Matthias Clasen's avatar
Matthias Clasen committed
286 287 288 289
program_list_selection_activated (GtkTreeView       *view,
                                  GtkTreePath       *path,
                                  GtkTreeViewColumn *column,
                                  gpointer           user_data)
290
{
291
  GtkAppChooserWidget *self = user_data;
292 293
  GtkTreeSelection *selection;

294 295 296
  if (path_is_heading (view, path))
    return;

297 298 299 300 301
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));

  refresh_and_emit_app_selected (self, selection);

  g_signal_emit (self, signals[SIGNAL_APPLICATION_ACTIVATED], 0,
Matthias Clasen's avatar
Matthias Clasen committed
302
                 self->priv->selected_app_info);
303 304 305
}

static gboolean
306
gtk_app_chooser_search_equal_func (GtkTreeModel *model,
Matthias Clasen's avatar
Matthias Clasen committed
307 308 309 310
                                   gint          column,
                                   const gchar  *key,
                                   GtkTreeIter  *iter,
                                   gpointer      user_data)
311
{
312 313
  gchar *name;
  gchar *exec_name;
314 315 316 317 318 319 320
  gboolean ret;

  if (key != NULL)
    {
      ret = TRUE;

      gtk_tree_model_get (model, iter,
Matthias Clasen's avatar
Matthias Clasen committed
321
                          COLUMN_NAME, &name,
322
                          COLUMN_EXEC, &exec_name,
Matthias Clasen's avatar
Matthias Clasen committed
323
                          -1);
324

325 326 327
      if ((name != NULL && g_str_match_string (key, name, TRUE)) ||
          (exec_name != NULL && g_str_match_string (key, exec_name, FALSE)))
        ret = FALSE;
328 329

      g_free (name);
330
      g_free (exec_name);
331 332 333 334 335 336 337 338 339 340

      return ret;
    }
  else
    {
      return TRUE;
    }
}

static gint
341
gtk_app_chooser_sort_func (GtkTreeModel *model,
Matthias Clasen's avatar
Matthias Clasen committed
342 343 344
                           GtkTreeIter  *a,
                           GtkTreeIter  *b,
                           gpointer      user_data)
345 346
{
  gboolean a_recommended, b_recommended;
347
  gboolean a_fallback, b_fallback;
348
  gboolean a_heading, b_heading;
349
  gboolean a_default, b_default;
350
  gchar *a_name, *b_name, *a_casefold, *b_casefold;
351
  gint retval = 0;
352 353 354 355 356 357 358 359

  /* this returns:
   * - <0 if a should show before b
   * - =0 if a is the same as b
   * - >0 if a should show after b
   */

  gtk_tree_model_get (model, a,
Matthias Clasen's avatar
Matthias Clasen committed
360 361 362 363 364 365
                      COLUMN_NAME, &a_name,
                      COLUMN_RECOMMENDED, &a_recommended,
                      COLUMN_FALLBACK, &a_fallback,
                      COLUMN_HEADING, &a_heading,
                      COLUMN_DEFAULT, &a_default,
                      -1);
366 367

  gtk_tree_model_get (model, b,
Matthias Clasen's avatar
Matthias Clasen committed
368 369 370 371 372 373
                      COLUMN_NAME, &b_name,
                      COLUMN_RECOMMENDED, &b_recommended,
                      COLUMN_FALLBACK, &b_fallback,
                      COLUMN_HEADING, &b_heading,
                      COLUMN_DEFAULT, &b_default,
                      -1);
374

375 376 377 378 379 380 381 382 383 384 385 386 387
  /* the default one always wins */
  if (a_default && !b_default)
    {
      retval = -1;
      goto out;
    }

  if (b_default && !a_default)
    {
      retval = 1;
      goto out;
    }
  
388 389 390 391 392 393 394 395 396 397 398 399 400
  /* the recommended one always wins */
  if (a_recommended && !b_recommended)
    {
      retval = -1;
      goto out;
    }

  if (b_recommended && !a_recommended)
    {
      retval = 1;
      goto out;
    }

401 402 403 404 405 406 407 408 409 410 411 412 413
  /* the recommended one always wins */
  if (a_fallback && !b_fallback)
    {
      retval = -1;
      goto out;
    }

  if (b_fallback && !a_fallback)
    {
      retval = 1;
      goto out;
    }

414
  /* they're both recommended/fallback or not, so if one is a heading, wins */
415 416
  if (a_heading)
    {
417
      retval = -1;
418 419 420 421 422
      goto out;
    }

  if (b_heading)
    {
423
      retval = 1;
424 425 426
      goto out;
    }

427 428 429 430
  /* don't order by name recommended applications, but use GLib's ordering */
  if (!a_recommended)
    {
      a_casefold = a_name != NULL ?
Matthias Clasen's avatar
Matthias Clasen committed
431
        g_utf8_casefold (a_name, -1) : NULL;
432
      b_casefold = b_name != NULL ?
Matthias Clasen's avatar
Matthias Clasen committed
433
        g_utf8_casefold (b_name, -1) : NULL;
434

435
      retval = g_strcmp0 (a_casefold, b_casefold);
436

437 438 439
      g_free (a_casefold);
      g_free (b_casefold);
    }
440 441 442 443 444 445 446 447 448 449

 out:
  g_free (a_name);
  g_free (b_name);

  return retval;
}

static void
padding_cell_renderer_func (GtkTreeViewColumn *column,
Matthias Clasen's avatar
Matthias Clasen committed
450 451 452 453
                            GtkCellRenderer   *cell,
                            GtkTreeModel      *model,
                            GtkTreeIter       *iter,
                            gpointer           user_data)
454 455 456 457
{
  gboolean heading;

  gtk_tree_model_get (model, iter,
Matthias Clasen's avatar
Matthias Clasen committed
458 459
                      COLUMN_HEADING, &heading,
                      -1);
460 461
  if (heading)
    g_object_set (cell,
Matthias Clasen's avatar
Matthias Clasen committed
462 463 464 465
                  "visible", FALSE,
                  "xpad", 0,
                  "ypad", 0,
                  NULL);
466 467
  else
    g_object_set (cell,
Matthias Clasen's avatar
Matthias Clasen committed
468 469 470 471
                  "visible", TRUE,
                  "xpad", 3,
                  "ypad", 3,
                  NULL);
472 473 474
}

static gboolean
475
gtk_app_chooser_selection_func (GtkTreeSelection *selection,
Matthias Clasen's avatar
Matthias Clasen committed
476 477 478 479
                                GtkTreeModel     *model,
                                GtkTreePath      *path,
                                gboolean          path_currently_selected,
                                gpointer          user_data)
480 481 482 483 484 485
{
  GtkTreeIter iter;
  gboolean heading;

  gtk_tree_model_get_iter (model, &iter, path);
  gtk_tree_model_get (model, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
486 487
                      COLUMN_HEADING, &heading,
                      -1);
488 489 490 491 492 493

  return !heading;
}

static gint
compare_apps_func (gconstpointer a,
Matthias Clasen's avatar
Matthias Clasen committed
494
                   gconstpointer b)
495 496 497 498
{
  return !g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b));
}

499
static gboolean
500
gtk_app_chooser_widget_add_section (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
501 502 503 504 505 506
                                    const gchar         *heading_title,
                                    gboolean             show_headings,
                                    gboolean             recommended,
                                    gboolean             fallback,
                                    GList               *applications,
                                    GList               *exclude_apps)
507
{
508
  gboolean heading_added, unref_icon;
509 510 511 512 513
  GtkTreeIter iter;
  GAppInfo *app;
  gchar *app_string, *bold_string;
  GIcon *icon;
  GList *l;
514
  gboolean retval;
515

516
  retval = FALSE;
517
  heading_added = FALSE;
518
  bold_string = g_strdup_printf ("<b>%s</b>", heading_title);
519
  
520
  for (l = applications; l != NULL; l = l->next)
521
    {
522
      app = l->data;
523

524 525
      if (self->priv->content_type != NULL &&
          !g_app_info_supports_uris (app) &&
Matthias Clasen's avatar
Matthias Clasen committed
526
          !g_app_info_supports_files (app))
527 528
        continue;

Matthias Clasen's avatar
Matthias Clasen committed
529 530 531
      if (g_list_find_custom (exclude_apps, app,
                              (GCompareFunc) compare_apps_func))
        continue;
532

533
      if (!heading_added && show_headings)
Matthias Clasen's avatar
Matthias Clasen committed
534 535 536 537 538 539 540 541 542 543 544
        {
          gtk_list_store_append (self->priv->program_list_store, &iter);
          gtk_list_store_set (self->priv->program_list_store, &iter,
                              COLUMN_HEADING_TEXT, bold_string,
                              COLUMN_HEADING, TRUE,
                              COLUMN_RECOMMENDED, recommended,
                              COLUMN_FALLBACK, fallback,
                              -1);

          heading_added = TRUE;
        }
545

546 547 548
      app_string = g_markup_printf_escaped ("%s",
                                            g_app_info_get_name (app) != NULL ?
                                            g_app_info_get_name (app) : "");
549 550

      icon = g_app_info_get_icon (app);
551
      unref_icon = FALSE;
552
      if (icon == NULL)
Matthias Clasen's avatar
Matthias Clasen committed
553 554 555 556
        {
          icon = g_themed_icon_new ("application-x-executable");
          unref_icon = TRUE;
        }
557

558 559
      gtk_list_store_append (self->priv->program_list_store, &iter);
      gtk_list_store_set (self->priv->program_list_store, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
560 561
                          COLUMN_APP_INFO, app,
                          COLUMN_GICON, icon,
562
                          COLUMN_NAME, g_app_info_get_name (app),
Matthias Clasen's avatar
Matthias Clasen committed
563 564 565 566 567 568
                          COLUMN_DESC, app_string,
                          COLUMN_EXEC, g_app_info_get_executable (app),
                          COLUMN_HEADING, FALSE,
                          COLUMN_RECOMMENDED, recommended,
                          COLUMN_FALLBACK, fallback,
                          -1);
569

570 571
      retval = TRUE;

572 573
      g_free (app_string);
      if (unref_icon)
Matthias Clasen's avatar
Matthias Clasen committed
574
        g_object_unref (icon);
575 576
    }

577
  g_free (bold_string);
578 579

  return retval;
580
}
581

582 583

static void
584
gtk_app_chooser_add_default (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
585
                             GAppInfo            *app)
586 587 588 589 590 591 592 593 594 595 596
{
  GtkTreeIter iter;
  GIcon *icon;
  gchar *string;
  gboolean unref_icon;

  unref_icon = FALSE;
  string = g_strdup_printf ("<b>%s</b>", _("Default Application"));

  gtk_list_store_append (self->priv->program_list_store, &iter);
  gtk_list_store_set (self->priv->program_list_store, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
597 598 599 600
                      COLUMN_HEADING_TEXT, string,
                      COLUMN_HEADING, TRUE,
                      COLUMN_DEFAULT, TRUE,
                      -1);
601 602 603

  g_free (string);

604 605 606
  string = g_markup_printf_escaped ("%s",
                                    g_app_info_get_name (app) != NULL ?
                                    g_app_info_get_name (app) : "");
607 608 609 610 611 612 613 614 615 616

  icon = g_app_info_get_icon (app);
  if (icon == NULL)
    {
      icon = g_themed_icon_new ("application-x-executable");
      unref_icon = TRUE;
    }

  gtk_list_store_append (self->priv->program_list_store, &iter);
  gtk_list_store_set (self->priv->program_list_store, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
617 618
                      COLUMN_APP_INFO, app,
                      COLUMN_GICON, icon,
619
                      COLUMN_NAME, g_app_info_get_name (app),
Matthias Clasen's avatar
Matthias Clasen committed
620 621 622 623 624
                      COLUMN_DESC, string,
                      COLUMN_EXEC, g_app_info_get_executable (app),
                      COLUMN_HEADING, FALSE,
                      COLUMN_DEFAULT, TRUE,
                      -1);
625 626 627 628 629 630 631

  g_free (string);

  if (unref_icon)
    g_object_unref (icon);
}

632
static void
633
update_no_applications_label (GtkAppChooserWidget *self)
634
{
635
  gchar *text = NULL, *desc = NULL;
636
  const gchar *string;
637

638 639
  if (self->priv->default_text == NULL)
    {
640 641 642
      if (self->priv->content_type)
	desc = g_content_type_get_description (self->priv->content_type);

643
      string = text = g_strdup_printf (_("No applications found for “%s”."), desc);
644 645 646 647 648 649
      g_free (desc);
    }
  else
    {
      string = self->priv->default_text;
    }
650

651
  gtk_label_set_text (GTK_LABEL (self->priv->no_apps_label), string);
652

Matthias Clasen's avatar
Matthias Clasen committed
653
  g_free (text);
654
}
655

656
static void
657
gtk_app_chooser_widget_select_first (GtkAppChooserWidget *self)
658 659 660 661 662 663
{
  GtkTreeIter iter;
  GAppInfo *info = NULL;
  GtkTreeModel *model;

  model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));
664 665
  if (!gtk_tree_model_get_iter_first (model, &iter))
    return;
666 667 668 669

  while (info == NULL)
    {
      gtk_tree_model_get (model, &iter,
Matthias Clasen's avatar
Matthias Clasen committed
670 671
                          COLUMN_APP_INFO, &info,
                          -1);
672 673

      if (info != NULL)
Matthias Clasen's avatar
Matthias Clasen committed
674
        break;
675 676

      if (!gtk_tree_model_iter_next (model, &iter))
Matthias Clasen's avatar
Matthias Clasen committed
677
        break;
678 679 680 681 682 683 684 685 686 687 688 689 690
    }

  if (info != NULL)
    {
      GtkTreeSelection *selection;

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
      gtk_tree_selection_select_iter (selection, &iter);

      g_object_unref (info);
    }
}

691
static void
692
gtk_app_chooser_widget_real_add_items (GtkAppChooserWidget *self)
693
{
Matthias Clasen's avatar
Matthias Clasen committed
694 695 696
  GList *all_applications = NULL;
  GList *recommended_apps = NULL;
  GList *fallback_apps = NULL;
697 698
  GList *exclude_apps = NULL;
  GAppInfo *default_app = NULL;
699 700
  gboolean show_headings;
  gboolean apps_added;
701

702 703 704 705 706 707
  show_headings = TRUE;
  apps_added = FALSE;

  if (self->priv->show_all)
    show_headings = FALSE;

708
  if (self->priv->show_default && self->priv->content_type)
709 710 711 712
    {
      default_app = g_app_info_get_default_for_type (self->priv->content_type, FALSE);

      if (default_app != NULL)
Matthias Clasen's avatar
Matthias Clasen committed
713 714 715 716 717
        {
          gtk_app_chooser_add_default (self, default_app);
          apps_added = TRUE;
          exclude_apps = g_list_prepend (exclude_apps, default_app);
        }
718 719
    }

Fridrich Strba's avatar
Fridrich Strba committed
720
#ifndef G_OS_WIN32
721
  if ((self->priv->content_type && self->priv->show_recommended) || self->priv->show_all)
722
    {
723 724
      if (self->priv->content_type)
	recommended_apps = g_app_info_get_recommended_for_type (self->priv->content_type);
725

726
      apps_added |= gtk_app_chooser_widget_add_section (self, _("Recommended Applications"),
Matthias Clasen's avatar
Matthias Clasen committed
727 728 729 730
                                                        show_headings,
                                                        !self->priv->show_all, /* mark as recommended */
                                                        FALSE, /* mark as fallback */
                                                        recommended_apps, exclude_apps);
731 732

      exclude_apps = g_list_concat (exclude_apps,
Matthias Clasen's avatar
Matthias Clasen committed
733
                                    g_list_copy (recommended_apps));
734
    }
735

736
  if ((self->priv->content_type && self->priv->show_fallback) || self->priv->show_all)
737
    {
738 739
      if (self->priv->content_type)
	fallback_apps = g_app_info_get_fallback_for_type (self->priv->content_type);
740

741
      apps_added |= gtk_app_chooser_widget_add_section (self, _("Related Applications"),
Matthias Clasen's avatar
Matthias Clasen committed
742 743 744 745
                                                        show_headings,
                                                        FALSE, /* mark as recommended */
                                                        !self->priv->show_all, /* mark as fallback */
                                                        fallback_apps, exclude_apps);
746
      exclude_apps = g_list_concat (exclude_apps,
Matthias Clasen's avatar
Matthias Clasen committed
747
                                    g_list_copy (fallback_apps));
748
    }
Fridrich Strba's avatar
Fridrich Strba committed
749
#endif
750

751
  if (self->priv->show_other || self->priv->show_all)
752
    {
753 754
      all_applications = g_app_info_get_all ();

755
      apps_added |= gtk_app_chooser_widget_add_section (self, _("Other Applications"),
Matthias Clasen's avatar
Matthias Clasen committed
756 757 758 759
                                                        show_headings,
                                                        FALSE,
                                                        FALSE,
                                                        all_applications, exclude_apps);
760
    }
761

762
  if (!apps_added)
763 764 765
    update_no_applications_label (self);

  gtk_widget_set_visible (self->priv->no_apps, !apps_added);
766

767
  gtk_app_chooser_widget_select_first (self);
768 769 770 771

  if (default_app != NULL)
    g_object_unref (default_app);

772 773 774 775
  g_list_free_full (all_applications, g_object_unref);
  g_list_free_full (recommended_apps, g_object_unref);
  g_list_free_full (fallback_apps, g_object_unref);
  g_list_free (exclude_apps);
776 777 778
}

static void
779
gtk_app_chooser_widget_initialize_items (GtkAppChooserWidget *self)
780 781
{
  /* initial padding */
782
  g_object_set (self->priv->padding_renderer,
Matthias Clasen's avatar
Matthias Clasen committed
783 784
                "xpad", self->priv->show_all ? 0 : 6,
                NULL);
785 786

  /* populate the widget */
787
  gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
788 789
}

790 791 792 793 794 795 796
static void
app_info_changed (GAppInfoMonitor     *monitor,
                  GtkAppChooserWidget *self)
{
  gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
}

797
static void
Matthias Clasen's avatar
Matthias Clasen committed
798 799 800 801
gtk_app_chooser_widget_set_property (GObject      *object,
                                     guint         property_id,
                                     const GValue *value,
                                     GParamSpec   *pspec)
802
{
803
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
804 805 806 807 808 809

  switch (property_id)
    {
    case PROP_CONTENT_TYPE:
      self->priv->content_type = g_value_dup_string (value);
      break;
810
    case PROP_SHOW_DEFAULT:
811
      gtk_app_chooser_widget_set_show_default (self, g_value_get_boolean (value));
812
      break;
813
    case PROP_SHOW_RECOMMENDED:
814
      gtk_app_chooser_widget_set_show_recommended (self, g_value_get_boolean (value));
815 816
      break;
    case PROP_SHOW_FALLBACK:
817
      gtk_app_chooser_widget_set_show_fallback (self, g_value_get_boolean (value));
818 819
      break;
    case PROP_SHOW_OTHER:
820
      gtk_app_chooser_widget_set_show_other (self, g_value_get_boolean (value));
821 822
      break;
    case PROP_SHOW_ALL:
823
      gtk_app_chooser_widget_set_show_all (self, g_value_get_boolean (value));
824
      break;
825
    case PROP_DEFAULT_TEXT:
826
      gtk_app_chooser_widget_set_default_text (self, g_value_get_string (value));
827
      break;
828 829 830 831 832 833 834
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
835 836 837 838
gtk_app_chooser_widget_get_property (GObject    *object,
                                     guint       property_id,
                                     GValue     *value,
                                     GParamSpec *pspec)
839
{
840
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
841 842 843 844 845 846

  switch (property_id)
    {
    case PROP_CONTENT_TYPE:
      g_value_set_string (value, self->priv->content_type);
      break;
847 848 849
    case PROP_SHOW_DEFAULT:
      g_value_set_boolean (value, self->priv->show_default);
      break;
850 851 852 853 854 855 856 857 858 859 860 861
    case PROP_SHOW_RECOMMENDED:
      g_value_set_boolean (value, self->priv->show_recommended);
      break;
    case PROP_SHOW_FALLBACK:
      g_value_set_boolean (value, self->priv->show_fallback);
      break;
    case PROP_SHOW_OTHER:
      g_value_set_boolean (value, self->priv->show_other);
      break;
    case PROP_SHOW_ALL:
      g_value_set_boolean (value, self->priv->show_all);
      break;
862 863 864
    case PROP_DEFAULT_TEXT:
      g_value_set_string (value, self->priv->default_text);
      break;
865 866 867 868 869 870 871
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
872
gtk_app_chooser_widget_constructed (GObject *object)
873
{
874
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
875

876 877
  if (G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed != NULL)
    G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed (object);
878

879
  gtk_app_chooser_widget_initialize_items (self);
880 881 882
}

static void
883
gtk_app_chooser_widget_finalize (GObject *object)
884
{
885
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
886 887

  g_free (self->priv->content_type);
888
  g_free (self->priv->default_text);
889 890
  g_signal_handlers_disconnect_by_func (self->priv->monitor, app_info_changed, self);
  g_object_unref (self->priv->monitor);
891

892
  G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->finalize (object);
893 894 895
}

static void
896
gtk_app_chooser_widget_dispose (GObject *object)
897
{
898
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
899

900
  g_clear_object (&self->priv->selected_app_info);
901

902
  G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->dispose (object);
903 904 905
}

static void
906
gtk_app_chooser_widget_class_init (GtkAppChooserWidgetClass *klass)
907
{
908
  GtkWidgetClass *widget_class;
909 910 911 912
  GObjectClass *gobject_class;
  GParamSpec *pspec;

  gobject_class = G_OBJECT_CLASS (klass);
913 914 915 916 917
  gobject_class->dispose = gtk_app_chooser_widget_dispose;
  gobject_class->finalize = gtk_app_chooser_widget_finalize;
  gobject_class->set_property = gtk_app_chooser_widget_set_property;
  gobject_class->get_property = gtk_app_chooser_widget_get_property;
  gobject_class->constructed = gtk_app_chooser_widget_constructed;
918 919 920

  g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type");

Matthias Clasen's avatar
Matthias Clasen committed
921 922 923 924 925 926 927 928
  /**
   * GtkAppChooserWidget:show-default:
   *
   * The ::show-default property determines whether the app chooser
   * should show the default handler for the content type in a
   * separate section. If %FALSE, the default handler is listed
   * among the recommended applications.
   */
929
  pspec = g_param_spec_boolean ("show-default",
Matthias Clasen's avatar
Matthias Clasen committed
930 931 932
                                P_("Show default app"),
                                P_("Whether the widget should show the default application"),
                                FALSE,
933
                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
934 935
  g_object_class_install_property (gobject_class, PROP_SHOW_DEFAULT, pspec);

Matthias Clasen's avatar
Matthias Clasen committed
936 937 938
  /**
   * GtkAppChooserWidget:show-recommended:
   *
939 940 941 942
   * The #GtkAppChooserWidget:show-recommended property determines
   * whether the app chooser should show a section for recommended
   * applications. If %FALSE, the recommended applications are listed
   * among the other applications.
Matthias Clasen's avatar
Matthias Clasen committed
943
   */
944
  pspec = g_param_spec_boolean ("show-recommended",
Matthias Clasen's avatar
Matthias Clasen committed
945 946 947
                                P_("Show recommended apps"),
                                P_("Whether the widget should show recommended applications"),
                                TRUE,
948
                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
949 950
  g_object_class_install_property (gobject_class, PROP_SHOW_RECOMMENDED, pspec);

Matthias Clasen's avatar
Matthias Clasen committed
951 952 953
  /**
   * GtkAppChooserWidget:show-fallback:
   *
954 955 956 957
   * The #GtkAppChooserWidget:show-fallback property determines whether
   * the app chooser should show a section for fallback applications.
   * If %FALSE, the fallback applications are listed among the other
   * applications.
Matthias Clasen's avatar
Matthias Clasen committed
958
   */
959
  pspec = g_param_spec_boolean ("show-fallback",
Matthias Clasen's avatar
Matthias Clasen committed
960 961 962
                                P_("Show fallback apps"),
                                P_("Whether the widget should show fallback applications"),
                                FALSE,
963
                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
964 965
  g_object_class_install_property (gobject_class, PROP_SHOW_FALLBACK, pspec);

Matthias Clasen's avatar
Matthias Clasen committed
966 967 968
  /**
   * GtkAppChooserWidget:show-other:
   *
969 970
   * The #GtkAppChooserWidget:show-other property determines whether
   * the app chooser should show a section for other applications.
Matthias Clasen's avatar
Matthias Clasen committed
971
   */
972
  pspec = g_param_spec_boolean ("show-other",
Matthias Clasen's avatar
Matthias Clasen committed
973 974 975
                                P_("Show other apps"),
                                P_("Whether the widget should show other applications"),
                                FALSE,
976
                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
977 978
  g_object_class_install_property (gobject_class, PROP_SHOW_OTHER, pspec);

Matthias Clasen's avatar
Matthias Clasen committed
979 980 981
  /**
   * GtkAppChooserWidget:show-all:
   *
982 983 984
   * If the #GtkAppChooserWidget:show-all property is %TRUE, the app
   * chooser presents all applications in a single list, without
   * subsections for default, recommended or related applications.
Matthias Clasen's avatar
Matthias Clasen committed
985
   */
986
  pspec = g_param_spec_boolean ("show-all",
Matthias Clasen's avatar
Matthias Clasen committed
987 988 989
                                P_("Show all apps"),
                                P_("Whether the widget should show all applications"),
                                FALSE,
990
                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
991 992
  g_object_class_install_property (gobject_class, PROP_SHOW_ALL, pspec);

993 994 995
  /**
   * GtkAppChooserWidget:default-text:
   *
996 997 998
   * The #GtkAppChooserWidget:default-text property determines the text
   * that appears in the widget when there are no applications for the
   * given content type.
999 1000
   * See also gtk_app_chooser_widget_set_default_text().
   */
1001
  pspec = g_param_spec_string ("default-text",
Matthias Clasen's avatar
Matthias Clasen committed
1002 1003 1004
                               P_("Widget's default text"),
                               P_("The default text appearing when there are no applications"),
                               NULL,
1005
                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
1006 1007
  g_object_class_install_property (gobject_class, PROP_DEFAULT_TEXT, pspec);

Cosimo Cecchi's avatar
Cosimo Cecchi committed
1008 1009 1010 1011 1012 1013 1014
  /**
   * GtkAppChooserWidget::application-selected:
   * @self: the object which received the signal
   * @application: the selected #GAppInfo
   *
   * Emitted when an application item is selected from the widget's list.
   */
1015
  signals[SIGNAL_APPLICATION_SELECTED] =
1016
    g_signal_new (I_("application-selected"),
Matthias Clasen's avatar
Matthias Clasen committed
1017 1018 1019 1020
                  GTK_TYPE_APP_CHOOSER_WIDGET,
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_selected),
                  NULL, NULL,
1021
                  NULL,
Matthias Clasen's avatar
Matthias Clasen committed
1022 1023
                  G_TYPE_NONE,
                  1, G_TYPE_APP_INFO);
1024

Cosimo Cecchi's avatar
Cosimo Cecchi committed
1025 1026 1027 1028 1029 1030
  /**
   * GtkAppChooserWidget::application-activated:
   * @self: the object which received the signal
   * @application: the activated #GAppInfo
   *
   * Emitted when an application item is activated from the widget's list.
1031
   *
Cosimo Cecchi's avatar
Cosimo Cecchi committed
1032 1033 1034 1035
   * This usually happens when the user double clicks an item, or an item
   * is selected and the user presses one of the keys Space, Shift+Space,
   * Return or Enter.
   */
1036
  signals[SIGNAL_APPLICATION_ACTIVATED] =
1037
    g_signal_new (I_("application-activated"),
Matthias Clasen's avatar
Matthias Clasen committed
1038 1039 1040 1041
                  GTK_TYPE_APP_CHOOSER_WIDGET,
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_activated),
                  NULL, NULL,
1042
                  NULL,
Matthias Clasen's avatar
Matthias Clasen committed
1043 1044
                  G_TYPE_NONE,
                  1, G_TYPE_APP_INFO);
1045

Cosimo Cecchi's avatar
Cosimo Cecchi committed
1046 1047 1048 1049 1050 1051 1052 1053
  /**
   * GtkAppChooserWidget::populate-popup:
   * @self: the object which received the signal
   * @menu: the #GtkMenu to populate
   * @application: the current #GAppInfo
   *
   * Emitted when a context menu is about to popup over an application item.
   * Clients can insert menu items into the provided #GtkMenu object in the
1054 1055
   * callback of this signal; the context menu will be shown over the item
   * if at least one item has been added to the menu.
Cosimo Cecchi's avatar
Cosimo Cecchi committed
1056
   */
1057
  signals[SIGNAL_POPULATE_POPUP] =
1058
    g_signal_new (I_("populate-popup"),
Matthias Clasen's avatar
Matthias Clasen committed
1059 1060 1061 1062 1063 1064 1065
                  GTK_TYPE_APP_CHOOSER_WIDGET,
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkAppChooserWidgetClass, populate_popup),
                  NULL, NULL,
                  _gtk_marshal_VOID__OBJECT_OBJECT,
                  G_TYPE_NONE,
                  2, GTK_TYPE_MENU, G_TYPE_APP_INFO);
1066

1067 1068 1069 1070
  /* Bind class to template
   */
  widget_class = GTK_WIDGET_CLASS (klass);
  gtk_widget_class_set_template_from_resource (widget_class,
1071
					       "/org/gtk/libgtk/ui/gtkappchooserwidget.ui");
1072 1073 1074 1075 1076
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, program_list);
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, program_list_store);
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, column);
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, padding_renderer);
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, secondary_padding);
1077 1078
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, no_apps_label);
  gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserWidget, no_apps);
1079 1080 1081
  gtk_widget_class_bind_template_callback (widget_class, refresh_and_emit_app_selected);
  gtk_widget_class_bind_template_callback (widget_class, program_list_selection_activated);
  gtk_widget_class_bind_template_callback (widget_class, widget_button_press_event_cb);
1082 1083

  gtk_widget_class_set_css_name (widget_class, "appchooser");
1084 1085 1086
}

static void
1087
gtk_app_chooser_widget_init (GtkAppChooserWidget *self)
1088 1089
{
  GtkTreeSelection *selection;
1090
  GtkTreeModel *sort;
1091

1092
  self->priv = gtk_app_chooser_widget_get_instance_private (self);
1093

1094 1095 1096 1097 1098
  gtk_widget_init_template (GTK_WIDGET (self));

  /* Various parts of the GtkTreeView code need custom code to setup, mostly
   * because we lack signals to connect to, or properties to set.
   */
1099
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
1100
  gtk_tree_selection_set_select_function (selection, gtk_app_chooser_selection_func,
Matthias Clasen's avatar
Matthias Clasen committed
1101
                                          self, NULL);
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120

  sort = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));
  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort),
                                        COLUMN_NAME,
                                        GTK_SORT_ASCENDING);
  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort),
                                   COLUMN_NAME,
                                   gtk_app_chooser_sort_func,
                                   self, NULL);

  gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->priv->program_list), COLUMN_NAME);
  gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->priv->program_list),
                                       gtk_app_chooser_search_equal_func,
                                       NULL, NULL);

  gtk_tree_view_column_set_cell_data_func (self->priv->column,
					   self->priv->secondary_padding,
                                           padding_cell_renderer_func,
                                           NULL, NULL);
1121 1122 1123 1124

  self->priv->monitor = g_app_info_monitor_get ();
  g_signal_connect (self->priv->monitor, "changed",
		    G_CALLBACK (app_info_changed), self);
1125 1126 1127
}

static GAppInfo *
1128
gtk_app_chooser_widget_get_app_info (GtkAppChooser *object)
1129
{
1130
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
1131 1132 1133 1134 1135 1136 1137 1138

  if (self->priv->selected_app_info == NULL)
    return NULL;

  return g_object_ref (self->priv->selected_app_info);
}

static void
1139
gtk_app_chooser_widget_refresh (GtkAppChooser *object)
1140
{
1141
  GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
1142

1143 1144 1145 1146 1147 1148
  if (self->priv->program_list_store != NULL)
    {
      gtk_list_store_clear (self->priv->program_list_store);

      /* don't add additional xpad if we don't have headings */
      g_object_set (self->priv->padding_renderer,
Matthias Clasen's avatar
Matthias Clasen committed
1149 1150
                    "visible", !self->priv->show_all,
                    NULL);
1151

1152
      gtk_app_chooser_widget_real_add_items (self);
1153 1154 1155
    }
}

1156
static void
1157
gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface)
1158
{
1159 1160
  iface->get_app_info = gtk_app_chooser_widget_get_app_info;
  iface->refresh = gtk_app_chooser_widget_refresh;
1161 1162
}

Matthias Clasen's avatar
Matthias Clasen committed
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
/**
 * gtk_app_chooser_widget_new:
 * @content_type: the content type to show applications for
 *
 * Creates a new #GtkAppChooserWidget for applications
 * that can handle content of the given type.
 *
 * Returns: a newly created #GtkAppChooserWidget
 *
 * Since: 3.0
 */
1174
GtkWidget *
1175
gtk_app_chooser_widget_new (const gchar *content_type)
1176
{
1177
  return g_object_new (GTK_TYPE_APP_CHOOSER_WIDGET,
Matthias Clasen's avatar
Matthias Clasen committed
1178 1179
                       "content-type", content_type,
                       NULL);
1180 1181
}

Matthias Clasen's avatar
Matthias Clasen committed
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
/**
 * gtk_app_chooser_widget_set_show_default:
 * @self: a #GtkAppChooserWidget
 * @setting: the new value for #GtkAppChooserWidget:show-default
 *
 * Sets whether the app chooser should show the default handler
 * for the content type in a separate section.
 *
 * Since: 3.0
 */
1192
void
1193
gtk_app_chooser_widget_set_show_default (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
1194
                                         gboolean             setting)
1195
{
1196
  g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1197 1198 1199 1200 1201 1202 1203

  if (self->priv->show_default != setting)
    {
      self->priv->show_default = setting;

      g_object_notify (G_OBJECT (self), "show-default");

1204
      gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1205 1206 1207
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
/**
 * gtk_app_chooser_widget_get_show_default:
 * @self: a #GtkAppChooserWidget
 *
 * Returns the current value of the #GtkAppChooserWidget:show-default
 * property.
 *
 * Returns: the value of #GtkAppChooserWidget:show-default
 *
 * Since: 3.0
 */
1219
gboolean
1220
gtk_app_chooser_widget_get_show_default (GtkAppChooserWidget *self)
1221
{
1222
  g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1223 1224 1225 1226

  return self->priv->show_default;
}

Matthias Clasen's avatar
Matthias Clasen committed
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
/**
 * gtk_app_chooser_widget_set_show_recommended:
 * @self: a #GtkAppChooserWidget
 * @setting: the new value for #GtkAppChooserWidget:show-recommended
 *
 * Sets whether the app chooser should show recommended applications
 * for the content type in a separate section.
 *
 * Since: 3.0
 */
1237
void
1238
gtk_app_chooser_widget_set_show_recommended (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
1239
                                             gboolean             setting)
1240
{
1241
  g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1242

1243
  if (self->priv->show_recommended != setting)
1244
    {
1245 1246 1247
      self->priv->show_recommended = setting;

      g_object_notify (G_OBJECT (self), "show-recommended");
1248

1249
      gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1250 1251 1252
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
/**
 * gtk_app_chooser_widget_get_show_recommended:
 * @self: a #GtkAppChooserWidget
 *
 * Returns the current value of the #GtkAppChooserWidget:show-recommended
 * property.
 *
 * Returns: the value of #GtkAppChooserWidget:show-recommended
 *
 * Since: 3.0
 */
1264
gboolean
1265
gtk_app_chooser_widget_get_show_recommended (GtkAppChooserWidget *self)
1266
{
1267
  g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1268 1269 1270 1271

  return self->priv->show_recommended;
}

Matthias Clasen's avatar
Matthias Clasen committed
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
/**
 * gtk_app_chooser_widget_set_show_fallback:
 * @self: a #GtkAppChooserWidget
 * @setting: the new value for #GtkAppChooserWidget:show-fallback
 *
 * Sets whether the app chooser should show related applications
 * for the content type in a separate section.
 *
 * Since: 3.0
 */
1282
void
1283
gtk_app_chooser_widget_set_show_fallback (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
1284
                                          gboolean             setting)
1285
{
1286
  g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1287 1288 1289 1290 1291 1292 1293

  if (self->priv->show_fallback != setting)
    {
      self->priv->show_fallback = setting;

      g_object_notify (G_OBJECT (self), "show-fallback");

1294
      gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1295 1296 1297
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308
/**
 * gtk_app_chooser_widget_get_show_fallback:
 * @self: a #GtkAppChooserWidget
 *
 * Returns the current value of the #GtkAppChooserWidget:show-fallback
 * property.
 *
 * Returns: the value of #GtkAppChooserWidget:show-fallback
 *
 * Since: 3.0
 */
1309
gboolean
1310
gtk_app_chooser_widget_get_show_fallback (GtkAppChooserWidget *self)
1311
{
1312
  g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1313 1314 1315 1316

  return self->priv->show_fallback;
}

Matthias Clasen's avatar
Matthias Clasen committed
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
/**
 * gtk_app_chooser_widget_set_show_other:
 * @self: a #GtkAppChooserWidget
 * @setting: the new value for #GtkAppChooserWidget:show-other
 *
 * Sets whether the app chooser should show applications
 * which are unrelated to the content type.
 *
 * Since: 3.0
 */
1327
void
1328
gtk_app_chooser_widget_set_show_other (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
1329
                                       gboolean             setting)
1330
{
1331
  g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1332 1333 1334 1335 1336 1337 1338

  if (self->priv->show_other != setting)
    {
      self->priv->show_other = setting;

      g_object_notify (G_OBJECT (self), "show-other");

1339
      gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1340 1341 1342
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
/**
 * gtk_app_chooser_widget_get_show_other:
 * @self: a #GtkAppChooserWidget
 *
 * Returns the current value of the #GtkAppChooserWidget:show-other
 * property.
 *
 * Returns: the value of #GtkAppChooserWidget:show-other
 *
 * Since: 3.0
 */
gboolean
gtk_app_chooser_widget_get_show_other (GtkAppChooserWidget *self)
1356
{
1357
  g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1358 1359 1360 1361

  return self->priv->show_other;
}

Matthias Clasen's avatar
Matthias Clasen committed
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
/**
 * gtk_app_chooser_widget_set_show_all:
 * @self: a #GtkAppChooserWidget
 * @setting: the new value for #GtkAppChooserWidget:show-all
 *
 * Sets whether the app chooser should show all applications
 * in a flat list.
 *
 * Since: 3.0
 */
1372
void
1373
gtk_app_chooser_widget_set_show_all (GtkAppChooserWidget *self,
Matthias Clasen's avatar
Matthias Clasen committed
1374
                                     gboolean             setting)
1375
{
1376
  g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1377 1378 1379 1380 1381 <