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