Hi guys:
I have been working on add to lxpanel run the functionality for open
URIs. I implemented it using gio. The source code of "gtk-run.c" is
attached.
But I found that my PcmanFm doesn't register the mime types in order to
open "smb", "file", "ssh", "ftp" and "sftp".
I would like to know if some one has done this.
Greetings!!
Alexis López Zubieta
Nova Light http://www.nova.cu
http://www.uci.cu
/*
* gtk-run.c: Little application launcher
* Copyright (C) 2006 Hong Jen Yee (PCMan) pcman.tw(AT)gmail.com
*
* 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; either version 2 of the
* License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <glib/gi18n.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <gio/gio.h>
#include "misc.h"
#include <menu-cache.h>
static GtkWidget* win = NULL; /* the run dialog */
static MenuCache* menu_cache = NULL;
static GSList* app_list = NULL; /* all known apps in menu cache */
static gpointer reload_notify_id = NULL;
typedef struct _ThreadData {
gboolean cancel; /* is the loading cancelled */
GSList* files; /* all executable files found */
GtkEntry* entry;
} ThreadData;
static ThreadData* thread_data = NULL; /* thread data used to load availble programs in PATH */
static MenuCacheApp* match_system_exec(const char* exec) {
GSList* l;
MenuCacheApp* ret = NULL;
char* exec_path = g_find_program_in_path(exec);
const char* pexec;
int path_len, exec_len, len;
if (!exec_path)
return NULL;
path_len = strlen(exec_path);
exec_len = strlen(exec);
for (l = app_list; l; l = l->next) {
MenuCacheApp* app = MENU_CACHE_APP(l->data);
const char* app_exec = menu_cache_app_get_exec(app);
if (!app_exec)
continue;
#if 0 /* This is useless and incorrect. */
/* Dirty hacks to skip sudo programs. This can be a little bit buggy */
if (g_str_has_prefix(app_exec, "gksu")) {
app_exec += 4;
if (app_exec[0] == '\0') /* "gksu" itself */
app_exec -= 4;
else if (app_exec[0] == ' ') /* "gksu something..." */
++app_exec;
else if (g_str_has_prefix(app_exec, "do ")) /* "gksudo something" */
app_exec += 3;
} else if (g_str_has_prefix(app_exec, "kdesu ")) /* kdesu */
app_exec += 6;
#endif
if (g_path_is_absolute(app_exec)) {
pexec = exec_path;
len = path_len;
} else {
pexec = exec;
len = exec_len;
}
if (strncmp(app_exec, pexec, len) == 0) {
/* exact match has the highest priority */
if (app_exec[len] == '\0') {
ret = app;
break;
}
/* those matches the pattern: exe_name %F|%f|%U|%u have higher priority */
if (app_exec[len] == ' ') {
if (app_exec[len + 1] == '%') {
if (strchr("FfUu", app_exec[len + 2])) {
ret = app;
break;
}
}
ret = app;
}
}
}
/* if this is a symlink */
if (!ret && g_file_test(exec_path, G_FILE_TEST_IS_SYMLINK)) {
char target[512]; /* FIXME: is this enough? */
len = readlink(exec_path, target, sizeof (target) - 1);
if (len > 0) {
target[len] = '\0';
ret = match_system_exec(target);
if (!ret) {
/* FIXME: Actually, target could be relative paths.
* So, actually path resolution is needed here. */
char* basename = g_path_get_basename(target);
char* locate = g_find_program_in_path(basename);
if (locate && strcmp(locate, target) == 0) {
ret = match_system_exec(basename);
g_free(locate);
}
g_free(basename);
}
}
}
g_free(exec_path);
return ret;
}
static MenuCacheApp* match_uri_app(const char* resource) {
GSList* l;
GAppInfo *app = NULL;
MenuCacheApp* ret = NULL;
char *uri_scheme;
fprintf(stderr, "DEBUG: matching uri\n");
/* Find an application capable to handle the resource *
* WARNING: resource must remains constant */
char *tmp_resource = strdup(resource);
uri_scheme = strtok(tmp_resource, ":");
app = g_app_info_get_default_for_uri_scheme(uri_scheme);
fprintf(stderr, "DEBUG: uri scheme: %s\n", uri_scheme);
free(tmp_resource);
if (app) {
/* Find the MenuCacheApp item for Aplication */
fprintf(stderr, "DEBUG: finding MenuCacheApp for %s", g_app_info_get_name(app));
const char* pexec;
const char *exec = g_app_info_get_executable(app),
*exec_path = g_find_program_in_path(exec);
int path_len, exec_len, len;
path_len = strlen(exec_path);
exec_len = strlen(exec);
for (l = app_list; l; l = l->next) {
MenuCacheApp* app = MENU_CACHE_APP(l->data);
const char* app_exec = menu_cache_app_get_exec(app);
if (!app_exec)
continue;
if (g_path_is_absolute(app_exec)) {
pexec = exec_path;
len = path_len;
} else {
pexec = exec;
len = exec_len;
}
if (strncmp(app_exec, pexec, len) == 0) {
/* exact match has the highest priority */
if (app_exec[len] == '\0') {
ret = app;
break;
}
/* those matches the pattern: exe_name %F|%f|%U|%u have higher priority */
if (app_exec[len] == ' ') {
if (app_exec[len + 1] == '%') {
if (strchr("FfUu", app_exec[len + 2])) {
ret = app;
break;
}
}
ret = app;
}
}
}
}
fprintf(stderr, "DEBUG: Done matching uri\n");
return ret;
}
static void setup_auto_complete_with_data(ThreadData* data) {
GtkListStore* store;
GSList *l;
GtkEntryCompletion* comp = gtk_entry_completion_new();
gtk_entry_completion_set_minimum_key_length(comp, 2);
gtk_entry_completion_set_inline_completion(comp, TRUE);
#if GTK_CHECK_VERSION( 2, 8, 0 )
gtk_entry_completion_set_popup_set_width(comp, TRUE);
gtk_entry_completion_set_popup_single_match(comp, FALSE);
#endif
store = gtk_list_store_new(1, G_TYPE_STRING);
for (l = data->files; l; l = l->next) {
const char *name = (const char*) l->data;
GtkTreeIter it;
gtk_list_store_append(store, &it);
gtk_list_store_set(store, &it, 0, name, -1);
}
gtk_entry_completion_set_model(comp, (GtkTreeModel*) store);
g_object_unref(store);
gtk_entry_completion_set_text_column(comp, 0);
gtk_entry_set_completion((GtkEntry*) data->entry, comp);
/* trigger entry completion */
gtk_entry_completion_complete(comp);
g_object_unref(comp);
}
void thread_data_free(ThreadData* data) {
g_slist_foreach(data->files, (GFunc) g_free, NULL);
g_slist_free(data->files);
g_slice_free(ThreadData, data);
}
static gboolean on_thread_finished(ThreadData* data) {
/* don't setup entry completion if the thread is already cancelled. */
if (!data->cancel)
setup_auto_complete_with_data(thread_data);
thread_data_free(data);
thread_data = NULL; /* global thread_data pointer */
return FALSE;
}
static gpointer thread_func(ThreadData* data) {
GSList *list = NULL, *l;
gchar **dirname;
gchar **dirnames = g_strsplit(g_getenv("PATH"), ":", 0);
for (dirname = dirnames; !thread_data->cancel && *dirname; ++dirname) {
GDir *dir = g_dir_open(*dirname, 0, NULL);
const char *name;
if (!dir)
continue;
while (!thread_data->cancel && (name = g_dir_read_name(dir))) {
char* filename = g_build_filename(*dirname, name, NULL);
if (g_file_test(filename, G_FILE_TEST_IS_EXECUTABLE)) {
if (thread_data->cancel)
break;
if (!g_slist_find_custom(list, name, (GCompareFunc) strcmp))
list = g_slist_prepend(list, g_strdup(name));
}
g_free(filename);
}
g_dir_close(dir);
}
g_strfreev(dirnames);
data->files = list;
/* install an idle handler to free associated data */
g_idle_add((GSourceFunc) on_thread_finished, data);
return NULL;
}
static void setup_auto_complete(GtkEntry* entry) {
gboolean cache_is_available = FALSE;
/* FIXME: consider saving the list of commands as on-disk cache. */
if (cache_is_available) {
/* load cached program list */
} else {
/* load in another working thread */
thread_data = g_slice_new0(ThreadData); /* the data will be freed in idle handler later. */
thread_data->entry = entry;
g_thread_create((GThreadFunc) thread_func, thread_data, FALSE, NULL);
}
}
static void reload_apps(MenuCache* cache, gpointer user_data) {
g_debug("reload apps!");
if (app_list) {
g_slist_foreach(app_list, (GFunc) menu_cache_item_unref, NULL);
g_slist_free(app_list);
}
app_list = (GSList*) menu_cache_list_all_apps(cache);
}
static void on_response(GtkDialog* dlg, gint response, gpointer user_data) {
GtkEntry* entry = (GtkEntry*) user_data;
if (G_LIKELY(response == GTK_RESPONSE_OK)) {
GError* err = NULL;
const gchar * str = gtk_entry_get_text(entry);
gchar command[1024];
memset(command, 0, sizeof(command));
MenuCacheApp* app = NULL;
if (str && *str && strcmp(str, "") != 0) {
/* Try to match the text with a system application */
app = match_system_exec(str);
if (app) {
strcpy(command, menu_cache_app_get_exec(app));
} else {
/* Try to match the text with a URI */
app = match_uri_app(str);
if (app) {
strcpy(command, menu_cache_app_get_exec(app));
strcat(command, " ");
strcat(command, str);
}
}
}
if (*command == '\0')
strcpy(command, str);
if (!g_spawn_command_line_async(command, &err)) {
show_error((GtkWindow*) dlg, err->message);
g_error_free(err);
g_signal_stop_emission_by_name(dlg, "response");
return;
}
}
/* cancel running thread if needed */
if (thread_data) /* the thread is still running */
thread_data->cancel = TRUE; /* cancel the thread */
gtk_widget_destroy((GtkWidget*) dlg);
win = NULL;
/* free app list */
g_slist_foreach(app_list, (GFunc) menu_cache_item_unref, NULL);
g_slist_free(app_list);
app_list = NULL;
/* free menu cache */
menu_cache_remove_reload_notify(menu_cache, reload_notify_id);
reload_notify_id = NULL;
menu_cache_unref(menu_cache);
menu_cache = NULL;
}
static void on_entry_changed(GtkEntry* entry, GtkImage* img) {
const char* str = gtk_entry_get_text(entry);
MenuCacheApp* app = NULL;
/* Try to match the text with a system application */
if (str && *str)
app = match_system_exec(str);
/* Try to match the text with a URI */
if (!app)
app = match_uri_app(str);
if (app) {
int w, h;
GdkPixbuf* pix;
gtk_icon_size_lookup(GTK_ICON_SIZE_DIALOG, &w, &h);
pix = lxpanel_load_icon(menu_cache_item_get_icon(MENU_CACHE_ITEM(app)), w, h, TRUE);
gtk_image_set_from_pixbuf(img, pix);
g_object_unref(pix);
} else {
gtk_image_set_from_stock(img, GTK_STOCK_EXECUTE, GTK_ICON_SIZE_DIALOG);
}
}
static void activate_window(GtkWindow* toplevel_window) {
/* Calling gtk_window_present() cannot support
* source indication. Use our own implementation.
* Without this, the window activated might be
* put in background by WM to avoid stealing
* of focus.
* See EWMH spec, source indication part:
* http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#sourceindication
*/
GtkWidget* widget = GTK_WIDGET(toplevel_window);
GdkScreen* screen = gtk_widget_get_screen(widget);
if (gdk_x11_screen_supports_net_wm_hint(screen,
gdk_atom_intern_static_string("_NET_ACTIVE_WINDOW"))) {
GdkWindow* window = widget->window;
GdkDisplay* display = gtk_widget_get_display(widget);
GdkWindow* root = gdk_screen_get_root_window(screen);
/* show the window first */
gtk_widget_show(widget);
/* then, activate it */
XClientMessageEvent xclient;
memset(&xclient, 0, sizeof (xclient));
xclient.type = ClientMessage;
xclient.window = GDK_WINDOW_XID(window);
xclient.message_type = gdk_x11_get_xatom_by_name_for_display(display,
"_NET_ACTIVE_WINDOW");
xclient.format = 32;
xclient.data.l[0] = 2; /* source indication: 2 represents direct user actions. */
xclient.data.l[1] = gtk_get_current_event_time();
xclient.data.l[2] = None; /* currently active window */
xclient.data.l[3] = 0;
xclient.data.l[4] = 0;
XSendEvent(GDK_DISPLAY_XDISPLAY(display), GDK_WINDOW_XID(root), False,
SubstructureRedirectMask | SubstructureNotifyMask,
(XEvent *) & xclient);
}/* if _NET_ACTIVE_WINDOW command is not supported */
else
gtk_window_present(toplevel_window);
}
void gtk_run() {
GtkWidget *entry, *hbox, *img;
if (!win) {
win = gtk_dialog_new_with_buttons(_("Run"),
NULL,
GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order((GtkDialog*) win,
GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
gtk_dialog_set_default_response((GtkDialog*) win, GTK_RESPONSE_OK);
entry = gtk_entry_new();
gtk_entry_set_activates_default((GtkEntry*) entry, TRUE);
gtk_box_pack_start((GtkBox*) ((GtkDialog*) win)->vbox,
gtk_label_new(_("Enter the command you want to execute:")),
FALSE, FALSE, 8);
hbox = gtk_hbox_new(FALSE, 2);
img = gtk_image_new_from_stock(GTK_STOCK_EXECUTE, GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start((GtkBox*) hbox, img,
FALSE, FALSE, 4);
gtk_box_pack_start((GtkBox*) hbox, entry, TRUE, TRUE, 4);
gtk_box_pack_start((GtkBox*) ((GtkDialog*) win)->vbox,
hbox, FALSE, FALSE, 8);
g_signal_connect(win, "response", G_CALLBACK(on_response), entry);
gtk_window_set_position((GtkWindow*) win, GTK_WIN_POS_CENTER);
gtk_window_set_default_size((GtkWindow*) win, 360, -1);
gtk_widget_show_all(win);
setup_auto_complete((GtkEntry*) entry);
gtk_widget_show(win);
g_signal_connect(entry, "changed", G_CALLBACK(on_entry_changed), img);
/* get all apps */
menu_cache = menu_cache_lookup(g_getenv("XDG_MENU_PREFIX") ? "applications.menu" : "lxde-applications.menu");
if (menu_cache) {
app_list = (GSList*) menu_cache_list_all_apps(menu_cache);
reload_notify_id = menu_cache_add_reload_notify(menu_cache, reload_apps, NULL);
}
}
activate_window(GTK_WINDOW(win));
}
------------------------------------------------------------------------------
Precog is a next-generation analytics platform capable of advanced
analytics on semi-structured data. The platform includes APIs for building
apps and a phenomenal toolset for data science. Developers can use
our toolset for easy data analysis & visualization. Get a free account!
http://www2.precog.com/precogplatform/slashdotnewsletter
_______________________________________________
Lxde-list mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/lxde-list