Move the browser's popup menu to its own file
This commit is contained in:
		
							
								
								
									
										190
									
								
								fiv-browser.c
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								fiv-browser.c
									
									
									
									
									
								
							@@ -21,6 +21,7 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include "fiv-browser.h"
 | 
			
		||||
#include "fiv-context-menu.h"
 | 
			
		||||
#include "fiv-io.h"
 | 
			
		||||
#include "fiv-thumbnail.h"
 | 
			
		||||
#include "fiv-view.h"
 | 
			
		||||
@@ -729,187 +730,6 @@ thumbnailers_start(FivBrowser *self)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Context menu-------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
typedef struct _OpenContext {
 | 
			
		||||
	GWeakRef widget;
 | 
			
		||||
	GFile *file;
 | 
			
		||||
	gchar *content_type;
 | 
			
		||||
	GAppInfo *app_info;
 | 
			
		||||
} OpenContext;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
open_context_notify(gpointer data, G_GNUC_UNUSED GClosure *closure)
 | 
			
		||||
{
 | 
			
		||||
	OpenContext *self = data;
 | 
			
		||||
	g_weak_ref_clear(&self->widget);
 | 
			
		||||
	g_clear_object(&self->app_info);
 | 
			
		||||
	g_clear_object(&self->file);
 | 
			
		||||
	g_free(self->content_type);
 | 
			
		||||
	g_free(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
open_context_launch(GtkWidget *widget, OpenContext *self)
 | 
			
		||||
{
 | 
			
		||||
	GdkAppLaunchContext *context =
 | 
			
		||||
		gdk_display_get_app_launch_context(gtk_widget_get_display(widget));
 | 
			
		||||
	gdk_app_launch_context_set_screen(context, gtk_widget_get_screen(widget));
 | 
			
		||||
	gdk_app_launch_context_set_timestamp(context, gtk_get_current_event_time());
 | 
			
		||||
 | 
			
		||||
	// TODO(p): Display errors.
 | 
			
		||||
	GList *files = g_list_append(NULL, self->file);
 | 
			
		||||
	if (g_app_info_launch(
 | 
			
		||||
			self->app_info, files, G_APP_LAUNCH_CONTEXT(context), NULL)) {
 | 
			
		||||
		g_app_info_set_as_last_used_for_type(
 | 
			
		||||
			self->app_info, self->content_type, NULL);
 | 
			
		||||
	}
 | 
			
		||||
	g_list_free(files);
 | 
			
		||||
	g_object_unref(context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
append_opener(GtkWidget *menu, GAppInfo *opener, const OpenContext *template)
 | 
			
		||||
{
 | 
			
		||||
	OpenContext *ctx = g_malloc0(sizeof *ctx);
 | 
			
		||||
	g_weak_ref_init(&ctx->widget, NULL);
 | 
			
		||||
	ctx->file = g_object_ref(template->file);
 | 
			
		||||
	ctx->content_type = g_strdup(template->content_type);
 | 
			
		||||
	ctx->app_info = opener;
 | 
			
		||||
 | 
			
		||||
	// On Linux, this prefers the obsoleted X-GNOME-FullName.
 | 
			
		||||
	gchar *name =
 | 
			
		||||
		g_strdup_printf("Open With %s", g_app_info_get_display_name(opener));
 | 
			
		||||
 | 
			
		||||
	// It's documented that we can touch the child, if we want to use markup.
 | 
			
		||||
#if 0
 | 
			
		||||
	GtkWidget *item = gtk_menu_item_new_with_label(name);
 | 
			
		||||
#else
 | 
			
		||||
	// GtkImageMenuItem overrides the toggle_size_request class method
 | 
			
		||||
	// to get the image shown in the "margin"--too much work to duplicate.
 | 
			
		||||
	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
 | 
			
		||||
 | 
			
		||||
	GtkWidget *item = gtk_image_menu_item_new_with_label(name);
 | 
			
		||||
	GIcon *icon = g_app_info_get_icon(opener);
 | 
			
		||||
	if (icon) {
 | 
			
		||||
		GtkWidget *image = gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_MENU);
 | 
			
		||||
		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
 | 
			
		||||
		gtk_image_menu_item_set_always_show_image(
 | 
			
		||||
			GTK_IMAGE_MENU_ITEM(item), TRUE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	G_GNUC_END_IGNORE_DEPRECATIONS;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	g_free(name);
 | 
			
		||||
	g_signal_connect_data(item, "activate", G_CALLBACK(open_context_launch),
 | 
			
		||||
		ctx, open_context_notify, 0);
 | 
			
		||||
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_chooser_activate(GtkMenuItem *item, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	OpenContext *ctx = user_data;
 | 
			
		||||
	GtkWindow *window = NULL;
 | 
			
		||||
	GtkWidget *widget = g_weak_ref_get(&ctx->widget);
 | 
			
		||||
	if (widget) {
 | 
			
		||||
		if (GTK_IS_WINDOW((widget = gtk_widget_get_toplevel(widget))))
 | 
			
		||||
			window = GTK_WINDOW(widget);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GtkWidget *dialog = gtk_app_chooser_dialog_new_for_content_type(window,
 | 
			
		||||
		GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, ctx->content_type);
 | 
			
		||||
	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
 | 
			
		||||
		ctx->app_info = gtk_app_chooser_get_app_info(GTK_APP_CHOOSER(dialog));
 | 
			
		||||
		open_context_launch(GTK_WIDGET(item), ctx);
 | 
			
		||||
	}
 | 
			
		||||
	gtk_widget_destroy(dialog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
destroy_widget_idle_source_func(GtkWidget *widget)
 | 
			
		||||
{
 | 
			
		||||
	// The whole menu is deactivated /before/ any item is activated,
 | 
			
		||||
	// and a destroyed child item will not activate.
 | 
			
		||||
	gtk_widget_destroy(widget);
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GtkMenu *
 | 
			
		||||
make_context_menu(GtkWidget *widget, GFile *file)
 | 
			
		||||
{
 | 
			
		||||
	GFileInfo *info = g_file_query_info(file,
 | 
			
		||||
		G_FILE_ATTRIBUTE_STANDARD_NAME
 | 
			
		||||
		"," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
 | 
			
		||||
		G_FILE_QUERY_INFO_NONE, NULL, NULL);
 | 
			
		||||
	if (!info)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	// This will have no application pre-assigned, for use with GTK+'s dialog.
 | 
			
		||||
	OpenContext *ctx = g_malloc0(sizeof *ctx);
 | 
			
		||||
	g_weak_ref_init(&ctx->widget, widget);
 | 
			
		||||
	ctx->file = g_object_ref(file);
 | 
			
		||||
	ctx->content_type = g_strdup(g_file_info_get_content_type(info));
 | 
			
		||||
	g_object_unref(info);
 | 
			
		||||
 | 
			
		||||
	GAppInfo *default_ =
 | 
			
		||||
		g_app_info_get_default_for_type(ctx->content_type, FALSE);
 | 
			
		||||
	GList *recommended = g_app_info_get_recommended_for_type(ctx->content_type);
 | 
			
		||||
	GList *fallback = g_app_info_get_fallback_for_type(ctx->content_type);
 | 
			
		||||
 | 
			
		||||
	GtkWidget *menu = gtk_menu_new();
 | 
			
		||||
	if (default_) {
 | 
			
		||||
		append_opener(menu, default_, ctx);
 | 
			
		||||
		gtk_menu_shell_append(
 | 
			
		||||
			GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (GList *iter = recommended; iter; iter = iter->next) {
 | 
			
		||||
		if (!default_ || !g_app_info_equal(iter->data, default_))
 | 
			
		||||
			append_opener(menu, iter->data, ctx);
 | 
			
		||||
		else
 | 
			
		||||
			g_object_unref(iter->data);
 | 
			
		||||
	}
 | 
			
		||||
	if (recommended) {
 | 
			
		||||
		g_list_free(recommended);
 | 
			
		||||
		gtk_menu_shell_append(
 | 
			
		||||
			GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (GList *iter = fallback; iter; iter = iter->next) {
 | 
			
		||||
		if (!default_ || !g_app_info_equal(iter->data, default_))
 | 
			
		||||
			append_opener(menu, iter->data, ctx);
 | 
			
		||||
		else
 | 
			
		||||
			g_object_unref(iter->data);
 | 
			
		||||
	}
 | 
			
		||||
	if (fallback) {
 | 
			
		||||
		g_list_free(fallback);
 | 
			
		||||
		gtk_menu_shell_append(
 | 
			
		||||
			GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GtkWidget *item = gtk_menu_item_new_with_label("Open With...");
 | 
			
		||||
	g_signal_connect_data(item, "activate", G_CALLBACK(on_chooser_activate),
 | 
			
		||||
		ctx, open_context_notify, 0);
 | 
			
		||||
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 | 
			
		||||
 | 
			
		||||
	// As per GTK+ 3 Common Questions, 1.5.
 | 
			
		||||
	g_object_ref_sink(menu);
 | 
			
		||||
	g_signal_connect_swapped(menu, "deactivate",
 | 
			
		||||
		G_CALLBACK(g_idle_add), destroy_widget_idle_source_func);
 | 
			
		||||
	g_signal_connect(menu, "destroy", G_CALLBACK(g_object_unref), NULL);
 | 
			
		||||
 | 
			
		||||
	gtk_widget_show_all(menu);
 | 
			
		||||
	return GTK_MENU(menu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
show_context_menu(GtkWidget *widget, GFile *file)
 | 
			
		||||
{
 | 
			
		||||
	gtk_menu_popup_at_pointer(make_context_menu(widget, file), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Boilerplate -------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE_EXTENDED(FivBrowser, fiv_browser, GTK_TYPE_WIDGET, 0,
 | 
			
		||||
@@ -1199,6 +1019,12 @@ open_entry(GtkWidget *self, const Entry *entry, gboolean new_window)
 | 
			
		||||
	return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
show_context_menu(GtkWidget *widget, GFile *file)
 | 
			
		||||
{
 | 
			
		||||
	gtk_menu_popup_at_pointer(fiv_context_menu_new(widget, file), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
abort_button_tracking(FivBrowser *self)
 | 
			
		||||
{
 | 
			
		||||
@@ -1600,7 +1426,7 @@ fiv_browser_popup_menu(GtkWidget *widget)
 | 
			
		||||
		file = g_object_ref(fiv_io_model_get_location(self->model));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gtk_menu_popup_at_rect(make_context_menu(widget, file),
 | 
			
		||||
	gtk_menu_popup_at_rect(fiv_context_menu_new(widget, file),
 | 
			
		||||
		gtk_widget_get_window(widget), &rect, GDK_GRAVITY_NORTH_WEST,
 | 
			
		||||
		GDK_GRAVITY_NORTH_WEST, NULL);
 | 
			
		||||
	g_object_unref(file);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user