gtkrecentchoosermenu.c 38.7 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3 4 5 6 7 8 9 10 11 12 13 14 15
 * gtkrecentchoosermenu.c - Recently used items menu widget
 * Copyright (C) 2005, Emmanuele Bassi
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 18 19 20 21 22
 */

#include "config.h"

#include <string.h>

23 24 25 26 27 28 29
#include "gtkrecentmanager.h"
#include "gtkrecentfilter.h"
#include "gtkrecentchooser.h"
#include "gtkrecentchooserutils.h"
#include "gtkrecentchooserprivate.h"
#include "gtkrecentchoosermenu.h"

30 31 32 33 34
#include "gtkicontheme.h"
#include "gtkintl.h"
#include "gtksettings.h"
#include "gtkmenushell.h"
#include "gtkmenuitem.h"
35
#include "deprecated/gtkimagemenuitem.h"
36 37 38
#include "gtkseparatormenuitem.h"
#include "gtkmenu.h"
#include "gtkimage.h"
39
#include "gtklabel.h"
40
#include "gtktooltip.h"
41
#include "deprecated/gtkactivatable.h"
42
#include "gtktypebuiltins.h"
43
#include "gtkprivate.h"
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

/**
 * SECTION:gtkrecentchoosermenu
 * @Short_description: Displays recently used files in a menu
 * @Title: GtkRecentChooserMenu
 * @See_also:#GtkRecentChooser
 *
 * #GtkRecentChooserMenu is a widget suitable for displaying recently used files
 * inside a menu.  It can be used to set a sub-menu of a #GtkMenuItem using
 * gtk_menu_item_set_submenu(), or as the menu of a #GtkMenuToolButton.
 *
 * Note that #GtkRecentChooserMenu does not have any methods of its own. Instead,
 * you should use the functions that work on a #GtkRecentChooser.
 *
 * Note also that #GtkRecentChooserMenu does not support multiple filters, as it
 * has no way to let the user choose between them as the #GtkRecentChooserWidget
 * and #GtkRecentChooserDialog widgets do. Thus using gtk_recent_chooser_add_filter()
 * on a #GtkRecentChooserMenu widget will yield the same effects as using
 * gtk_recent_chooser_set_filter(), replacing any currently set filter
 * with the supplied filter; gtk_recent_chooser_remove_filter() will remove
 * any currently set #GtkRecentFilter object and will unset the current filter;
 * gtk_recent_chooser_list_filters() will return a list containing a single
 * #GtkRecentFilter object.
 *
 * Recently used files are supported since GTK+ 2.10.
 */


72 73 74 75 76
struct _GtkRecentChooserMenuPrivate
{
  /* the recent manager object */
  GtkRecentManager *manager;
  
77 78 79
  /* max size of the menu item label */
  gint label_width;

80 81 82
  gint first_recent_item_pos;
  GtkWidget *placeholder;

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
  /* RecentChooser properties */
  gint limit;  
  guint show_private : 1;
  guint show_not_found : 1;
  guint show_tips : 1;
  guint show_icons : 1;
  guint local_only : 1;
  
  guint show_numbers : 1;
  
  GtkRecentSortType sort_type;
  GtkRecentSortFunc sort_func;
  gpointer sort_data;
  GDestroyNotify sort_data_destroy;
  
  GSList *filters;
  GtkRecentFilter *current_filter;
 
  guint local_manager : 1;
  gulong manager_changed_id;

104
  gulong populate_id;
105 106 107 108
};

enum {
  PROP_0,
109
  PROP_SHOW_NUMBERS,
110

111 112 113
  /* activatable properties */
  PROP_ACTIVATABLE_RELATED_ACTION,
  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
114 115
};

116

117
#define FALLBACK_ITEM_LIMIT 	10
118
#define DEFAULT_LABEL_WIDTH     30
119 120 121


static void     gtk_recent_chooser_menu_finalize    (GObject                   *object);
122
static void     gtk_recent_chooser_menu_dispose     (GObject                   *object);
123
static void     gtk_recent_chooser_menu_constructed (GObject                   *object);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

static void gtk_recent_chooser_iface_init      (GtkRecentChooserIface     *iface);

static void gtk_recent_chooser_menu_set_property (GObject      *object,
						  guint         prop_id,
						  const GValue *value,
						  GParamSpec   *pspec);
static void gtk_recent_chooser_menu_get_property (GObject      *object,
						  guint         prop_id,
						  GValue       *value,
						  GParamSpec   *pspec);

static gboolean          gtk_recent_chooser_menu_set_current_uri    (GtkRecentChooser  *chooser,
							             const gchar       *uri,
							             GError           **error);
static gchar *           gtk_recent_chooser_menu_get_current_uri    (GtkRecentChooser  *chooser);
static gboolean          gtk_recent_chooser_menu_select_uri         (GtkRecentChooser  *chooser,
								     const gchar       *uri,
								     GError           **error);
static void              gtk_recent_chooser_menu_unselect_uri       (GtkRecentChooser  *chooser,
								     const gchar       *uri);
static void              gtk_recent_chooser_menu_select_all         (GtkRecentChooser  *chooser);
static void              gtk_recent_chooser_menu_unselect_all       (GtkRecentChooser  *chooser);
static GList *           gtk_recent_chooser_menu_get_items          (GtkRecentChooser  *chooser);
static GtkRecentManager *gtk_recent_chooser_menu_get_recent_manager (GtkRecentChooser  *chooser);
static void              gtk_recent_chooser_menu_set_sort_func      (GtkRecentChooser  *chooser,
								     GtkRecentSortFunc  sort_func,
								     gpointer           sort_data,
								     GDestroyNotify     data_destroy);
