1 /*
2 * This file is part of Amtk - Actions, Menus and Toolbars Kit
3 *
4 * Copyright 2017 - Sébastien Wilmet <swilmet@gnome.org>
5 *
6 * Amtk is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by the
8 * Free Software Foundation; either version 2.1 of the License, or (at your
9 * option) any later version.
10 *
11 * Amtk is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 * License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21 #include "amtk-application-window.h"
22 #include <glib/gi18n-lib.h>
23 #include "amtk-action-info.h"
24 #include "amtk-action-info-central-store.h"
25 #include "amtk-menu-item.h"
26 #include "amtk-menu-shell.h"
27 #include "amtk-utils.h"
28
29 /**
30 * SECTION:amtk-application-window
31 * @Short_description: An extension of GtkApplicationWindow
32 * @Title: AmtkApplicationWindow
33 *
34 * #AmtkApplicationWindow extends the #GtkApplicationWindow class with a
35 * #AmtkApplicationWindow:statusbar property and functions to show longer
36 * descriptions of #GtkMenuItem's to the #GtkStatusbar.
37 *
38 * Note that #AmtkApplicationWindow extends the #GtkApplicationWindow class but
39 * without subclassing it, because several libraries might want to extend
40 * #GtkApplicationWindow and an application needs to be able to use all those
41 * extensions at the same time.
42 */
43
44 /* API design:
45 *
46 * AmtkApplicationWindow was first implemented in Tepl, and
47 * TeplApplicationWindow needs to access the GtkApplicationWindow for the
48 * GActionMap. Currently AmtkApplicationWindow could be renamed to AmtkWindow
49 * and be an extension of GtkWindow instead of GtkApplicationWindow, it would
50 * have the advantage to have shorter function names and be a little more
51 * re-usable. But I think it's not a big problem to keep AmtkApplicationWindow
52 * as is, because (1) the A in Amtk is all about GActions, so normally the app
53 * already uses GtkApplicationWindow, (2) it is easy to port an application to
54 * use GtkApplicationWindow, most of the time it's just changing the parent
55 * class when subclassing GtkWindow, (3) it is more future-proof for Amtk to
56 * have access to the GtkApplicationWindow, in case we want to add some features
57 * that require the GActionMap or whatever. -- swilmet
58 */
59
60 struct _AmtkApplicationWindowPrivate
61 {
62 GtkApplicationWindow *gtk_window;
63 GtkStatusbar *statusbar;
64 };
65
66 enum
67 {
68 PROP_0,
69 PROP_APPLICATION_WINDOW,
70 PROP_STATUSBAR,
71 N_PROPERTIES
72 };
73
74 #define AMTK_APPLICATION_WINDOW_KEY "amtk-application-window-key"
75 #define MENU_SHELL_STATUSBAR_CONTEXT_ID_KEY "amtk-menu-shell-statusbar-context-id-key"
76 #define MENU_SHELL_FOR_RECENT_CHOOSER_KEY "amtk-menu-shell-for-recent-chooser-key"
77
78 static GParamSpec *properties[N_PROPERTIES];
79
G_DEFINE_TYPE_WITH_PRIVATE(AmtkApplicationWindow,amtk_application_window,G_TYPE_OBJECT)80 G_DEFINE_TYPE_WITH_PRIVATE (AmtkApplicationWindow, amtk_application_window, G_TYPE_OBJECT)
81
82 static void
83 amtk_application_window_get_property (GObject *object,
84 guint prop_id,
85 GValue *value,
86 GParamSpec *pspec)
87 {
88 AmtkApplicationWindow *amtk_window = AMTK_APPLICATION_WINDOW (object);
89
90 switch (prop_id)
91 {
92 case PROP_APPLICATION_WINDOW:
93 g_value_set_object (value, amtk_application_window_get_application_window (amtk_window));
94 break;
95
96 case PROP_STATUSBAR:
97 g_value_set_object (value, amtk_application_window_get_statusbar (amtk_window));
98 break;
99
100 default:
101 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
102 break;
103 }
104 }
105
106 static void
amtk_application_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)107 amtk_application_window_set_property (GObject *object,
108 guint prop_id,
109 const GValue *value,
110 GParamSpec *pspec)
111 {
112 AmtkApplicationWindow *amtk_window = AMTK_APPLICATION_WINDOW (object);
113
114 switch (prop_id)
115 {
116 case PROP_APPLICATION_WINDOW:
117 g_assert (amtk_window->priv->gtk_window == NULL);
118 amtk_window->priv->gtk_window = g_value_get_object (value);
119 break;
120
121 case PROP_STATUSBAR:
122 amtk_application_window_set_statusbar (amtk_window, g_value_get_object (value));
123 break;
124
125 default:
126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127 break;
128 }
129 }
130
131 static void
amtk_application_window_dispose(GObject * object)132 amtk_application_window_dispose (GObject *object)
133 {
134 AmtkApplicationWindow *amtk_window = AMTK_APPLICATION_WINDOW (object);
135
136 amtk_window->priv->gtk_window = NULL;
137 g_clear_object (&amtk_window->priv->statusbar);
138
139 G_OBJECT_CLASS (amtk_application_window_parent_class)->dispose (object);
140 }
141
142 static void
amtk_application_window_class_init(AmtkApplicationWindowClass * klass)143 amtk_application_window_class_init (AmtkApplicationWindowClass *klass)
144 {
145 GObjectClass *object_class = G_OBJECT_CLASS (klass);
146
147 object_class->get_property = amtk_application_window_get_property;
148 object_class->set_property = amtk_application_window_set_property;
149 object_class->dispose = amtk_application_window_dispose;
150
151 /**
152 * AmtkApplicationWindow:application-window:
153 *
154 * The #GtkApplicationWindow.
155 *
156 * Since: 2.0
157 */
158 properties[PROP_APPLICATION_WINDOW] =
159 g_param_spec_object ("application-window",
160 "GtkApplicationWindow",
161 "",
162 GTK_TYPE_APPLICATION_WINDOW,
163 G_PARAM_READWRITE |
164 G_PARAM_CONSTRUCT_ONLY |
165 G_PARAM_STATIC_STRINGS);
166
167 /**
168 * AmtkApplicationWindow:statusbar:
169 *
170 * The #GtkStatusbar. %NULL by default.
171 *
172 * Since: 2.0
173 */
174 properties[PROP_STATUSBAR] =
175 g_param_spec_object ("statusbar",
176 "GtkStatusbar",
177 "",
178 GTK_TYPE_STATUSBAR,
179 G_PARAM_READWRITE |
180 G_PARAM_STATIC_STRINGS);
181
182 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
183 }
184
185 static void
amtk_application_window_init(AmtkApplicationWindow * amtk_window)186 amtk_application_window_init (AmtkApplicationWindow *amtk_window)
187 {
188 amtk_window->priv = amtk_application_window_get_instance_private (amtk_window);
189 }
190
191 /**
192 * amtk_application_window_get_from_gtk_application_window:
193 * @gtk_window: a #GtkApplicationWindow.
194 *
195 * Returns the #AmtkApplicationWindow of @gtk_window. The returned object is
196 * guaranteed to be the same for the lifetime of @gtk_window.
197 *
198 * Returns: (transfer none): the #AmtkApplicationWindow of @gtk_window.
199 * Since: 2.0
200 */
201 AmtkApplicationWindow *
amtk_application_window_get_from_gtk_application_window(GtkApplicationWindow * gtk_window)202 amtk_application_window_get_from_gtk_application_window (GtkApplicationWindow *gtk_window)
203 {
204 AmtkApplicationWindow *amtk_window;
205
206 g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (gtk_window), NULL);
207
208 amtk_window = g_object_get_data (G_OBJECT (gtk_window), AMTK_APPLICATION_WINDOW_KEY);
209
210 if (amtk_window == NULL)
211 {
212 amtk_window = g_object_new (AMTK_TYPE_APPLICATION_WINDOW,
213 "application-window", gtk_window,
214 NULL);
215
216 g_object_set_data_full (G_OBJECT (gtk_window),
217 AMTK_APPLICATION_WINDOW_KEY,
218 amtk_window,
219 g_object_unref);
220 }
221
222 g_return_val_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window), NULL);
223 return amtk_window;
224 }
225
226 /**
227 * amtk_application_window_get_application_window:
228 * @amtk_window: an #AmtkApplicationWindow.
229 *
230 * Returns: (transfer none): the #GtkApplicationWindow of @amtk_window.
231 * Since: 2.0
232 */
233 GtkApplicationWindow *
amtk_application_window_get_application_window(AmtkApplicationWindow * amtk_window)234 amtk_application_window_get_application_window (AmtkApplicationWindow *amtk_window)
235 {
236 g_return_val_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window), NULL);
237
238 return amtk_window->priv->gtk_window;
239 }
240
241 /**
242 * amtk_application_window_get_statusbar:
243 * @amtk_window: an #AmtkApplicationWindow.
244 *
245 * Returns: (transfer none) (nullable): the #AmtkApplicationWindow:statusbar.
246 * Since: 2.0
247 */
248 GtkStatusbar *
amtk_application_window_get_statusbar(AmtkApplicationWindow * amtk_window)249 amtk_application_window_get_statusbar (AmtkApplicationWindow *amtk_window)
250 {
251 g_return_val_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window), NULL);
252
253 return amtk_window->priv->statusbar;
254 }
255
256 /**
257 * amtk_application_window_set_statusbar:
258 * @amtk_window: an #AmtkApplicationWindow.
259 * @statusbar: (nullable): a #GtkStatusbar, or %NULL.
260 *
261 * Sets the #AmtkApplicationWindow:statusbar property.
262 *
263 * Since: 2.0
264 */
265 void
amtk_application_window_set_statusbar(AmtkApplicationWindow * amtk_window,GtkStatusbar * statusbar)266 amtk_application_window_set_statusbar (AmtkApplicationWindow *amtk_window,
267 GtkStatusbar *statusbar)
268 {
269 g_return_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window));
270 g_return_if_fail (statusbar == NULL || GTK_IS_STATUSBAR (statusbar));
271
272 if (amtk_window->priv->statusbar == statusbar)
273 {
274 return;
275 }
276
277 if (statusbar != NULL)
278 {
279 g_object_ref_sink (statusbar);
280 }
281
282 if (amtk_window->priv->statusbar != NULL)
283 {
284 g_object_unref (amtk_window->priv->statusbar);
285 }
286
287 amtk_window->priv->statusbar = statusbar;
288 g_object_notify_by_pspec (G_OBJECT (amtk_window), properties[PROP_STATUSBAR]);
289 }
290
291 /* Returns: %TRUE if a context ID exists and has been set to @context_id. */
292 static gboolean
get_statusbar_context_id_for_menu_shell(AmtkApplicationWindow * amtk_window,AmtkMenuShell * amtk_menu_shell,gboolean create,guint * context_id)293 get_statusbar_context_id_for_menu_shell (AmtkApplicationWindow *amtk_window,
294 AmtkMenuShell *amtk_menu_shell,
295 gboolean create,
296 guint *context_id)
297 {
298 gpointer data;
299
300 g_assert (amtk_window->priv->statusbar != NULL);
301 g_assert (context_id != NULL);
302
303 data = g_object_get_data (G_OBJECT (amtk_menu_shell), MENU_SHELL_STATUSBAR_CONTEXT_ID_KEY);
304
305 if (data == NULL && !create)
306 {
307 return FALSE;
308 }
309
310 if (data == NULL)
311 {
312 *context_id = gtk_statusbar_get_context_id (amtk_window->priv->statusbar,
313 "Show long description of menu items.");
314
315 g_object_set_data (G_OBJECT (amtk_menu_shell),
316 MENU_SHELL_STATUSBAR_CONTEXT_ID_KEY,
317 GUINT_TO_POINTER (*context_id));
318 }
319 else
320 {
321 *context_id = GPOINTER_TO_UINT (data);
322 }
323
324 return TRUE;
325 }
326
327 /* Free the return value with g_free(). */
328 static gchar *
get_menu_item_long_description(AmtkMenuShell * amtk_menu_shell,GtkMenuItem * menu_item)329 get_menu_item_long_description (AmtkMenuShell *amtk_menu_shell,
330 GtkMenuItem *menu_item)
331 {
332 const gchar *long_description;
333 gpointer data;
334 gboolean is_for_recent_chooser;
335
336 long_description = amtk_menu_item_get_long_description (menu_item);
337 if (long_description != NULL)
338 {
339 return g_strdup (long_description);
340 }
341
342 data = g_object_get_data (G_OBJECT (amtk_menu_shell), MENU_SHELL_FOR_RECENT_CHOOSER_KEY);
343 is_for_recent_chooser = data != NULL ? GPOINTER_TO_INT (data) : FALSE;
344
345 if (is_for_recent_chooser)
346 {
347 GtkMenuShell *gtk_menu_shell;
348 GtkRecentChooserMenu *recent_chooser_menu;
349 gchar *uri;
350 GFile *file;
351 gchar *parse_name;
352 gchar *nicer_filename;
353 gchar *ret;
354
355 gtk_menu_shell = amtk_menu_shell_get_menu_shell (amtk_menu_shell);
356 recent_chooser_menu = GTK_RECENT_CHOOSER_MENU (gtk_menu_shell);
357 uri = amtk_utils_recent_chooser_menu_get_item_uri (recent_chooser_menu, menu_item);
358
359 if (uri == NULL)
360 {
361 return NULL;
362 }
363
364 file = g_file_new_for_uri (uri);
365 g_free (uri);
366
367 parse_name = g_file_get_parse_name (file);
368 g_object_unref (file);
369
370 nicer_filename = _amtk_utils_replace_home_dir_with_tilde (parse_name);
371 g_free (parse_name);
372
373 /* Translators: %s is a filename. */
374 ret = g_strdup_printf (_("Open “%s”"), nicer_filename);
375 g_free (nicer_filename);
376
377 return ret;
378 }
379
380 return NULL;
381 }
382
383 static void
menu_item_selected_cb(AmtkMenuShell * amtk_menu_shell,GtkMenuItem * menu_item,gpointer user_data)384 menu_item_selected_cb (AmtkMenuShell *amtk_menu_shell,
385 GtkMenuItem *menu_item,
386 gpointer user_data)
387 {
388 AmtkApplicationWindow *amtk_window = AMTK_APPLICATION_WINDOW (user_data);
389 gchar *long_description;
390 guint context_id;
391
392 if (amtk_window->priv->statusbar == NULL)
393 {
394 return;
395 }
396
397 long_description = get_menu_item_long_description (amtk_menu_shell, menu_item);
398 if (long_description == NULL)
399 {
400 return;
401 }
402
403 get_statusbar_context_id_for_menu_shell (amtk_window,
404 amtk_menu_shell,
405 TRUE,
406 &context_id);
407
408 gtk_statusbar_push (amtk_window->priv->statusbar,
409 context_id,
410 long_description);
411
412 g_free (long_description);
413 }
414
415 static void
menu_item_deselected_cb(AmtkMenuShell * amtk_menu_shell,GtkMenuItem * menu_item,gpointer user_data)416 menu_item_deselected_cb (AmtkMenuShell *amtk_menu_shell,
417 GtkMenuItem *menu_item,
418 gpointer user_data)
419 {
420 AmtkApplicationWindow *amtk_window = AMTK_APPLICATION_WINDOW (user_data);
421 const gchar *long_description;
422 gpointer data;
423 gboolean is_for_recent_chooser;
424 guint context_id;
425
426 if (amtk_window->priv->statusbar == NULL)
427 {
428 return;
429 }
430
431 long_description = amtk_menu_item_get_long_description (menu_item);
432
433 data = g_object_get_data (G_OBJECT (amtk_menu_shell), MENU_SHELL_FOR_RECENT_CHOOSER_KEY);
434 is_for_recent_chooser = data != NULL ? GPOINTER_TO_INT (data) : FALSE;
435
436 if (long_description == NULL && !is_for_recent_chooser)
437 {
438 return;
439 }
440
441 if (get_statusbar_context_id_for_menu_shell (amtk_window,
442 amtk_menu_shell,
443 FALSE,
444 &context_id))
445 {
446 gtk_statusbar_pop (amtk_window->priv->statusbar, context_id);
447 }
448 }
449
450 static void
statusbar_notify_cb(AmtkApplicationWindow * amtk_window,GParamSpec * pspec,gpointer user_data)451 statusbar_notify_cb (AmtkApplicationWindow *amtk_window,
452 GParamSpec *pspec,
453 gpointer user_data)
454 {
455 AmtkMenuShell *amtk_menu_shell = AMTK_MENU_SHELL (user_data);
456
457 g_object_set_data (G_OBJECT (amtk_menu_shell),
458 MENU_SHELL_STATUSBAR_CONTEXT_ID_KEY,
459 NULL);
460 }
461
462 /**
463 * amtk_application_window_connect_menu_to_statusbar:
464 * @amtk_window: an #AmtkApplicationWindow.
465 * @menu_shell: a #GtkMenuShell.
466 *
467 * Connects to the #AmtkMenuShell::menu-item-selected and
468 * #AmtkMenuShell::menu-item-deselected signals of @menu_shell to push/pop the
469 * long description of #GtkMenuItem's to the #AmtkApplicationWindow:statusbar.
470 *
471 * The long description is retrieved with amtk_menu_item_get_long_description().
472 * So amtk_menu_item_set_long_description() must have been called, which is the
473 * case if the #GtkMenuItem has been created with #AmtkFactory.
474 *
475 * Since: 2.0
476 */
477 void
amtk_application_window_connect_menu_to_statusbar(AmtkApplicationWindow * amtk_window,GtkMenuShell * menu_shell)478 amtk_application_window_connect_menu_to_statusbar (AmtkApplicationWindow *amtk_window,
479 GtkMenuShell *menu_shell)
480 {
481 AmtkMenuShell *amtk_menu_shell;
482
483 g_return_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window));
484 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
485
486 amtk_menu_shell = amtk_menu_shell_get_from_gtk_menu_shell (menu_shell);
487
488 g_signal_connect_object (amtk_menu_shell,
489 "menu-item-selected",
490 G_CALLBACK (menu_item_selected_cb),
491 amtk_window,
492 0);
493
494 g_signal_connect_object (amtk_menu_shell,
495 "menu-item-deselected",
496 G_CALLBACK (menu_item_deselected_cb),
497 amtk_window,
498 0);
499
500 g_signal_connect_object (amtk_window,
501 "notify::statusbar",
502 G_CALLBACK (statusbar_notify_cb),
503 amtk_menu_shell,
504 0);
505 }
506
507 /**
508 * amtk_application_window_connect_recent_chooser_menu_to_statusbar:
509 * @amtk_window: an #AmtkApplicationWindow.
510 * @menu: a #GtkRecentChooserMenu.
511 *
512 * An alternative to gtk_recent_chooser_set_show_tips(). Shows the full path in
513 * the #AmtkApplicationWindow:statusbar when a #GtkMenuItem of @menu is
514 * selected.
515 *
516 * The full path is retrieved with
517 * amtk_utils_recent_chooser_menu_get_item_uri().
518 *
519 * Since: 2.0
520 */
521 void
amtk_application_window_connect_recent_chooser_menu_to_statusbar(AmtkApplicationWindow * amtk_window,GtkRecentChooserMenu * menu)522 amtk_application_window_connect_recent_chooser_menu_to_statusbar (AmtkApplicationWindow *amtk_window,
523 GtkRecentChooserMenu *menu)
524 {
525 AmtkMenuShell *amtk_menu_shell;
526
527 g_return_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window));
528 g_return_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu));
529
530 amtk_menu_shell = amtk_menu_shell_get_from_gtk_menu_shell (GTK_MENU_SHELL (menu));
531
532 g_object_set_data (G_OBJECT (amtk_menu_shell),
533 MENU_SHELL_FOR_RECENT_CHOOSER_KEY,
534 GINT_TO_POINTER (TRUE));
535
536 amtk_application_window_connect_menu_to_statusbar (amtk_window, GTK_MENU_SHELL (menu));
537 }
538
539 static void
open_recent_file_cb(GtkRecentChooser * recent_chooser,gpointer user_data)540 open_recent_file_cb (GtkRecentChooser *recent_chooser,
541 gpointer user_data)
542 {
543 AmtkApplicationWindow *amtk_window = AMTK_APPLICATION_WINDOW (user_data);
544 gchar *uri;
545 GFile *files[1];
546 GtkApplication *app;
547
548 uri = gtk_recent_chooser_get_current_uri (recent_chooser);
549 files[0] = g_file_new_for_uri (uri);
550
551 app = gtk_window_get_application (GTK_WINDOW (amtk_window->priv->gtk_window));
552 g_application_open (G_APPLICATION (app), files, 1, "");
553
554 g_free (uri);
555 g_object_unref (files[0]);
556 }
557
558 /**
559 * amtk_application_window_create_open_recent_menu:
560 * @amtk_window: an #AmtkApplicationWindow.
561 *
562 * Creates a simple and generic #GtkRecentChooserMenu.
563 *
564 * The #GtkRecentChooser is configured to show files only recently used with the
565 * current application, as returned by g_get_application_name(). If recent files
566 * are added to the default #GtkRecentManager with
567 * gtk_recent_manager_add_item(), the files will normally show up in the
568 * #GtkRecentChooserMenu.
569 *
570 * The #GtkRecentChooserMenu is connected to the statusbar with
571 * amtk_application_window_connect_recent_chooser_menu_to_statusbar().
572 *
573 * When the #GtkRecentChooser::item-activated signal is emitted,
574 * g_application_open() is called (with an empty hint), so the #GApplication
575 * must have the %G_APPLICATION_HANDLES_OPEN flag set.
576 *
577 * Returns: (transfer floating): a new #GtkRecentChooserMenu.
578 * Since: 3.0
579 */
580 GtkWidget *
amtk_application_window_create_open_recent_menu(AmtkApplicationWindow * amtk_window)581 amtk_application_window_create_open_recent_menu (AmtkApplicationWindow *amtk_window)
582 {
583 GtkRecentChooserMenu *recent_chooser_menu;
584 GtkRecentChooser *recent_chooser;
585 GtkRecentFilter *filter;
586
587 g_return_val_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window), NULL);
588
589 recent_chooser_menu = GTK_RECENT_CHOOSER_MENU (gtk_recent_chooser_menu_new ());
590
591 recent_chooser = GTK_RECENT_CHOOSER (recent_chooser_menu);
592 gtk_recent_chooser_set_local_only (recent_chooser, FALSE);
593 gtk_recent_chooser_set_sort_type (recent_chooser, GTK_RECENT_SORT_MRU);
594
595 filter = gtk_recent_filter_new ();
596 gtk_recent_filter_add_application (filter, g_get_application_name ());
597 gtk_recent_chooser_set_filter (recent_chooser, filter);
598
599 amtk_application_window_connect_recent_chooser_menu_to_statusbar (amtk_window, recent_chooser_menu);
600
601 g_signal_connect_object (recent_chooser,
602 "item-activated",
603 G_CALLBACK (open_recent_file_cb),
604 amtk_window,
605 0);
606
607 return GTK_WIDGET (recent_chooser_menu);
608 }
609
610 /**
611 * amtk_application_window_create_open_recent_menu_item:
612 * @amtk_window: an #AmtkApplicationWindow.
613 *
614 * Creates a #GtkMenuItem with a simple and generic #GtkRecentChooserMenu as
615 * submenu. The #GtkRecentChooserMenu is created with
616 * amtk_application_window_create_open_recent_menu().
617 *
618 * Returns: (transfer floating): a new #GtkMenuItem.
619 * Since: 2.0
620 */
621 GtkWidget *
amtk_application_window_create_open_recent_menu_item(AmtkApplicationWindow * amtk_window)622 amtk_application_window_create_open_recent_menu_item (AmtkApplicationWindow *amtk_window)
623 {
624 GtkMenuItem *menu_item;
625 gchar *long_description;
626 GtkWidget *recent_chooser_menu;
627
628 g_return_val_if_fail (AMTK_IS_APPLICATION_WINDOW (amtk_window), NULL);
629
630 menu_item = GTK_MENU_ITEM (gtk_menu_item_new_with_mnemonic (_("Open _Recent")));
631
632 /* Translators: %s is the application name. */
633 long_description = g_strdup_printf (_("Open a file recently used with %s"),
634 g_get_application_name ());
635 amtk_menu_item_set_long_description (menu_item, long_description);
636 g_free (long_description);
637
638 recent_chooser_menu = amtk_application_window_create_open_recent_menu (amtk_window);
639 gtk_menu_item_set_submenu (menu_item, recent_chooser_menu);
640
641 return GTK_WIDGET (menu_item);
642 }
643
644 /* ex:set ts=8 noet: */
645