mc/gnome/gnome-open-dialog.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 */
static GSList *
get_presorted_from(char *dir)
{
char buf[PATH_MAX+1];
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,PATH_MAX+1,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_ctree_set_line_style (GTK_CTREE (retval), GTK_CTREE_LINES_DOTTED);
gtk_ctree_set_expander_style (GTK_CTREE (retval), GTK_CTREE_EXPANDER_SQUARE);
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);
}