static void              gtk_recent_chooser_menu_add_filter         (GtkRecentChooser  *chooser,
								     GtkRecentFilter   *filter);
static void              gtk_recent_chooser_menu_remove_filter      (GtkRecentChooser  *chooser,
								     GtkRecentFilter   *filter);
static GSList *          gtk_recent_chooser_menu_list_filters       (GtkRecentChooser  *chooser);
static void              gtk_recent_chooser_menu_set_current_filter (GtkRecentChooserMenu *menu,
								     GtkRecentFilter      *filter);

161
static void              gtk_recent_chooser_menu_populate           (GtkRecentChooserMenu *menu);
162 163 164 165 166 167 168 169 170 171 172
static void              gtk_recent_chooser_menu_set_show_tips      (GtkRecentChooserMenu *menu,
								     gboolean              show_tips);

static void     set_recent_manager (GtkRecentChooserMenu *menu,
				    GtkRecentManager     *manager);

static void     item_activate_cb   (GtkWidget        *widget,
			            gpointer          user_data);
static void     manager_changed_cb (GtkRecentManager *manager,
				    gpointer          user_data);

173
static void gtk_recent_chooser_activatable_iface_init (GtkActivatableIface  *iface);
174
static void gtk_recent_chooser_update                 (GtkActivatable       *activatable,
175 176
						       GtkAction            *action,
						       const gchar          *property_name);
177
static void gtk_recent_chooser_sync_action_properties (GtkActivatable       *activatable,
178 179
						       GtkAction            *action);

180
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
181 182 183
G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserMenu,
			 gtk_recent_chooser_menu,
			 GTK_TYPE_MENU,
184
                         G_ADD_PRIVATE (GtkRecentChooserMenu)
185
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
186 187 188
				 		gtk_recent_chooser_iface_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
				 		gtk_recent_chooser_activatable_iface_init))
189
G_GNUC_END_IGNORE_DEPRECATIONS;
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

static void
gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
{
  iface->set_current_uri = gtk_recent_chooser_menu_set_current_uri;
  iface->get_current_uri = gtk_recent_chooser_menu_get_current_uri;
  iface->select_uri = gtk_recent_chooser_menu_select_uri;
  iface->unselect_uri = gtk_recent_chooser_menu_unselect_uri;
  iface->select_all = gtk_recent_chooser_menu_select_all;
  iface->unselect_all = gtk_recent_chooser_menu_unselect_all;
  iface->get_items = gtk_recent_chooser_menu_get_items;
  iface->get_recent_manager = gtk_recent_chooser_menu_get_recent_manager;
  iface->set_sort_func = gtk_recent_chooser_menu_set_sort_func;
  iface->add_filter = gtk_recent_chooser_menu_add_filter;
  iface->remove_filter = gtk_recent_chooser_menu_remove_filter;
  iface->list_filters = gtk_recent_chooser_menu_list_filters;
}

208 209 210 211 212
static void
gtk_recent_chooser_activatable_iface_init (GtkActivatableIface *iface)
{
  iface->update = gtk_recent_chooser_update;
  iface->sync_action_properties = gtk_recent_chooser_sync_action_properties;
213 214
}

