858 lines
22 KiB
C
858 lines
22 KiB
C
/* $Id$ */
|
|
/* Copyright (c) 2009-2020 Pierre Pronchery <khorben@defora.org> */
|
|
/* This file is part of DeforaOS Desktop Panel */
|
|
/* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#include <sys/stat.h>
|
|
#if defined(__sun)
|
|
# include <fcntl.h>
|
|
#endif
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <libintl.h>
|
|
#include <System.h>
|
|
#include <Desktop.h>
|
|
#include "Panel/applet.h"
|
|
#include "../../config.h"
|
|
#define _(string) gettext(string)
|
|
#define N_(string) string
|
|
|
|
/* constants */
|
|
#ifndef PROGNAME_RUN
|
|
# define PROGNAME_RUN "run"
|
|
#endif
|
|
|
|
#ifndef PREFIX
|
|
# define PREFIX "/usr/local"
|
|
#endif
|
|
#ifndef BINDIR
|
|
# define BINDIR PREFIX "/bin"
|
|
#endif
|
|
#ifndef DATADIR
|
|
# define DATADIR PREFIX "/share"
|
|
#endif
|
|
|
|
|
|
/* Menu */
|
|
/* private */
|
|
/* types */
|
|
typedef struct _PanelApplet
|
|
{
|
|
PanelAppletHelper * helper;
|
|
GSList * apps;
|
|
guint idle;
|
|
gboolean refresh;
|
|
time_t refresh_mti;
|
|
GtkWidget * widget;
|
|
} Menu;
|
|
|
|
typedef struct _MenuApp
|
|
{
|
|
MimeHandler * handler;
|
|
char * path;
|
|
} MenuApp;
|
|
|
|
typedef struct _MenuCategory
|
|
{
|
|
char const * category;
|
|
char const * label;
|
|
char const * stock;
|
|
} MenuCategory;
|
|
|
|
|
|
/* constants */
|
|
static const MenuCategory _menu_categories[] =
|
|
{
|
|
{ "Audio", N_("Audio"), "gnome-mime-audio", },
|
|
{ "Development",N_("Development"),"applications-development", },
|
|
{ "Education", N_("Education"),"applications-science", },
|
|
{ "Game", N_("Games"), "applications-games", },
|
|
{ "Graphics", N_("Graphics"), "applications-graphics", },
|
|
{ "AudioVideo", N_("Multimedia"),"applications-multimedia", },
|
|
{ "Network", N_("Network"), "applications-internet", },
|
|
{ "Office", N_("Office"), "applications-office", },
|
|
{ "Settings", N_("Settings"), "gnome-settings", },
|
|
{ "System", N_("System"), "applications-system", },
|
|
{ "Utility", N_("Utilities"),"applications-utilities", },
|
|
{ "Video", N_("Video"), "video", }
|
|
};
|
|
#define MENU_MENUS_COUNT (sizeof(_menu_categories) / sizeof(*_menu_categories))
|
|
|
|
|
|
/* prototypes */
|
|
static Menu * _menu_init(PanelAppletHelper * helper, GtkWidget ** widget);
|
|
static void _menu_destroy(Menu * menu);
|
|
|
|
/* helpers */
|
|
static GtkWidget * _menu_applications(Menu * menu);
|
|
static GtkWidget * _menu_icon(Menu * menu, char const * path,
|
|
char const * icon);
|
|
static GtkWidget * _menu_menuitem(Menu * menu, char const * path,
|
|
char const * label, char const * icon);
|
|
static GtkWidget * _menu_menuitem_stock(char const * icon, char const * label,
|
|
gboolean mnemonic);
|
|
|
|
static void _menu_xdg_dirs(Menu * menu, void (*callback)(Menu * menu,
|
|
char const * path, char const * apppath));
|
|
|
|
/* callbacks */
|
|
static void _menu_on_about(gpointer data);
|
|
static void _menu_on_clicked(gpointer data);
|
|
static gboolean _menu_on_idle(gpointer data);
|
|
static void _menu_on_lock(gpointer data);
|
|
static void _menu_on_logout(gpointer data);
|
|
#ifdef EMBEDDED
|
|
static void _menu_on_rotate(gpointer data);
|
|
#endif
|
|
static void _menu_on_run(gpointer data);
|
|
static void _menu_on_shutdown(gpointer data);
|
|
static void _menu_on_suspend(gpointer data);
|
|
static gboolean _menu_on_timeout(gpointer data);
|
|
|
|
/* MenuApp */
|
|
static MenuApp * _menuapp_new(MimeHandler * handler, String const * path);
|
|
static void _menuapp_delete(MenuApp * menuapp);
|
|
|
|
|
|
/* public */
|
|
/* variables */
|
|
PanelAppletDefinition applet =
|
|
{
|
|
N_("Main menu"),
|
|
"start-here",
|
|
NULL,
|
|
_menu_init,
|
|
_menu_destroy,
|
|
NULL,
|
|
FALSE,
|
|
TRUE
|
|
};
|
|
|
|
|
|
/* private */
|
|
/* functions */
|
|
/* menu_init */
|
|
static Menu * _menu_init(PanelAppletHelper * helper, GtkWidget ** widget)
|
|
{
|
|
Menu * menu;
|
|
GtkWidget * hbox;
|
|
GtkWidget * image;
|
|
char const * p;
|
|
PangoFontDescription * bold;
|
|
GtkWidget * label;
|
|
|
|
if((menu = malloc(sizeof(*menu))) == NULL)
|
|
{
|
|
error_set("%s: %s", applet.name, strerror(errno));
|
|
return NULL;
|
|
}
|
|
menu->helper = helper;
|
|
menu->apps = NULL;
|
|
menu->idle = g_idle_add(_menu_on_idle, menu);
|
|
menu->refresh_mti = 0;
|
|
menu->widget = gtk_button_new();
|
|
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
|
|
if((p = helper->config_get(helper->panel, "menu", "icon")) == NULL)
|
|
p = applet.icon;
|
|
image = gtk_image_new_from_icon_name(p,
|
|
panel_window_get_icon_size(helper->window));
|
|
gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, TRUE, 0);
|
|
/* add some text if configured so */
|
|
if((p = helper->config_get(helper->panel, "menu", "text")) != NULL
|
|
&& strlen(p) > 0)
|
|
{
|
|
bold = pango_font_description_new();
|
|
pango_font_description_set_weight(bold, PANGO_WEIGHT_BOLD);
|
|
label = gtk_label_new(p);
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
|
gtk_widget_override_font(label, bold);
|
|
#else
|
|
gtk_widget_modify_font(label, bold);
|
|
#endif
|
|
pango_font_description_free(bold);
|
|
gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
|
|
}
|
|
gtk_button_set_relief(GTK_BUTTON(menu->widget), GTK_RELIEF_NONE);
|
|
gtk_widget_set_tooltip_text(menu->widget, _("Main menu"));
|
|
g_signal_connect_swapped(menu->widget, "clicked", G_CALLBACK(
|
|
_menu_on_clicked), menu);
|
|
gtk_container_add(GTK_CONTAINER(menu->widget), hbox);
|
|
gtk_widget_show_all(menu->widget);
|
|
*widget = menu->widget;
|
|
return menu;
|
|
}
|
|
|
|
|
|
/* menu_destroy */
|
|
static void _menu_destroy(Menu * menu)
|
|
{
|
|
if(menu->idle != 0)
|
|
g_source_remove(menu->idle);
|
|
g_slist_foreach(menu->apps, (GFunc)_menuapp_delete, NULL);
|
|
g_slist_free(menu->apps);
|
|
gtk_widget_destroy(menu->widget);
|
|
free(menu);
|
|
}
|
|
|
|
|
|
/* helpers */
|
|
/* menu_applications */
|
|
static void _applications_on_activate(gpointer data);
|
|
static void _applications_categories(GtkWidget * menu, GtkWidget ** menus);
|
|
|
|
static GtkWidget * _menu_applications(Menu * menu)
|
|
{
|
|
GtkWidget * menus[MENU_MENUS_COUNT];
|
|
GSList * p;
|
|
GtkWidget * menushell;
|
|
GtkWidget * menuitem;
|
|
MenuApp * menuapp;
|
|
MimeHandler * handler;
|
|
char const * name;
|
|
#if GTK_CHECK_VERSION(2, 12, 0)
|
|
char const * comment;
|
|
#endif
|
|
char const * q;
|
|
String const ** categories;
|
|
size_t i;
|
|
size_t j;
|
|
|
|
if(menu->apps == NULL)
|
|
_menu_on_idle(menu);
|
|
memset(&menus, 0, sizeof(menus));
|
|
menushell = gtk_menu_new();
|
|
for(p = menu->apps; p != NULL; p = p->next)
|
|
{
|
|
menuapp = p->data;
|
|
handler = menuapp->handler;
|
|
if((name = mimehandler_get_name(handler, 1)) == NULL)
|
|
{
|
|
menu->helper->error(NULL, error_get(NULL), 0);
|
|
continue;
|
|
}
|
|
#if GTK_CHECK_VERSION(2, 12, 0)
|
|
comment = mimehandler_get_comment(handler, 1);
|
|
#endif
|
|
if((q = mimehandler_get_generic_name(handler, 1)) != NULL)
|
|
{
|
|
#if GTK_CHECK_VERSION(2, 12, 0)
|
|
if(comment == NULL)
|
|
comment = name;
|
|
#endif
|
|
name = q;
|
|
}
|
|
menuitem = _menu_menuitem(menu, menuapp->path, name,
|
|
mimehandler_get_icon(handler, 1));
|
|
#if GTK_CHECK_VERSION(2, 12, 0)
|
|
if(comment != NULL)
|
|
gtk_widget_set_tooltip_text(menuitem, comment);
|
|
#endif
|
|
if(mimehandler_get_type(handler)
|
|
== MIMEHANDLER_TYPE_APPLICATION
|
|
&& mimehandler_can_execute(handler) == 0)
|
|
gtk_widget_set_sensitive(menuitem, FALSE);
|
|
else
|
|
g_signal_connect_swapped(menuitem, "activate",
|
|
G_CALLBACK(_applications_on_activate),
|
|
handler);
|
|
if((categories = mimehandler_get_categories(handler)) == NULL
|
|
|| categories[0] == NULL)
|
|
{
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell),
|
|
menuitem);
|
|
continue;
|
|
}
|
|
for(i = 0; i < MENU_MENUS_COUNT; i++)
|
|
{
|
|
for(j = 0; categories[j] != NULL; j++)
|
|
if(string_compare(_menu_categories[i].category,
|
|
categories[j]) == 0)
|
|
break;
|
|
if(categories[j] != NULL)
|
|
break;
|
|
}
|
|
if(i == MENU_MENUS_COUNT)
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell),
|
|
menuitem);
|
|
else
|
|
{
|
|
if(menus[i] == NULL)
|
|
menus[i] = gtk_menu_new();
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menus[i]),
|
|
menuitem);
|
|
}
|
|
}
|
|
_applications_categories(menushell, menus);
|
|
return menushell;
|
|
}
|
|
|
|
static void _applications_on_activate(gpointer data)
|
|
{
|
|
MimeHandler * handler = data;
|
|
|
|
if(mimehandler_open(handler, NULL) != 0)
|
|
/* XXX really report error */
|
|
error_print(NULL);
|
|
}
|
|
|
|
static void _applications_categories(GtkWidget * menu, GtkWidget ** menus)
|
|
{
|
|
size_t i;
|
|
MenuCategory const * m;
|
|
GtkWidget * menuitem;
|
|
size_t pos = 0;
|
|
|
|
for(i = 0; i < MENU_MENUS_COUNT; i++)
|
|
{
|
|
if(menus[i] == NULL)
|
|
continue;
|
|
m = &_menu_categories[i];
|
|
menuitem = _menu_menuitem_stock(m->stock, _(m->label), FALSE);
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menus[i]);
|
|
gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, pos++);
|
|
}
|
|
}
|
|
|
|
|
|
/* menu_icon */
|
|
static GtkWidget * _menu_icon(Menu * menu, char const * path, char const * icon)
|
|
{
|
|
const char pixmaps[] = "/pixmaps/";
|
|
int width = 16;
|
|
int height = 16;
|
|
String * buf;
|
|
GdkPixbuf * pixbuf = NULL;
|
|
GError * error = NULL;
|
|
|
|
gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
|
|
if(icon[0] == '/')
|
|
pixbuf = gdk_pixbuf_new_from_file_at_size(icon, width, height,
|
|
&error);
|
|
else if(strchr(icon, '.') != NULL)
|
|
{
|
|
if(path == NULL)
|
|
path = DATADIR;
|
|
if((buf = string_new_append(path, pixmaps, icon, NULL)) != NULL)
|
|
{
|
|
pixbuf = gdk_pixbuf_new_from_file_at_size(buf, width,
|
|
height, &error);
|
|
string_delete(buf);
|
|
}
|
|
}
|
|
if(error != NULL)
|
|
{
|
|
menu->helper->error(NULL, error->message, 1);
|
|
g_error_free(error);
|
|
}
|
|
if(pixbuf == NULL)
|
|
return gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU);
|
|
return gtk_image_new_from_pixbuf(pixbuf);
|
|
}
|
|
|
|
|
|
/* menu_menuitem */
|
|
static GtkWidget * _menu_menuitem(Menu * menu, char const * path,
|
|
char const * label, char const * icon)
|
|
{
|
|
GtkWidget * ret;
|
|
GtkWidget * image;
|
|
|
|
ret = gtk_image_menu_item_new_with_label(label);
|
|
if(icon != NULL)
|
|
{
|
|
image = _menu_icon(menu, path, icon);
|
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(ret), image);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* menu_menuitem_stock */
|
|
static GtkWidget * _menu_menuitem_stock(char const * icon, char const * label,
|
|
gboolean mnemonic)
|
|
{
|
|
GtkWidget * ret;
|
|
GtkWidget * image;
|
|
|
|
ret = (mnemonic) ? gtk_image_menu_item_new_with_mnemonic(label)
|
|
: gtk_image_menu_item_new_with_label(label);
|
|
if(icon != NULL)
|
|
{
|
|
image = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU);
|
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(ret), image);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* menu_xdg_dirs */
|
|
static void _xdg_dirs_home(Menu * menu, void (*callback)(Menu * menu,
|
|
char const * path, char const * apppath));
|
|
static void _xdg_dirs_path(Menu * menu, void (*callback)(Menu * menu,
|
|
char const * path, char const * apppath),
|
|
char const * path);
|
|
|
|
static void _menu_xdg_dirs(Menu * menu, void (*callback)(Menu * menu,
|
|
char const * path, char const * apppath))
|
|
{
|
|
char const * path;
|
|
char * p;
|
|
size_t i;
|
|
size_t j;
|
|
int datadir = 1;
|
|
|
|
/* read through every XDG application folder */
|
|
if((path = getenv("XDG_DATA_DIRS")) == NULL || strlen(path) == 0)
|
|
{
|
|
#if defined(__FreeBSD__)
|
|
/* XXX really detect if DATADIR is already included */
|
|
path = DATADIR ":/usr/share";
|
|
#elif defined(__NetBSD__)
|
|
/* XXX include the default path for pkgsrc */
|
|
path = "/usr/pkg/share:" DATADIR ":/usr/share";
|
|
#else
|
|
path = "/usr/local/share:" DATADIR ":/usr/share";
|
|
#endif
|
|
datadir = 0;
|
|
}
|
|
if((p = strdup(path)) == NULL)
|
|
menu->helper->error(NULL, "strdup", 1);
|
|
else
|
|
for(i = 0, j = 0;; i++)
|
|
if(p[i] == '\0')
|
|
{
|
|
string_rtrim(&p[j], "/");
|
|
_xdg_dirs_path(menu, callback, &p[j]);
|
|
datadir |= (strcmp(&p[j], DATADIR) == 0);
|
|
break;
|
|
}
|
|
else if(p[i] == ':')
|
|
{
|
|
p[i] = '\0';
|
|
string_rtrim(&p[j], "/");
|
|
_xdg_dirs_path(menu, callback, &p[j]);
|
|
datadir |= (strcmp(&p[j], DATADIR) == 0);
|
|
j = i + 1;
|
|
}
|
|
free(p);
|
|
if(datadir == 0)
|
|
_xdg_dirs_path(menu, callback, DATADIR);
|
|
_xdg_dirs_home(menu, callback);
|
|
}
|
|
|
|
static void _xdg_dirs_home(Menu * menu, void (*callback)(Menu * menu,
|
|
char const * path, char const * apppath))
|
|
{
|
|
char const fallback[] = ".local/share";
|
|
char const * path;
|
|
char const * homedir;
|
|
String * p;
|
|
|
|
/* use $XDG_DATA_HOME if set and not empty */
|
|
if((path = getenv("XDG_DATA_HOME")) != NULL && strlen(path) > 0)
|
|
{
|
|
_xdg_dirs_path(menu, callback, path);
|
|
return;
|
|
}
|
|
/* fallback to "$HOME/.local/share" */
|
|
if((homedir = getenv("HOME")) == NULL)
|
|
homedir = g_get_home_dir();
|
|
if((p = string_new_append(homedir, "/", fallback, NULL)) == NULL)
|
|
{
|
|
menu->helper->error(NULL, homedir, 1);
|
|
return;
|
|
}
|
|
_xdg_dirs_path(menu, callback, p);
|
|
string_delete(p);
|
|
}
|
|
|
|
static void _xdg_dirs_path(Menu * menu, void (*callback)(Menu * menu,
|
|
char const * path, char const * apppath),
|
|
char const * path)
|
|
{
|
|
const char applications[] = "/applications";
|
|
char * apppath;
|
|
|
|
if((apppath = string_new_append(path, applications, NULL)) == NULL)
|
|
menu->helper->error(NULL, path, 1);
|
|
callback(menu, path, apppath);
|
|
string_delete(apppath);
|
|
}
|
|
|
|
|
|
/* callbacks */
|
|
/* menu_on_about */
|
|
static void _menu_on_about(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->helper->about_dialog(menu->helper->panel);
|
|
}
|
|
|
|
|
|
/* menu_on_clicked */
|
|
static void _clicked_position_menu(GtkMenu * widget, gint * x, gint * y,
|
|
gboolean * push_in, gpointer data);
|
|
|
|
static void _menu_on_clicked(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
PanelAppletHelper * helper = menu->helper;
|
|
GtkWidget * menushell;
|
|
GtkWidget * menuitem;
|
|
GtkWidget * widget;
|
|
char const * p;
|
|
|
|
menushell = gtk_menu_new();
|
|
if((p = helper->config_get(helper->panel, "menu", "applications"))
|
|
== NULL || strtol(p, NULL, 0) != 0)
|
|
{
|
|
menuitem = _menu_menuitem_stock("gnome-applications",
|
|
_("A_pplications"), TRUE);
|
|
widget = _menu_applications(menu);
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), widget);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
menuitem = gtk_separator_menu_item_new();
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
}
|
|
if((p = helper->config_get(helper->panel, "menu", "run")) == NULL
|
|
|| strtol(p, NULL, 0) != 0)
|
|
{
|
|
menuitem = _menu_menuitem_stock(GTK_STOCK_EXECUTE, _("_Run..."),
|
|
TRUE);
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_run), menu);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
menuitem = gtk_separator_menu_item_new();
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
}
|
|
if((p = helper->config_get(helper->panel, "menu", "about")) == NULL
|
|
|| strtol(p, NULL, 0) != 0)
|
|
{
|
|
#if GTK_CHECK_VERSION(3, 10, 0)
|
|
menuitem = gtk_image_menu_item_new_with_mnemonic(_("_About"));
|
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem),
|
|
gtk_image_new_from_icon_name(GTK_STOCK_ABOUT,
|
|
GTK_ICON_SIZE_MENU));
|
|
#else
|
|
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,
|
|
NULL);
|
|
#endif
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_about), menu);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
menuitem = gtk_separator_menu_item_new();
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
}
|
|
/* lock screen */
|
|
menuitem = _menu_menuitem_stock("gnome-lockscreen", _("_Lock screen"),
|
|
TRUE);
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_lock), menu);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
#ifdef EMBEDDED
|
|
/* rotate screen */
|
|
/* XXX find a more appropriate icon */
|
|
menuitem = _menu_menuitem_stock(GTK_STOCK_REFRESH, _("R_otate"), TRUE);
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_rotate), data);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
#endif
|
|
/* logout */
|
|
if(menu->helper->logout_dialog != NULL)
|
|
{
|
|
menuitem = _menu_menuitem_stock("gnome-logout", _("Lo_gout..."),
|
|
TRUE);
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_logout), data);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
}
|
|
/* suspend */
|
|
if(menu->helper->suspend != NULL)
|
|
{
|
|
menuitem = _menu_menuitem_stock("gtk-media-pause",
|
|
_("S_uspend"), TRUE);
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_suspend), data);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
}
|
|
/* shutdown */
|
|
if(menu->helper->shutdown_dialog != NULL)
|
|
{
|
|
menuitem = _menu_menuitem_stock("gnome-shutdown",
|
|
_("_Shutdown..."), TRUE);
|
|
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(
|
|
_menu_on_shutdown), data);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menushell), menuitem);
|
|
}
|
|
gtk_widget_show_all(menushell);
|
|
gtk_menu_popup(GTK_MENU(menushell), NULL, NULL, _clicked_position_menu,
|
|
menu, 0, gtk_get_current_event_time());
|
|
}
|
|
|
|
static void _clicked_position_menu(GtkMenu * widget, gint * x, gint * y,
|
|
gboolean * push_in, gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
GtkAllocation a;
|
|
|
|
gtk_widget_get_allocation(menu->widget, &a);
|
|
*x = a.x;
|
|
*y = a.y;
|
|
menu->helper->position_menu(menu->helper->panel, widget, x, y, push_in);
|
|
}
|
|
|
|
|
|
/* menu_on_idle */
|
|
static gint _idle_apps_compare(gconstpointer a, gconstpointer b);
|
|
static void _idle_path(Menu * menu, char const * path, char const * apppath);
|
|
|
|
static gboolean _menu_on_idle(gpointer data)
|
|
{
|
|
const int timeout = 10000;
|
|
Menu * menu = data;
|
|
|
|
if(menu->apps != NULL)
|
|
{
|
|
menu->idle = 0;
|
|
return FALSE;
|
|
}
|
|
_menu_xdg_dirs(menu, _idle_path);
|
|
menu->idle = g_timeout_add(timeout, _menu_on_timeout, menu);
|
|
return FALSE;
|
|
}
|
|
|
|
static gint _idle_apps_compare(gconstpointer a, gconstpointer b)
|
|
{
|
|
MenuApp * maa = (MenuApp *)a;
|
|
MenuApp * mab = (MenuApp *)b;
|
|
MimeHandler * mha = maa->handler;
|
|
MimeHandler * mhb = mab->handler;
|
|
String const * mhas;
|
|
String const * mhbs;
|
|
|
|
if((mhas = mimehandler_get_generic_name(mha, 1)) == NULL)
|
|
mhas = mimehandler_get_name(mha, 1);
|
|
if((mhbs = mimehandler_get_generic_name(mhb, 1)) == NULL)
|
|
mhbs = mimehandler_get_name(mhb, 1);
|
|
return string_compare(mhas, mhbs);
|
|
}
|
|
|
|
static void _idle_path(Menu * menu, char const * path, char const * apppath)
|
|
{
|
|
DIR * dir;
|
|
int fd;
|
|
struct stat st;
|
|
struct dirent * de;
|
|
size_t len;
|
|
const char ext[] = ".desktop";
|
|
char * name = NULL;
|
|
char * p;
|
|
MimeHandler * handler;
|
|
MenuApp * menuapp;
|
|
(void) path;
|
|
|
|
#if defined(__sun)
|
|
if((fd = open(apppath, O_RDONLY)) < 0
|
|
|| fstat(fd, &st) != 0
|
|
|| (dir = fdopendir(fd)) == NULL)
|
|
#else
|
|
if((dir = opendir(apppath)) == NULL
|
|
|| (fd = dirfd(dir)) < 0
|
|
|| fstat(fd, &st) != 0)
|
|
#endif
|
|
{
|
|
if(errno != ENOENT)
|
|
menu->helper->error(NULL, apppath, 1);
|
|
return;
|
|
}
|
|
if(st.st_mtime > menu->refresh_mti)
|
|
menu->refresh_mti = st.st_mtime;
|
|
while((de = readdir(dir)) != NULL)
|
|
{
|
|
if(de->d_name[0] == '.')
|
|
if(de->d_name[1] == '\0' || (de->d_name[1] == '.'
|
|
&& de->d_name[2] == '\0'))
|
|
continue;
|
|
len = strlen(de->d_name);
|
|
if(len < sizeof(ext))
|
|
continue;
|
|
if(strncmp(&de->d_name[len - sizeof(ext) + 1], ext,
|
|
sizeof(ext)) != 0)
|
|
continue;
|
|
if((p = realloc(name, strlen(apppath) + len + 2)) == NULL)
|
|
{
|
|
menu->helper->error(NULL, apppath, 1);
|
|
continue;
|
|
}
|
|
name = p;
|
|
snprintf(name, strlen(apppath) + len + 2, "%s/%s", apppath,
|
|
de->d_name);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, name);
|
|
#endif
|
|
if((handler = mimehandler_new_load(name)) == NULL)
|
|
{
|
|
menu->helper->error(NULL, error_get(NULL), 1);
|
|
continue;
|
|
}
|
|
/* skip this entry if cannot be displayed or opened */
|
|
if(mimehandler_can_display(handler) == 0
|
|
|| mimehandler_can_execute(handler) == 0
|
|
|| (menuapp = _menuapp_new(handler, path))
|
|
== NULL)
|
|
{
|
|
mimehandler_delete(handler);
|
|
continue;
|
|
}
|
|
menu->apps = g_slist_insert_sorted(menu->apps, menuapp,
|
|
_idle_apps_compare);
|
|
}
|
|
free(name);
|
|
closedir(dir);
|
|
}
|
|
|
|
|
|
/* menu_on_lock */
|
|
static void _menu_on_lock(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->helper->lock(menu->helper->panel);
|
|
}
|
|
|
|
|
|
/* menu_on_logout */
|
|
static void _menu_on_logout(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->helper->logout_dialog(menu->helper->panel);
|
|
}
|
|
|
|
|
|
#ifdef EMBEDDED
|
|
/* menu_on_rotate */
|
|
static void _menu_on_rotate(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->helper->rotate_screen(menu->helper->panel);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* menu_on_run */
|
|
static void _menu_on_run(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
char * argv[] = { BINDIR "/" PROGNAME_RUN, NULL };
|
|
const unsigned int flags = G_SPAWN_STDOUT_TO_DEV_NULL
|
|
| G_SPAWN_STDERR_TO_DEV_NULL;
|
|
GError * error = NULL;
|
|
|
|
if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
|
|
!= TRUE)
|
|
{
|
|
menu->helper->error(menu->helper->panel, error->message, 1);
|
|
g_error_free(error);
|
|
}
|
|
}
|
|
|
|
|
|
/* menu_on_shutdown */
|
|
static void _menu_on_shutdown(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->helper->shutdown_dialog(menu->helper->panel);
|
|
}
|
|
|
|
|
|
/* menu_on_suspend */
|
|
static void _menu_on_suspend(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->helper->suspend(menu->helper->panel);
|
|
}
|
|
|
|
|
|
/* menu_on_timeout */
|
|
static void _timeout_path(Menu * menu, char const * path, char const * apppath);
|
|
|
|
static gboolean _menu_on_timeout(gpointer data)
|
|
{
|
|
Menu * menu = data;
|
|
|
|
menu->refresh = FALSE;
|
|
_menu_xdg_dirs(menu, _timeout_path);
|
|
if(menu->refresh == FALSE)
|
|
return TRUE;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() resetting the menu\n", __func__);
|
|
#endif
|
|
g_slist_foreach(menu->apps, (GFunc)_menuapp_delete, NULL);
|
|
g_slist_free(menu->apps);
|
|
menu->apps = NULL;
|
|
menu->idle = g_idle_add(_menu_on_idle, menu);
|
|
return FALSE;
|
|
}
|
|
|
|
static void _timeout_path(Menu * menu, char const * path, char const * apppath)
|
|
{
|
|
struct stat st;
|
|
(void) path;
|
|
|
|
if(menu->refresh != TRUE
|
|
&& stat(apppath, &st) == 0
|
|
&& st.st_mtime > menu->refresh_mti)
|
|
menu->refresh = TRUE;
|
|
}
|
|
|
|
|
|
/* MenuApp */
|
|
/* menuapp_new */
|
|
static MenuApp * _menuapp_new(MimeHandler * handler, String const * path)
|
|
{
|
|
MenuApp * menuapp;
|
|
|
|
if((menuapp = object_new(sizeof(*menuapp))) == NULL)
|
|
return NULL;
|
|
menuapp->handler = NULL;
|
|
if(path == NULL)
|
|
menuapp->path = NULL;
|
|
else if((menuapp->path = string_new(path)) == NULL)
|
|
{
|
|
_menuapp_delete(menuapp);
|
|
return NULL;
|
|
}
|
|
menuapp->handler = handler;
|
|
return menuapp;
|
|
}
|
|
|
|
|
|
/* menuapp_delete */
|
|
static void _menuapp_delete(MenuApp * menuapp)
|
|
{
|
|
if(menuapp->handler != NULL)
|
|
mimehandler_delete(menuapp->handler);
|
|
string_delete(menuapp->path);
|
|
object_delete(menuapp);
|
|
}
|