From 8251c0a4623e281fe25e420820f57b4c727a5bd2 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 25 Jan 2015 01:12:13 +0100 Subject: add recent items to context menu --- configure.ac | 20 +++- libmdock/Makefile.am | 18 +--- libmdock/mdock-enums.c.template | 38 ------- libmdock/mdock-enums.h.template | 24 ----- libmdock/mdock-item-menu.c | 185 ++++++++++++++++++++++++++++++---- libmdock/mdock-item-window-selector.c | 6 ++ libmdock/mdock-item.c | 20 +++- libmdock/mdock-item.h | 2 + libmdock/mdock-widget.c | 11 ++ 9 files changed, 223 insertions(+), 101 deletions(-) delete mode 100644 libmdock/mdock-enums.c.template delete mode 100644 libmdock/mdock-enums.h.template diff --git a/configure.ac b/configure.ac index 5e64854..5a205be 100644 --- a/configure.ac +++ b/configure.ac @@ -14,6 +14,11 @@ AC_ARG_ENABLE([mate-applet], [], [enable_mate_applet=check]) +AC_ARG_WITH([zeitgeist], + [AS_HELP_STRING([--with-zeitgeist], [use libzeitgeist to populate recent items menu @<:@default=check@:>@])], + [], + [with_zeitgeist=check]) + # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O @@ -31,7 +36,7 @@ PKG_CHECK_MODULES([GTOP], [libgtop-2.0]) AS_IF([test "x$enable_mate_applet" != xno], [PKG_CHECK_MODULES([MATEPANELAPPLET], [libmatepanelapplet-4.0], [ - AC_DEFINE([HAVE_MATEPANELAPPLET], [1], [Define if you have libmatepanelapplet]) + AC_DEFINE([HAVE_MATEPANELAPPLET], [1], [Define to 1 if you have libmatepanelapplet]) enable_mate_applet=yes ], [if test "x$enable_mate_applet" = xyes; then @@ -40,6 +45,19 @@ AS_IF([test "x$enable_mate_applet" != xno], )] ) +AS_IF([test "x$with_zeitgeist" != xno], + [PKG_CHECK_MODULES([ZEITGEIST], [zeitgeist-2.0], + [ + AC_DEFINE([HAVE_ZEITGEIST], [1], [Define to 1 if you have libzeitgeist]) + with_zeitgeist=yes + ], + [if test "x$with_zeitgeist" = xyes; then + AC_MSG_FAILURE([--with-zeitgeist was given, but test for zeitgeist-2.0 failed]) + fi] + )] +) + + AM_CONDITIONAL([WANT_MATE_APPLET], [test x$enable_mate_applet = xyes]) # Output files diff --git a/libmdock/Makefile.am b/libmdock/Makefile.am index 3abcc9d..25c0fff 100644 --- a/libmdock/Makefile.am +++ b/libmdock/Makefile.am @@ -1,20 +1,10 @@ lib_LTLIBRARIES = libmdock.la -libmdock_la_SOURCES = mdock-widget.h mdock-widget.c mdock-enums.h mdock-enums.c mdock-item.h mdock-item.c mdock-item-menu.c mdock-item-menu.h mdock-item-window-selector.c mdock-item-window-selector.h mdock-window.h mdock-window.c matcher.h matcher.c app-id.h app-id.c thumbnailer.h thumbnailer.c -libmdock_la_CPPFLAGS = $(GTK_CFLAGS) $(GIO_CFLAGS) $(WNCK_CFLAGS) $(GTOP_CFLAGS) -D_GNU_SOURCE -DG_LOG_DOMAIN=\"libmdock\" -libmdock_la_LIBADD = $(GTK_LIBS) $(GIO_LIBS) $(WNCK_LIBS) $(GTOP_LIBS) +libmdock_la_SOURCES = mdock-widget.h mdock-widget.c mdock-item.h mdock-item.c mdock-item-menu.c mdock-item-menu.h mdock-item-window-selector.c mdock-item-window-selector.h mdock-window.h mdock-window.c matcher.h matcher.c app-id.h app-id.c thumbnailer.h thumbnailer.c +libmdock_la_CPPFLAGS = $(GTK_CFLAGS) $(GIO_CFLAGS) $(WNCK_CFLAGS) $(GTOP_CFLAGS) $(ZEITGEIST_CFLAGS) -D_GNU_SOURCE -DG_LOG_DOMAIN=\"libmdock\" +libmdock_la_LIBADD = $(GTK_LIBS) $(GIO_LIBS) $(WNCK_LIBS) $(GTOP_LIBS) $(ZEITGEIST_LIBS) gsettings_SCHEMAS = com.javispedro.mdock.widget.gschema.xml -include_HEADERS = mdock-widget.h mdock-item.h -CLEANFILES = mdock-enums.c mdock-enums.h - -EXTRA_DIST = mdock-enums.h.template mdock-enums.c.template -BUILT_SOURCES = mdock-enums.h mdock-enums.c - -mdock-enums.c: mdock-enums.c.template $(include_HEADERS) - glib-mkenums --template $< $(include_HEADERS) > $@ - -mdock-enums.h: mdock-enums.h.template $(include_HEADERS) - glib-mkenums --template $< $(include_HEADERS) > $@ +include_HEADERS = mdock-widget.h @GSETTINGS_RULES@ diff --git a/libmdock/mdock-enums.c.template b/libmdock/mdock-enums.c.template deleted file mode 100644 index 551a860..0000000 --- a/libmdock/mdock-enums.c.template +++ /dev/null @@ -1,38 +0,0 @@ -/*** BEGIN file-header ***/ -#include "mdock-enums.h" -#include "mdock-widget.h" -#include "mdock-item.h" - -/*** END file-header ***/ - -/*** BEGIN file-production ***/ -/* enumerations from "@filename@" */ -/*** END file-production ***/ - -/*** BEGIN value-header ***/ -GType -@enum_name@_get_type (void) -{ - static volatile gsize g_define_type_id__volatile = 0; - - if (g_once_init_enter (&g_define_type_id__volatile)) - { - static const G@Type@Value values[] = { -/*** END value-header ***/ - -/*** BEGIN value-production ***/ - { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, -/*** END value-production ***/ - -/*** BEGIN value-tail ***/ - { 0, NULL, NULL } - }; - GType g_define_type_id = - g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); - g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); - } - - return g_define_type_id__volatile; -} - -/*** END value-tail ***/ diff --git a/libmdock/mdock-enums.h.template b/libmdock/mdock-enums.h.template deleted file mode 100644 index d669580..0000000 --- a/libmdock/mdock-enums.h.template +++ /dev/null @@ -1,24 +0,0 @@ -/*** BEGIN file-header ***/ -#ifndef _MDOCK_ENUMS_H_ -#define _MDOCK_ENUMS_H_ - -#include - -G_BEGIN_DECLS -/*** END file-header ***/ - -/*** BEGIN file-production ***/ - -/* enumerations from "@filename@" */ -/*** END file-production ***/ - -/*** BEGIN value-header ***/ -GLIB_AVAILABLE_IN_ALL GType @enum_name@_get_type (void) G_GNUC_CONST; -#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) -/*** END value-header ***/ - -/*** BEGIN file-tail ***/ -G_END_DECLS - -#endif /* _MDOCK_ENUMS_H_ */ -/*** END file-tail ***/ diff --git a/libmdock/mdock-item-menu.c b/libmdock/mdock-item-menu.c index 3138c86..bdf0184 100644 --- a/libmdock/mdock-item-menu.c +++ b/libmdock/mdock-item-menu.c @@ -18,9 +18,15 @@ */ #include - +#include "../config.h" #include "mdock-item-menu.h" +#ifdef HAVE_ZEITGEIST +#include +#endif + +#define NUM_RECENT_FILES 10u + #define MDOCK_ITEM_MENU_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), MDOCK_TYPE_ITEM_MENU, MDockItemMenuPrivate)) struct _MDockItemMenuPrivate @@ -29,6 +35,9 @@ struct _MDockItemMenuPrivate GtkImageMenuItem *menu_close_all; GtkImageMenuItem *menu_pin; GtkImageMenuItem *menu_launch; + + GPtrArray *menu_recent; + GCancellable *recent_fetch; }; G_DEFINE_TYPE (MDockItemMenu, mdock_item_menu, GTK_TYPE_MENU) @@ -41,6 +50,144 @@ enum { static GParamSpec *obj_properties[N_PROPERTIES] = { NULL }; +#ifdef HAVE_ZEITGEIST + +G_DEFINE_QUARK(mdock-item-menu-file, mdock_item_menu_file) + +static void clear_recent_menu(MDockItemMenu *self) +{ + g_ptr_array_set_size(self->priv->menu_recent, 0); + gtk_widget_hide(GTK_WIDGET(self->priv->menu_sep1)); +} + +static void handle_recent_activate(MDockItemMenu *self, GtkImageMenuItem *menu_item) +{ + GFile *file = g_object_get_qdata(G_OBJECT(menu_item), mdock_item_menu_file_quark()); + g_return_if_fail(file); + + mdock_item_launch_file(self->item, file); +} + +static void recents_icon_ready(GObject *source, GAsyncResult *res, gpointer user_data) +{ + GtkImageMenuItem *menu_item = GTK_IMAGE_MENU_ITEM(user_data); + GFile *file = G_FILE(source); + GError *error = NULL; + GFileInfo *info = g_file_query_info_finish(file, res, &error); + if (info) { + + } else { + gchar *uri = g_file_get_uri(file); + g_message("Could not get icon for file '%s': %s", uri, error->message); + g_free(uri); + g_error_free(error); + return; + } + GIcon *icon = g_file_info_get_icon(info); + if (icon) { + GtkImage *image = GTK_IMAGE(gtk_image_menu_item_get_image(menu_item)); + gtk_image_set_from_gicon(image, icon, GTK_ICON_SIZE_MENU); + } + + g_object_unref(info); +} + +static void recents_result_ready(GObject *source, GAsyncResult *res, gpointer user_data) +{ + MDockItemMenu *self = MDOCK_ITEM_MENU(user_data); + ZeitgeistLog *log = ZEITGEIST_LOG(source); + GError *error = NULL; + ZeitgeistResultSet *results = zeitgeist_log_find_events_finish(log, res, &error); + if (error) { + g_warning("Could not get recents from Zeitgeist: %s", error->message); + g_error_free(error); + return; + } + g_return_if_fail(results); + + guint nelems = zeitgeist_result_set_size(results); + g_return_if_fail(nelems <= NUM_RECENT_FILES); + + g_ptr_array_set_size(self->priv->menu_recent, nelems); + + guint i = 0; + + while (zeitgeist_result_set_has_next(results)) { + ZeitgeistEvent *event = zeitgeist_result_set_next_value(results); + g_warn_if_fail(event); + g_warn_if_fail(zeitgeist_event_num_subjects(event) == 1); + ZeitgeistSubject *subject = zeitgeist_event_get_subject(event, 0); + g_warn_if_fail(subject); + GFile *file = g_file_new_for_uri(zeitgeist_subject_get_uri(subject)); + GtkImageMenuItem *menu_item = self->priv->menu_recent->pdata[i]; + if (!menu_item) { + menu_item = self->priv->menu_recent->pdata[i] = gtk_image_menu_item_new_with_label(zeitgeist_subject_get_text(subject)); + gtk_image_menu_item_set_image(menu_item, gtk_image_new()); + gtk_image_menu_item_set_always_show_image(menu_item, TRUE); + gtk_menu_shell_append(GTK_MENU_SHELL(self), GTK_WIDGET(menu_item)); + gtk_widget_show(GTK_WIDGET(menu_item)); + g_signal_connect_swapped(menu_item, "activate", + G_CALLBACK(handle_recent_activate), self); + } else { + gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), zeitgeist_subject_get_text(subject)); + gtk_image_clear(GTK_IMAGE(gtk_image_menu_item_get_image(menu_item))); + } + + // Takes ownership of "file" + g_object_set_qdata_full(G_OBJECT(menu_item), mdock_item_menu_file_quark(), + file, (GDestroyNotify)g_object_unref); + + g_file_query_info_async(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, + G_PRIORITY_LOW, self->priv->recent_fetch, + recents_icon_ready, menu_item); + + g_object_unref(event); + g_object_unref(subject); + + i++; + } + + g_object_unref(results); + + g_ptr_array_set_size(self->priv->menu_recent, i); + + gtk_widget_set_visible(GTK_WIDGET(self->priv->menu_sep1), i > 0); +} + +static void refresh_recent_menu(MDockItemMenu *self) +{ + ZeitgeistLog *log = zeitgeist_log_get_default(); + g_return_if_fail(log); + + if (self->priv->recent_fetch) { + g_cancellable_cancel(self->priv->recent_fetch); + g_clear_object(&self->priv->recent_fetch); + } + + self->priv->recent_fetch = g_cancellable_new(); + + GDesktopAppInfo *appinfo = mdock_item_get_desktop_app_info(self->item); + if (!appinfo) { + clear_recent_menu(self); + return; + } + + ZeitgeistTimeRange *range = zeitgeist_time_range_new_anytime(); + + GPtrArray *templs = g_ptr_array_new_full(1, (GDestroyNotify)g_object_unref); + ZeitgeistEvent *templ = zeitgeist_event_new(); + zeitgeist_event_set_actor_from_app_info(templ, G_APP_INFO(appinfo)); + g_ptr_array_add(templs, templ); + + zeitgeist_log_find_events(log, range, templs, ZEITGEIST_STORAGE_STATE_ANY, + NUM_RECENT_FILES, ZEITGEIST_RESULT_TYPE_MOST_RECENT_SUBJECTS, + NULL, recents_result_ready, self); + + g_object_unref(range); + g_ptr_array_unref(templs); +} +#endif + static void handle_item_pinned(MDockItemMenu *self, GParamSpec *spec, MDockItem *item) { const gboolean pinned = mdock_item_get_pinned(item); @@ -56,23 +203,9 @@ static void handle_item_desktop_app_info(MDockItemMenu *self, GParamSpec *spec, gtk_widget_set_visible(GTK_WIDGET(self->priv->menu_launch), appinfo != NULL); if (!gtk_image_menu_item_get_image(self->priv->menu_launch) && appinfo) { - gint width, height; - if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height)) { - GtkIconTheme *theme = gtk_icon_theme_get_default(); - gint icon_size = MAX(width, height); - GIcon *icon = g_app_info_get_icon(G_APP_INFO(appinfo)); - GtkIconInfo *info = gtk_icon_theme_lookup_by_gicon(theme, icon, icon_size, - GTK_ICON_LOOKUP_GENERIC_FALLBACK | GTK_ICON_LOOKUP_FORCE_SIZE); - if (info) { - GdkPixbuf *pixbuf = gtk_icon_info_load_icon(info, NULL); - if (pixbuf) { - GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); - gtk_image_menu_item_set_image(self->priv->menu_launch, image); - g_object_unref(pixbuf); - } - gtk_icon_info_free(info); - } - } + GIcon *icon = g_app_info_get_icon(G_APP_INFO(appinfo)); + GtkWidget *image = gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(self->priv->menu_launch, image); } } @@ -148,6 +281,10 @@ mdock_item_menu_constructed(GObject *object) handle_item_desktop_app_info(self, NULL, self->item); handle_item_pinned(self, NULL, self->item); + +#ifdef HAVE_ZEITGEIST + refresh_recent_menu(self); +#endif } @@ -155,13 +292,17 @@ static void mdock_item_menu_dispose(GObject *object) { MDockItemMenu *self = MDOCK_ITEM_MENU(object); + g_cancellable_cancel(self->priv->recent_fetch); g_clear_object(&self->item); + g_ptr_array_free(self->priv->menu_recent, TRUE); + g_clear_object(&self->priv->recent_fetch); G_OBJECT_CLASS (mdock_item_menu_parent_class)->dispose(object); } static void mdock_item_menu_finalize (GObject *object) { + MDockItemMenu *self = MDOCK_ITEM_MENU(object); G_OBJECT_CLASS (mdock_item_menu_parent_class)->finalize (object); } @@ -170,9 +311,6 @@ mdock_item_menu_init (MDockItemMenu *self) { self->priv = MDOCK_ITEM_MENU_GET_PRIVATE (self); - self->priv->menu_sep1 = GTK_SEPARATOR_MENU_ITEM(gtk_separator_menu_item_new()); - gtk_menu_shell_append(GTK_MENU_SHELL(self), GTK_WIDGET(self->priv->menu_sep1)); - self->priv->menu_close_all = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, NULL)); gtk_menu_shell_append(GTK_MENU_SHELL(self), GTK_WIDGET(self->priv->menu_close_all)); @@ -181,6 +319,11 @@ mdock_item_menu_init (MDockItemMenu *self) self->priv->menu_launch = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(_("_New instance"))); gtk_menu_shell_append(GTK_MENU_SHELL(self), GTK_WIDGET(self->priv->menu_launch)); + + self->priv->menu_sep1 = GTK_SEPARATOR_MENU_ITEM(gtk_separator_menu_item_new()); + gtk_menu_shell_append(GTK_MENU_SHELL(self), GTK_WIDGET(self->priv->menu_sep1)); + + self->priv->menu_recent = g_ptr_array_new_with_free_func((GDestroyNotify)gtk_widget_destroy); } static void diff --git a/libmdock/mdock-item-window-selector.c b/libmdock/mdock-item-window-selector.c index ea5ceb8..2885a76 100644 --- a/libmdock/mdock-item-window-selector.c +++ b/libmdock/mdock-item-window-selector.c @@ -64,6 +64,8 @@ static GSequenceIter * find_window(MDockItemWindowSelector *self, WnckWindow *wi static gboolean handle_window_button_release(MDockItemWindowSelector *self, GdkEventButton *event, MDockWindow *window) { + g_debug("window item button release"); + switch (event->button) { case 1: wnck_window_activate_transient(window->window, event->time); @@ -201,6 +203,10 @@ void mdock_item_window_selector_remove_window(MDockItemWindowSelector *self, Wnc if (g_sequence_get_length(self->priv->windows) == 0) { gtk_widget_show(GTK_WIDGET(self->priv->app_label)); } + + GtkRequisition requisition; + gtk_widget_size_request(GTK_WIDGET(self), &requisition); + gtk_window_resize(GTK_WINDOW(self), requisition.width, requisition.height); } void mdock_item_window_selector_set_active_window(MDockItemWindowSelector *self, WnckWindow *window) diff --git a/libmdock/mdock-item.c b/libmdock/mdock-item.c index 67f581f..67d3638 100644 --- a/libmdock/mdock-item.c +++ b/libmdock/mdock-item.c @@ -17,7 +17,6 @@ * along with MDock. If not, see . */ -#include "mdock-enums.h" #include "mdock-item.h" #define MDOCK_ITEM_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), MDOCK_TYPE_ITEM, MDockItemPrivate)) @@ -293,12 +292,13 @@ void mdock_item_set_desktop_app_info(MDockItem *self, GDesktopAppInfo *app_info) g_object_set(self, "desktop-app-info", app_info, NULL); } -void mdock_item_launch(MDockItem *self) +void mdock_item_launch_files(MDockItem *self, GList *files) { g_return_if_fail(self->priv->appinfo); GError *error = NULL; g_debug("Launching '%s'", g_app_info_get_commandline(G_APP_INFO(self->priv->appinfo))); - if (!g_app_info_launch(G_APP_INFO(self->priv->appinfo), NULL, NULL, &error)) { + + if (!g_app_info_launch(G_APP_INFO(self->priv->appinfo), files, NULL, &error)) { GtkWidget *msg; g_warning("Cannot launch '%s': %s", @@ -319,6 +319,20 @@ void mdock_item_launch(MDockItem *self) gtk_widget_show_all(msg); g_error_free(error); } + + +} + +void mdock_item_launch_file(MDockItem *self, GFile *file) +{ + GList *files = g_list_append(NULL, file); + mdock_item_launch_files(self, files); + g_list_free(files); +} + +void mdock_item_launch(MDockItem *self) +{ + mdock_item_launch_files(self, NULL); } const gchar *mdock_item_get_display_name(MDockItem *self) diff --git a/libmdock/mdock-item.h b/libmdock/mdock-item.h index 0048a8d..7379687 100644 --- a/libmdock/mdock-item.h +++ b/libmdock/mdock-item.h @@ -58,6 +58,8 @@ void mdock_item_toggle_pinned(MDockItem *self); GDesktopAppInfo *mdock_item_get_desktop_app_info(MDockItem *self); void mdock_item_set_desktop_app_info(MDockItem *self, GDesktopAppInfo *app_info); +void mdock_item_launch_files(MDockItem *self, GList *files); +void mdock_item_launch_file(MDockItem *self, GFile *file); void mdock_item_launch(MDockItem *self); const gchar *mdock_item_get_display_name(MDockItem *self); diff --git a/libmdock/mdock-widget.c b/libmdock/mdock-widget.c index 004a590..5b3daed 100644 --- a/libmdock/mdock-widget.c +++ b/libmdock/mdock-widget.c @@ -22,12 +22,17 @@ #define WNCK_I_KNOW_THIS_IS_UNSTABLE 1 #include +#include "../config.h" #include "mdock-widget.h" #include "mdock-item-window-selector.h" #include "mdock-item-menu.h" #include "mdock-item.h" #include "matcher.h" +#ifdef HAVE_ZEITGEIST +#include +#endif + #define POPUP_SHOW_TIMEOUT 200 #define POPUP_HIDE_TIMEOUT 500 @@ -769,6 +774,12 @@ static void mdock_widget_init(MDockWidget *self) { self->priv = MDOCK_WIDGET_GET_PRIVATE(self); +#ifdef HAVE_ZEITGEIST + // Initialize zeitgeist now, + // since hilarity ensues if you try to do it on a Wnck callback... + zeitgeist_log_get_default(); +#endif + gtk_drag_dest_set(GTK_WIDGET(self), 0, drag_types, G_N_ELEMENTS(drag_types), GDK_ACTION_MOVE); -- cgit v1.2.3