215 216 217 218
static void
gtk_recent_chooser_menu_class_init (GtkRecentChooserMenuClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
219

220
  gobject_class->constructed = gtk_recent_chooser_menu_constructed;
221
  gobject_class->dispose = gtk_recent_chooser_menu_dispose;
222 223 224 225 226 227 228
  gobject_class->finalize = gtk_recent_chooser_menu_finalize;
  gobject_class->set_property = gtk_recent_chooser_menu_set_property;
  gobject_class->get_property = gtk_recent_chooser_menu_get_property;

  _gtk_recent_chooser_install_properties (gobject_class);

  /**
229
   * GtkRecentChooserMenu:show-numbers:
230 231 232 233 234 235 236 237 238 239 240 241
   *
   * Whether the first ten items in the menu should be prepended by
   * a number acting as a unique mnemonic.
   *
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
		  		   PROP_SHOW_NUMBERS,
				   g_param_spec_boolean ("show-numbers",
							 P_("Show Numbers"),
							 P_("Whether the items should be displayed with a number"),
							 FALSE,
242
							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
243
  
244 245 246

  g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
  g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
247 248 249 250 251 252
}

static void
gtk_recent_chooser_menu_init (GtkRecentChooserMenu *menu)
{
  GtkRecentChooserMenuPrivate *priv;
253

254
  menu->priv = gtk_recent_chooser_menu_get_instance_private (menu);
255 256
  priv = menu->priv;

257 258 259
  priv->show_icons= TRUE;
  priv->show_numbers = FALSE;
  priv->show_tips = FALSE;
260
  priv->show_not_found = TRUE;
261 262 263 264 265
  priv->show_private = FALSE;
  priv->local_only = TRUE;
  
  priv->limit = FALLBACK_ITEM_LIMIT;
  priv->sort_type = GTK_RECENT_SORT_NONE;
266 267
  priv->label_width = DEFAULT_LABEL_WIDTH;
  
268 269 270
  priv->first_recent_item_pos = -1;
  priv->placeholder = NULL;

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
  priv->current_filter = NULL;
}

static void
gtk_recent_chooser_menu_finalize (GObject *object)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
  GtkRecentChooserMenuPrivate *priv = menu->priv;
  
  priv->manager = NULL;
  
  if (priv->sort_data_destroy)
    {
      priv->sort_data_destroy (priv->sort_data);
      priv->sort_data_destroy = NULL;
    }
287 288 289

  priv->sort_data = NULL;
  priv->sort_func = NULL;
290
  
291 292 293 294 295 296 297 298 299 300 301
  G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->finalize (object);
}

static void
gtk_recent_chooser_menu_dispose (GObject *object)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
  GtkRecentChooserMenuPrivate *priv = menu->priv;

  if (priv->manager_changed_id)
    {
302 303 304
      if (priv->manager)
        g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);

305 306
      priv->manager_changed_id = 0;
    }
307
  
308 309 310 311 312 313
  if (priv->populate_id)
    {
      g_source_remove (priv->populate_id);
      priv->populate_id = 0;
    }

314
  if (priv->current_filter)
315 316 317 318
    {
      g_object_unref (priv->current_filter);
      priv->current_filter = NULL;
    }
319
  
320
  G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->dispose (object);
321 322
}

323 324
static void
gtk_recent_chooser_menu_constructed (GObject *object)
325
{
326 327 328 329
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
  GtkRecentChooserMenuPrivate *priv = menu->priv;

  G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->constructed (object);
330
  
331 332
  g_assert (priv->manager);

333 334
  /* we create a placeholder menuitem, to be used in case
   * the menu is empty. this placeholder will stay around
335 336 337 338
   * for the entire lifetime of the menu, and we just hide it
   * when it's not used. we have to do this, and do it here,
   * because we need a marker for the beginning of the recent
   * items list, so that we can insert the new items at the
339 340 341
   * right place when idly populating the menu in case the
   * user appended or prepended custom menu items to the
   * recent chooser menu widget.
342 343 344 345 346 347 348 349
   */
  priv->placeholder = gtk_menu_item_new_with_label (_("No items found"));
  gtk_widget_set_sensitive (priv->placeholder, FALSE);
  g_object_set_data (G_OBJECT (priv->placeholder),
                     "gtk-recent-menu-placeholder",
                     GINT_TO_POINTER (TRUE));

  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), priv->placeholder, 0);
350
  gtk_widget_set_no_show_all (priv->placeholder, TRUE);
351 352 353 354
  gtk_widget_show (priv->placeholder);

  /* (re)populate the menu */
  gtk_recent_chooser_menu_populate (menu);
355 356 357 358 359 360 361 362 363
}

static void
gtk_recent_chooser_menu_set_property (GObject      *object,
				      guint         prop_id,
				      const GValue *value,
				      GParamSpec   *pspec)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
364
  GtkRecentChooserMenuPrivate *priv = menu->priv;
