source: trunk/third/gnome-core/panel/gnome-run.c @ 17152

Revision 17152, 41.3 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17151, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2/*
3 *   grun: Popup a command dialog. Original version by Elliot Lee,
4 *    bloatware edition by Havoc Pennington. Both versions written in 10
5 *    minutes or less. :-)
6 *   Copyright (C) 1998 Havoc Pennington <hp@pobox.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <config.h>
25#include <gnome.h>
26#include <libgnomeui/gnome-window-icon.h>
27#include <errno.h>
28#include <sys/types.h>
29#include <dirent.h>
30
31#include "panel-include.h"
32#include "foobar-widget.h"
33#include "menu-fentry.h"
34#include "menu.h"
35#include "main.h"
36#include "multiscreen-stuff.h"
37
38#include "gnome-run.h"
39
40#define ADVANCED_DIALOG_KEY "advanced_run_dialog"
41
42extern GSList *applets_last;
43extern GtkTooltips *panel_tooltips;
44extern GlobalConfig global_config;
45extern gboolean no_run_box;
46
47static GtkWidget *run_dialog = NULL;
48
49static GList *executables = NULL;
50static GCompletion *exe_completion = NULL;
51
52static GtkWidget* create_advanced_contents (void);
53static void       update_contents          (GtkWidget *dialog);
54static void       unset_selected           (GtkWidget *dialog);
55
56static void
57fill_executables_from (const char *dirname)
58{
59        struct dirent *dent;
60        DIR *dir;
61
62        dir = opendir (dirname);
63
64        if (dir == NULL)
65                return;
66
67        while ( (dent = readdir (dir)) != NULL) {
68                char *file = g_strconcat (dirname, "/", dent->d_name, NULL);
69
70                if (access (file, X_OK) == 0)
71                        executables = g_list_prepend (executables,
72                                                      g_strdup (dent->d_name));
73        }
74
75        closedir (dir);
76}
77
78static void
79fill_executables (void)
80{
81        int i;
82        char *path;
83        char **pathv;
84
85        g_list_foreach (executables, (GFunc) g_free, NULL);
86        g_list_free (executables);
87        executables = NULL;
88
89        path = g_getenv ("PATH");
90
91        if (path == NULL ||
92            path[0] == '\0')
93                return;
94
95        pathv = g_strsplit (path, ":", 0);
96
97        for (i = 0; pathv[i] != NULL; i++)
98                fill_executables_from (pathv[i]);
99
100        g_strfreev (pathv);
101}
102
103static void
104ensure_completion (void)
105{
106        if (exe_completion == NULL) {
107                exe_completion = g_completion_new (NULL);
108                fill_executables ();
109
110                g_completion_add_items (exe_completion, executables);
111        }
112}
113
114static void
115kill_completion (void)
116{
117        if (executables != NULL) {
118                g_list_foreach (executables, (GFunc) g_free, NULL);
119                g_list_free (executables);
120                executables = NULL;
121        }
122
123        if (exe_completion != NULL) {
124                g_completion_free (exe_completion);
125                exe_completion = NULL;
126        }
127}
128
129/* Note, this expects a vector allocated by popt, where we can
130 * just forget about entries as they are part of the same buffer */
131static void
132get_environment (int *argc, char ***argv, int *envc, char ***envv)
133{
134        GList *envar = NULL, *li;
135        int i, moveby;
136
137        *envv = NULL;
138        *envc = 0;
139
140        moveby = 0;
141        for (i = 0; i < *argc; i++) {
142                if (strchr ((*argv)[i], '=') == NULL) {
143                        break;
144                }
145                envar = g_list_append (envar, g_strdup ((*argv)[i]));
146                moveby ++;
147        }
148
149        if (moveby == *argc) {
150                g_list_foreach (envar, (GFunc) g_free, NULL);
151                g_list_free (envar);
152                return;
153        }
154
155        if (envar == NULL)
156                return;
157
158        for (i = 0; i < *argc; i++) {
159                if (i + moveby < *argc)
160                        (*argv)[i] = (*argv)[i+moveby];
161                else
162                        (*argv)[i] = NULL;
163        }
164        *argc -= moveby;
165
166        *envc = g_list_length (envar);
167        *envv = g_new0 (char *, *envc + 1);
168        for (i = 0, li = envar; li != NULL; li = li->next, i++) {
169                (*envv)[i] = li->data;
170                li->data = NULL;
171        }       
172        (*envv)[i] = NULL;
173        g_list_free (envar);
174}
175
176static void
177string_callback (GtkWidget *w, int button_num, gpointer data)
178{
179        GtkEntry *entry;
180        GtkWidget *clist;
181        GtkToggleButton *terminal;
182        char **argv, **temp_argv = NULL;
183        int argc, temp_argc;
184        char *s;
185        GSList *tofree = NULL;
186        char **envv = NULL;
187        int envc;
188        gboolean use_advanced;
189        gboolean add_to_favourites;
190        GtkWidget *favorites;
191       
192        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
193       
194        if (button_num == 2/*help*/) {
195                panel_show_help ("specialobjects.html#RUNBUTTON");
196                /* just return as we don't want to close */
197                return;
198        } else if (button_num == 1/*cancel*/) {
199                goto return_and_close;
200        }
201
202       
203       
204        clist = gtk_object_get_data (GTK_OBJECT (run_dialog), "dentry_list");
205        terminal = GTK_TOGGLE_BUTTON (gtk_object_get_data(GTK_OBJECT(w),
206                                                          "terminal"));
207        favorites = gtk_object_get_data (GTK_OBJECT (run_dialog), "favorites");
208
209        add_to_favourites = GTK_TOGGLE_BUTTON (favorites)->active;
210       
211        if (gtk_object_get_data (GTK_OBJECT (run_dialog), "use_list")) {
212                char *name;
213               
214                if (GTK_CLIST (clist)->selection == NULL)
215                        return;
216       
217                name = gtk_clist_get_row_data (GTK_CLIST (clist),
218                                               GPOINTER_TO_INT (GTK_CLIST (clist)->selection->data));
219                if (name) {
220                        GnomeDesktopEntry *dentry;
221                       
222                        dentry = gnome_desktop_entry_load (name);
223                        if (dentry && dentry->exec) {
224                                /* Honor "run in terminal" button */
225                                dentry->terminal = terminal->active;
226                               
227                                gnome_desktop_entry_launch (dentry);
228                                gnome_desktop_entry_free (dentry);
229
230                                if (add_to_favourites)
231                                        panel_add_favourite (name);
232                        } else {
233                                panel_error_dialog (_("Failed to load this program!\n"));
234                        }
235                }
236        } else {
237                entry = GTK_ENTRY (gtk_object_get_data(GTK_OBJECT(w), "entry"));
238
239                s = gtk_entry_get_text(entry);
240
241                if (string_empty (s))
242                        goto return_and_close;
243
244                /* evil eggies, do not translate! */
245                if (strcmp (s, "time shall be unixey") == 0) {
246                        foobar_widget_global_set_clock_format ("%s");
247                        goto return_and_close;
248                }
249                if (strcmp (s, "you shall bring us a shrubbery") == 0) {
250                        gnome_ok_dialog ("NI! NI! NI! NI! NI! NI!");
251                        goto return_and_close;
252                }
253                if (strcmp (s, "supreme executive power") == 0) {
254                        gnome_ok_dialog ("Listen -- strange women lying in\n"
255                                         "ponds distributing swords is no\n"
256                                         "basis for a system of government.\n"
257                                         "Supreme executive power derives from\n"
258                                         "a mandate from the masses, not from\n"
259                                         "some farcical aquatic ceremony!");
260                        goto return_and_close;
261                }
262                if (strcmp (s, "free the fish") == 0) {
263                        start_screen_check ();
264                        goto return_and_close;
265                }
266
267                /* Somewhat of a hack I suppose */
268                if (panel_is_url (s)) {
269                        gnome_url_show (s);
270                        goto return_and_close;
271                }
272
273                /* we use a popt function as it does exactly what we want to do and
274                   gnome already uses popt */
275                if (poptParseArgvString (s, &temp_argc, &temp_argv) != 0) {
276                        panel_error_dialog (_("Failed to execute command:\n"
277                                              "%s"), s);
278                        goto return_and_close;
279                }
280
281                get_environment (&temp_argc, &temp_argv, &envc, &envv);
282
283                if (terminal->active) {
284                        char **term_argv;
285                        int term_argc;
286                        gnome_config_get_vector ("/Gnome/Applications/Terminal",
287                                                 &term_argc, &term_argv);
288                        if (term_argv) {
289                                int i;
290                                argv = g_new(char *, term_argc + temp_argc + 1);
291                                tofree = g_slist_prepend(tofree, argv);
292                                argc = term_argc + temp_argc;
293                                for(i = 0; i < term_argc; i++) {
294                                        argv[i] = term_argv[i];
295                                        tofree = g_slist_prepend(tofree, argv[i]);
296                                }
297                                for(i = term_argc; i < term_argc+temp_argc; i++)
298                                        argv[i] = temp_argv[i-term_argc];
299                                argv[i] = NULL;
300                                g_free (term_argv);
301                        } else {
302                                char *check;
303                                int i;
304                                check = panel_is_program_in_path("gnome-terminal");
305                                argv = g_new(char *, 2 + temp_argc + 1);
306                                tofree = g_slist_prepend(tofree, argv);
307                                argc = 2 + temp_argc;
308                                if(!check) {
309                                        argv[0] = "xterm";
310                                        argv[1] = "-e";
311                                } else {
312                                        argv[0] = check;
313                                        tofree = g_slist_prepend(tofree, check);
314                                        argv[1] = "-x";
315                                }
316                                for(i = 2; i < 2+temp_argc; i++)
317                                        argv[i] = temp_argv[i-2];
318                                argv[i] = NULL;
319                        }
320                } else {
321                        argv = temp_argv;
322                        argc = temp_argc;
323                }
324
325                if (gnome_execute_async_with_env (g_get_home_dir (),
326                                                  argc, argv,
327                                                  envc, envv) < 0) {
328                        panel_error_dialog(_("Failed to execute command:\n"
329                                             "%s\n"
330                                             "%s"),
331                                           s, g_unix_error_string(errno));
332                }
333        }
334       
335return_and_close:
336        g_slist_foreach (tofree, (GFunc)g_free, NULL);
337        g_slist_free (tofree);
338        /* this was obtained from the popt function and thus free and not
339         * g_free */
340        if (temp_argv)
341                free (temp_argv);
342        g_strfreev (envv);
343        gnome_dialog_close (GNOME_DIALOG (w));
344}
345
346static void
347browse_ok(GtkWidget *widget, GtkFileSelection *fsel)
348{
349        char *fname;
350        GtkWidget *entry;
351
352        g_return_if_fail(GTK_IS_FILE_SELECTION(fsel));
353
354        entry = gtk_object_get_user_data(GTK_OBJECT(fsel));
355
356        fname = gtk_file_selection_get_filename(fsel);
357        if(fname != NULL) {
358                char *s = gtk_entry_get_text (GTK_ENTRY (entry));
359                if (string_empty (s)) {
360                        gtk_entry_set_text (GTK_ENTRY (entry), fname);
361                } else {
362                        s = g_strconcat (s, " ", fname, NULL);
363                        gtk_entry_set_text (GTK_ENTRY (entry), s);
364                        g_free (s);
365                }
366        }
367        gtk_widget_destroy(GTK_WIDGET(fsel));
368}
369
370static void
371browse(GtkWidget *w, GtkWidget *entry)
372{
373        GtkFileSelection *fsel;
374
375        fsel = GTK_FILE_SELECTION(gtk_file_selection_new(_("Browse...")));
376        gtk_window_set_transient_for (GTK_WINDOW (fsel),
377                                      GTK_WINDOW (run_dialog));
378        gtk_object_set_user_data(GTK_OBJECT(fsel), entry);
379
380        gtk_signal_connect (GTK_OBJECT (fsel->ok_button), "clicked",
381                            GTK_SIGNAL_FUNC (browse_ok), fsel);
382        gtk_signal_connect_object
383                (GTK_OBJECT (fsel->cancel_button), "clicked",
384                 GTK_SIGNAL_FUNC (gtk_widget_destroy),
385                 GTK_OBJECT(fsel));
386        gtk_signal_connect_object_while_alive
387                (GTK_OBJECT (entry), "destroy",
388                 GTK_SIGNAL_FUNC (gtk_widget_destroy),
389                 GTK_OBJECT (fsel));
390
391        gtk_window_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
392        /* we must do show_now so that we can raise the window in the next
393         * call after set_dialog_layer */
394        gtk_widget_show_now (GTK_WIDGET (fsel));
395        panel_set_dialog_layer (GTK_WIDGET (fsel));
396        gdk_window_raise (GTK_WIDGET (fsel)->window);
397}
398
399static gboolean
400entry_event (GtkEntry * entry, GdkEventKey * event, gpointer data)
401{
402        if (event->type != GDK_KEY_PRESS)
403                return FALSE;
404
405        /* completion */
406        if ((event->keyval == GDK_Tab) &&
407            (event->state & GDK_CONTROL_MASK)) {
408                gchar* prefix;
409                gchar* nprefix = NULL;
410                gint pos;
411
412                ensure_completion ();
413
414                pos = GTK_EDITABLE (entry)->current_pos;
415                prefix = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, pos);
416
417                g_completion_complete (exe_completion, prefix, &nprefix);
418
419                if (nprefix != NULL &&
420                    strlen (nprefix) > strlen (prefix)) {
421                        gtk_editable_insert_text (GTK_EDITABLE (entry),
422                                                  nprefix + pos,
423                                                  strlen (nprefix) -
424                                                    strlen (prefix),
425                                                  &pos);
426                        GTK_EDITABLE (entry)->current_pos = pos;
427                } else {
428                        gdk_beep ();
429                }
430
431                g_free (nprefix);
432                g_free (prefix);
433
434                return TRUE;
435        }
436
437        return FALSE;
438}
439
440static void
441sync_entry_to_list (GtkWidget *dialog)
442{
443        GtkWidget *clist;
444        GtkWidget *entry;
445        gboolean blocked;
446
447        blocked = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (dialog),
448                                                        "sync_entry_to_list_blocked"));
449        if (blocked)
450                return;
451       
452        clist = gtk_object_get_data (GTK_OBJECT (dialog), "dentry_list");
453        entry = gtk_object_get_data(GTK_OBJECT(dialog), "entry");
454
455        unset_selected (dialog);
456}
457
458static void
459sync_list_to_entry (GtkWidget *dialog)
460{
461        GtkWidget *clist;
462        GtkWidget *entry;
463        GtkWidget *terminal_toggle;
464        gchar *name;
465
466        gtk_object_set_data (GTK_OBJECT (dialog),
467                             "sync_entry_to_list_blocked",
468                             GINT_TO_POINTER (TRUE));
469       
470        clist = gtk_object_get_data (GTK_OBJECT (dialog), "dentry_list");
471        entry = gtk_object_get_data (GTK_OBJECT (dialog), "entry");
472        terminal_toggle = gtk_object_get_data (GTK_OBJECT (dialog), "terminal");
473       
474        if (GTK_CLIST (clist)->selection) {
475                name = gtk_clist_get_row_data (GTK_CLIST (clist),
476                                               GPOINTER_TO_INT (GTK_CLIST (clist)->selection->data));
477                if (name) {
478                        GnomeDesktopEntry *dentry;
479
480                        dentry = gnome_desktop_entry_load (name);
481                        if (dentry && dentry->exec) {
482                                char *command;
483
484                                command = g_strjoinv (" ", dentry->exec);
485                               
486                                gtk_entry_set_text (GTK_ENTRY (entry),
487                                                    command);
488
489                                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (terminal_toggle),
490                                                              dentry->terminal);
491                               
492                                gnome_desktop_entry_free (dentry);
493                                g_free (command);
494                        }
495                }
496        }
497
498        gtk_object_set_data (GTK_OBJECT (dialog),
499                             "sync_entry_to_list_blocked",
500                             GINT_TO_POINTER (FALSE));
501
502        gtk_object_set_data (GTK_OBJECT (dialog), "use_list",
503                             GINT_TO_POINTER (TRUE));
504}
505
506static void
507toggle_contents (GtkWidget *button,
508                 GtkWidget *dialog)
509{
510        gboolean use_advanced;
511       
512        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
513
514        gnome_config_set_bool ("/panel/State/"ADVANCED_DIALOG_KEY, !use_advanced);
515        gnome_config_sync ();
516
517        update_contents (dialog);
518}
519
520static GtkWidget*
521create_toggle_advanced_button (const char *label)
522{
523        GtkWidget *align;
524        GtkWidget *button;
525
526        align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
527
528        button = gtk_button_new_with_label (label);
529
530        gtk_container_add (GTK_CONTAINER (align), button);
531
532        gtk_signal_connect (GTK_OBJECT (button), "clicked",
533                            GTK_SIGNAL_FUNC (toggle_contents),
534                            run_dialog);
535
536        gtk_object_set_data (GTK_OBJECT (run_dialog),
537                             "advanced_toggle_label",
538                             GTK_BIN (button)->child);
539       
540        return align;
541}
542
543static void
544entry_changed (GtkWidget *entry,
545               gpointer   data)
546{
547        sync_entry_to_list (GTK_WIDGET (data));
548}
549
550/* Called when advanced contents are switched to or first shown */
551static void
552advanced_contents_shown (GtkWidget *vbox,
553                         GtkWidget *dialog)
554{
555        /* does nothing at the moment */
556}
557
558static GtkWidget*
559create_advanced_contents (void)
560{
561        GtkWidget *vbox;
562        GtkWidget *entry;
563        GtkWidget *gentry;
564        GtkWidget *hbox;
565        GtkWidget *w;
566       
567        vbox = gtk_vbox_new (FALSE, 0);
568
569        hbox = gtk_hbox_new(0, FALSE);
570       
571        gentry = gnome_entry_new ("gnome-run");
572        gtk_box_pack_start (GTK_BOX (hbox), gentry, TRUE, TRUE, 0);
573        /* 1/4 the width of the first screen should be a good value */
574        gtk_widget_set_usize (GTK_WIDGET (gentry),
575                              multiscreen_width (0) / 4, -2);
576
577        entry = gnome_entry_gtk_entry (GNOME_ENTRY (gentry));
578
579        gtk_signal_connect (GTK_OBJECT (entry), "event",
580                            GTK_SIGNAL_FUNC (entry_event),
581                            NULL);
582        gtk_signal_connect (GTK_OBJECT (entry), "destroy",
583                            GTK_SIGNAL_FUNC (kill_completion),
584                            NULL);
585 
586        gtk_window_set_focus (GTK_WINDOW (run_dialog), entry);
587        gtk_combo_set_use_arrows_always (GTK_COMBO (gentry), TRUE);
588        gtk_object_set_data (GTK_OBJECT (run_dialog), "entry", entry);
589
590        gnome_dialog_editable_enters (GNOME_DIALOG (run_dialog),
591                                      GTK_EDITABLE (entry));
592        gtk_signal_connect (GTK_OBJECT (entry),
593                            "changed",
594                            GTK_SIGNAL_FUNC (entry_changed),
595                            run_dialog);
596       
597        w = gtk_button_new_with_label(_("Browse..."));
598        gtk_signal_connect(GTK_OBJECT(w), "clicked",
599                           GTK_SIGNAL_FUNC (browse), entry);
600        gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE,
601                            GNOME_PAD_SMALL);
602
603        gtk_box_pack_start (GTK_BOX (vbox), hbox,
604                            FALSE, FALSE, GNOME_PAD_SMALL);
605
606        w = gtk_check_button_new_with_label(_("Run in terminal"));
607        gtk_object_set_data (GTK_OBJECT (run_dialog), "terminal", w);
608        gtk_box_pack_start (GTK_BOX (vbox), w,
609                            FALSE, FALSE, GNOME_PAD_SMALL);
610       
611        gtk_object_ref (GTK_OBJECT (vbox));
612       
613        gtk_object_set_data_full (GTK_OBJECT (run_dialog),
614                                  "advanced",
615                                  vbox,
616                                  (GtkDestroyNotify) gtk_object_unref);
617       
618        gtk_signal_connect (GTK_OBJECT (vbox),
619                            "show",
620                            GTK_SIGNAL_FUNC (advanced_contents_shown),
621                            run_dialog);
622
623        return vbox;
624}
625
626static void
627scan_dir_recurse (DirRec  *dr,
628                  GSList **entry_list)
629{
630        GSList *li;
631       
632        for (li = dr->recs; li != NULL; li = li->next) {
633                FileRec *fr = li->data;
634               
635                if (fr->type == FILE_REC_FILE) {
636                        if (fr->goad_id != NULL)
637                                continue; /* applet */
638
639                        *entry_list = g_slist_prepend (*entry_list, fr);
640                       
641                } else if (fr->type == FILE_REC_DIR) {
642                        scan_dir_recurse ((DirRec*)fr, entry_list);
643                } else {
644                        continue;
645                }
646        }
647}
648
649static int
650sort_by_name (FileRec *fra,
651              FileRec *frb)
652{
653        return strcoll (fra->fullname, frb->fullname);
654}
655
656#define CLIST_ICON_SIZE 20
657
658/* Called when simple contents are switched to or first shown */
659static void
660simple_contents_shown (GtkWidget *vbox,
661                       GtkWidget *dialog)
662{
663        GtkWidget *advanced;
664        GSList *entries;
665        GSList *tmp;
666        GSList *files;
667        GSList *prev;
668        GtkWidget *clist;
669        char *prev_name;
670       
671        clist = gtk_object_get_data (GTK_OBJECT (dialog), "dentry_list");
672        advanced = gtk_object_get_data (GTK_OBJECT (dialog), "advanced");
673       
674        if (advanced) {
675                /* If we have advanced contents containing a command,
676                 * try to match that command to some desktop entry
677                 * in order to fill in our default.
678                 */
679               
680                /*  FIXME */
681        }
682
683        if (GTK_CLIST (clist)->rows == 0) {
684                GdkPixmap *spacer_pixmap;
685                GdkBitmap *spacer_mask;
686                GdkGC *gc;
687                GdkColor color;
688
689                /* Create invisible pixmap/mask to put space
690                 * before entries with no icon
691                 */
692                spacer_pixmap = gdk_pixmap_new (NULL,
693                                                CLIST_ICON_SIZE,
694                                                CLIST_ICON_SIZE,
695                                                gtk_widget_get_visual (clist)->depth);
696
697                spacer_mask = gdk_pixmap_new (NULL,
698                                              CLIST_ICON_SIZE,
699                                              CLIST_ICON_SIZE,
700                                              1);
701
702                gc = gdk_gc_new (spacer_mask);
703                color.pixel = 0;
704                gdk_gc_set_foreground (gc, &color);
705                gdk_draw_rectangle (spacer_mask,
706                                    gc,
707                                    TRUE, 0, 0, CLIST_ICON_SIZE, CLIST_ICON_SIZE);
708                gdk_gc_unref (gc);
709                gc = NULL;
710               
711                entries = fr_get_all_dirs ();
712                files = NULL;
713                tmp = entries;
714                while (tmp != NULL) {
715                        DirRec *dr = tmp->data;
716
717                        scan_dir_recurse (dr, &files);
718               
719                        tmp = tmp->next;
720                }
721
722                /* Collate */
723                files = g_slist_sort (files, (GCompareFunc) sort_by_name);
724
725                /* Strip duplicates */
726                tmp = files;
727                prev = NULL;
728                prev_name = NULL;
729                while (tmp) {
730                        FileRec *fr;
731                       
732                        fr = tmp->data;
733                        if (prev_name && strcmp (fr->fullname, prev_name) == 0) {
734                                GSList *del = tmp;
735                       
736                                prev->next = del->next;
737                                g_slist_free_1 (del);
738                                tmp = prev->next;
739                        } else {
740                                prev = tmp;
741                                prev_name = fr->fullname;
742                                tmp = tmp->next;
743                        }
744                }
745       
746                tmp = files;
747                while (tmp != NULL) {
748                        FileRec *fr;
749                        GdkPixbuf *pixbuf;
750                        GdkPixmap *pixmap;
751                        GdkBitmap *mask;
752                        int row;
753                        char *text[2];
754               
755                        fr = tmp->data;
756
757                        if (fr->icon != NULL) {
758                                pixbuf = gdk_pixbuf_new_from_file (fr->icon);
759                        } else {
760                                pixbuf = NULL;
761                        }
762               
763                        if (pixbuf) {
764                                GdkPixbuf *scaled;
765                                scaled = gdk_pixbuf_scale_simple (pixbuf, CLIST_ICON_SIZE, CLIST_ICON_SIZE, GDK_INTERP_BILINEAR);
766                                gdk_pixbuf_render_pixmap_and_mask (scaled,
767                                                                   &pixmap, &mask, 128);
768                                gdk_pixbuf_unref (pixbuf);
769                                gdk_pixbuf_unref (scaled);
770                        } else {
771                                pixmap = spacer_pixmap;
772                                mask = spacer_mask;
773                        }
774
775                        text[0] = fr->fullname;
776                        text[1] = fr->comment;
777                        row = gtk_clist_append (GTK_CLIST (clist),
778                                                text);
779
780                        gtk_clist_set_pixtext (GTK_CLIST (clist),
781                                               row, 0,
782                                               fr->fullname,
783                                               3,
784                                               pixmap, mask);
785
786                        if (pixbuf) {
787                                if (pixmap)
788                                        gdk_pixmap_unref (pixmap);
789                                if (mask)
790                                        gdk_bitmap_unref (mask);
791                        }
792                               
793                        gtk_clist_set_row_data (GTK_CLIST (clist),
794                                                row, fr->name);
795               
796                        tmp = tmp->next;
797                }
798
799                gdk_pixmap_unref (spacer_pixmap);
800                gdk_bitmap_unref (spacer_mask);
801                g_slist_free (files);
802
803                gtk_clist_columns_autosize (GTK_CLIST (clist));
804        }
805}
806
807#define DEFAULT_ICON "nautilus/i-executable.png"
808#define FALLBACK_DEFAULT_ICON "gnome-logo-icon-transparent.png"
809
810static void
811unset_pixmap (GtkWidget *gpixmap)
812{
813       
814        gchar *file;
815
816        file = gnome_pixmap_file (DEFAULT_ICON);
817
818        if (file == NULL)
819                file = gnome_pixmap_file (FALLBACK_DEFAULT_ICON);
820       
821        if (file != NULL) {
822                gnome_pixmap_load_file (GNOME_PIXMAP (gpixmap),
823                                        file);
824                g_free (file);
825        } else {
826                /* Clear the pixmap, yay GnomePixmap rules */
827                gnome_pixmap_load_file (GNOME_PIXMAP (gpixmap),
828                                        "I do not exist anywhere 3413hjrneljghlkjflkjf");
829        }
830}
831
832static void
833unset_selected (GtkWidget *dialog)
834{
835        GtkWidget *label;
836        GtkWidget *gpixmap;
837        GtkWidget *desc_label;
838        GtkWidget *entry;
839        GtkWidget *clist;
840        char *text;
841       
842        label = gtk_object_get_data (GTK_OBJECT (dialog), "label");
843        gpixmap = gtk_object_get_data (GTK_OBJECT (dialog), "pixmap");
844        desc_label = gtk_object_get_data (GTK_OBJECT (dialog), "desc_label");
845        entry = gtk_object_get_data (GTK_OBJECT (dialog), "entry");
846        clist = gtk_object_get_data (GTK_OBJECT (dialog), "dentry_list");
847       
848        if (entry != NULL) {
849                text = gtk_editable_get_chars (GTK_EDITABLE (entry),
850                                               0, -1);
851        } else {
852                text = NULL;
853        }
854
855        if ( ! string_empty (text)) {
856                char *msg;
857                msg = g_strdup_printf (_("Will run '%s'"),
858                                       text);
859                if (label)
860                        gtk_label_set_text (GTK_LABEL (label), msg);
861               
862                if (desc_label)
863                        gtk_label_set_text (GTK_LABEL (desc_label), msg);
864
865                g_free (msg);
866        } else {
867                if (label)
868                        gtk_label_set_text (GTK_LABEL (label), _("No program selected"));
869               
870                if (desc_label)
871                        gtk_label_set_text (GTK_LABEL (desc_label), _("No program selected"));
872        }
873
874        g_free (text);
875       
876        unset_pixmap (gpixmap);
877
878        gtk_object_set_data (GTK_OBJECT (dialog), "use_list",
879                             GPOINTER_TO_INT (FALSE));
880
881        gtk_clist_set_selection_mode (GTK_CLIST (clist),
882                                      GTK_SELECTION_SINGLE);
883        gtk_clist_unselect_all (GTK_CLIST (clist));
884
885        /* Can't add non-listed items to favorites for now. */
886        gtk_widget_set_sensitive (gtk_object_get_data (GTK_OBJECT (dialog),
887                                                       "favorites"),
888                                  FALSE);
889}
890
891static void
892select_row_handler (GtkCList *clist,
893                    gint      row,
894                    gint      column,
895                    GdkEvent *event,
896                    gpointer  data)
897{
898        GtkWidget *label;
899        GtkWidget *gpixmap;
900        GtkWidget *desc_label;
901        GtkWidget *dialog = data;
902        gchar *name;
903
904        if (clist->selection == NULL)
905                return;
906
907        /* Change selection mode once we have a selection */
908        gtk_clist_set_selection_mode (GTK_CLIST (clist),
909                                      GTK_SELECTION_BROWSE);
910       
911        label = gtk_object_get_data (GTK_OBJECT (dialog), "label");
912        gpixmap = gtk_object_get_data (GTK_OBJECT (dialog), "pixmap");
913        desc_label = gtk_object_get_data (GTK_OBJECT (dialog), "desc_label");
914
915        name = gtk_clist_get_row_data (GTK_CLIST (clist),
916                                       row);
917
918        if (name) {
919                GnomeDesktopEntry *dentry;
920               
921                dentry = gnome_desktop_entry_load (name);
922                if (dentry != NULL) {
923                        GdkPixbuf *pixbuf;
924
925                        if (label != NULL)
926                                gtk_label_set_text (GTK_LABEL (label),
927                                                    dentry->name);
928
929                        if (desc_label != NULL)
930                                gtk_label_set_text (GTK_LABEL (desc_label),
931                                                    dentry->comment);
932
933                        if (dentry->icon != NULL) {
934                                pixbuf = gdk_pixbuf_new_from_file (dentry->icon);
935                                if (pixbuf == NULL) {
936                                        char *file = gnome_pixmap_file (dentry->icon);
937                                        if (file != NULL)
938                                                pixbuf = gdk_pixbuf_new_from_file (file);
939                                        g_free (file);
940                                }
941                        } else {
942                                pixbuf = NULL;
943                        }
944                       
945                        if (pixbuf) {
946                                GdkPixmap *pixmap;
947                                GdkBitmap *mask;
948
949                                gdk_pixbuf_render_pixmap_and_mask (pixbuf,
950                                                                   &pixmap, &mask, 128);
951
952                                /* GnomePixmap bites me */
953                                gdk_pixmap_unref (GNOME_PIXMAP (gpixmap)->pixmap);
954                                gdk_pixmap_unref (GNOME_PIXMAP (gpixmap)->mask);
955                                GNOME_PIXMAP (gpixmap)->pixmap = pixmap;
956                                GNOME_PIXMAP (gpixmap)->mask = mask;
957                                gtk_widget_queue_resize (gpixmap);
958                                gdk_pixbuf_unref (pixbuf);
959                        } else {
960                                unset_pixmap (gpixmap);
961                        }
962                       
963                        gnome_desktop_entry_free (dentry);
964                }
965        }
966
967        sync_list_to_entry (dialog);
968
969        /* Allow adding to favorites */
970        gtk_widget_set_sensitive (gtk_object_get_data (GTK_OBJECT (dialog),
971                                                       "favorites"),
972                                  TRUE);
973}
974
975static GtkWidget*
976create_simple_contents (void)
977{
978        GtkWidget *vbox;
979        GtkWidget *w;
980        GtkWidget *label;
981        GtkWidget *pixmap;
982        GtkWidget *clist;
983        GtkWidget *hbox;
984        GtkWidget *favorites;
985        char *titles[2];
986       
987        vbox = gtk_vbox_new (FALSE, 1);
988       
989        titles[0] = _("Available Programs");
990        titles[1] = _("Description");
991        clist = gtk_clist_new_with_titles (1 /* 2 */, titles);
992        gtk_object_set_data (GTK_OBJECT (run_dialog), "dentry_list", clist);
993
994        gtk_clist_set_selection_mode (GTK_CLIST (clist),
995                                      GTK_SELECTION_SINGLE);
996
997        gtk_clist_column_titles_passive (GTK_CLIST (clist));
998       
999        gtk_widget_ensure_style (clist);
1000        gtk_clist_set_row_height (GTK_CLIST (clist),
1001                                  MAX (clist->style->font->ascent +
1002                                       clist->style->font->descent,
1003                                       CLIST_ICON_SIZE));
1004
1005        gtk_signal_connect (GTK_OBJECT (clist),
1006                            "select_row",
1007                            GTK_SIGNAL_FUNC (select_row_handler),
1008                            run_dialog);
1009       
1010        w = gtk_scrolled_window_new (NULL, NULL);
1011        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
1012                                        GTK_POLICY_AUTOMATIC,
1013                                        GTK_POLICY_AUTOMATIC);
1014        gtk_container_add (GTK_CONTAINER (w), clist);
1015       
1016        gtk_box_pack_start (GTK_BOX (vbox), w,
1017                            TRUE, TRUE, GNOME_PAD_SMALL);
1018
1019
1020        w = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1021        gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
1022        hbox = gtk_hbox_new (FALSE, 3);
1023        gtk_container_add (GTK_CONTAINER (w), hbox);
1024       
1025        pixmap = gtk_type_new (GNOME_TYPE_PIXMAP);
1026        gtk_box_pack_start (GTK_BOX (hbox), pixmap, FALSE, FALSE, 0);
1027        gtk_object_set_data (GTK_OBJECT (run_dialog), "pixmap", pixmap);
1028       
1029        label = gtk_label_new ("");
1030        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1031        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1032        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1033        gtk_object_set_data (GTK_OBJECT (run_dialog), "desc_label", label);       
1034
1035#if 0
1036        label = gtk_label_new ("");
1037        gtk_object_set_data (GTK_OBJECT (run_dialog), "label", label);
1038        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
1039#endif
1040
1041        favorites = gtk_check_button_new_with_label (_("Add this program to Favorites"));
1042        gtk_object_set_data (GTK_OBJECT (run_dialog), "favorites", favorites);
1043        gtk_box_pack_start (GTK_BOX (vbox), favorites, FALSE, FALSE, 0);
1044       
1045        unset_selected (run_dialog);
1046       
1047        w = create_toggle_advanced_button ("");
1048        gtk_box_pack_end (GTK_BOX (GNOME_DIALOG (run_dialog)->vbox), w,
1049                          FALSE, FALSE, GNOME_PAD_SMALL);
1050       
1051        gtk_object_ref (GTK_OBJECT (vbox));
1052       
1053        gtk_object_set_data_full (GTK_OBJECT (run_dialog),
1054                                  "simple",
1055                                  vbox,
1056                                  (GtkDestroyNotify) gtk_object_unref);
1057
1058        gtk_signal_connect (GTK_OBJECT (vbox),
1059                            "show",
1060                            GTK_SIGNAL_FUNC (simple_contents_shown),
1061                            run_dialog);
1062
1063        gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (run_dialog)->vbox),
1064                            vbox,
1065                            TRUE, TRUE, 0);
1066       
1067        return vbox;
1068}
1069
1070
1071static void
1072update_contents (GtkWidget *dialog)
1073{
1074        GtkWidget *advanced = NULL;
1075        GtkWidget *advanced_toggle;
1076        gboolean use_advanced;
1077        GtkWidget *clist;
1078       
1079        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");       
1080        advanced_toggle = gtk_object_get_data (GTK_OBJECT (dialog),
1081                                               "advanced_toggle_label");
1082
1083        clist = gtk_object_get_data (GTK_OBJECT (dialog), "dentry_list");
1084       
1085        if (use_advanced) {
1086                advanced = gtk_object_get_data (GTK_OBJECT (dialog), "advanced");
1087               
1088                if (advanced && advanced->parent == NULL) {
1089                        gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
1090                                            advanced,
1091                                            FALSE, FALSE, 0);
1092               
1093                        gtk_widget_show_all (advanced);
1094
1095                        gtk_widget_grab_focus (advanced);
1096                }
1097
1098                gtk_label_set_text (GTK_LABEL (advanced_toggle),
1099                                    _("Hide advanced options"));
1100
1101                gtk_tooltips_set_tip (panel_tooltips, advanced_toggle->parent,
1102                                      _("Hide the advanced controls below this button."),
1103                                      NULL);               
1104
1105        } else {               
1106                advanced = gtk_object_get_data (GTK_OBJECT (dialog), "advanced");
1107               
1108                if (advanced && advanced->parent != NULL)
1109                        gtk_container_remove (GTK_CONTAINER (advanced->parent), advanced);               
1110                gtk_label_set_text (GTK_LABEL (advanced_toggle),
1111                                    _("Advanced..."));
1112
1113                gtk_tooltips_set_tip (panel_tooltips, advanced_toggle->parent,
1114                                      _("Allow typing in a command line instead of choosing an application from the list"),
1115                                      NULL);
1116
1117                gtk_widget_grab_focus (clist);
1118        }
1119}
1120
1121void
1122show_run_dialog (void)
1123{
1124        gboolean use_advanced;         
1125
1126        if (no_run_box)
1127                return;
1128
1129        if(run_dialog != NULL) {
1130                gtk_widget_show_now(run_dialog);
1131                gdk_window_raise(run_dialog->window);
1132                return;
1133        }
1134
1135        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
1136       
1137        run_dialog = gnome_dialog_new(_("Run Program"), NULL);
1138
1139        /* This is lame in advanced mode, but if you change it on mode
1140         * toggle it creates weird effects, so always use this policy
1141         */
1142        gtk_window_set_policy (GTK_WINDOW (run_dialog),
1143                               FALSE, TRUE, FALSE);
1144
1145        /* Get some reasonable height in simple list mode */
1146        if (!use_advanced)
1147                gtk_window_set_default_size (GTK_WINDOW (run_dialog),
1148                                             -1, 400);
1149       
1150        gnome_window_icon_set_from_file (GTK_WINDOW (run_dialog),
1151                                         GNOME_ICONDIR"/gnome-run.png");
1152        gtk_signal_connect(GTK_OBJECT(run_dialog), "destroy",
1153                           GTK_SIGNAL_FUNC(gtk_widget_destroyed),
1154                           &run_dialog);
1155        gtk_window_position(GTK_WINDOW(run_dialog), GTK_WIN_POS_MOUSE);
1156        gtk_window_set_wmclass (GTK_WINDOW (run_dialog), "run_dialog", "Panel");
1157        gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (run_dialog),
1158                                                _("Run"),
1159                                                GNOME_STOCK_PIXMAP_EXEC);
1160        gnome_dialog_append_button (GNOME_DIALOG (run_dialog),
1161                                    GNOME_STOCK_BUTTON_CANCEL);
1162        gnome_dialog_append_button (GNOME_DIALOG (run_dialog),
1163                                    GNOME_STOCK_BUTTON_HELP);
1164
1165        gnome_dialog_set_default (GNOME_DIALOG (run_dialog), 0);
1166        gtk_signal_connect (GTK_OBJECT (run_dialog), "clicked",
1167                            GTK_SIGNAL_FUNC (string_callback), NULL);
1168
1169        create_simple_contents ();
1170        create_advanced_contents ();
1171        update_contents (run_dialog);
1172       
1173        gtk_widget_show_all (run_dialog);
1174        panel_set_dialog_layer (run_dialog);
1175}
1176
1177void
1178show_run_dialog_with_text (const char *text)
1179{
1180        GtkWidget *entry;
1181
1182        g_return_if_fail(text != NULL);
1183
1184        show_run_dialog ();
1185
1186        if(run_dialog == NULL) {
1187                return;
1188        }
1189       
1190        entry = gtk_object_get_data(GTK_OBJECT(run_dialog), "entry");
1191
1192        gtk_entry_set_text(GTK_ENTRY(entry), text);
1193}
1194
1195static void 
1196drag_data_get_cb (GtkWidget          *widget,
1197                  GdkDragContext     *context,
1198                  GtkSelectionData   *selection_data,
1199                  guint               info,
1200                  guint               time,
1201                  gpointer            data)
1202{
1203        char *foo;
1204
1205        foo = g_strdup_printf ("RUN:%d", find_applet (widget));
1206
1207        gtk_selection_data_set (selection_data,
1208                                selection_data->target, 8, (guchar *)foo,
1209                                strlen (foo));
1210
1211        g_free (foo);
1212}
1213
1214
1215static GtkWidget *
1216create_run_widget(void)
1217{
1218        static GtkTargetEntry dnd_targets[] = {
1219                { "application/x-panel-applet-internal", 0, 0 }
1220        };
1221        GtkWidget *button;
1222        char *pixmap_name;
1223
1224        pixmap_name = gnome_pixmap_file("gnome-run.png");
1225
1226        button = button_widget_new(pixmap_name,-1,
1227                                   MISC_TILE,
1228                                   FALSE,
1229                                   ORIENT_UP,
1230                                   _("Run..."));
1231
1232        /*A hack since this function only pretends to work on window
1233          widgets (which we actually kind of are) this will select
1234          some (already selected) events on the panel instead of
1235          the button window (where they are also selected) but
1236          we don't mind*/
1237        GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW);
1238        gtk_drag_source_set (button,
1239                             GDK_BUTTON1_MASK,
1240                             dnd_targets, 1,
1241                             GDK_ACTION_COPY | GDK_ACTION_MOVE);
1242        GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
1243
1244        gtk_signal_connect (GTK_OBJECT (button), "drag_data_get",
1245                            GTK_SIGNAL_FUNC (drag_data_get_cb),
1246                            NULL);
1247
1248        g_free(pixmap_name);
1249        gtk_tooltips_set_tip (panel_tooltips, button, _("Run..."), NULL);
1250
1251        gtk_signal_connect(GTK_OBJECT(button), "clicked",
1252                           GTK_SIGNAL_FUNC(show_run_dialog), NULL);
1253
1254        return button;
1255}
1256
1257void
1258load_run_applet(PanelWidget *panel, int pos, gboolean exactpos)
1259{
1260        GtkWidget *run;
1261
1262        run = create_run_widget();
1263        if(!run)
1264                return;
1265
1266        if (!register_toy(run, NULL, NULL, panel,
1267                          pos, exactpos, APPLET_RUN))
1268                return;
1269
1270        applet_add_callback(applets_last->data, "help",
1271                            GNOME_STOCK_PIXMAP_HELP,
1272                            _("Help"));
1273}
Note: See TracBrowser for help on using the repository browser.