mirror of https://github.com/MidnightCommander/mc
436 lines
14 KiB
C
436 lines
14 KiB
C
/* gnome-open-dialog.c
|
|
* Copyright (C) 1999 Red Hat Software. Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include <config.h>
|
|
#include "util.h"
|
|
#include <gnome.h>
|
|
#include "gnome-open-dialog.h"
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
static void gnome_open_dialog_init (GnomeOpenDialog *open_dialog);
|
|
static void gnome_open_dialog_class_init (GnomeOpenDialogClass *klass);
|
|
static void gnome_open_dialog_destroy (GtkObject *object);
|
|
static void gnome_open_dialog_generate_tree_helper (GtkCTree *ctree, GtkCTreeNode *parent, GNode *node);
|
|
static GSList *get_presorted_from(char *dir);
|
|
static void gnome_open_dialog_destroy_func (GtkCTree *ctree, GtkCTreeNode *node, gpointer data);
|
|
|
|
static GnomeDialogClass *parent_class = NULL;
|
|
#define SMALL_ICON_SIZE 20
|
|
|
|
|
|
GtkType
|
|
gnome_open_dialog_get_type (void)
|
|
{
|
|
static GtkType open_dialog_type = 0;
|
|
|
|
if (!open_dialog_type)
|
|
{
|
|
static const GtkTypeInfo open_dialog_info =
|
|
{
|
|
"GnomeOpenDialog",
|
|
sizeof (GnomeOpenDialog),
|
|
sizeof (GnomeOpenDialogClass),
|
|
(GtkClassInitFunc) gnome_open_dialog_class_init,
|
|
(GtkObjectInitFunc) gnome_open_dialog_init,
|
|
/* reserved_1 */ NULL,
|
|
/* reserved_2 */ NULL,
|
|
(GtkClassInitFunc) NULL,
|
|
};
|
|
|
|
open_dialog_type = gtk_type_unique (gnome_dialog_get_type (), &open_dialog_info);
|
|
}
|
|
|
|
return open_dialog_type;
|
|
}
|
|
|
|
static void
|
|
gnome_open_dialog_class_init (GnomeOpenDialogClass *klass)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
|
|
object_class = (GtkObjectClass*) klass;
|
|
|
|
parent_class = gtk_type_class (gnome_dialog_get_type ());
|
|
object_class->destroy = gnome_open_dialog_destroy;
|
|
|
|
}
|
|
static void
|
|
gnome_open_dialog_destroy_func (GtkCTree *ctree,
|
|
GtkCTreeNode *node,
|
|
gpointer data)
|
|
{
|
|
GnomeDesktopEntry *gde;
|
|
if (ctree == NULL || node == NULL)
|
|
return;
|
|
|
|
gde = (GnomeDesktopEntry *)gtk_ctree_node_get_row_data (GTK_CTREE (ctree),node);
|
|
if (gde)
|
|
gnome_desktop_entry_free (gde);
|
|
}
|
|
static void
|
|
gnome_open_dialog_destroy (GtkObject *object)
|
|
{
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GNOME_IS_OPEN_DIALOG (object));
|
|
g_free (GNOME_OPEN_DIALOG (object)->file_name);
|
|
gtk_ctree_post_recursive (GTK_CTREE (GNOME_OPEN_DIALOG (object)->ctree),
|
|
NULL,
|
|
gnome_open_dialog_destroy_func,
|
|
NULL);
|
|
gtk_widget_unref (GNOME_OPEN_DIALOG (object)->ctree);
|
|
gtk_widget_unref (GNOME_OPEN_DIALOG (object)->entry);
|
|
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
|
}
|
|
static void
|
|
gnome_open_dialog_init (GnomeOpenDialog *open_dialog)
|
|
{
|
|
|
|
}
|
|
/* Private internal functions */
|
|
/* this will help order our tree */
|
|
static gchar *
|
|
strip_name_from_gde (GnomeDesktopEntry *gde,
|
|
gboolean fname)
|
|
{
|
|
gchar *retval = NULL;
|
|
gchar *ptr;
|
|
|
|
if (gde == NULL)
|
|
return NULL;
|
|
if (!fname)
|
|
return (g_strdup (gde->name));
|
|
|
|
if (strcmp (x_basename (gde->location), ".directory")) {
|
|
return g_strdup (x_basename(gde->location));
|
|
}
|
|
|
|
ptr = strrchr (gde->location, '/');
|
|
if (ptr)
|
|
ptr--;
|
|
while (ptr && ptr != gde->location && *ptr != '/') {
|
|
ptr--;
|
|
}
|
|
if (ptr) {
|
|
retval = g_strdup (ptr +1);
|
|
ptr = strrchr (retval, '/');
|
|
if (ptr)
|
|
ptr[0] = '\000';
|
|
}
|
|
return retval;
|
|
}
|
|
static void
|
|
insert_node_custom (GSList *order,
|
|
GNode *sorted_list,
|
|
GNode *node,
|
|
gchar *name)
|
|
{
|
|
/* This little insertion sort is pretty ugly, but I guess
|
|
* it works. It'd be good to move this information into
|
|
* Gnome-libs, somewhere...
|
|
*/
|
|
GNode *temp_node;
|
|
gchar *fname;
|
|
gchar *fname2;
|
|
|
|
g_return_if_fail (sorted_list != NULL);
|
|
|
|
temp_node = sorted_list->children;
|
|
while (order) {
|
|
if (!strcmp ((gchar *) order->data, x_basename (name))) {
|
|
if (temp_node)
|
|
g_node_insert_before (sorted_list, temp_node, node);
|
|
else
|
|
g_node_append (sorted_list, node);
|
|
return;
|
|
}
|
|
if (temp_node) {
|
|
fname = strip_name_from_gde ((GnomeDesktopEntry *) temp_node->data, TRUE);
|
|
if (fname && !strcmp (fname, (gchar *) order->data)) {
|
|
temp_node = temp_node->next;
|
|
}
|
|
g_free (fname);
|
|
}
|
|
order = order->next;
|
|
}
|
|
fname2 = strip_name_from_gde (node->data, FALSE);
|
|
for (;temp_node;temp_node = temp_node->next) {
|
|
fname = strip_name_from_gde (temp_node->data, FALSE);
|
|
if (fname && fname2 && strcmp (fname2, fname) < 0) {
|
|
g_node_insert_before (sorted_list, temp_node, node);
|
|
return;
|
|
}
|
|
g_free (fname);
|
|
}
|
|
g_free (fname2);
|
|
g_node_append (sorted_list, node);
|
|
}
|
|
/* Stolen from gnomecc:tree.c */
|
|
static GNode *
|
|
read_directory (gchar *directory)
|
|
{
|
|
DIR *parent_dir;
|
|
struct dirent *child_dir;
|
|
struct stat filedata;
|
|
GSList *order;
|
|
GNode *retval = g_node_new(NULL);
|
|
GNode *new_node;
|
|
|
|
parent_dir = opendir (directory);
|
|
if (parent_dir == NULL)
|
|
return NULL;
|
|
order = get_presorted_from (directory);
|
|
while ((child_dir = readdir (parent_dir)) != NULL) {
|
|
new_node = NULL;
|
|
if (child_dir->d_name[0] != '.') {
|
|
|
|
/* we check to see if it is interesting. */
|
|
GString *name = g_string_new (directory);
|
|
g_string_append (name, "/");
|
|
g_string_append (name, child_dir->d_name);
|
|
|
|
if (stat (name->str, &filedata) != -1) {
|
|
gchar* test;
|
|
if (S_ISDIR (filedata.st_mode)) {
|
|
/* it might be interesting... */
|
|
new_node = read_directory (name->str);
|
|
if (new_node)
|
|
/* it is interesting!!! */
|
|
insert_node_custom (order, retval, new_node, name->str);
|
|
/* g_node_prepend (retval, new_node);*/
|
|
}
|
|
test = strrchr(child_dir->d_name, '.');
|
|
if (test && !strcmp (".desktop", test)) {
|
|
/* it's a .desktop file -- it's interesting for sure! */
|
|
new_node = g_node_new (gnome_desktop_entry_load (name->str));
|
|
insert_node_custom (order, retval, new_node, name->str);
|
|
/* g_node_prepend (retval, new_node);*/
|
|
}
|
|
}
|
|
g_string_free (name, TRUE);
|
|
} else if (!strcmp (child_dir->d_name, ".directory")) {
|
|
GString *name = g_string_new (directory);
|
|
g_string_append (name, "/.directory");
|
|
retval->data = gnome_desktop_entry_load (name->str);
|
|
g_string_free (name, TRUE);
|
|
}
|
|
}
|
|
closedir (parent_dir);
|
|
if (retval->children == NULL) {
|
|
if (retval->data)
|
|
gnome_desktop_entry_free (retval->data);
|
|
g_node_destroy (retval);
|
|
return NULL;
|
|
}
|
|
return retval;
|
|
}
|
|
static void
|
|
gnome_open_dialog_generate_tree_helper (GtkCTree *ctree, GtkCTreeNode *parent, GNode *node)
|
|
{
|
|
GNode *i;
|
|
GtkCTreeNode *child = NULL;
|
|
static char *text[2];
|
|
GnomePixmap *icon_gpixmap;
|
|
gchar *icon;
|
|
GdkPixmap *icon_pixmap, *icon_mask;
|
|
|
|
|
|
text[0] = NULL;
|
|
text[1] = NULL;
|
|
|
|
for (i = node;i;i = i->next) {
|
|
icon_pixmap=NULL;
|
|
icon_mask=NULL;
|
|
icon=NULL;
|
|
icon_gpixmap = NULL;
|
|
|
|
if (i->data && ((GnomeDesktopEntry *)i->data)->name) {
|
|
text[0] = ((GnomeDesktopEntry *)i->data)->name;
|
|
} else
|
|
text[0] = NULL;
|
|
if ((i->data) && ((GnomeDesktopEntry *)i->data)->icon)
|
|
icon = ((GnomeDesktopEntry *)i->data)->icon;
|
|
if (icon && g_file_exists (icon))
|
|
icon_gpixmap = (GnomePixmap *)gnome_pixmap_new_from_file_at_size(icon,
|
|
SMALL_ICON_SIZE,
|
|
SMALL_ICON_SIZE);
|
|
if (icon_gpixmap) {
|
|
icon_pixmap = icon_gpixmap->pixmap;
|
|
icon_mask = icon_gpixmap->mask;
|
|
}
|
|
|
|
if (i->data && text[0]) {
|
|
if (((GnomeDesktopEntry *)i->data)->type && !strcmp(((GnomeDesktopEntry *)i->data)->type,"Directory"))
|
|
child = gtk_ctree_insert_node (ctree,parent,NULL, text, 3, icon_pixmap, icon_mask, icon_pixmap, icon_mask,FALSE,FALSE);
|
|
else
|
|
child = gtk_ctree_insert_node (ctree,parent,NULL, text, 3, icon_pixmap, icon_mask, icon_pixmap, icon_mask,TRUE,FALSE);
|
|
gtk_ctree_node_set_row_data (ctree, child, i->data);
|
|
} else
|
|
gnome_desktop_entry_free (i->data);
|
|
if (i->children)
|
|
gnome_open_dialog_generate_tree_helper (ctree, child, i->children);
|
|
/*
|
|
if (parent == NULL)
|
|
gtk_ctree_expand_recursive (ctree, child);
|
|
*/
|
|
}
|
|
}
|
|
|
|
/* Stolen from gnome-core/panel/menu.c */
|
|
#define MAX_ORDER_ENTRY 1024
|
|
|
|
static GSList *
|
|
get_presorted_from(char *dir)
|
|
{
|
|
char buf[MAX_ORDER_ENTRY];
|
|
GSList *list = NULL;
|
|
char *fname = g_concat_dir_and_file(dir,".order");
|
|
FILE *fp = fopen(fname,"r");
|
|
|
|
if(!fp) {
|
|
g_free(fname);
|
|
return NULL;
|
|
}
|
|
while(fgets(buf, sizeof (buf), fp) != NULL) {
|
|
char *p = strchr(buf,'\n');
|
|
if(p) *p = '\0';
|
|
list = g_slist_prepend(list,g_strdup(buf));
|
|
}
|
|
fclose(fp);
|
|
g_free(fname);
|
|
return g_slist_reverse(list);
|
|
}
|
|
static void
|
|
gnome_open_dialog_selected_row_callback (GtkWidget *widget, GtkCTreeNode *node, gint column, gpointer *data)
|
|
{
|
|
GnomeDesktopEntry *gde = NULL;
|
|
GnomeOpenDialog *dialog = NULL;
|
|
GtkWidget *entry;
|
|
|
|
if (column < 0)
|
|
return;
|
|
|
|
dialog = GNOME_OPEN_DIALOG (data);
|
|
gde = (GnomeDesktopEntry *)gtk_ctree_node_get_row_data (GTK_CTREE (widget),node);
|
|
|
|
g_return_if_fail (dialog != NULL);
|
|
g_return_if_fail (gde != NULL);
|
|
|
|
if (gde->exec) {
|
|
entry = gnome_file_entry_gtk_entry (GNOME_FILE_ENTRY (dialog->entry));
|
|
gtk_entry_set_text (GTK_ENTRY (entry), gde->exec[0]);
|
|
}
|
|
}
|
|
GtkWidget *
|
|
gnome_open_dialog_get_tree (GnomeOpenDialog *dialog)
|
|
{
|
|
GtkWidget *retval;
|
|
gchar *prefix;
|
|
GNode *node;
|
|
|
|
/* widget stuff */
|
|
retval = gtk_ctree_new (1, 0);
|
|
gtk_clist_set_row_height(GTK_CLIST (retval),20);
|
|
gtk_clist_set_column_width(GTK_CLIST (retval), 0, 150);
|
|
gtk_signal_connect( GTK_OBJECT (retval),
|
|
"tree_select_row",
|
|
GTK_SIGNAL_FUNC (gnome_open_dialog_selected_row_callback),
|
|
dialog);
|
|
|
|
gtk_ctree_set_indent (GTK_CTREE (retval), 15);
|
|
gtk_clist_set_column_auto_resize (GTK_CLIST (retval), 0, TRUE);
|
|
|
|
/* set up the apps */
|
|
prefix = gnome_unconditional_datadir_file ("gnome/apps");
|
|
node = read_directory (prefix);
|
|
gnome_open_dialog_generate_tree_helper (GTK_CTREE (retval), NULL, node);
|
|
gtk_ctree_expand_recursive (GTK_CTREE (retval), NULL);
|
|
g_node_destroy (node);
|
|
g_free (prefix);
|
|
return retval;
|
|
}
|
|
/* Public functions */
|
|
GtkWidget *
|
|
gnome_open_dialog_new (gchar *file_name)
|
|
{
|
|
GnomeOpenDialog *dialog;
|
|
GtkWidget *label;
|
|
GtkWidget *frame;
|
|
GtkWidget *sw;
|
|
gchar *label_string;
|
|
|
|
g_return_val_if_fail (file_name != NULL, NULL);
|
|
dialog = gtk_type_new (gnome_open_dialog_get_type ());
|
|
|
|
/* the first label */
|
|
label_string = g_strdup_printf (
|
|
_("Select an application to open \"%s\" with."),file_name);
|
|
label = gtk_label_new (label_string);
|
|
gtk_widget_set_usize (label, 300, -1);
|
|
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
|
|
g_free (label_string);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
|
|
/* The entry */
|
|
dialog->file_name = g_strdup (file_name);
|
|
dialog->entry = gnome_file_entry_new ("GNOME_OPEN_DIALOG",
|
|
_("Select a file to run with"));
|
|
gtk_widget_ref (dialog->entry);
|
|
/* Watch me do something evil... (-: */
|
|
label_string = gnome_unconditional_libdir_file ("");
|
|
strcpy (label_string + strlen (label_string) - 4, "bin");
|
|
|
|
gnome_file_entry_set_default_path (GNOME_FILE_ENTRY (dialog->entry),
|
|
label_string);
|
|
g_free (label_string);
|
|
|
|
/* the file tree */
|
|
frame = gtk_frame_new (_("Applications"));
|
|
dialog->ctree = gnome_open_dialog_get_tree (dialog);
|
|
gtk_widget_ref (dialog->ctree);
|
|
sw = gtk_scrolled_window_new (GTK_CLIST (dialog->ctree)->hadjustment,
|
|
GTK_CLIST (dialog->ctree)->vadjustment);
|
|
gtk_widget_set_usize (sw, 300, 170);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
gtk_container_set_border_width (GTK_CONTAINER (sw), GNOME_PAD_SMALL);
|
|
gtk_container_add (GTK_CONTAINER (sw), dialog->ctree);
|
|
gtk_container_add (GTK_CONTAINER (frame), sw);
|
|
|
|
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
|
|
label, FALSE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
|
|
frame, FALSE, FALSE, 0);
|
|
|
|
frame = gtk_frame_new (_("Program to run"));
|
|
gtk_container_set_border_width (GTK_CONTAINER (dialog->entry), GNOME_PAD_SMALL);
|
|
gtk_container_add (GTK_CONTAINER (frame), dialog->entry);
|
|
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
|
|
frame, FALSE, FALSE, 0);
|
|
gnome_dialog_append_button (GNOME_DIALOG(dialog),
|
|
GNOME_STOCK_BUTTON_OK);
|
|
gnome_dialog_append_button (GNOME_DIALOG(dialog),
|
|
GNOME_STOCK_BUTTON_CANCEL);
|
|
gtk_widget_show_all (GNOME_DIALOG (dialog)->vbox);
|
|
return GTK_WIDGET (dialog);
|
|
}
|
|
|
|
|