365 366 367 368
  
  switch (prop_id)
    {
    case PROP_SHOW_NUMBERS:
369 370 371 372 373
      if (priv->show_numbers != g_value_get_boolean (value))
        {
          priv->show_numbers = g_value_get_boolean (value);
          g_object_notify_by_pspec (object, pspec);
        }
374 375 376 377 378
      break;
    case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
      set_recent_manager (menu, g_value_get_object (value));
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
379 380 381 382 383
      if (priv->show_private != g_value_get_boolean (value))
        {
          priv->show_private = g_value_get_boolean (value);
          g_object_notify_by_pspec (object, pspec);
        }
384 385
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
386 387 388 389 390
      if (priv->show_not_found != g_value_get_boolean (value))
        {
          priv->show_not_found = g_value_get_boolean (value);
          g_object_notify_by_pspec (object, pspec);
        }
391 392 393 394 395
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
      gtk_recent_chooser_menu_set_show_tips (menu, g_value_get_boolean (value));
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
396 397 398 399 400
      if (priv->show_icons != g_value_get_boolean (value))
        {
          priv->show_icons = g_value_get_boolean (value);
          g_object_notify_by_pspec (object, pspec);
        }
401 402
      break;
    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
403
      g_warning ("%s: Choosers of type '%s' do not support selecting multiple items.",
404 405 406 407
                 G_STRFUNC,
                 G_OBJECT_TYPE_NAME (object));
      break;
    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
408 409 410 411 412
      if (priv->local_only != g_value_get_boolean (value))
        {
          priv->local_only = g_value_get_boolean (value);
          g_object_notify_by_pspec (object, pspec);
        }
413 414
      break;
    case GTK_RECENT_CHOOSER_PROP_LIMIT:
415 416 417 418 419
      if (priv->limit != g_value_get_int (value))
        {
          priv->limit = g_value_get_int (value);
          g_object_notify_by_pspec (object, pspec);
        }
420 421
      break;
    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
422 423 424 425 426
      if (priv->sort_type != g_value_get_enum (value))
        {
          priv->sort_type = g_value_get_enum (value);
          g_object_notify_by_pspec (object, pspec);
        } 
427 428 429 430
      break;
    case GTK_RECENT_CHOOSER_PROP_FILTER:
      gtk_recent_chooser_menu_set_current_filter (menu, g_value_get_object (value));
      break;
431 432 433 434 435 436
    case PROP_ACTIVATABLE_RELATED_ACTION:
      _gtk_recent_chooser_set_related_action (GTK_RECENT_CHOOSER (menu), g_value_get_object (value));
      break;
    case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: 
      _gtk_recent_chooser_set_use_action_appearance (GTK_RECENT_CHOOSER (menu), g_value_get_boolean (value));
      break;
437 438 439 440 441 442 443 444 445 446 447 448 449
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_recent_chooser_menu_get_property (GObject    *object,
				      guint       prop_id,
				      GValue     *value,
				      GParamSpec *pspec)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
450
  GtkRecentChooserMenuPrivate *priv = menu->priv;
451 452 453 454
  
  switch (prop_id)
    {
    case PROP_SHOW_NUMBERS:
455
      g_value_set_boolean (value, priv->show_numbers);
456 457
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
458
      g_value_set_boolean (value, priv->show_tips);
459 460
      break;
    case GTK_RECENT_CHOOSER_PROP_LIMIT:
461
      g_value_set_int (value, priv->limit);
462 463
      break;
    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
464
      g_value_set_boolean (value, priv->local_only);
465
      break;
466
    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
467
      g_value_set_enum (value, priv->sort_type);
468 469
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
470
      g_value_set_boolean (value, priv->show_private);
471 472
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
473
      g_value_set_boolean (value, priv->show_not_found);
474 475
      break;
    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
476
      g_value_set_boolean (value, priv->show_icons);
477 478
      break;
    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
479
      g_value_set_boolean (value, FALSE);
480 481
      break;
    case GTK_RECENT_CHOOSER_PROP_FILTER:
482
      g_value_set_object (value, priv->current_filter);
483
      break;
484 485 486 487 488 489
    case PROP_ACTIVATABLE_RELATED_ACTION:
      g_value_set_object (value, _gtk_recent_chooser_get_related_action (GTK_RECENT_CHOOSER (menu)));
      break;
    case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: 
      g_value_set_boolean (value, _gtk_recent_chooser_get_use_action_appearance (GTK_RECENT_CHOOSER (menu)));
      break;
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static gboolean
gtk_recent_chooser_menu_set_current_uri (GtkRecentChooser  *chooser,
					 const gchar       *uri,
					 GError           **error)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
  GList *children, *l;
  GtkWidget *menu_item = NULL;
  gboolean found = FALSE;
505
  gint i = 0;
506 507
  
  children = gtk_container_get_children (GTK_CONTAINER (menu));
508
  
509
  for (l = children; l != NULL; l = l->next, i++)
510 511 512 513 514 515 516 517 518
    {
      GtkRecentInfo *info;
      
      menu_item = GTK_WIDGET (l->data);
      
      info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
      if (!info)
        continue;
      
519 520
      if (strcmp (uri, gtk_recent_info_get_uri (info)) == 0)
        {
521
          gtk_menu_set_active (GTK_MENU (menu), i);
522 523 524 525
	  found = TRUE;

	  break;
	}
526 527 528 529 530 531 532 533
    }

  g_list_free (children);
  
  if (!found)  
    {
      g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
      		   GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
534
                   _("No recently used resource found with URI '%s'"),
535 536
      		   uri);
    }
537 538
  
  return found;
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
}

static gchar *
gtk_recent_chooser_menu_get_current_uri (GtkRecentChooser  *chooser)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
  GtkWidget *menu_item;
  GtkRecentInfo *info;
  
  menu_item = gtk_menu_get_active (GTK_MENU (menu));
  if (!menu_item)
    return NULL;
  
  info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
  if (!info)
    return NULL;
  
  return g_strdup (gtk_recent_info_get_uri (info));
}

static gboolean
gtk_recent_chooser_menu_select_uri (GtkRecentChooser  *chooser,
				    const gchar       *uri,
				    GError           **error)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
  GList *children, *l;
  GtkWidget *menu_item = NULL;
  gboolean found = FALSE;
  
  children = gtk_container_get_children (GTK_CONTAINER (menu));
  for (l = children; l != NULL; l = l->next)
    {
      GtkRecentInfo *info;
      
      menu_item = GTK_WIDGET (l->data);
      
      info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
      if (!info)
        continue;
      
      if (0 == strcmp (uri, gtk_recent_info_get_uri (info)))
        found = TRUE;
    }

  g_list_free (children);
  
  if (!found)  
    {
      g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
      		   GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
590
                   _("No recently used resource found with URI '%s'"),
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
      		   uri);
      return FALSE;
    }
  else
    {
      gtk_menu_shell_select_item (GTK_MENU_SHELL (menu), menu_item);

      return TRUE;
    }
}

static void
gtk_recent_chooser_menu_unselect_uri (GtkRecentChooser *chooser,
				       const gchar     *uri)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
  
  gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
}

static void
gtk_recent_chooser_menu_select_all (GtkRecentChooser *chooser)
{
614 615
  g_warning ("This function is not implemented for "
	     "widgets of class '%s'",
616 617 618 619 620 621
             g_type_name (G_OBJECT_TYPE (chooser)));
}

static void
gtk_recent_chooser_menu_unselect_all (GtkRecentChooser *chooser)
{
622 623
  g_warning ("This function is not implemented for "
	     "widgets of class '%s'",
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
             g_type_name (G_OBJECT_TYPE (chooser)));
}

static void
gtk_recent_chooser_menu_set_sort_func (GtkRecentChooser  *chooser,
				       GtkRecentSortFunc  sort_func,
				       gpointer           sort_data,
				       GDestroyNotify     data_destroy)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
  GtkRecentChooserMenuPrivate *priv = menu->priv;
  
  if (priv->sort_data_destroy)
    {
      priv->sort_data_destroy (priv->sort_data);
      priv->sort_data_destroy = NULL;
    }
