Files
xfce-winxp-tc/shell/taskband/src/start/personal.c

1319 lines
32 KiB
C

#include <garcon/garcon.h>
#include <garcon-gtk/garcon-gtk.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <pwd.h>
#include <sys/types.h>
#include <wintc/comctl.h>
#include <wintc/comgtk.h>
#include <wintc/exec.h>
#include <wintc/shelldpa.h>
#include <wintc/shlang.h>
#include "menumod.h"
#include "mfu.h"
#include "personal.h"
#include "progmenu.h"
#include "shared.h"
#include "toolbar.h"
#include "util.h"
#define DEFAULT_ITEM_COUNT 2
#define MAX_MFU_ITEM_COUNT 6
#define TOTAL_PROGRAMS_ITEM_COUNT (DEFAULT_ITEM_COUNT + MAX_MFU_ITEM_COUNT)
#define PLACE_ICON_SIZE 24
#define PROGRAM_ICON_SIZE 32
//
// PRIVATE STRUCTS
//
typedef struct _StartSignalTuple
{
WinTCToolbarStart* toolbar_start;
gboolean is_action;
gpointer user_data;
} StartSignalTuple;
//
// FORWARD DECLARATIONS
//
static void connect_menu_shell_signals(
GtkMenuShell* menu_shell,
WinTCToolbarStart* toolbar_start
);
static GtkWidget* create_personal_menu_item(
GtkMenuShell* menu_shell,
const gchar* icon_name,
const gchar* program_name,
const gchar* comment,
const gchar* generic_name
);
static GtkWidget* create_personal_menu_item_from_desktop_entry(
GDesktopAppInfo* entry,
StartSignalTuple* signal_tuple,
const gchar* comment,
const gchar* generic_name
);
static void refresh_personal_menu(
WinTCToolbarStart* toolbar_start
);
static void refresh_userpic(
WinTCToolbarStart* toolbar_start
);
static void update_personal_menu_mfu_items(
WinTCToolbarStart* toolbar_start
);
static void clear_signal_tuple(
StartSignalTuple* tuple
);
static gboolean recent_filter_exclude_directories(
const GtkRecentFilterInfo* filter_info,
gpointer user_data
);
static void on_button_power_clicked(
GtkButton* button,
gpointer user_data
);
static void on_event_box_userpic_clicked(
GtkWidget* widget,
GdkEventButton* event,
gpointer user_data
);
static void on_menu_item_launcher_activate(
GtkMenuItem* self,
gpointer user_data
);
static void on_menu_item_recent_activated(
GtkRecentChooser* self,
gpointer user_data
);
static void on_menu_item_with_submenu_deselect(
GtkMenuItem* self,
gpointer user_data
);
static void on_menu_shell_selection_done(
GtkMenuShell* self,
gpointer user_data
);
static void on_menu_shell_submenu_selection_done(
GtkMenuShell* self,
gpointer user_data
);
static void on_mfu_tracker_updated(
WinTCStartMfuTracker* self,
gpointer user_data
);
static void on_personal_menu_hide(
GtkWidget* self,
gpointer user_data
);
//
// STATIC DATA
//
static GQuark S_QUARK_PERSONAL_ITEM_TUPLE = 0;
//
// INTERNAL FUNCTIONS
//
void close_personal_menu(
WinTCToolbarStart* toolbar_start
)
{
gtk_widget_hide(toolbar_start->personal.popup_menu);
}
void create_personal_menu(
WinTCToolbarStart* toolbar_start
)
{
GtkBuilder* builder;
if (!S_QUARK_PERSONAL_ITEM_TUPLE)
{
S_QUARK_PERSONAL_ITEM_TUPLE =
g_quark_from_static_string("personal-tuple");
}
// Set default states
//
toolbar_start->personal.sync_menu_refresh = TRUE;
// Init GArrays for signal tuples
//
toolbar_start->personal.tuples_places = g_array_new(
FALSE,
TRUE,
sizeof(StartSignalTuple)
);
toolbar_start->personal.tuples_programs = g_array_new(
FALSE,
TRUE,
sizeof(StartSignalTuple)
);
g_array_set_clear_func(
toolbar_start->personal.tuples_programs,
(GDestroyNotify) clear_signal_tuple
);
// Ensure our modded menu type is known to GObject
//
g_type_ensure(WINTC_TYPE_MENU_MODDED);
// Construct menu from XML
//
builder =
gtk_builder_new_from_resource(
"/uk/oddmatics/wintc/taskband/personal-menu.ui"
);
wintc_lc_builder_preprocess_widget_text(builder);
// Pull UI elements we store for later usage
//
toolbar_start->personal.button_logoff =
GTK_WIDGET(gtk_builder_get_object(builder, "button-logoff"));
toolbar_start->personal.button_shutdown =
GTK_WIDGET(gtk_builder_get_object(builder, "button-shutdown"));
toolbar_start->personal.eventbox_userpic =
GTK_WIDGET(gtk_builder_get_object(builder, "eventbox-userpic"));
toolbar_start->personal.menubar_places =
GTK_WIDGET(gtk_builder_get_object(builder, "menubar-places"));
toolbar_start->personal.menubar_programs =
GTK_WIDGET(gtk_builder_get_object(builder, "menubar-programs"));
toolbar_start->personal.menuitem_all_programs =
GTK_WIDGET(gtk_builder_get_object(builder, "menuitem-all-programs"));
toolbar_start->personal.separator_all_programs =
GTK_WIDGET(gtk_builder_get_object(builder, "separator-all-programs"));
// Sort out user pic
//
toolbar_start->personal.style_userpic =
GTK_STYLE_PROVIDER(gtk_css_provider_new());
gtk_style_context_add_provider(
gtk_widget_get_style_context(
gtk_widget_get_parent(toolbar_start->personal.eventbox_userpic)
),
toolbar_start->personal.style_userpic,
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
gtk_widget_set_events(
toolbar_start->personal.eventbox_userpic,
GDK_BUTTON_PRESS_MASK
);
g_signal_connect(
toolbar_start->personal.eventbox_userpic,
"button-press-event",
G_CALLBACK(on_event_box_userpic_clicked),
NULL
);
refresh_userpic(toolbar_start);
// Set username labels
//
struct passwd* user_pwd = getpwuid(getuid());
gtk_label_set_text(
GTK_LABEL(gtk_builder_get_object(builder, "label-username-horz")),
user_pwd->pw_name
);
gtk_label_set_text(
GTK_LABEL(gtk_builder_get_object(builder, "label-username-vert")),
user_pwd->pw_name
);
// Attach Recent Documents submenu
//
GtkWidget* menu_recents = gtk_recent_chooser_menu_new();
GtkRecentFilter* recent_filter = gtk_recent_filter_new();
gtk_recent_filter_add_custom(
recent_filter,
GTK_RECENT_FILTER_MIME_TYPE,
(GtkRecentFilterFunc) recent_filter_exclude_directories,
NULL,
NULL
);
gtk_recent_chooser_set_filter(
GTK_RECENT_CHOOSER(menu_recents),
recent_filter
);
gtk_recent_chooser_set_sort_type(
GTK_RECENT_CHOOSER(menu_recents),
GTK_RECENT_SORT_MRU
);
gtk_menu_item_set_submenu(
GTK_MENU_ITEM(gtk_builder_get_object(builder, "menuitem-recent-docs")),
menu_recents
);
g_signal_connect(
menu_recents,
"item-activated",
G_CALLBACK(on_menu_item_recent_activated),
toolbar_start
);
// Attach All Programs submenu
//
gtk_menu_item_set_submenu(
GTK_MENU_ITEM(toolbar_start->personal.menuitem_all_programs),
wintc_toolbar_start_progmenu_new_gtk_menu(
toolbar_start->progmenu,
&(toolbar_start->personal.all_programs_binding)
)
);
// Transfer to popup
//
toolbar_start->personal.popup_menu =
wintc_dpa_create_popup(
toolbar_start->start_button,
TRUE
);
wintc_widget_add_style_class(
toolbar_start->personal.popup_menu,
"wintc-personal-menu"
);
gtk_container_add(
GTK_CONTAINER(toolbar_start->personal.popup_menu),
GTK_WIDGET(gtk_builder_get_object(builder, "main-box"))
);
g_object_unref(G_OBJECT(builder));
g_signal_connect(
toolbar_start->personal.popup_menu,
"hide",
G_CALLBACK(on_personal_menu_hide),
toolbar_start
);
// Take extra references to All Programs items, so we can safely take
// them out of the menu bar whilst refreshing without them being disposed
//
g_object_ref(toolbar_start->personal.menuitem_all_programs);
g_object_ref(toolbar_start->personal.separator_all_programs);
// Attach signals
//
connect_menu_shell_signals(
GTK_MENU_SHELL(toolbar_start->personal.menubar_places),
toolbar_start
);
connect_menu_shell_signals(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
toolbar_start
);
// Attach actions for places - the order of WinTCAction is the same as
// these Start menu items
//
StartSignalTuple* tuple;
GList* elements;
GList* li;
gint i = 0;
elements =
gtk_container_get_children(
GTK_CONTAINER(toolbar_start->personal.menubar_places)
);
li = elements;
g_array_set_size(
toolbar_start->personal.tuples_places,
g_list_length(elements)
);
while (li)
{
GtkMenuItem* menu_item = GTK_MENU_ITEM(li->data);
// Skip separators
//
if (!GTK_IS_SEPARATOR_MENU_ITEM(menu_item))
{
// Only connect activation for items without submenus
//
if (!gtk_menu_item_get_submenu(menu_item))
{
tuple =
&g_array_index(
toolbar_start->personal.tuples_places,
StartSignalTuple,
i
);
tuple->is_action = TRUE;
tuple->toolbar_start = toolbar_start;
tuple->user_data = GINT_TO_POINTER(i);
g_signal_connect(
menu_item,
"activate",
G_CALLBACK(on_menu_item_launcher_activate),
tuple
);
}
i++;
}
li = li->next;
}
g_list_free(elements);
// Attach power button signals
//
g_signal_connect(
toolbar_start->personal.button_logoff,
"clicked",
G_CALLBACK(on_button_power_clicked),
GINT_TO_POINTER(WINTC_ACTION_LOGOFF)
);
g_signal_connect(
toolbar_start->personal.button_shutdown,
"clicked",
G_CALLBACK(on_button_power_clicked),
GINT_TO_POINTER(WINTC_ACTION_SHUTDOWN)
);
}
void destroy_personal_menu(
WinTCToolbarStart* toolbar_start
)
{
// Unref extra references on all programs items
//
g_object_unref(
g_steal_pointer(&(toolbar_start->personal.menuitem_all_programs))
);
g_object_unref(
g_steal_pointer(&(toolbar_start->personal.separator_all_programs))
);
// Clear all programs menu model binding object
//
g_clear_object(
&(toolbar_start->personal.all_programs_binding)
);
// Clear signal tuple data
//
g_array_unref(
g_steal_pointer(&(toolbar_start->personal.tuples_places))
);
g_array_unref(
g_steal_pointer(&(toolbar_start->personal.tuples_programs))
);
// Destroy popup
//
gtk_widget_destroy(
g_steal_pointer(&(toolbar_start->personal.popup_menu))
);
}
void open_personal_menu(
WinTCToolbarStart* toolbar_start
)
{
// Reset should close flag, so that selection-done knows no item has
// actually been 'activated' yet
//
toolbar_start->sync_menu_should_close = FALSE;
if (toolbar_start->personal.sync_menu_refresh)
{
refresh_personal_menu(toolbar_start);
}
wintc_dpa_show_popup(
toolbar_start->personal.popup_menu,
toolbar_start->start_button
);
}
//
// PRIVATE FUNCTIONS
//
static void connect_menu_shell_signals(
GtkMenuShell* menu_shell,
WinTCToolbarStart* toolbar_start
)
{
GtkWidget* child;
GList* children = gtk_container_get_children(
GTK_CONTAINER(menu_shell)
);
GList* li = children;
while (li)
{
child = li->data;
// Connect enter/leave events for auto-focusing the menu shell
//
g_signal_connect(
child,
"enter-notify-event",
G_CALLBACK(wintc_menu_shell_select_on_enter),
menu_shell
);
g_signal_connect(
child,
"leave-notify-event",
G_CALLBACK(wintc_menu_shell_deselect_on_leave),
menu_shell
);
// If the item has a sub-menu, apply a hacks to deal with the funny
// pop-up menu behaviour (if we deselect the menu ourselves, it avoids
// the issue of the submenu somehow 'detaching' itself)
//
if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(child)))
{
g_signal_connect(
child,
"deselect",
G_CALLBACK(on_menu_item_with_submenu_deselect),
NULL
);
g_signal_connect(
gtk_menu_item_get_submenu(GTK_MENU_ITEM(child)),
"selection-done",
G_CALLBACK(on_menu_shell_submenu_selection_done),
toolbar_start
);
}
li = li->next;
}
g_list_free(g_steal_pointer(&children));
g_signal_connect(
menu_shell,
"selection-done",
G_CALLBACK(on_menu_shell_selection_done),
toolbar_start
);
}
static GtkWidget* create_personal_menu_item(
GtkMenuShell* menu_shell,
const gchar* icon_name,
const gchar* program_name,
const gchar* comment,
const gchar* generic_name
)
{
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget* image_icon = gtk_image_new();
GtkWidget* label_program = gtk_label_new(program_name);
GtkWidget* menu_item = gtk_menu_item_new();
// Attempt to load the icon...
//
if (icon_name)
{
GdkPixbuf* pixbuf_icon =
gtk_icon_theme_load_icon(
gtk_icon_theme_get_default(),
icon_name,
PROGRAM_ICON_SIZE,
GTK_ICON_LOOKUP_FORCE_SIZE,
NULL
);
if (!pixbuf_icon)
{
gtk_icon_theme_load_icon(
gtk_icon_theme_get_default(),
"application-x-generic",
PROGRAM_ICON_SIZE,
GTK_ICON_LOOKUP_FORCE_SIZE,
NULL
);
}
if (pixbuf_icon)
{
gtk_image_set_from_pixbuf(
GTK_IMAGE(image_icon),
pixbuf_icon
);
g_object_unref(pixbuf_icon);
}
}
// Set up label properties
//
gtk_label_set_line_wrap(
GTK_LABEL(label_program),
TRUE
);
gtk_label_set_max_width_chars(
GTK_LABEL(label_program),
24
);
gtk_label_set_xalign(
GTK_LABEL(label_program),
0.0
);
// Ensure image widget always requests the right size, in case no icon was
// loaded
//
gtk_image_set_pixel_size(
GTK_IMAGE(image_icon),
PROGRAM_ICON_SIZE
);
gtk_widget_set_size_request(
image_icon,
PROGRAM_ICON_SIZE,
PROGRAM_ICON_SIZE
);
// Pack icon first...
//
gtk_box_pack_start(
GTK_BOX(box),
image_icon,
FALSE,
FALSE,
0
);
// ...then pack the program name/heading...
//
if (generic_name)
{
GtkWidget* inner_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
GtkWidget* label_generic = gtk_label_new(generic_name);
wintc_widget_add_style_class(
menu_item,
"start-default-item"
);
gtk_label_set_xalign(
GTK_LABEL(label_generic),
0.0
);
gtk_widget_set_valign(inner_box, GTK_ALIGN_CENTER);
gtk_box_pack_start(
GTK_BOX(inner_box),
label_generic,
TRUE,
TRUE,
0
);
gtk_box_pack_start(
GTK_BOX(inner_box),
label_program,
TRUE,
TRUE,
0
);
gtk_box_pack_start(
GTK_BOX(box),
inner_box,
TRUE,
TRUE,
0
);
}
else
{
gtk_box_pack_start(
GTK_BOX(box),
label_program,
FALSE,
FALSE,
0
);
}
gtk_container_add(
GTK_CONTAINER(menu_item),
box
);
// ...finally add the comment to the menu item tooltip
//
if (comment)
{
gchar* real_comment = wintc_str_set_suffix(comment, ".");
gtk_widget_set_tooltip_text(menu_item, real_comment);
g_free(real_comment);
}
// Add enter/leave signals for auto-selecting menushell
//
g_signal_connect(
menu_item,
"enter-notify-event",
G_CALLBACK(wintc_menu_shell_select_on_enter),
menu_shell
);
g_signal_connect(
menu_item,
"leave-notify-event",
G_CALLBACK(wintc_menu_shell_deselect_on_leave),
menu_shell
);
return menu_item;
}
static GtkWidget* create_personal_menu_item_from_desktop_entry(
GDesktopAppInfo* entry,
StartSignalTuple* signal_tuple,
const gchar* comment,
const gchar* generic_name
)
{
GtkWidget* menu_item;
if (entry)
{
GAppInfo* app_info = G_APP_INFO(entry);
gchar* icon_name = g_desktop_app_info_get_string(entry, "Icon");
if (!icon_name) // If no icon, try the executable name...
{
icon_name =
g_path_get_basename(
g_app_info_get_executable(app_info)
);
}
menu_item =
create_personal_menu_item(
GTK_MENU_SHELL(
signal_tuple->toolbar_start->personal.menubar_programs
),
icon_name,
g_app_info_get_name(app_info),
comment,
generic_name
);
signal_tuple->is_action = FALSE;
signal_tuple->user_data = wintc_desktop_app_info_get_command(entry);
g_signal_connect(
menu_item,
"activate",
G_CALLBACK(on_menu_item_launcher_activate),
signal_tuple
);
g_free(icon_name);
}
else
{
menu_item =
create_personal_menu_item(
GTK_MENU_SHELL(
signal_tuple->toolbar_start->personal.menubar_programs
),
"dialog-warning",
_("Click to specify a default"),
comment,
generic_name
);
signal_tuple->is_action = TRUE;
signal_tuple->user_data = GINT_TO_POINTER(WINTC_ACTION_MIMEMGMT);
g_signal_connect(
menu_item,
"activate",
G_CALLBACK(on_menu_item_launcher_activate),
signal_tuple
);
}
return menu_item;
}
static void refresh_personal_menu(
WinTCToolbarStart* toolbar_start
)
{
// Temporarily remove All Programs items from meubar (safe because we
// took extra references to them already)
//
gtk_container_remove(
GTK_CONTAINER(toolbar_start->personal.menubar_programs),
toolbar_start->personal.menuitem_all_programs
);
gtk_container_remove(
GTK_CONTAINER(toolbar_start->personal.menubar_programs),
toolbar_start->personal.separator_all_programs
);
// Clear existing items
//
wintc_container_clear(
GTK_CONTAINER(toolbar_start->personal.menubar_programs)
);
// Set up signal tuple array
//
g_array_remove_range(
toolbar_start->personal.tuples_programs,
0,
toolbar_start->personal.tuples_programs->len
);
g_array_set_size(
toolbar_start->personal.tuples_programs,
TOTAL_PROGRAMS_ITEM_COUNT
);
// Add default items
//
GDesktopAppInfo* entry_internet = wintc_query_mime_handler(
"x-scheme-handler/http",
NULL
);
GDesktopAppInfo* entry_email = wintc_query_mime_handler(
"x-scheme-handler/mailto",
NULL
);
StartSignalTuple* tuple;
tuple =
&g_array_index(
toolbar_start->personal.tuples_programs,
StartSignalTuple,
0
);
tuple->toolbar_start = toolbar_start;
gtk_menu_shell_append(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
create_personal_menu_item_from_desktop_entry(
entry_internet,
tuple,
_("Opens your Internet browser."),
_("Internet")
)
);
tuple =
&g_array_index(
toolbar_start->personal.tuples_programs,
StartSignalTuple,
1
);
tuple->toolbar_start = toolbar_start;
gtk_menu_shell_append(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
create_personal_menu_item_from_desktop_entry(
entry_email,
tuple,
_("Opens your e-mail program so you can send or read a message."),
_("E-mail")
)
);
g_clear_object(&entry_internet);
g_clear_object(&entry_email);
// Add separator between defaults & MFU
//
gtk_menu_shell_append(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
gtk_separator_menu_item_new()
);
// Loop over to add the MFU items
//
for (gint i = 0; i < MAX_MFU_ITEM_COUNT; i++)
{
GtkWidget* personal_item =
create_personal_menu_item(
GTK_MENU_SHELL(
toolbar_start->personal.menubar_programs
),
NULL,
NULL,
NULL,
NULL
);
tuple =
&g_array_index(
toolbar_start->personal.tuples_programs,
StartSignalTuple,
DEFAULT_ITEM_COUNT + i
);
tuple->is_action = FALSE;
tuple->toolbar_start = toolbar_start;
g_object_set_qdata(
G_OBJECT(personal_item),
S_QUARK_PERSONAL_ITEM_TUPLE,
tuple
);
g_signal_connect(
personal_item,
"activate",
G_CALLBACK(on_menu_item_launcher_activate),
tuple
);
gtk_menu_shell_append(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
personal_item
);
}
update_personal_menu_mfu_items(toolbar_start);
g_signal_connect(
wintc_start_mfu_tracker_get_default(),
"mfu-updated",
G_CALLBACK(on_mfu_tracker_updated),
toolbar_start
);
// Re-append All Programs items
//
gtk_menu_shell_append(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
toolbar_start->personal.separator_all_programs
);
gtk_menu_shell_append(
GTK_MENU_SHELL(toolbar_start->personal.menubar_programs),
toolbar_start->personal.menuitem_all_programs
);
// Successfully refreshed the menu
//
toolbar_start->personal.sync_menu_refresh = FALSE;
}
static void refresh_userpic(
WinTCToolbarStart* toolbar_start
)
{
static const gchar* s_path_face = NULL;
if (!s_path_face)
{
s_path_face =
g_build_path(
G_DIR_SEPARATOR_S,
g_get_home_dir(),
".face",
NULL
);
}
// Update the pic CSS if we have a face, otherwise default to the built-in
// pic
//
const gchar* actual_path;
gchar* css;
if (g_file_test(s_path_face, G_FILE_TEST_IS_REGULAR))
{
actual_path = s_path_face;
}
else
{
actual_path = "resource:///uk/oddmatics/wintc/taskband/userpic.bmp";
}
css =
g_strdup_printf(
"* { background-image: url('%s'); }",
actual_path
);
gtk_css_provider_load_from_data(
GTK_CSS_PROVIDER(toolbar_start->personal.style_userpic),
css,
-1,
NULL
);
g_free(css);
}
static void update_personal_menu_mfu_items(
WinTCToolbarStart* toolbar_start
)
{
// Find the first MFU item
//
GList* li_mfu = NULL;
GList* list_children =
gtk_container_get_children(
GTK_CONTAINER(toolbar_start->personal.menubar_programs)
);
for (GList* iter = list_children; iter; iter = iter->next)
{
if (
g_object_get_qdata(
G_OBJECT(iter->data),
S_QUARK_PERSONAL_ITEM_TUPLE
)
)
{
li_mfu = iter;
break;
}
}
if (!li_mfu)
{
g_critical("%s", "taskband: somehow found no mfu items");
g_list_free(list_children);
return;
}
// Start updating the items
//
GList* list_mfu =
wintc_start_mfu_tracker_get_mfu_list(
wintc_start_mfu_tracker_get_default()
);
for (
GList* iter = list_mfu;
iter;
iter = iter->next, li_mfu = li_mfu->next
)
{
StartSignalTuple* tuple =
g_object_get_qdata(
G_OBJECT(li_mfu->data),
S_QUARK_PERSONAL_ITEM_TUPLE
);
// Protect against the case where there could be more MFU items tracked
// than personal menu items
//
if (!tuple)
{
break;
}
// Update the menu item and tuple
//
GarconMenuItem* garcon_item = GARCON_MENU_ITEM(iter->data);
const gchar* icon_name = garcon_menu_item_get_icon_name(
garcon_item
);
GList* list_menu_children;
GtkWidget* menu_item = GTK_WIDGET(li_mfu->data);
GdkPixbuf* pixbuf_icon = NULL;
list_menu_children =
gtk_container_get_children(
GTK_CONTAINER(
gtk_bin_get_child(GTK_BIN(menu_item))
)
);
if (icon_name)
{
pixbuf_icon =
gtk_icon_theme_load_icon(
gtk_icon_theme_get_default(),
icon_name,
PROGRAM_ICON_SIZE,
GTK_ICON_LOOKUP_FORCE_SIZE,
NULL
);
}
if (!pixbuf_icon)
{
gtk_icon_theme_load_icon(
gtk_icon_theme_get_default(),
"application-x-generic",
PROGRAM_ICON_SIZE,
GTK_ICON_LOOKUP_FORCE_SIZE,
NULL
);
}
gtk_image_set_from_pixbuf(
GTK_IMAGE(list_menu_children->data),
pixbuf_icon
);
gtk_label_set_text(
GTK_LABEL(list_menu_children->next->data),
garcon_menu_item_get_name(garcon_item)
);
gtk_widget_set_tooltip_text(
menu_item,
garcon_menu_item_get_comment(garcon_item)
);
g_free(tuple->user_data);
tuple->user_data =
garcon_menu_item_get_command_expanded(garcon_item);
if (pixbuf_icon)
{
g_object_unref(pixbuf_icon);
}
g_list_free(list_menu_children);
}
g_list_free(list_mfu);
g_list_free(list_children);
}
//
// CALLBACKS
//
static void clear_signal_tuple(
StartSignalTuple* tuple
)
{
if (!(tuple->is_action))
{
g_clear_pointer(&(tuple->user_data), g_free);
}
}
static gboolean recent_filter_exclude_directories(
const GtkRecentFilterInfo* filter_info,
WINTC_UNUSED(gpointer user_data)
)
{
return g_strcmp0(filter_info->mime_type, "inode/directory") != 0;
}
static void on_button_power_clicked(
GtkButton* button,
gpointer user_data
)
{
GError* error = NULL;
gtk_widget_hide(
GTK_WIDGET(
wintc_widget_get_toplevel_window(GTK_WIDGET(button))
)
);
if (!wintc_launch_action(GPOINTER_TO_INT(user_data), &error))
{
if (
error->domain == WINTC_EXEC_ERROR &&
error->code == WINTC_EXEC_ERROR_FELLTHRU
)
{
g_clear_error(&error);
// Localise
//
wintc_messagebox_show(
NULL,
"Unable to find any programs for exiting the session.",
"Windows",
GTK_BUTTONS_OK,
GTK_MESSAGE_ERROR
);
}
else
{
wintc_nice_error_and_clear(&error, NULL);
}
}
}
static void on_event_box_userpic_clicked(
WINTC_UNUSED(GtkWidget* widget),
GdkEventButton* event,
WINTC_UNUSED(gpointer user_data)
)
{
//
// TODO: Implement this when the user pic cpl is done!
//
if (event->button > 1)
{
return;
}
GError* error = NULL;
g_set_error(
&error,
WINTC_GENERAL_ERROR,
WINTC_GENERAL_ERROR_NOTIMPL,
"Cannot edit user pic yet!"
);
wintc_nice_error_and_clear(&error, NULL);
}
static void on_menu_item_launcher_activate(
WINTC_UNUSED(GtkMenuItem* self),
gpointer user_data
)
{
StartSignalTuple* tuple = (StartSignalTuple*) user_data;
// Raise flag to let Start menu know to close, and launch the action
//
tuple->toolbar_start->sync_menu_should_close = TRUE;
// Launch either the associated action or cmdline depending on what's in
// the tuple
//
GError* error = NULL;
if (tuple->is_action)
{
wintc_launch_action(GPOINTER_TO_INT(tuple->user_data), &error);
}
else
{
if (wintc_launch_command(tuple->user_data, &error))
{
wintc_start_mfu_tracker_bump_cmdline(
wintc_start_mfu_tracker_get_default(),
tuple->user_data
);
}
}
if (error)
{
wintc_nice_error_and_clear(&error, NULL);
}
}
static void on_menu_item_recent_activated(
GtkRecentChooser* self,
gpointer user_data
)
{
WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data);
toolbar_start->sync_menu_should_close = TRUE;
// Launch the specified URI
//
GError* error = NULL;
gchar* uri = gtk_recent_chooser_get_current_uri(self);
wintc_launch_command(uri, &error);
g_free(uri);
}
static void on_menu_item_with_submenu_deselect(
GtkMenuItem* self,
WINTC_UNUSED(gpointer user_data)
)
{
// HACK: I suppose infighting with select-on-enter... we popdown the menu
// ourselves when the item is deselected, otherwise for some asinine
// reason the menu has a chance to get stuck open, in a detached
// state
//
// This is still a bit glitchy if someone spam clicks the submenu
// item, but it's better than the alternative
//
gtk_menu_popdown(
GTK_MENU(gtk_menu_item_get_submenu(self))
);
}
static void on_menu_shell_selection_done(
WINTC_UNUSED(GtkMenuShell* self),
gpointer user_data
)
{
WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data);
if (!toolbar_start->sync_menu_should_close)
{
return;
}
gtk_widget_hide(
toolbar_start->personal.popup_menu
);
}
static void on_menu_shell_submenu_selection_done(
WINTC_UNUSED(GtkMenuShell* self),
gpointer user_data
)
{
WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data);
toolbar_start->sync_menu_should_close = TRUE;
}
static void on_mfu_tracker_updated(
WINTC_UNUSED(WinTCStartMfuTracker* self),
gpointer user_data
)
{
WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data);
update_personal_menu_mfu_items(toolbar_start);
}
static void on_personal_menu_hide(
WINTC_UNUSED(GtkWidget* self),
gpointer user_data
)
{
WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data);
// Track the last closed time, important for toggling the menu properly
// (see toolbar.c)
//
toolbar_start->time_menu_closed = g_get_monotonic_time();
// Sync Start button state
//
toolbar_start->sync_button = TRUE;
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(toolbar_start->start_button),
FALSE
);
toolbar_start->sync_button = FALSE;
}