1 /*
2  *  $RCSfile: gkrellmlaunch.c,v $
3  *  Revision: $Revision: 1.12 $
4  *  Date: $Date: 2002/09/26 13:53:08 $
5  *
6  *  Copyright (C) 2001 Lee Webb
7  *
8  *  Author: Lee Webb leewebb@users.sourceforge.net
9  *  Latest versions: http://gkrellmlaunch.sourceforge.net
10  *
11  *  Contributions:
12  *  Bill Wilson http://bill.nalens.com/ GTK2 port
13  *  Etan Reisner GTK2 port
14  *
15  *  This program is free software which I release under the GNU General Public
16  *  License. You may redistribute and/or modify this program under the terms
17  *  of that license as published by the Free Software Foundation; either
18  *  version 2 of the License, or (at your option) any later version.
19  *
20  *  This program is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *  GNU General Public License for more details.
24  *
25  *  You should have received a copy of the GNU General Public License
26  *  along with this program; if not, write to the Free Software
27  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  *
29  *  Requires GKrellM 2 or better
30  *
31  *  gcc -fPIC `pkg-config gtk+-2.0 --cflags` `pkg-config gtk+-2.0 --libs` -c gkrellmlaunch.c
32  *  gcc -shared -Wl -o gkrellmlaunch.so gkrellmlaunch.o
33  *  gkrellm -p gkrellmlaunch.so
34  */
35 
36 #include <gkrellm2/gkrellm.h>
37 
38 /*
39  * Make sure we have a compatible version of GKrellM
40  * (Version 2+ is required due to the use of GTK2)
41  */
42 #if !defined(GKRELLM_VERSION_MAJOR) || GKRELLM_VERSION_MAJOR < 2
43 #error This plugin requires GKrellM version >= 2
44 #endif
45 
46 #define CONFIG_NAME "GKrellMLaunch"
47 
48 #define PLUGIN_PLACEMENT (MON_UPTIME | MON_INSERT_AFTER)
49 
50 #define PLUGIN_CONFIG_KEYWORD "gkrellmlaunch"
51 
52 #define STYLE_NAME "GKrellMLaunch"
53 
54 static gchar *GKrellMLaunchInfo[] =
55 {
56   "<b>Usage\n\n",
57   "Label: ",
58   "This is the text that will appear on the button for the command.\n\n",
59   "Command: ",
60   "This is the binary name to run (e.g., mozilla).\n",
61   "Visible: ",
62   "Check box to allow buttons to be hidden if not used.\n\n",
63   "Use the \"Add\" button to create a new button.\n",
64   "Use the \"Replace\" button to update changes made for currently selected ",
65   "list entry.\n",
66   "Use the \"Delete\" button to delete the selected entry.\n",
67   "Use the /\\ & \\/  buttons to move selected entry up & down in position.\n",
68 };
69 
70 static gchar GKrellMLaunchAbout[] =
71   "GKrellMLaunch Version 0.5 (GKrellm2)\n"\
72   "GKrellM plugin to give one-click access to frequently used programs.\n\n"\
73   "Copyright (c) 2001-2002 by Lee Webb\n"\
74   "Release Date: 26/09/2002\n"\
75   "leewebb@users.sourceforge.net\n"\
76   "http://gkrellmlaunch.sourceforge.net\n\n"\
77   "Released under the GNU Public License.\n";
78 
79 static GkrellmMonitor *monitor;
80 
81 typedef struct
82 {
83   gint  visible;
84   gchar *cmd;
85   gchar *label;
86 
87   /* Each launcher has its own Panel & Decal */
88   GkrellmPanel *panel;
89   GkrellmDecal *decal;
90 } GLauncher;
91 
92 /*
93  * We need a list to hold our series of GLaunchers.
94  */
95 static GList *launcherList;
96 
97 /*
98  * Unlike the array version of GKrellMLaunch, where buttons were always
99  * kept in memory, regardless if they were used or not (due to the
100  * limitations of fixed size arrays), the new list implementation zaps the
101  * entire list & recreates it when apply_plugin_config is reached. Although
102  * not necessarily a major problem, the graphical effect of GKrellM
103  * deleting and (re)adding the Panels/Decals is not pretty. So we're going
104  * to use a varible to keep track of modifications to the list & only zap
105  * the list should something change.
106  */
107 static gboolean listModified;
108 
109 static gint style_id;
110 
111 /*
112  * Create Config tab widgets.
113  */
114 static GtkWidget *cmdEntry;
115 static GtkWidget *cmdEntryLabel;
116 static GtkWidget *toggleButton;
117 static GtkWidget *launcherVbox;
118 /*
119  * Listbox widget for the config tab.
120  */
121 static GtkWidget *launcherCList;
122 
123 /*
124  * Keep track of selected item in the Config listbox.
125  */
126 static gint selectedRow;
127 
128 /*
129  * Handle decal button presses
130  */
buttonPress(GkrellmDecalbutton * button)131 static void buttonPress (GkrellmDecalbutton *button)
132 {
133   gchar     *cmdRun = "";
134   gint      i;
135   gint      selected;
136   GLauncher *launcher;
137   GList     *list;
138 
139   /*
140    * Perform the appropriate action acccording to the button pressed.
141    * Note: we append the '&' for the user to ensure background execution.
142    *
143    * Bill has informed me that using system() can be iffy:
144    * "Whenever a program execs a child, the child inherits all open
145    * file descriptors and this could cause a problem because of plugins like
146    * Volume which have the audio device open.  So, for example, a user can
147    * launch a program, then later after quitting gkrellm, can try to unload
148    * a sound module.  But this will fail if the child program launched from
149    * gkrellm is still running because it has the audio device opened."
150    *
151    * Thus, use gkrellm_system() (a wrapper around system() that closes all
152    * open files before doing the system()).
153    * Unfortunately, such safety comes at a price: this call requires GKrellM
154    * version 1.2.2.
155    */
156 
157   selected = (GPOINTER_TO_INT (button->data));
158   /*
159    * Allocate space for cmd.
160    * Note: We're adding 2 (not 1) to the length.
161    *       One for the '&' and one for the null.
162    *       No point having a variable just to hold a '&'.
163    */
164 
165   /*
166    * Move to selected button in the list
167    */
168   for (i = 0, list = launcherList; i < selected; i += 1, list = list->next);
169 
170   launcher = (GLauncher *) list->data;
171 
172   /*
173    * g_spawn_command_line_async() replaces gkrellm_system as a built in GTK2
174    * call. Runs the specified command in the background so no need to append
175    * '&' ourselves.
176    */
177   cmdRun = g_strdup (launcher->cmd);
178   g_spawn_command_line_async (cmdRun, NULL);
179 
180   /*
181    * Cleanup as g_strdup doesn't automatically free()
182    */
183   g_free (cmdRun);
184 
185 }
186 
panel_expose_event(GtkWidget * widget,GdkEventExpose * ev)187 static gint panel_expose_event (GtkWidget *widget, GdkEventExpose *ev)
188 {
189   GLauncher *launcher;
190   GList     *list;
191 
192   /*
193    * O.K. This isn't particularly efficient, but in the interests of
194    * maintainability, I'm going to keep this in.
195    */
196   for (list = launcherList; list; list = list->next)
197   {
198     launcher = (GLauncher *) list->data;
199     if (widget == launcher->panel->drawing_area)
200     {
201       gdk_draw_pixmap (widget->window,
202                        widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
203                        launcher->panel->pixmap, ev->area.x, ev->area.y,
204                       ev->area.x, ev->area.y, ev->area.width, ev->area.height);
205     }
206   }
207 
208   return FALSE;
209 }
210 
211 /*
212  * Func called by create_plugin() & apply_config() to show/hide panels
213  * at startup according to related config item.
214  */
setVisibility()215 static void setVisibility ()
216 {
217   GLauncher *launcher;
218   GList     *list;
219 
220   for (list = launcherList ; list ; list = list->next)
221   {
222     launcher = (GLauncher *) list->data;
223     if (launcher->visible == 0)
224     {
225       gkrellm_panel_hide (launcher->panel);
226     }
227     else
228     {
229       gkrellm_panel_show (launcher->panel);
230     }
231   }
232 }
233 
update_plugin()234 static void update_plugin ()
235 {
236   GLauncher *launcher;
237   GList     *list;
238 
239   for (list = launcherList; list; list = list->next)
240   {
241     launcher = (GLauncher *) list->data;
242     gkrellm_draw_panel_layers (launcher->panel);
243   }
244 }
245 
246 /*
247  * Configuration
248  */
save_plugin_config(FILE * f)249 static void save_plugin_config (FILE *f)
250 {
251   GLauncher *launcher;
252   GList     *list;
253   gchar     *ptr;
254 
255   for (list = launcherList; list; list = list->next)
256   {
257     launcher = (GLauncher *) list->data;
258 
259     /*
260      * Since labels may have spaces, we need to convert those to
261      * underscores: else sscanf won't work properly on load.
262      */
263     for (ptr = launcher->label; *ptr; ptr++)
264     {
265       if (*ptr == ' ')
266       {
267         *ptr = '_';
268       }
269     }
270     fprintf (f, "%s visible=%d label=%s cmd=%s\n",
271              PLUGIN_CONFIG_KEYWORD, launcher->visible,
272              launcher->label, launcher->cmd);
273   }
274 }
275 
apply_plugin_config()276 static void apply_plugin_config ()
277 {
278   gchar     *string;
279   gint      i;
280   gint      row;
281   GLauncher *launcher;
282   GList     *list;
283   GList     *newList;
284   GkrellmStyle     *style;
285   GkrellmTextstyle *ts;
286   GkrellmTextstyle *ts_alt;
287 
288   if (listModified)
289   {
290     /*
291      * Create a new list with the applied settings
292      * Trash the old list.
293      */
294     newList = NULL;
295 
296     /*
297      * Read each row of the listbox & create a new launcher.
298      * Append each launcher to the new list.
299      */
300     for (row = 0; row < (GTK_CLIST (launcherCList)->rows); row += 1)
301     {
302       launcher = g_new0 (GLauncher, 1);
303       newList = g_list_append (newList, launcher);
304 
305       gtk_clist_set_row_data (GTK_CLIST (launcherCList), row, launcher);
306 
307       /*
308        * Fill the visible option.
309        */
310       gtk_clist_get_text (GTK_CLIST (launcherCList), row, 0, &string);
311       launcher->visible = (strcmp (string, "No") ? 1 : 0);
312 
313       /*
314        * Fill the label option.
315        */
316       gtk_clist_get_text (GTK_CLIST (launcherCList), row, 1, &string);
317       gkrellm_dup_string (&launcher->label, string);
318 
319       /*
320        * Fill the command option.
321        */
322       gtk_clist_get_text (GTK_CLIST (launcherCList), row, 2, &string);
323       gkrellm_dup_string (&launcher->cmd, string);
324 
325     }
326 
327     /*
328      * Wipe out the old list.
329      */
330     while (launcherList)
331     {
332       launcher = (GLauncher *) launcherList->data;
333       gkrellm_panel_destroy (launcher->panel);
334       launcherList = g_list_remove (launcherList, launcher);
335     }
336 
337     /*
338      * And then update to the new list.
339      */
340     launcherList = newList;
341 
342     /*
343      * Since we've destroyed the old list & the panels/decals with it,
344      * we have to recreate those associated panels/decals.
345      */
346 
347     /*
348      * First make sure we have the styles set up.
349      */
350     style = gkrellm_meter_style (style_id);
351     ts = gkrellm_meter_textstyle (style_id);
352     ts_alt = gkrellm_meter_alt_textstyle (style_id);
353 
354     for (i = 0, list = launcherList; list; i += 1, list = list->next)
355     {
356       launcher = (GLauncher *) list->data;
357       launcher->panel = gkrellm_panel_new0();
358       launcher->decal = gkrellm_create_decal_text (launcher->panel,
359                             launcher->label, ts_alt, style, -1, -1, -1);
360 
361       /*
362        * Configure the panel to the created decal, and create it.
363        */
364       gkrellm_panel_configure (launcher->panel, NULL, style);
365       gkrellm_panel_create (launcherVbox, monitor, launcher->panel);
366 
367       /*
368        * Panel's been created so convert the decal into a button.
369        */
370       gkrellm_draw_decal_text (launcher->panel, launcher->decal,
371                                launcher->label, 1);
372 
373       gkrellm_put_decal_in_meter_button (launcher->panel, launcher->decal,
374                                          buttonPress,
375                                          GINT_TO_POINTER (i), NULL);
376       /*
377        * Connect our panel to the expose event to allow it to be drawn in
378        * update_plugin().
379        */
380       gtk_signal_connect (GTK_OBJECT (launcher->panel->drawing_area),
381                           "expose_event", (GtkSignalFunc) panel_expose_event,
382                           NULL);
383 
384     }
385     setVisibility ();
386 
387     /*
388      * Reset the modification state.
389      */
390     listModified = FALSE;
391   }
392 }
393 
load_plugin_config(gchar * arg)394 static void load_plugin_config (gchar *arg)
395 {
396   gchar     cmd[255];
397   gchar     label[25];
398   gchar     visible[2];
399   gchar     *ptr;
400   gint      n;
401   GLauncher *launcher;
402   GList     *list;
403 
404   n = sscanf (arg, "visible=%s label=%s cmd=%[^\n]", visible, label, cmd);
405 
406   if (n == 3)
407   {
408     launcher = g_new0 (GLauncher, 1);
409     launcher->cmd = g_strdup (cmd);
410 
411     /*
412      * Spaces in labels will have been converted to underscores in
413      * save_plugin_config(). Convert them back to spaces.
414      */
415     for (ptr = label; *ptr; ptr++)
416     {
417       if (*ptr == '_')
418       {
419         *ptr = ' ';
420       }
421     }
422     launcher->label = g_strdup (label);
423     launcher->visible = atoi (visible);
424     launcherList = g_list_append (launcherList, launcher);
425   }
426 
427   for (list = launcherList; list; list = list->next)
428   {
429     launcher = (GLauncher *) list->data;
430   }
431 }
432 
cbMoveUp(GtkWidget * widget,gpointer drawer)433 static void cbMoveUp (GtkWidget *widget, gpointer drawer)
434 {
435   gint      row;
436   GtkWidget *clist;
437 
438   clist = launcherCList;
439   row = selectedRow;
440 
441   /*
442    * Only attempt a move if we're not on the first row.
443    */
444   if (row > 0)
445   {
446     /*
447      * Move the selected row up one position.
448      * Note that we have to reselect it afterwards.
449      */
450     gtk_clist_row_move (GTK_CLIST (clist), row, row - 1);
451     gtk_clist_select_row (GTK_CLIST (clist), row - 1, -1);
452 
453     selectedRow = row - 1;
454     listModified = TRUE;
455   }
456 }
457 
cbMoveDown(GtkWidget * widget,gpointer drawer)458 static void cbMoveDown (GtkWidget *widget, gpointer drawer)
459 {
460   gint      row;
461   GtkWidget *clist;
462 
463   clist = launcherCList;
464   row = selectedRow;
465 
466   /*
467    * Only attempt a row if we're not on the last row.
468    * Note that we have to reselect it afterwards.
469    */
470   if ((row >= 0) && (row < (GTK_CLIST (clist)->rows - 1)))
471   {
472     gtk_clist_row_move (GTK_CLIST (clist), row, row + 1);
473     gtk_clist_select_row (GTK_CLIST (clist), row + 1, -1);
474 
475     selectedRow = row + 1;
476     listModified = TRUE;
477   }
478 }
479 
cListSelected(GtkWidget * clist,gint row,gint column,GdkEventButton * bevent,gpointer data)480 static void cListSelected (GtkWidget *clist, gint row, gint column,
481                           GdkEventButton *bevent, gpointer data)
482 {
483   gchar *string;
484 
485   /*
486    * Fill the entry widgets & check box accoring to the selected row's text
487    */
488   gtk_clist_get_text (GTK_CLIST (launcherCList), row, 0, &string);
489   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggleButton),
490                                 strcmp (string, "No") ? TRUE : FALSE);
491 
492   gtk_clist_get_text (GTK_CLIST (launcherCList), row, 1, &string);
493   gtk_entry_set_text (GTK_ENTRY (cmdEntryLabel), string);
494 
495   gtk_clist_get_text (GTK_CLIST (launcherCList), row, 2, &string);
496   gtk_entry_set_text (GTK_ENTRY (cmdEntry), string);
497 
498   selectedRow = row;
499 }
500 
cListUnSelected(GtkWidget * clist,gint row,gint column,GdkEventButton * bevent,gpointer data)501 static void cListUnSelected (GtkWidget *clist, gint row, gint column,
502                              GdkEventButton *bevent, gpointer data)
503 {
504   /*
505    * Reset the entry widgets & check box
506    */
507   gtk_entry_set_text (GTK_ENTRY (cmdEntryLabel), "");
508   gtk_entry_set_text (GTK_ENTRY (cmdEntry), "");
509   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggleButton), 0);
510 
511   selectedRow = -1;
512 }
513 
cbAdd(GtkWidget * widget,gpointer data)514 static void cbAdd (GtkWidget *widget, gpointer data)
515 {
516   gchar *buffer[3];
517 
518   buffer[0] = (gtk_toggle_button_get_active
519                (GTK_TOGGLE_BUTTON (toggleButton)) == TRUE ? "0" : "1");
520   buffer[1] = gkrellm_gtk_entry_get_text (&cmdEntryLabel);
521   buffer[2] = gkrellm_gtk_entry_get_text (&cmdEntry);
522 
523   /*
524    * If either of the Label or Command entries are empty, forget it.
525    */
526   if ((!strlen (buffer[1])) || (!strlen (buffer[2])))
527   {
528     return;
529   }
530 
531   buffer[0] = gtk_toggle_button_get_active
532               (GTK_TOGGLE_BUTTON (toggleButton)) == TRUE ? "Yes" : "No";
533   gtk_clist_append (GTK_CLIST (launcherCList), buffer);
534   listModified = TRUE;
535 
536   /*
537    * Reset the entry widgets & check box
538    */
539   gtk_entry_set_text (GTK_ENTRY (cmdEntryLabel), "");
540   gtk_entry_set_text (GTK_ENTRY (cmdEntry), "");
541   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggleButton), 0);
542 }
543 
cbReplace(GtkWidget * widget,gpointer data)544 static void cbReplace (GtkWidget *widget, gpointer data)
545 {
546   gchar *buffer[3];
547 
548   buffer[0] = (gtk_toggle_button_get_active
549                (GTK_TOGGLE_BUTTON (toggleButton)) == TRUE ? "0" : "1");
550   buffer[1] = gkrellm_gtk_entry_get_text (&cmdEntryLabel);
551   buffer[2] = gkrellm_gtk_entry_get_text (&cmdEntry);
552 
553   /*
554    * If either of the Label or Command entries are empty, forget it.
555    */
556   if ((!strlen (buffer[1])) || (!strlen (buffer[2])))
557   {
558     return;
559   }
560 
561   /*
562    * If a row is selected, we're modifying an existing entry,
563    */
564   if (selectedRow >= 0)
565   {
566     gtk_clist_set_text (GTK_CLIST (launcherCList),
567                         selectedRow, 1, buffer[1]);
568     gtk_clist_set_text (GTK_CLIST (launcherCList),
569                         selectedRow, 2, buffer[2]);
570     gtk_clist_set_text (GTK_CLIST (launcherCList),
571                         selectedRow, 0,
572                         gtk_toggle_button_get_active
573                         (GTK_TOGGLE_BUTTON (toggleButton))
574                         == TRUE ? "Yes" : "No");
575     gtk_clist_unselect_row (GTK_CLIST (launcherCList), selectedRow, 0);
576     selectedRow = -1;
577     listModified = TRUE;
578   }
579 
580   /*
581    * Reset the entry widgets & check box
582    */
583   gtk_entry_set_text (GTK_ENTRY (cmdEntryLabel), "");
584   gtk_entry_set_text (GTK_ENTRY (cmdEntry), "");
585   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggleButton), 0);
586 
587   gtk_clist_unselect_row (GTK_CLIST (launcherCList), selectedRow, 0);
588 }
589 
cbDelete(GtkWidget * widget,gpointer data)590 static void cbDelete (GtkWidget *widget, gpointer data)
591 {
592   /*
593    * Reset the entry widgets & check box
594    */
595   gtk_entry_set_text (GTK_ENTRY (cmdEntryLabel), "");
596   gtk_entry_set_text (GTK_ENTRY (cmdEntry), "");
597   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggleButton), 0);
598 
599   if (selectedRow >= 0)
600   {
601     gtk_clist_remove (GTK_CLIST (launcherCList), selectedRow);
602     selectedRow = -1;
603     listModified = TRUE;
604   }
605 }
606 
607 /*
608  * Create a Config tab with:
609  * 1. Checkbox to make panel visible/hidden
610  * 2. Text entry widget for command to run
611  * 3. Text entry widget for button label
612  * 4. Listbox to show items
613  * 5. Info tab
614  */
create_plugin_tab(GtkWidget * tab_vbox)615 static void create_plugin_tab (GtkWidget *tab_vbox)
616 {
617   gchar     *titles[3] = {"Visible", "Label", "Command"};
618   gchar     *buffer[3];
619   gchar     visible[5];
620   gint      i = 0;
621   GLauncher *launcher;
622   GList     *list;
623   GtkWidget *tabs;
624   GtkWidget *vbox;
625   GtkWidget *hbox;
626   GtkWidget *scrolled;
627   GtkWidget *text;
628   GtkWidget *label;
629   GtkWidget *button;
630   GtkWidget *aboutLabel;
631   GtkWidget *aboutText;
632 
633   /*
634    * Make a couple of tabs.  One for Config and one for info.
635    */
636   tabs = gtk_notebook_new ();
637   gtk_notebook_set_tab_pos (GTK_NOTEBOOK (tabs), GTK_POS_TOP);
638   gtk_box_pack_start (GTK_BOX (tab_vbox), tabs, TRUE, TRUE, 0);
639 
640   /*
641    * Setup tab: give it some scroll bars to keep the config
642    * window size compact.
643    */
644   vbox = gkrellm_gtk_notebook_page (tabs, "Setup");
645   vbox = gkrellm_gtk_scrolled_vbox (vbox, NULL,
646                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
647 
648   /*
649    * Dynamically create the options on the Config tab according to MAX_BUTTONS.
650    */
651 
652   /*
653    * Create text boxes to put Labels and Commands in
654    */
655 
656   label = gtk_label_new ("Label: ");
657   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
658   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
659   gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
660 
661   cmdEntryLabel = gtk_entry_new_with_max_length (255) ;
662   gtk_entry_set_text (GTK_ENTRY (cmdEntryLabel), "") ;
663   gtk_entry_set_editable (GTK_ENTRY (cmdEntryLabel), TRUE);
664   gtk_box_pack_start (GTK_BOX (vbox), cmdEntryLabel, FALSE, FALSE, 0) ;
665 
666   label = gtk_label_new ("Command:");
667   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, i);
668   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
669   gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
670 
671   cmdEntry = gtk_entry_new_with_max_length (255) ;
672   gtk_entry_set_text (GTK_ENTRY (cmdEntry), "") ;
673   gtk_entry_set_editable (GTK_ENTRY (cmdEntry), TRUE) ;
674   gtk_box_pack_start (GTK_BOX (vbox), cmdEntry, FALSE, FALSE, 0) ;
675 
676   toggleButton = gtk_check_button_new_with_label ("Visible?");
677   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggleButton), 0);
678   gtk_box_pack_start (GTK_BOX (vbox), toggleButton, FALSE, TRUE, 0);
679 
680   /*
681    * Add buttons into their own box
682    * => Add  Replace  Delete    /\     \/
683    */
684   hbox = gtk_hbox_new (FALSE, 0);
685   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
686 
687   /*
688    * Add "Add", "Replace" & "Delete" buttons
689    */
690   button = gtk_button_new_with_label ("Add");
691   gtk_signal_connect (GTK_OBJECT (button), "clicked",
692                       (GtkSignalFunc) cbAdd, NULL);
693 
694   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
695 
696   button = gtk_button_new_with_label ("Replace");
697   gtk_signal_connect (GTK_OBJECT (button), "clicked",
698                       (GtkSignalFunc) cbReplace, NULL);
699   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
700 
701   button = gtk_button_new_with_label ("Delete");
702   gtk_signal_connect (GTK_OBJECT (button), "clicked",
703                       (GtkSignalFunc) cbDelete, NULL);
704   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
705 
706   /*
707    * Add reposition buttons
708    */
709   button = gtk_button_new_with_label ("/\\");
710   gtk_signal_connect (GTK_OBJECT (button), "clicked",
711                       (GtkSignalFunc) cbMoveUp, NULL);
712 
713   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
714   button = gtk_button_new_with_label ("\\/");
715   gtk_signal_connect (GTK_OBJECT (button), "clicked",
716                       (GtkSignalFunc) cbMoveDown, NULL);
717   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
718 
719   /*
720    * Create listbox to hold each item
721    */
722   scrolled = gtk_scrolled_window_new (NULL, NULL);
723   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
724                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
725   gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
726 
727   /*
728    * Create the CList with 3 titles:
729    * Label, Command, Visible
730    */
731   launcherCList = gtk_clist_new_with_titles (3, titles);
732   gtk_clist_set_shadow_type (GTK_CLIST (launcherCList), GTK_SHADOW_OUT);
733 
734   /*
735    * Set the column widths
736    */
737   /* Label */
738   gtk_clist_set_column_width (GTK_CLIST (launcherCList), 0, 30);
739   /* Command */
740   gtk_clist_set_column_width (GTK_CLIST (launcherCList), 1, 100);
741   /* Visible */
742   gtk_clist_set_column_width (GTK_CLIST (launcherCList), 2, 200);
743 
744   gtk_clist_set_column_justification (GTK_CLIST (launcherCList),
745                                       0, GTK_JUSTIFY_LEFT);
746   gtk_clist_set_column_justification (GTK_CLIST (launcherCList),
747                                       1, GTK_JUSTIFY_LEFT);
748   gtk_clist_set_column_justification (GTK_CLIST (launcherCList),
749                                       2, GTK_JUSTIFY_LEFT);
750 
751   /*
752    * Add signals for selecting a row in the CList.
753    */
754   gtk_signal_connect (GTK_OBJECT (launcherCList), "select_row",
755                       (GtkSignalFunc) cListSelected, NULL);
756 
757   /*
758    * Add signal for deselecting a row in the CList.
759    * If not, delselecting & attempting to create a new entry will overwrite
760    * the previuosly selected row.
761    */
762   gtk_signal_connect (GTK_OBJECT (launcherCList), "unselect_row",
763                       (GtkSignalFunc) cListUnSelected, NULL);
764 
765   /*
766    * Add the CList to the scrolling window
767    */
768   gtk_container_add (GTK_CONTAINER (scrolled), launcherCList);
769 
770   /*
771    * Fill the CList with our commands etc.
772    */
773   for (i = 0, list = launcherList; list; i += 1, list = list->next)
774   {
775     launcher = (GLauncher *) list->data;
776     sprintf (visible, "%s", (launcher->visible == 1 ? "Yes" : "No"));
777              buffer[0] = visible;
778     buffer[1] = launcher->label;
779     buffer[2] = launcher->cmd;
780     gtk_clist_append (GTK_CLIST (launcherCList), buffer);
781     gtk_clist_set_row_data (GTK_CLIST (launcherCList), i, launcher);
782   }
783 
784   /*
785    * Info tab
786    */
787   vbox = gkrellm_gtk_notebook_page (tabs, "Info");
788   text = gkrellm_gtk_scrolled_text_view (vbox, NULL, GTK_POLICY_AUTOMATIC,
789                                          GTK_POLICY_AUTOMATIC);
790   gkrellm_gtk_text_view_append_strings (text, GKrellMLaunchInfo,
791                                        (sizeof (GKrellMLaunchInfo)
792                                        / sizeof (gchar *)));
793 
794   /*
795    * About tab
796    */
797   aboutText = gtk_label_new (GKrellMLaunchAbout);
798   aboutLabel = gtk_label_new ("About");
799   gtk_notebook_append_page (GTK_NOTEBOOK (tabs), aboutText, aboutLabel);
800 
801 }
802 
create_plugin(GtkWidget * vbox,gint first_create)803 static void create_plugin (GtkWidget *vbox, gint first_create)
804 {
805   gint      i;
806   GLauncher *launcher;
807   GList     *list;
808   GkrellmStyle     *style;
809   GkrellmTextstyle *ts;
810   GkrellmTextstyle *ts_alt;
811 
812   launcherVbox = vbox;
813 
814   if (first_create)
815   {
816     for (list = launcherList; list; list = list->next)
817     {
818       launcher = (GLauncher *) list->data;
819       launcher->panel = gkrellm_panel_new0();
820     }
821   }
822 
823   style = gkrellm_meter_style (style_id);
824 
825   /*
826    * Each GkrellmStyle has two text styles.  The theme designer has picked the
827    * colors and font sizes, presumably based on knowledge of what you draw
828    * on your panel.  You just do the drawing.  You probably could assume
829    * the ts font is larger than the ts_alt font, but again you can be
830    * overridden by the theme designer.
831    */
832   ts = gkrellm_meter_textstyle (style_id);
833   ts_alt = gkrellm_meter_alt_textstyle (style_id);
834 
835   /*
836    * Create a text decal that will be converted to a button.
837    * Make it the entire width of the panel.
838    */
839   for (i = 0, list = launcherList; list; i += 1, list = list->next)
840   {
841     launcher = (GLauncher *) list->data;
842     launcher->decal = gkrellm_create_decal_text (launcher->panel,
843                             launcher->label, ts_alt, style, -1, -1, -1);
844   /*
845    * Configure the panel to created decal, and create it.
846    */
847     gkrellm_panel_configure (launcher->panel, NULL, style);
848     gkrellm_panel_create (vbox, monitor, launcher->panel);
849 
850   /*
851    * After the panel is created, the decal can be converted into a button.
852    * First draw the initial text into the text decal button and then
853    * put the text decal into a meter button.
854    */
855     gkrellm_draw_decal_text (launcher->panel, launcher->decal,
856                               launcher->label,1);
857     gkrellm_put_decal_in_meter_button (launcher->panel, launcher->decal,
858                                        buttonPress,
859                                        GINT_TO_POINTER (i), NULL);
860   }
861 
862   /*
863    * Note: all of the above gkrellm_draw_decal_XXX() calls will not
864    * appear on the panel until a gkrellm_draw_panel_layers() call is
865    * made.  This will be done in update_plugin(), otherwise we would
866    * make the call here and anytime the decals are changed.
867    */
868 
869   if (first_create)
870   {
871     for (list = launcherList; list; list = list->next)
872     {
873       launcher = (GLauncher *) list->data;
874       gtk_signal_connect (GTK_OBJECT (launcher->panel->drawing_area),
875                   "expose_event", (GtkSignalFunc) panel_expose_event, NULL);
876     }
877     /*
878      * Setup the initial visible status of each panel
879      * according to the config item read in.
880      */
881     setVisibility ();
882   }
883 }
884 
885 
886 /*
887  * The monitor structure tells GKrellM how to call the plugin routines.
888  */
889 static GkrellmMonitor plugin_mon	=
890 {
891   CONFIG_NAME,           /* Name, for config tab.          */
892   0,                     /* Id,  0 if a plugin             */
893   create_plugin,         /* The create function            */
894   update_plugin,         /* The update function            */
895   create_plugin_tab,     /* The config tab create function */
896   apply_plugin_config,   /* Apply the config function      */
897   save_plugin_config,    /* Save user config               */
898   load_plugin_config,    /* Load user config               */
899   PLUGIN_CONFIG_KEYWORD, /* config keyword                 */
900 
901   NULL,        /* Undefined 2 */
902   NULL,        /* Undefined 1 */
903   NULL,        /* private     */
904 
905   PLUGIN_PLACEMENT,    /* Insert plugin before this monitor */
906 
907   NULL,               /* Handle if a plugin, filled in by GKrellM */
908   NULL                /* path if a plugin, filled in by GKrellM   */
909 };
910 
911 /*
912  * All GKrellM plugins must have one global routine named gkrellm_init_plugin()
913  * which returns a pointer to a filled in monitor structure.
914  */
gkrellm_init_plugin()915 GkrellmMonitor* gkrellm_init_plugin ()
916 {
917   /*
918    * Don't want any row in the Config tab initially selected.
919    */
920   selectedRow = -1;
921   listModified = FALSE;
922 
923   style_id = gkrellm_add_meter_style (&plugin_mon, STYLE_NAME);
924   monitor = &plugin_mon;
925   return &plugin_mon;
926 }
927