641 642 643 644
      
  priv->sort_func = NULL;
  priv->sort_data = NULL;
  priv->sort_data_destroy = NULL;
645 646 647 648 649 650 651 652 653 654 655 656 657
  
  if (sort_func)
    {
      priv->sort_func = sort_func;
      priv->sort_data = sort_data;
      priv->sort_data_destroy = data_destroy;
    }
}

static GList *
gtk_recent_chooser_menu_get_items (GtkRecentChooser *chooser)
{
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
658
  GtkRecentChooserMenuPrivate *priv = menu->priv;
659

660
  return _gtk_recent_chooser_get_items (chooser,
661
                                        priv->current_filter,
662 663
                                        priv->sort_func,
                                        priv->sort_data);
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
}

static GtkRecentManager *
gtk_recent_chooser_menu_get_recent_manager (GtkRecentChooser *chooser)
{
  GtkRecentChooserMenuPrivate *priv;
 
  priv = GTK_RECENT_CHOOSER_MENU (chooser)->priv;
  
  return priv->manager;
}

static void
gtk_recent_chooser_menu_add_filter (GtkRecentChooser *chooser,
				    GtkRecentFilter  *filter)
{
680 681 682 683 684
  GtkRecentChooserMenu *menu;

  menu = GTK_RECENT_CHOOSER_MENU (chooser);
  
  gtk_recent_chooser_menu_set_current_filter (menu, filter);
685 686 687 688 689 690
}

static void
gtk_recent_chooser_menu_remove_filter (GtkRecentChooser *chooser,
				       GtkRecentFilter  *filter)
{
691 692 693 694 695 696 697 698 699 700 701
  GtkRecentChooserMenu *menu;

  menu = GTK_RECENT_CHOOSER_MENU (chooser);
  
  if (filter == menu->priv->current_filter)
    {
      g_object_unref (menu->priv->current_filter);
      menu->priv->current_filter = NULL;

      g_object_notify (G_OBJECT (menu), "filter");
    }
702 703 704
}

static GSList *
Matthias Clasen's avatar
Matthias Clasen committed
705
gtk_recent_chooser_menu_list_filters (GtkRecentChooser *chooser)
706
{
707 708 709
  GtkRecentChooserMenu *menu;
  GSList *retval = NULL;

Matthias Clasen's avatar
Matthias Clasen committed
710 711
  menu = GTK_RECENT_CHOOSER_MENU (chooser);
 
712 713
  if (menu->priv->current_filter)
    retval = g_slist_prepend (retval, menu->priv->current_filter);
714

715
  return retval;
716 717 718 719 720 721 722 723 724 725 726 727 728
}

static void
gtk_recent_chooser_menu_set_current_filter (GtkRecentChooserMenu *menu,
					    GtkRecentFilter      *filter)
{
  GtkRecentChooserMenuPrivate *priv;

  priv = menu->priv;
  
  if (priv->current_filter)
    g_object_unref (G_OBJECT (priv->current_filter));
  
729 730 731 732
  priv->current_filter = filter;

  if (priv->current_filter)
    g_object_ref_sink (priv->current_filter);
733

734
  gtk_recent_chooser_menu_populate (menu);
735
  
736
  g_object_notify (G_OBJECT (menu), "filter");
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
}

/* taken from libeel/eel-strings.c */
static gchar *
escape_underscores (const gchar *string)
{
  gint underscores;
  const gchar *p;
  gchar *q;
  gchar *escaped;

  if (!string)
    return NULL;
	
  underscores = 0;
  for (p = string; *p != '\0'; p++)
    underscores += (*p == '_');

  if (underscores == 0)
    return g_strdup (string);

  escaped = g_new (char, strlen (string) + underscores + 1);
  for (p = string, q = escaped; *p != '\0'; p++, q++)
    {
      /* Add an extra underscore. */
      if (*p == '_')
        *q++ = '_';
      
      *q = *p;
    }
  
  *q = '\0';
	
  return escaped;
}

static void
gtk_recent_chooser_menu_add_tip (GtkRecentChooserMenu *menu,
				 GtkRecentInfo        *info,
				 GtkWidget            *item)
{
  GtkRecentChooserMenuPrivate *priv;
779
  gchar *path;
780 781 782 783 784 785 786

  g_assert (info != NULL);
  g_assert (item != NULL);

  priv = menu->priv;
  
  path = gtk_recent_info_get_uri_display (info);
787 788
  if (path)
    {
789
      gchar *tip_text = g_strdup_printf (_("Open '%s'"), path);
790

791 792
      gtk_widget_set_tooltip_text (item, tip_text);
      gtk_widget_set_has_tooltip (item, priv->show_tips);
793 794 795 796

      g_free (path);
      g_free (tip_text);
    }
797 798 799 800 801 802 803 804
}

static GtkWidget *
gtk_recent_chooser_menu_create_item (GtkRecentChooserMenu *menu,
				     GtkRecentInfo        *info,
				     gint                  count)
{
  GtkRecentChooserMenuPrivate *priv;
805 806
  gchar *text;
  GtkWidget *item, *image, *label;
807
  GIcon *icon;
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824

  g_assert (info != NULL);

  priv = menu->priv;

  if (priv->show_numbers)
    {
      gchar *name, *escaped;
      
      name = g_strdup (gtk_recent_info_get_display_name (info));
      if (!name)
        name = g_strdup (_("Unknown item"));
      
      escaped = escape_underscores (name);
      
      /* avoid clashing mnemonics */
      if (count <= 10)
825
        /* This is the label format that is used for the first 10 items
826 827 828 829
         * in a recent files menu. The %d is the number of the item,
         * the %s is the name of the item. Please keep the _ in front
         * of the number to give these menu items a mnemonic.
         */
830
        text = g_strdup_printf (C_("recent menu label", "_%d. %s"), count, escaped);
831
      else
832 833
        /* This is the format that is used for items in a recent files menu.
         * The %d is the number of the item, the %s is the name of the item.
834
         */
835
        text = g_strdup_printf (C_("recent menu label", "%d. %s"), count, escaped);
836 837

      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
838
      item = gtk_image_menu_item_new_with_mnemonic (text);
839 840
      G_GNUC_END_IGNORE_DEPRECATIONS;

841 842 843 844 845
      g_free (escaped);
      g_free (name);
    }
  else
    {
846
      text = g_strdup (gtk_recent_info_get_display_name (info));
847
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
848
      item = gtk_image_menu_item_new_with_label (text);
849
      G_GNUC_END_IGNORE_DEPRECATIONS;
850 851 852 853
    }

  g_free (text);

854
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
Matthias Clasen's avatar
Matthias Clasen committed
855 856
  gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (item),
                                             TRUE);
857
  G_GNUC_END_IGNORE_DEPRECATIONS;
Matthias Clasen's avatar
Matthias Clasen committed
858

859 860 861
  /* ellipsize the menu item label, in case the recent document
   * display name is huge.
   */
Javier Jardón's avatar
Javier Jardón committed
862
  label = gtk_bin_get_child (GTK_BIN (item));
863
  if (GTK_IS_LABEL (label))
864 865 866
    {
      gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
      gtk_label_set_max_width_chars (GTK_LABEL (label), priv->label_width);
867 868 869 870
    }
  
  if (priv->show_icons)
    {
871 872 873
      icon = gtk_recent_info_get_gicon (info);

      image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
874
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
875
      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
876
      gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (item), TRUE);
877
      G_GNUC_END_IGNORE_DEPRECATIONS;
878 879
      if (icon)
        g_object_unref (icon);
880
    }
881

882 883 884 885 886 887 888
  g_signal_connect (item, "activate",
  		    G_CALLBACK (item_activate_cb),
  		    menu);

  return item;
}

889 890 891 892 893 894 895 896 897 898
static void
gtk_recent_chooser_menu_insert_item (GtkRecentChooserMenu *menu,
                                     GtkWidget            *menuitem,
                                     gint                  position)
{
  GtkRecentChooserMenuPrivate *priv = menu->priv;
  gint real_position;

  if (priv->first_recent_item_pos == -1)
    {
899 900
      GList *children, *l;

901
      children = gtk_container_get_children (GTK_CONTAINER (menu));
902

903 904 905 906
      for (real_position = 0, l = children;
           l != NULL;
           real_position += 1, l = l->next)
        {
907 908
          GObject *child = l->data;
          gboolean is_placeholder = FALSE;
909

910 911 912 913
          is_placeholder =
            GPOINTER_TO_INT (g_object_get_data (child, "gtk-recent-menu-placeholder"));

          if (is_placeholder)
914 915
            break;
        }
916

917 918 919 920 921 922 923 924 925 926 927
      g_list_free (children);
      priv->first_recent_item_pos = real_position;
    }
  else
    real_position = priv->first_recent_item_pos;

  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem,
                         real_position + position);
  gtk_widget_show (menuitem);
}

928 929 930 931 932 933 934 935 936 937
/* removes the items we own from the menu */
static void
gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
{
  GList *children, *l;
 
  children = gtk_container_get_children (GTK_CONTAINER (menu));
  for (l = children; l != NULL; l = l->next)
    {
      GtkWidget *menu_item = GTK_WIDGET (l->data);
938
      gboolean has_mark = FALSE;
939 940
      
      /* check for our mark, in order to remove just the items we own */
941 942 943 944
      has_mark =
        GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "gtk-recent-menu-mark"));

      if (has_mark)
945 946 947 948 949 950 951 952 953 954 955 956 957 958
        {
          GtkRecentInfo *info;
          
          /* destroy the attached RecentInfo struct, if found */
          info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
          if (info)
            g_object_set_data_full (G_OBJECT (menu_item), "gtk-recent-info",
            			    NULL, NULL);
          
          /* and finally remove the item from the menu */
          gtk_container_remove (GTK_CONTAINER (menu), menu_item);
        }
    }

959 960 961
  /* recalculate the position of the first recent item */
  menu->priv->first_recent_item_pos = -1;

962 963 964
  g_list_free (children);
}

965
typedef struct
966
{
967 968 969
  GList *items;
  gint n_items;
  gint loaded_items;
970
  gint displayed_items;
971
  GtkRecentChooserMenu *menu;
972
  GtkWidget *placeholder;
973 974
} MenuPopulateData;

975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
static MenuPopulateData *
create_menu_populate_data (GtkRecentChooserMenu *menu)
{
  MenuPopulateData *pdata;

  pdata = g_slice_new (MenuPopulateData);
  pdata->items = NULL;
  pdata->n_items = 0;
  pdata->loaded_items = 0;
  pdata->displayed_items = 0;
  pdata->menu = menu;
  pdata->placeholder = g_object_ref (menu->priv->placeholder);

  return pdata;
}

static void
free_menu_populate_data (MenuPopulateData *pdata)
{
  if (pdata->placeholder)
    g_object_unref (pdata->placeholder);
  g_slice_free (MenuPopulateData, pdata);
}

999 1000 1001 1002 1003 1004 1005 1006
static gboolean
idle_populate_func (gpointer data)
{
  MenuPopulateData *pdata;
  GtkRecentChooserMenuPrivate *priv;
  GtkRecentInfo *info;
  gboolean retval;
  GtkWidget *item;
1007

1008
  pdata = (MenuPopulateData *) data;
1009 1010
  priv = pdata->menu->priv;

1011
  if (!pdata->items)
1012
    {
1013 1014 1015
      pdata->items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (pdata->menu));
      if (!pdata->items)
        {
1016
          /* show the placeholder here */
1017
          gtk_widget_show (pdata->placeholder);
1018
          pdata->displayed_items = 1;
1019
          priv->populate_id = 0;
1020

1021 1022
	  return FALSE;
	}
1023 1024
      else
        gtk_widget_hide (pdata->placeholder);
1025
      
1026 1027 1028 1029 1030 1031 1032
      pdata->n_items = g_list_length (pdata->items);
      pdata->loaded_items = 0;
    }

  info = g_list_nth_data (pdata->items, pdata->loaded_items);
  item = gtk_recent_chooser_menu_create_item (pdata->menu,
                                              info,
1033
					      pdata->displayed_items);
1034 1035
  if (!item)
    goto check_and_return;
1036
      
1037
  gtk_recent_chooser_menu_add_tip (pdata->menu, info, item);
1038 1039 1040
  gtk_recent_chooser_menu_insert_item (pdata->menu, item,
                                       pdata->displayed_items);
  
1041
  pdata->displayed_items += 1;
1042
      
1043
  /* mark the menu item as one of our own */
1044 1045 1046
  g_object_set_data (G_OBJECT (item),
                     "gtk-recent-menu-mark",
      		     GINT_TO_POINTER (TRUE));
1047
      
1048 1049 1050
  /* attach the RecentInfo object to the menu item, and own a reference
   * to it, so that it will be destroyed with the menu item when it's
   * not needed anymore.
1051
   */
1052 1053 1054
  g_object_set_data_full (G_OBJECT (item), "gtk-recent-info",
      			  gtk_recent_info_ref (info),
      			  (GDestroyNotify) gtk_recent_info_unref);
1055
  
1056 1057 1058 1059
check_and_return:
  pdata->loaded_items += 1;

  if (pdata->loaded_items == pdata->n_items)
1060
    {
1061
      g_list_free_full (pdata->items, (GDestroyNotify) gtk_recent_info_unref);
1062

1063 1064
      priv->populate_id = 0;

1065
      retval = FALSE;
1066
    }
1067 1068 1069 1070 1071 1072 1073 1074 1075
  else
    retval = TRUE;

  return retval;
}

static void
idle_populate_clean_up (gpointer data)
{
1076 1077
  MenuPopulateData *pdata = data;

1078 1079 1080 1081 1082 1083 1084 1085
  if (pdata->menu->priv->populate_id == 0)
    {
      /* show the placeholder in case no item survived
       * the filtering process in the idle loop
       */
      if (!pdata->displayed_items)
        gtk_widget_show (pdata->placeholder);
    }
1086 1087

  free_menu_populate_data (pdata);
1088 1089 1090 1091 1092 1093
}

static void
gtk_recent_chooser_menu_populate (GtkRecentChooserMenu *menu)
{
  MenuPopulateData *pdata;
1094
  GtkRecentChooserMenuPrivate *priv = menu->priv;
1095

1096
  if (priv->populate_id)
1097 1098
    return;

1099
  pdata = create_menu_populate_data (menu);
1100

1101
  /* remove our menu items first */
1102
  gtk_recent_chooser_menu_dispose_items (menu);
1103

1104 1105 1106 1107
  priv->populate_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
  					         idle_populate_func,
					         pdata,
                                                 idle_populate_clean_up);
1108
  g_source_set_name_by_id (priv->populate_id, "[gtk+] idle_populate_func");
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
}

/* bounce activate signal from the recent menu item widget 
 * to the recent menu widget
 */
static void
item_activate_cb (GtkWidget *widget,
		  gpointer   user_data)
{
  GtkRecentChooser *chooser = GTK_RECENT_CHOOSER (user_data);
1119 1120 1121
  GtkRecentInfo *info = g_object_get_data (G_OBJECT (widget), "gtk-recent-info");

  gtk_recent_chooser_menu_set_current_uri (chooser, gtk_recent_info_get_uri (info), NULL);
1122 1123 1124 1125 1126 1127 1128 1129
  _gtk_recent_chooser_item_activated (chooser);
}

/* we force a redraw if the manager changes when we are showing */
static void
manager_changed_cb (GtkRecentManager *manager,
		    gpointer          user_data)
{
1130 1131 1132
  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (user_data);

  gtk_recent_chooser_menu_populate (menu);
1133 1134 1135 1136 1137 1138
}

static void
set_recent_manager (GtkRecentChooserMenu *menu,
		    GtkRecentManager     *manager)
{
1139 1140 1141
  GtkRecentChooserMenuPrivate *priv = menu->priv;

  if (priv->manager)
1142
    {
1143
      if (priv->manager_changed_id)
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
        {
          g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
          priv->manager_changed_id = 0;
        }

      if (priv->populate_id)
        {
          g_source_remove (priv->populate_id);
          priv->populate_id = 0;
        }
1154 1155

      priv->manager = NULL;
1156
    }
1157 1158
  
  if (manager)
1159
    priv->manager = manager;
1160
  else
1161
    priv->manager = gtk_recent_manager_get_default ();
1162
  
1163 1164 1165 1166
  if (priv->manager)
    priv->manager_changed_id = g_signal_connect (priv->manager, "changed",
                                                 G_CALLBACK (manager_changed_cb),
                                                 menu);
1167 1168
}

1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
static void
foreach_set_shot_tips (GtkWidget *widget,
                       gpointer   user_data)
{
  GtkRecentChooserMenu *menu = user_data;
  GtkRecentChooserMenuPrivate *priv = menu->priv;
  gboolean has_mark;

  /* toggle the tooltip only on the items we create */
  has_mark =
    GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "gtk-recent-menu-mark"));

  if (has_mark)
    gtk_widget_set_has_tooltip (widget, priv->show_tips);
}

1185 1186 1187 1188
static void
gtk_recent_chooser_menu_set_show_tips (GtkRecentChooserMenu *menu,
				       gboolean              show_tips)
{
1189 1190 1191
  GtkRecentChooserMenuPrivate *priv = menu->priv;

  if (priv->show_tips == show_tips)
1192 1193
    return;
  
1194 1195
  priv->show_tips = show_tips;
  gtk_container_foreach (GTK_CONTAINER (menu), foreach_set_shot_tips, menu);
1196
  g_object_notify (G_OBJECT (menu), "show-tips");
1197 1198
}

1199 1200 1201 1202
static void
gtk_recent_chooser_update (GtkActivatable *activatable,
			   GtkAction      *action,
			   const gchar    *property_name)
1203
{
1204 1205
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;

1206 1207 1208
  if (strcmp (property_name, "sensitive") == 0)
    gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));

1209 1210
  G_GNUC_END_IGNORE_DEPRECATIONS;

1211
  _gtk_recent_chooser_update (activatable, action, property_name);
1212 1213
}

1214 1215 1216
static void
gtk_recent_chooser_sync_action_properties (GtkActivatable *activatable,
				           GtkAction      *action)
1217
{
1218 1219 1220
  if (!action)
    return;

1221 1222
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;

1223 1224
  gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));

1225 1226
  G_GNUC_END_IGNORE_DEPRECATIONS;

1227
  _gtk_recent_chooser_sync_action_properties (activatable, action);
1228 1229 1230
}


1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
/*
 * Public API
 */

/**
 * gtk_recent_chooser_menu_new:
 *
 * Creates a new #GtkRecentChooserMenu widget.
 *
 * This kind of widget shows the list of recently used resources as
 * a menu, each item as a menu item.  Each item inside the menu might
 * have an icon, representing its MIME type, and a number, for mnemonic
 * access.
 *
 * This widget implements the #GtkRecentChooser interface.
 *
 * This widget creates its own #GtkRecentManager object.  See the
 * gtk_recent_chooser_menu_new_for_manager() function to know how to create
 * a #GtkRecentChooserMenu widget bound to another #GtkRecentManager object.
 *
1251
 * Returns: a new #GtkRecentChooserMenu
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
 *
 * Since: 2.10
 */
GtkWidget *
gtk_recent_chooser_menu_new (void)
{
  return g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
  		       "recent-manager", NULL,
  		       NULL);
}

/**
 * gtk_recent_chooser_menu_new_for_manager:
 * @manager: a #GtkRecentManager
 *
 * Creates a new #GtkRecentChooserMenu widget using @manager as
 * the underlying recently used resources manager.
 *
 * This is useful if you have implemented your own recent manager,
 * or if you have a customized instance of a #GtkRecentManager
 * object or if you wish to share a common #GtkRecentManager object
 * among multiple #GtkRecentChooser widgets.
 *
1275
 * Returns: a new #GtkRecentChooserMenu, bound to @manager.
1276 1277 1278 1279 1280 1281
 *
 * Since: 2.10
 */
GtkWidget *
gtk_recent_chooser_menu_new_for_manager (GtkRecentManager *manager)
{
1282
  g_return_val_if_fail (manager == NULL || GTK_IS_RECENT_MANAGER (manager), NULL);
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
  
  return g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
  		       "recent-manager", manager,
  		       NULL);
}

/**
 * gtk_recent_chooser_menu_get_show_numbers:
 * @menu: a #GtkRecentChooserMenu
 *
 * Returns the value set by gtk_recent_chooser_menu_set_show_numbers().
 * 
1295
 * Returns: %TRUE if numbers should be shown.
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
 *
 * Since: 2.10
 */
gboolean
gtk_recent_chooser_menu_get_show_numbers (GtkRecentChooserMenu *menu)
{
  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu), FALSE);

  return menu->priv->show_numbers;
}

/**
 * gtk_recent_chooser_menu_set_show_numbers:
 * @menu: a #GtkRecentChooserMenu
 * @show_numbers: whether to show numbers
 *
 * Sets whether a number should be added to the items of @menu.  The
 * numbers are shown to provide a unique character for a mnemonic to
1314
 * be used inside ten menu item’s label.  Only the first the items
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
 * get a number to avoid clashes.
 *
 * Since: 2.10
 */
void
gtk_recent_chooser_menu_set_show_numbers (GtkRecentChooserMenu *menu,
					  gboolean              show_numbers)
{
  g_return_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu));

  if (menu->priv->show_numbers == show_numbers)
    return;

  menu->priv->show_numbers = show_numbers;
  g_object_notify (G_OBJECT (menu), "show-numbers");
}