~ubuntu-branches/ubuntu/trusty/gkrellkam/trusty

« back to all changes in this revision

Viewing changes to gkrellkam2.c

  • Committer: Bazaar Package Importer
  • Author(s): paul cannon
  • Date: 2003-01-10 13:10:45 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20030110131045-l5mif76uzpl1art1
Tags: 2.0.0-1
New upstream release- rewritten for gkrellm2. I had planned a
gkrellkam2 package for this, but it seems the old gkrellm is
now defunct, and the gkrellm package is really gkrellm2.
Whatever. (closes: #176116gkrellkam is incompatible with
gkrellm)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  gkrellkam2 -- image watcher plugin for GKrellM
 
3
  Copyright (C) 2001-2002 paul cannon
 
4
 
 
5
  This program is free software; you can redistribute it and/or
 
6
  modify it under the terms of the GNU General Public License
 
7
  as published by the Free Software Foundation; either version 2
 
8
  of the License, or (at your option) any later version.
 
9
 
 
10
  This program is distributed in the hope that it will be useful,
 
11
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
  GNU General Public License for more details.
 
14
 
 
15
  You should have received a copy of the GNU General Public License
 
16
  along with this program; if not, write to the Free Software
 
17
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
18
      02111-1307, USA.
 
19
 
 
20
  To contact the author try:
 
21
  paul cannon <pik@debian.org>
 
22
 
 
23
  A note on style here:
 
24
 
 
25
    I use (gchar *) for a string's type when the array is dynamically
 
26
    allocated with the g_* functions, and will eventually need to be
 
27
    g_free'd. Strings of type (char *) are static memory.
 
28
*/
 
29
 
 
30
#if !defined(WIN32)
 
31
# include <gkrellm2/gkrellm.h>
 
32
# include <libgen.h>
 
33
# include <unistd.h>
 
34
#else
 
35
# include <src/gkrellm.h>
 
36
# include <src/win32-plugin.h>
 
37
#endif
 
38
 
 
39
#include <errno.h>
 
40
#include <stdlib.h>
 
41
#include <time.h>
 
42
 
 
43
#define PLUGIN_VER "2.0.0"
 
44
 
 
45
#define PLUGIN_NAME "GKrellKam"
 
46
#define PLUGIN_DESC "GKrellM Image Watcher plugin"
 
47
#define PLUGIN_URL "http://gkrellkam.sf.net/"
 
48
#define PLUGIN_STYLE PLUGIN_NAME
 
49
#define PLUGIN_KEYWORD PLUGIN_NAME
 
50
 
 
51
#define TEMPTEMPLATE "/tmp/krellkam"
 
52
 
 
53
#define DEBUGGING 0
 
54
 
 
55
static gchar *kkam_info_text[] = 
 
56
{
 
57
"<b>" PLUGIN_NAME " " PLUGIN_VER "\n\n",
 
58
 
 
59
"GKrellKam is a plugin that can watch a number of image files,\n",
 
60
"and display them sized to fit in panels on your gkrellm.\n\n",
 
61
 
 
62
"You can ", "<b>left-click", " on an image panel to display the\n",
 
63
"original, unsized image, or you can ", "<b>middle-click", " on it\n",
 
64
"to get an immediate update.\n\n",
 
65
 
 
66
"Scrolling the mouse wheel up over any of the panels increases\n",
 
67
"the number of visible panels, and scrolling it down decreases\n",
 
68
"the number. This is so you can hide panels you don't always want\n",
 
69
"open."
 
70
 
 
71
"You can also ", "<b>right-click", " on a panel to open the\n",
 
72
"configuration window.",
 
73
 
 
74
"\n\n",
 
75
"<b>-- EASY START --\n\n",
 
76
 
 
77
"Just put the address of a webcam image in the Image Source\n",
 
78
"box on the configuration tab for one of the panels. The\n",
 
79
"address will probably start with \"http://\" and end in \".jpg\",\n",
 
80
"\".png\", or \".gif\".\n\n",
 
81
 
 
82
"<b>-- CONFIGURATION TABS --\n\n",
 
83
 
 
84
"<b>Global Options\n\n",
 
85
 
 
86
"<i>Path to image viewer program\n",
 
87
"When you left-click on a GKrellKam panel, it will start your\n",
 
88
"favorite image viewer and display the original, unsized image.\n",
 
89
"Put the name of the image viewer program in this box. To use\n",
 
90
"the included GKrellKam internal viewer, just leave this field\n",
 
91
"blank. Try clicking on the internal viewer window to get the\n",
 
92
"menu.\n\n",
 
93
 
 
94
"<i>Popup errors\n",
 
95
"When something goes wrong with an image download or the parsing\n",
 
96
"of a list, GKrellKam lets you know. When Popup errors is checked,\n",
 
97
"the message will appear as a popup window with an \"OK\" button.\n",
 
98
"When you turn this option off, error messages will be reported\n",
 
99
"in the tooltip for the image in question. They are not very\n",
 
100
"visible this way, but perhaps they are less annoying too.\n\n",
 
101
 
 
102
"<i>Number of panels\n",
 
103
"This lets you adjust the number of visible GKrellKam panels\n",
 
104
"between 0 and 5.\n\n",
 
105
 
 
106
"<b>Panel Config Tabs\n\n",
 
107
 
 
108
"<i>Default number of seconds per update\n",
 
109
"After an image has been successfully loaded, GKrellKam will\n",
 
110
"wait this many seconds before updating the image. Updating\n",
 
111
"might involve reloading the same image, or getting the next\n",
 
112
"image in a list. If you middle-click on a panel, it will do\n",
 
113
"the update immediately. This setting can be overridden by\n",
 
114
"specific items in lists.\n\n",
 
115
 
 
116
"<i>Height of viewer, in pixels\n",
 
117
"You can adjust this for each panel to give your pictures a\n",
 
118
"nice-looking aspect.\n\n",
 
119
 
 
120
"<i>Border size\n",
 
121
"Adds a border space around the image to match better with\n",
 
122
"your GKrellM theme.\n\n",
 
123
 
 
124
"<i>Maintain aspect ratio\n",
 
125
"When checked, images are not sized to fit perfectly in this\n",
 
126
"panel. They maintain their aspect, and theme background is\n",
 
127
"shown on either the sides or the top and bottom.\n\n",
 
128
 
 
129
"<i>Select list images at random\n",
 
130
"When this is checked, and your image source is a list, then\n",
 
131
"images will be taken from the list at random rather than\n",
 
132
"cycled through one by one.\n\n",
 
133
 
 
134
"<i>Image Source\n",
 
135
"Each panel has a \"source\" associated with it. A source can be\n",
 
136
"a local picture, the web address of a picture, a list of\n",
 
137
"pictures, or a script to call to get a new picture. To give a\n",
 
138
"local picture file, list of files, or a script, enter the\n",
 
139
"_full_ filename in the \"Image Source\" box. To watch a webcam\n",
 
140
"or other online picture, or use an online list, just put its\n",
 
141
"address (beginning with http:// or ftp://) in the \"Image Source\n",
 
142
"box. Lists should end in \"-list\" or \".list\". You'll need GNU\n",
 
143
"wget installed to be able to get files from the internet.\n",
 
144
"Special case: when this field begins with \"-x\" followed by a\n",
 
145
"space and some more text, the remaining text is assumed to be a\n",
 
146
"script or other system commmand, and the whole path does not\n",
 
147
"need to be specified.\n\n",
 
148
 
 
149
"<i>Reread source button\n",
 
150
"When your image source is a list, GKrellKam will only read in\n",
 
151
"that list once. If it is changed, and you want GKrellKam to\n",
 
152
"follow the changes, you can hit the \"Reread source\" button.\n",
 
153
"This will restart the list. If the source is a script, it will\n",
 
154
"be re-executed. Other sources will be reread as well.\n\n",
 
155
 
 
156
"See the included example list, the ",
 
157
"<b>gkrellkam-list(5)",
 
158
" manpage,\nand ",
 
159
"<b>" PLUGIN_URL,
 
160
" online for more info."
 
161
};
 
162
 
 
163
static gchar *kkam_about_text = _(
 
164
  PLUGIN_NAME " " PLUGIN_VER
 
165
  "\n" PLUGIN_DESC
 
166
  "\n\nCopyright (C) 2001 paul cannon\n"
 
167
  "paul@cannon.cs.usu.edu\n"
 
168
  "space software lab/utah state university\n\n"
 
169
  PLUGIN_NAME " comes with ABSOLUTELY NO WARRANTY;\n"
 
170
  "see the file COPYING for details.\n\n"
 
171
  PLUGIN_URL );
 
172
 
 
173
static const char *default_source[] = {
 
174
  "http://aggies.usu.edu/webcam/fullsize.jpg",
 
175
  "",
 
176
  "",
 
177
  "",
 
178
  ""
 
179
};
 
180
 
 
181
#define wget_opts "--cache=off"
 
182
#define BUFLEN 256
 
183
#define MIN_NUMPANELS 0
 
184
#define MAX_NUMPANELS 5
 
185
#define MAX_DEPTH 64
 
186
#define MAX_SECONDS 604800 /* one week */
 
187
 
 
188
typedef enum {
 
189
  SOURCE_URL,
 
190
  SOURCE_FILE,
 
191
  SOURCE_SCRIPT,
 
192
  SOURCE_LIST,
 
193
  SOURCE_LISTURL
 
194
} SourceEnum;
 
195
 
 
196
/* source information structure- panels each have a GList of these */
 
197
 
 
198
typedef struct
 
199
{
 
200
  gchar *img_name;
 
201
  gchar *tooltip;
 
202
  SourceEnum type;
 
203
  int seconds;
 
204
  int next_dl;
 
205
  gchar *tfile;
 
206
  int tlife;
 
207
} KKamSource;
 
208
 
 
209
/* items that each panel needs */
 
210
 
 
211
typedef struct
 
212
{
 
213
  GkrellmPanel *panel;
 
214
  GkrellmDecal *decal;
 
215
  GdkPixmap *pixmap;
 
216
  FILE *cmd_pipe;
 
217
  int count;
 
218
  int height;
 
219
  int boundary;
 
220
  int default_period;
 
221
  gboolean maintain_aspect;
 
222
  gboolean random;
 
223
  gint visible;
 
224
 
 
225
  GtkWidget *period_spinner;
 
226
  GtkWidget *boundary_spinner;
 
227
  GtkWidget *height_spinner;
 
228
  GtkWidget *aspect_box;
 
229
  GtkWidget *random_box;
 
230
  GtkWidget *sourcebox;
 
231
  GdkPixbuf *pixbuf;
 
232
 
 
233
  FILE *listurl_pipe;
 
234
  gchar *listurl_file;
 
235
 
 
236
  gchar *source;
 
237
  GList *sources;
 
238
} KKamPanel;
 
239
 
 
240
typedef struct
 
241
{
 
242
  GtkWidget *window;
 
243
  GtkWidget *menu;
 
244
  GtkWidget *pmap;
 
245
  GtkWidget *fdialog;
 
246
  GdkPixbuf *pixbuf;
 
247
} ViewerInfo;
 
248
 
 
249
static GkrellmMonitor *monitor;
 
250
 
 
251
static GkrellmTicks *pGK;
 
252
 
 
253
static int created = 0;
 
254
static int numpanels = 0;
 
255
static int newnumpanels = 1;
 
256
static KKamPanel *panels = NULL;
 
257
 
 
258
static GkrellmStyle *img_style = NULL;
 
259
static gint style_id;
 
260
 
 
261
static char *viewer_prog = NULL;
 
262
static int popup_errors = 0;
 
263
 
 
264
static GtkWidget *viewerbox;
 
265
static GtkWidget *numpanel_spinner = NULL;
 
266
static GtkWidget *popup_errors_box = NULL;
 
267
static GtkWidget *tabs = NULL;
 
268
static GtkWidget *kkam_vbox = NULL;
 
269
static GtkWidget *filebox = NULL;
 
270
 
 
271
static GtkTooltips *tooltipobj;
 
272
 
 
273
static void change_num_panels ();
 
274
static void create_sources_list (KKamPanel *p);
 
275
static void kkam_read_list (KKamPanel *p, char *listname, int depth);
 
276
 
 
277
static KKamSource empty_source = { "", "No config",
 
278
                                   SOURCE_FILE,
 
279
                                   0, 0, NULL, 0 };
 
280
 
 
281
static char *IMG_EXTENSIONS[] = {
 
282
  ".jpg",
 
283
  ".jpeg",
 
284
  ".png",
 
285
  ".bmp",
 
286
  ".xpm",
 
287
  ".pbm",
 
288
  ".ppm",
 
289
  ".tif",
 
290
  ".tiff",
 
291
  ".gif"
 
292
};
 
293
 
 
294
/*
 
295
  report_error ()
 
296
 
 
297
  if popup errors are on, brings up a message window.
 
298
  if not, sets the tooltip text to a warning.
 
299
*/
 
300
static void report_error (KKamPanel *p, char *fmt, ...)
 
301
{
 
302
  va_list ap;
 
303
  char *str;
 
304
 
 
305
  va_start (ap, fmt);
 
306
  str = g_strdup_vprintf (fmt, ap);
 
307
  va_end (ap);
 
308
 
 
309
  if (popup_errors)
 
310
  {
 
311
    GtkWidget *label, *vbox, *dialog;
 
312
 
 
313
    dialog = gtk_dialog_new_with_buttons(_("GKrellKam warning:"),
 
314
             NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
 
315
             GTK_STOCK_OK, GTK_RESPONSE_NONE,
 
316
             NULL);
 
317
    g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
 
318
             G_CALLBACK(gtk_widget_destroy), GTK_OBJECT(dialog));
 
319
 
 
320
    vbox = gtk_vbox_new (FALSE, 0);
 
321
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
 
322
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
 
323
             FALSE, FALSE, 0);
 
324
  
 
325
    label = gtk_label_new (_("GKrellKam warning:"));
 
326
    gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
 
327
    label = gtk_label_new (str);
 
328
    gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
 
329
 
 
330
    gtk_widget_show_all (dialog);
 
331
  }
 
332
  else
 
333
  {
 
334
    if (p && tooltipobj && p->panel && p->panel->drawing_area)
 
335
      gtk_tooltips_set_tip (tooltipobj, p->panel->drawing_area, str, NULL);
 
336
  }
 
337
}
 
338
 
 
339
/*
 
340
  kkam_add_menu_item ()
 
341
 
 
342
  Used to add a simple item to a menu more easily
 
343
*/
 
344
static void kkam_add_menu_item (GtkWidget *menu, char *label,
 
345
                                GtkSignalFunc cb, gpointer d)
 
346
{
 
347
  GtkWidget *mi;
 
348
 
 
349
  mi = gtk_menu_item_new_with_label (label);
 
350
  g_signal_connect_swapped (G_OBJECT (mi), "activate", G_CALLBACK(cb), d);
 
351
  gtk_widget_show (mi);
 
352
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
 
353
}
 
354
 
 
355
/*
 
356
  kkam_iv_destroy ()
 
357
 
 
358
  The delete_event callback for the internal viewer
 
359
*/
 
360
static void kkam_iv_destroy (ViewerInfo *vi)
 
361
{
 
362
  if (vi->fdialog)
 
363
    gtk_widget_destroy (vi->fdialog);
 
364
  g_object_unref (G_OBJECT(vi->pixbuf));
 
365
  gtk_widget_destroy (vi->menu);
 
366
  gtk_widget_destroy (vi->window);
 
367
  g_free (vi);
 
368
}
 
369
 
 
370
/*
 
371
  kkam_iv_donesave ()
 
372
 
 
373
  Turns off the viewer's "Save as" file dialog and sets the pointer
 
374
  to NULL  
 
375
*/
 
376
static void kkam_iv_donesave (ViewerInfo *vi)
 
377
{
 
378
  gtk_widget_destroy (vi->fdialog);
 
379
  vi->fdialog = NULL;
 
380
}
 
381
 
 
382
/*
 
383
  kkam_iv_dosave ()
 
384
 
 
385
  Called when user hits ok on the "Save as" file dialog. Gets the
 
386
  filename, closes the file dialog, and saves the image appropriately.
 
387
  gdk_pixbuf_save() supports only "png" or "jpeg" in Gtk+-2.0.
 
388
*/
 
389
static void kkam_iv_dosave (ViewerInfo *vi)
 
390
{
 
391
  gchar *fname;
 
392
  char  *type = NULL, *s;
 
393
 
 
394
  fname = g_strdup (
 
395
       gtk_file_selection_get_filename (GTK_FILE_SELECTION (vi->fdialog)));
 
396
  kkam_iv_donesave (vi);
 
397
  if ((s = strstr(fname, ".png")) != NULL)
 
398
    type = "png";
 
399
  else if ((s = strstr(fname, ".jpg")) != NULL)
 
400
        type = "jpeg";
 
401
  else if ((s = strstr(fname, ".jpeg")) != NULL)
 
402
        type = "jpeg";
 
403
  if (!type)
 
404
    {
 
405
    report_error(NULL, "Saved images must be .jpg or .png only.\n", NULL);
 
406
    return;
 
407
        }
 
408
 
 
409
  gdk_pixbuf_save(vi->pixbuf, fname, type, NULL, NULL);
 
410
  g_free (fname);
 
411
}
 
412
 
 
413
/*
 
414
  kkam_iv_saveas ()
 
415
 
 
416
  Opens the "Save as" file dialog for an image displayed on the
 
417
  internal viewer.
 
418
*/
 
419
static void kkam_iv_saveas (ViewerInfo *vi)
 
420
{
 
421
  if (vi->fdialog)
 
422
  {
 
423
    gdk_window_raise (vi->fdialog->window);
 
424
    return;
 
425
  }
 
426
 
 
427
  vi->fdialog = gtk_file_selection_new (_("Save As:"));
 
428
 
 
429
  g_signal_connect_swapped (
 
430
           G_OBJECT (GTK_FILE_SELECTION (vi->fdialog)->ok_button),
 
431
           "clicked",
 
432
           G_CALLBACK (kkam_iv_dosave), (gpointer)vi);
 
433
  g_signal_connect_swapped (
 
434
           G_OBJECT (GTK_FILE_SELECTION (vi->fdialog)->cancel_button),
 
435
           "clicked",
 
436
           G_CALLBACK (kkam_iv_donesave), (gpointer)vi);
 
437
  gtk_widget_show (vi->fdialog);
 
438
}
 
439
 
 
440
/*
 
441
  kkam_iv_makemenu ()
 
442
 
 
443
  Creates the popup menu seen when left- or right- clicking on the
 
444
  internal viewer
 
445
*/
 
446
static void kkam_iv_makemenu (ViewerInfo *vi)
 
447
{
 
448
  vi->menu = gtk_menu_new ();
 
449
  kkam_add_menu_item (vi->menu, _("Close"),
 
450
                      GTK_SIGNAL_FUNC (kkam_iv_destroy), (gpointer)vi);
 
451
  kkam_add_menu_item (vi->menu, _("Save As.."),
 
452
                      GTK_SIGNAL_FUNC (kkam_iv_saveas), (gpointer)vi);
 
453
}
 
454
 
 
455
/*
 
456
  kkam_iv_popup ()
 
457
 
 
458
  Shows the popup menu after user left- or right- clicks on the
 
459
  internal viewer
 
460
*/
 
461
static gint kkam_iv_popup (ViewerInfo *vi, GdkEventButton *ev)
 
462
{
 
463
  if (ev->button == 1 || ev->button == 3)
 
464
    gtk_menu_popup (GTK_MENU (vi->menu), NULL, NULL, NULL, NULL,
 
465
                    ev->button, ev->time);
 
466
  return FALSE;
 
467
}
 
468
 
 
469
/*
 
470
  kkam_iv_resize ()
 
471
 
 
472
  Rerenders the image when a user resizes the internal viewer window
 
473
*/
 
474
static gint kkam_iv_resize (ViewerInfo *vi, GdkEventConfigure *ev)
 
475
{
 
476
  GdkPixmap *pix = NULL;
 
477
  GdkBitmap *bit = NULL;
 
478
 
 
479
  gkrellm_scale_pixbuf_to_pixmap(vi->pixbuf, &pix, &bit,
 
480
                      ev->width, ev->height);
 
481
 
 
482
  gtk_image_set_from_pixmap (GTK_IMAGE (vi->pmap), pix, bit);
 
483
  g_object_unref(G_OBJECT(pix));
 
484
  if (bit)
 
485
    g_object_unref(G_OBJECT(bit));
 
486
 
 
487
  return TRUE;
 
488
}
 
489
 
 
490
/*
 
491
  kkam_internal_viewer ()
 
492
 
 
493
  Opens a very simple full-size image viewer window for the image in
 
494
  question. Users can specify an external viewer if they don't like it :)
 
495
*/
 
496
static void kkam_internal_viewer (char *filename)
 
497
{
 
498
  GtkWidget *ebox;
 
499
  GdkPixmap *pix = NULL;
 
500
  GdkBitmap *bit = NULL;
 
501
  ViewerInfo *vi;
 
502
 
 
503
  vi = g_new0 (ViewerInfo, 1);
 
504
  if ((vi->pixbuf = gdk_pixbuf_new_from_file (filename, NULL)) == NULL)
 
505
  {
 
506
    g_free (vi);
 
507
    return;
 
508
  }
 
509
 
 
510
  vi->fdialog = NULL;
 
511
  kkam_iv_makemenu (vi);
 
512
  vi->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
513
  gtk_window_set_title (GTK_WINDOW (vi->window), filename);
 
514
  g_signal_connect_swapped (G_OBJECT (vi->window), "delete_event",
 
515
                             G_CALLBACK (kkam_iv_destroy),
 
516
                             (gpointer)vi);
 
517
  g_signal_connect_swapped (G_OBJECT (vi->window), "configure_event",
 
518
                             G_CALLBACK (kkam_iv_resize),
 
519
                             (gpointer)vi);
 
520
//  gtk_window_set_policy (GTK_WINDOW (vi->window), TRUE, TRUE, FALSE);
 
521
  gtk_window_set_wmclass (GTK_WINDOW (vi->window), "KKamViewer", "GKrellm");
 
522
 
 
523
  gkrellm_scale_pixbuf_to_pixmap(vi->pixbuf, &pix, &bit,
 
524
                      gdk_pixbuf_get_width(vi->pixbuf),
 
525
                      gdk_pixbuf_get_height(vi->pixbuf));
 
526
 
 
527
  vi->pmap = gtk_image_new_from_pixmap(pix, bit);
 
528
  g_object_unref(G_OBJECT(pix));
 
529
  if (bit)
 
530
    g_object_unref(G_OBJECT(bit));
 
531
 
 
532
  ebox = gtk_event_box_new ();
 
533
  gtk_container_add (GTK_CONTAINER (ebox), vi->pmap);
 
534
  gtk_container_add (GTK_CONTAINER (vi->window), ebox);
 
535
  gtk_widget_set_events (ebox, GDK_BUTTON_PRESS_MASK);
 
536
  g_signal_connect_swapped (G_OBJECT (ebox), "button_press_event",
 
537
                             G_CALLBACK (kkam_iv_popup), (gpointer)vi);
 
538
  gtk_widget_show_all (vi->window);
 
539
}
 
540
 
 
541
/*
 
542
  validnum ()
 
543
 
 
544
  returns TRUE if the given number is a valid index
 
545
  into the panels array
 
546
*/
 
547
static gboolean validnum (int num)
 
548
{
 
549
  return (panels && num >= 0 && num < MAX_NUMPANELS);
 
550
}
 
551
 
 
552
/*
 
553
  activenum ()
 
554
 
 
555
  returns TRUE if the given number is an active index
 
556
  into the panels array
 
557
*/
 
558
static gboolean activenum (int num)
 
559
{
 
560
  return (panels && num >= 0 && num < numpanels);
 
561
}
 
562
 
 
563
/*
 
564
  panel_cursource ()
 
565
 
 
566
  returns a pointer to the source definition of the current image for
 
567
  the current panel, or &empty_source if none.
 
568
*/
 
569
static KKamSource *panel_cursource (KKamPanel *p)
 
570
{
 
571
  if (p->sources == NULL)
 
572
    return &empty_source;
 
573
  return (KKamSource *)(p->sources->data);
 
574
}
 
575
 
 
576
/*
 
577
  get_period ()
 
578
 
 
579
  returns the time that should elapse before the current image is
 
580
  changed. If the current source's seconds member is 0, use the
 
581
  default_period.
 
582
*/
 
583
static int get_period (KKamPanel *p)
 
584
{
 
585
  int per;
 
586
  
 
587
  if ((per = panel_cursource (p)->seconds) == 0)
 
588
    return p->default_period;
 
589
  return per;
 
590
}
 
591
 
 
592
/*
 
593
  tfile_release ()
 
594
 
 
595
  If a kkamsource has been loaded before, and a copy of the image
 
596
  is still attached to it, then unattach that copy. If it is a
 
597
  temporary file, then unlink it as well.
 
598
*/
 
599
static void tfile_release (KKamSource *ks)
 
600
{
 
601
  if (ks == NULL || ks->tfile == NULL)
 
602
    return;
 
603
 
 
604
  if (ks->type == SOURCE_URL)
 
605
    unlink (ks->tfile);
 
606
  g_free (ks->tfile);
 
607
  ks->tfile = NULL;
 
608
  ks->next_dl = 0;
 
609
}
 
610
 
 
611
/*
 
612
  draw_pixbuf ()
 
613
  
 
614
  renders the current image into the panel at the right size.
 
615
  aspect-scaling patch from Benjamin Johnson (benj@visi.com)- thanks
 
616
*/
 
617
static void draw_pixbuf (KKamPanel *p)
 
618
{
 
619
  int pan_x, pan_y;       /* panel x and y sizes */
 
620
  int scale_x, scale_y;   /* size of scaled image */
 
621
  int loc_x, loc_y;       /* location for scaled image */
 
622
  int image_w, image_h;
 
623
 
 
624
  if (p->pixbuf == NULL)
 
625
    return;
 
626
 
 
627
  pan_x = gkrellm_chart_width () - 2 * p->boundary;
 
628
  pan_y = p->height - 2 * p->boundary;
 
629
 
 
630
  image_w = gdk_pixbuf_get_width(p->pixbuf);
 
631
  image_h = gdk_pixbuf_get_height(p->pixbuf);
 
632
 
 
633
  if (p->maintain_aspect)
 
634
  {
 
635
    /* determine sizing here - maintain aspect ratio */
 
636
 
 
637
    if (pan_x >= image_w && pan_y >= image_h)
 
638
    {
 
639
      /* the image is smaller then the panel. do no sizing, just center. */
 
640
 
 
641
      loc_x = (pan_x - image_w) / 2 + p->boundary;
 
642
      loc_y = (pan_y - image_h) / 2 + p->boundary;
 
643
 
 
644
      scale_x = 0; /* scale of 0 defaults to use image size */
 
645
      scale_y = 0;
 
646
    }
 
647
    else if ((double) image_w / (double)pan_x >
 
648
             (double) image_h / (double)pan_y)
 
649
    {
 
650
      /* scale to width (image is wider compared to panel size) */
 
651
 
 
652
      scale_x = pan_x;
 
653
      scale_y = image_h * pan_x / image_w;
 
654
 
 
655
      loc_x = p->boundary;
 
656
      loc_y = (pan_y - scale_y) / 2 + p->boundary;
 
657
    }
 
658
    else
 
659
    {
 
660
      /* scale to height (image is taller compared to panel size) */
 
661
 
 
662
      scale_x = image_w * pan_y / image_h;
 
663
      scale_y = pan_y;
 
664
 
 
665
      loc_x = (pan_x - scale_x) / 2 + p->boundary;
 
666
      loc_y = p->boundary;
 
667
    }
 
668
  }
 
669
  else
 
670
  {
 
671
    /* Scale to size of panel */
 
672
 
 
673
    scale_x = pan_x;
 
674
    scale_y = pan_y;
 
675
 
 
676
    loc_x = p->boundary;
 
677
    loc_y = p->boundary;
 
678
  }
 
679
  
 
680
  gkrellm_destroy_decal (p->decal);
 
681
  gkrellm_scale_pixbuf_to_pixmap (p->pixbuf, &(p->pixmap), NULL,
 
682
                            scale_x, scale_y);
 
683
 
 
684
  p->decal = gkrellm_create_decal_pixmap (p->panel, p->pixmap,
 
685
                                          NULL, 1, img_style, loc_x, loc_y);
 
686
  gkrellm_draw_decal_pixmap (p->panel, p->decal, 0);
 
687
  gkrellm_draw_panel_layers (p->panel);
 
688
}
 
689
 
 
690
/*
 
691
  start_img_dl ()
 
692
 
 
693
  Open a pipe and spawn wget.
 
694
*/
 
695
static void start_img_dl (KKamPanel *p)
 
696
{
 
697
  gchar *wget_str;
 
698
  char tmpfile[] = TEMPTEMPLATE "XXXXXX";
 
699
  int tmpfd;
 
700
 
 
701
  if (p->cmd_pipe) /* already open */
 
702
    return;
 
703
 
 
704
  tmpfd = mkstemp (tmpfile); /* this will create the file, perm 0600 */
 
705
  if (tmpfd == -1)
 
706
  {
 
707
    report_error (p, _("Couldn't create temporary file for download: %s"),
 
708
                  strerror (errno));
 
709
    return;
 
710
  }
 
711
  close (tmpfd);
 
712
 
 
713
  wget_str = g_strdup_printf ("wget -q %s -O %s \"%s\"",
 
714
                              wget_opts, tmpfile,
 
715
                              panel_cursource (p)->img_name);
 
716
 
 
717
  p->cmd_pipe = popen (wget_str, "r");
 
718
  g_free (wget_str);
 
719
  if (p->cmd_pipe == NULL)
 
720
  {
 
721
    unlink (tmpfile);
 
722
    report_error (p, _("Couldn't start wget: %s"), strerror (errno));
 
723
    return;
 
724
  }
 
725
  
 
726
  panel_cursource (p)->tfile = g_strdup (tmpfile);
 
727
  fcntl (fileno (p->cmd_pipe), F_SETFL, O_NONBLOCK);
 
728
}
 
729
 
 
730
/*
 
731
  start_script_dl ()
 
732
 
 
733
  open a pipe for a user-defined script
 
734
*/
 
735
static void start_script_dl (KKamPanel *p)
 
736
{
 
737
  char *scriptname;
 
738
 
 
739
  if (p->cmd_pipe) /* already open */
 
740
    return;
 
741
 
 
742
  scriptname = panel_cursource (p)->img_name;
 
743
  if (!strncmp (scriptname, "-x", 2))
 
744
    scriptname += 2;
 
745
 
 
746
  p->cmd_pipe = popen (scriptname, "r");
 
747
  if (p->cmd_pipe == NULL)
 
748
  {
 
749
    report_error (p, _("Couldn't start script \"%s\": %s\n"),
 
750
                  panel_cursource (p)->img_name, strerror (errno));
 
751
    return;
 
752
  }
 
753
  fcntl (fileno (p->cmd_pipe), F_SETFL, O_NONBLOCK);
 
754
}
 
755
 
 
756
/*
 
757
  load_image_file ()
 
758
 
 
759
  If the file in p's cursource exists, loads it into p's pixbuf
 
760
  member. Calls draw_pixbuf. Also, sets image tooltip.
 
761
 
 
762
  Returns -1 on file missing, and 1 otherwise (not necessarily
 
763
    success, but probably)
 
764
*/
 
765
static int load_image_file (KKamPanel *p)
 
766
{
 
767
  struct stat img_st;
 
768
  KKamSource *ks;
 
769
 
 
770
  ks = panel_cursource (p);
 
771
 
 
772
  /* make sure file is really there. when the imlib stuff
 
773
     fails it's really loud */
 
774
  if (ks->tfile == NULL || stat (ks->tfile, &img_st) == -1)
 
775
  {
 
776
    ks->next_dl = 0;
 
777
    return -1;
 
778
  }
 
779
 
 
780
  if (p->pixbuf)
 
781
    g_object_unref (G_OBJECT(p->pixbuf));
 
782
  p->pixbuf = gdk_pixbuf_new_from_file (ks->tfile, NULL);
 
783
  draw_pixbuf (p);
 
784
 
 
785
  if (ks->tooltip)
 
786
    gtk_tooltips_set_tip (tooltipobj, p->panel->drawing_area,
 
787
                          ks->tooltip, NULL);
 
788
  else
 
789
    gtk_tooltips_set_tip (tooltipobj, p->panel->drawing_area,
 
790
                          ks->img_name, NULL);
 
791
 
 
792
  return 1;
 
793
}
 
794
 
 
795
/*
 
796
  cmd_results ()
 
797
 
 
798
  Checks if the command pipe is dead yet. If so, checks its output
 
799
  status and returns 1 if the command was successful.
 
800
 
 
801
  On error, returns -1. On not yet ready, returns 0.
 
802
*/
 
803
static int cmd_results (KKamPanel *p)
 
804
{
 
805
  int code, len;
 
806
  char buf[BUFLEN];
 
807
  KKamSource *ks;
 
808
 
 
809
  ks = panel_cursource (p);
 
810
 
 
811
  if (fread (buf, sizeof (char), 1, p->cmd_pipe) < 1)
 
812
  {
 
813
    /* if we get EAGAIN, wait some more */
 
814
    if (ferror (p->cmd_pipe) && errno == EAGAIN)
 
815
      return 0;
 
816
 
 
817
    /* if we reach here the pipe is dead- the command has finished. */
 
818
    code = pclose (p->cmd_pipe);
 
819
    p->cmd_pipe = NULL;
 
820
 
 
821
    /* pclose will return a -1 on a wait4 error. If that happens,
 
822
       we have no way to know whether wget succeeded. Just try */
 
823
    if (ks->type == SOURCE_URL && code <= 0)
 
824
    {
 
825
      ks->next_dl = time (NULL) + ks->tlife;
 
826
      load_image_file (p);
 
827
      return 1;
 
828
    }
 
829
 
 
830
    report_error (p, _("Error: wget gave bad code or script died. code %d"),
 
831
                  code);
 
832
    return -1;
 
833
  }
 
834
  
 
835
  len = fread (&buf[1], sizeof (char), BUFLEN - 2, p->cmd_pipe);
 
836
  buf[len + 1] = '\0';
 
837
  g_strstrip (buf);
 
838
 
 
839
  pclose (p->cmd_pipe);
 
840
  p->cmd_pipe = NULL;
 
841
 
 
842
  if (ks->type == SOURCE_SCRIPT)
 
843
  {
 
844
    ks->tfile = g_strdup (buf);
 
845
    ks->next_dl = time (NULL) + ks->tlife;
 
846
    load_image_file (p);
 
847
    return 1;
 
848
  }
 
849
  else
 
850
  {
 
851
    /* if we get here with wget, then wget said something. This is generally
 
852
       not good, since we passed -q. We'll have to wait for it to die */
 
853
    
 
854
    report_error (p, _("wget said: \"%s\""), buf);
 
855
    return -1;
 
856
  }
 
857
}
 
858
 
 
859
/*
 
860
  rotate_sources ()
 
861
 
 
862
  moves the current source item to the end of the list, unless
 
863
  p is set for random.
 
864
*/
 
865
static void rotate_sources (KKamPanel *p)
 
866
{
 
867
  GList *link;
 
868
  int times, i, len;
 
869
 
 
870
  if (p == NULL || p->sources == NULL ||
 
871
        (len = g_list_length (p->sources)) == 1)
 
872
    return;
 
873
 
 
874
  times = p->random ? (rand () % (len - 1) + 1) : 1;
 
875
  for (i = 0; i < times; i++)
 
876
  {
 
877
    link = p->sources;
 
878
    p->sources = g_list_remove_link (p->sources, link);
 
879
    p->sources = g_list_concat (p->sources, link);
 
880
  }
 
881
}
 
882
 
 
883
/*
 
884
  update_image ()
 
885
 
 
886
  determines what the current image source is, and updates or
 
887
  starts an update as appropriate
 
888
*/
 
889
static void update_image (KKamPanel *p)
 
890
{
 
891
  KKamSource *ks;
 
892
  
 
893
  p->count = get_period (p);
 
894
 
 
895
  ks = panel_cursource (p);
 
896
  if (ks->img_name == NULL || ks->img_name[0] == '\0')
 
897
    return;
 
898
 
 
899
  if (ks->next_dl > time (NULL))
 
900
    load_image_file (p);
 
901
  else
 
902
  {
 
903
    tfile_release (ks);
 
904
 
 
905
    switch (ks->type)
 
906
    {
 
907
    case SOURCE_SCRIPT:
 
908
      start_script_dl (p);
 
909
      break;
 
910
    case SOURCE_URL:
 
911
      start_img_dl (p);
 
912
      break;
 
913
    case SOURCE_FILE:
 
914
      ks->tfile = g_strdup (ks->img_name);
 
915
      ks->next_dl = 0; /* next_dl is meaningless for SOURCE_FILE */
 
916
      load_image_file (p);
 
917
      break;
 
918
    default:
 
919
      report_error (p, _("Invalid type %d found in sources list!"), ks->type);
 
920
    }
 
921
  }
 
922
}
 
923
 
 
924
/*
 
925
  listurl_results ()
 
926
 
 
927
  when the listurl download is finished, reads the list and deletes it.
 
928
  Returns 0 if still waiting, 1 on success or error.
 
929
*/
 
930
static int listurl_results (KKamPanel *p)
 
931
{
 
932
  int code;
 
933
  char c;
 
934
  KKamSource *ks;
 
935
 
 
936
  ks = panel_cursource (p);
 
937
 
 
938
  if (fread (&c, sizeof (char), 1, p->listurl_pipe) < 1)
 
939
  {
 
940
    /* if we get EAGAIN, wait some more */
 
941
    if (ferror (p->listurl_pipe) && errno == EAGAIN)
 
942
      return 0;
 
943
 
 
944
    /* if we reach here the pipe is dead- the command has finished. */
 
945
    code = pclose (p->listurl_pipe);
 
946
    p->listurl_pipe = NULL;
 
947
  }
 
948
  else
 
949
    code = 256;
 
950
 
 
951
  /* pclose will return a -1 on a wait4 error. If that happens,
 
952
     we have no way to know whether wget succeeded. Just try */
 
953
  if (code <= 0)
 
954
  {
 
955
    kkam_read_list (p, p->listurl_file, 0);
 
956
    update_image (p);
 
957
  }
 
958
  else
 
959
    report_error (p, _("Error: wget listurl download died. code %d"), code);
 
960
 
 
961
  unlink (p->listurl_file);
 
962
  g_free (p->listurl_file);
 
963
  p->listurl_file = NULL;
 
964
 
 
965
  return 1;
 
966
}
 
967
 
 
968
/*
 
969
  kkam_update_plugin ()
 
970
 
 
971
  callback from gkrellm. Counts seconds until next update, and when
 
972
  count is reached, starts the update process.
 
973
*/
 
974
static void kkam_update_plugin ()
 
975
{
 
976
  int i;
 
977
 
 
978
  if (pGK->second_tick)
 
979
    for (i = 0; i < numpanels; i++)
 
980
    {
 
981
      if (panels[i].listurl_pipe)
 
982
        listurl_results (&panels[i]);
 
983
      else if (panels[i].cmd_pipe)
 
984
        cmd_results (&panels[i]);
 
985
      else if (--panels[i].count <= 0)
 
986
      {
 
987
        rotate_sources (&panels[i]);
 
988
        update_image (&panels[i]);
 
989
      }
 
990
    }
 
991
}
 
992
 
 
993
void showsource (KKamSource *s)
 
994
{
 
995
  fprintf (stderr, "name %s, type %d, seconds %d, tooltip %s\n",
 
996
                   s->img_name, s->type, s->seconds, s->tooltip);
 
997
}
 
998
 
 
999
static gint
 
1000
wheel_callback(GtkWidget *widget, GdkEventScroll *ev)
 
1001
{
 
1002
  if (ev->direction == GDK_SCROLL_UP)
 
1003
  {
 
1004
    newnumpanels = MIN (numpanels + 1, MAX_NUMPANELS);
 
1005
    change_num_panels ();
 
1006
  }
 
1007
  else if (ev->direction == GDK_SCROLL_DOWN)
 
1008
  {
 
1009
    newnumpanels = MAX (numpanels - 1, MIN_NUMPANELS);
 
1010
    change_num_panels ();
 
1011
  }
 
1012
  return TRUE;
 
1013
}
 
1014
 
 
1015
/*
 
1016
  click_callback ()
 
1017
 
 
1018
  Process user's mouse-clicks on the panels. Open viewers, refresh images,
 
1019
  increase/decrease number of panels, etc.
 
1020
*/
 
1021
static gint click_callback (GtkWidget *widget, GdkEventButton *ev, gpointer gw)
 
1022
{
 
1023
  gchar *cmd;
 
1024
  int which;
 
1025
  KKamSource *ks;
 
1026
 
 
1027
  which = GPOINTER_TO_INT (gw);
 
1028
  if (!activenum (which))
 
1029
    return FALSE;
 
1030
  
 
1031
  ks = panel_cursource (&panels[which]);
 
1032
 
 
1033
  switch (ev->button)
 
1034
  {
 
1035
  case 1: /* view image */
 
1036
    if (ks->tfile)
 
1037
    {
 
1038
      if (viewer_prog == NULL || viewer_prog[0] == '\0')
 
1039
        kkam_internal_viewer (ks->tfile);
 
1040
      else
 
1041
      {
 
1042
        cmd = g_strdup_printf ("%s '%s' &", viewer_prog, ks->tfile);
 
1043
        system (cmd);
 
1044
        g_free (cmd);
 
1045
      }
 
1046
    }
 
1047
    break;
 
1048
  case 2: /* immediate update */
 
1049
    panels[which].count = 0;
 
1050
    ks->next_dl = 0;
 
1051
    break;
 
1052
 
 
1053
  case 3:
 
1054
    gkrellm_open_config_window (monitor);
 
1055
    break;
 
1056
 
 
1057
  }
 
1058
  return FALSE;
 
1059
}
 
1060
 
 
1061
static gint panel_expose_event (GtkWidget *widget,
 
1062
                                GdkEventExpose *ev,
 
1063
                                gpointer gw)
 
1064
{
 
1065
  int which;
 
1066
 
 
1067
  which = GPOINTER_TO_INT (gw);
 
1068
  if (!activenum (which))
 
1069
    return FALSE;
 
1070
 
 
1071
  gdk_draw_drawable (widget->window,
 
1072
       widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
 
1073
       panels[which].panel->pixmap,
 
1074
       ev->area.x, ev->area.y, ev->area.x, ev->area.y,
 
1075
       ev->area.width, ev->area.height);
 
1076
 
 
1077
  return FALSE;
 
1078
}
 
1079
 
 
1080
static void cb_aspect_box (KKamPanel *p)
 
1081
{
 
1082
  p->maintain_aspect = GTK_TOGGLE_BUTTON (p->aspect_box)->active;
 
1083
  gkrellm_config_modified ();
 
1084
  draw_pixbuf (p);
 
1085
}
 
1086
 
 
1087
static void cb_boundary_spinner (gpointer w, KKamPanel *p)
 
1088
{
 
1089
  p->boundary = gtk_spin_button_get_value_as_int
 
1090
                                 (GTK_SPIN_BUTTON (p->boundary_spinner));
 
1091
  gkrellm_config_modified ();
 
1092
  draw_pixbuf (p);
 
1093
}
 
1094
 
 
1095
static void cb_height_spinner (gpointer w, KKamPanel *p)
 
1096
{
 
1097
  int newheight;
 
1098
 
 
1099
  newheight = gtk_spin_button_get_value_as_int (
 
1100
                                   GTK_SPIN_BUTTON (p->height_spinner));
 
1101
  
 
1102
  if (newheight != p->height)
 
1103
  {
 
1104
    gkrellm_panel_configure_add_height (p->panel, newheight - p->height);
 
1105
    p->height = newheight;
 
1106
    gkrellm_panel_create (kkam_vbox, monitor, p->panel);
 
1107
 
 
1108
    gkrellm_config_modified ();                            
 
1109
    draw_pixbuf (p);
 
1110
  }
 
1111
}
 
1112
 
 
1113
/*
 
1114
  src_set ()
 
1115
 
 
1116
  sets the image source from the file selection dialog
 
1117
*/
 
1118
static void src_set (KKamPanel *p)
 
1119
{
 
1120
  g_free (p->source);
 
1121
  p->source = g_strdup (gtk_file_selection_get_filename (
 
1122
                                            GTK_FILE_SELECTION (filebox)));
 
1123
  gkrellm_config_modified ();
 
1124
  gtk_entry_set_text (GTK_ENTRY (p->sourcebox), p->source);
 
1125
  gtk_widget_destroy (GTK_WIDGET (filebox));
 
1126
  create_sources_list (p);
 
1127
  p->count = get_period (p);
 
1128
  update_image (p);
 
1129
}
 
1130
 
 
1131
/*
 
1132
  srcbrowse ()
 
1133
 
 
1134
  brings up a file browser window to select a source
 
1135
*/
 
1136
static void srcbrowse (KKamPanel *p)
 
1137
{
 
1138
  filebox = gtk_file_selection_new (_("Select Image Source"));
 
1139
  g_signal_connect_swapped (
 
1140
                     G_OBJECT (GTK_FILE_SELECTION (filebox)->ok_button),
 
1141
                     "clicked", G_CALLBACK (src_set), (gpointer)p);
 
1142
  g_signal_connect_swapped (
 
1143
                     G_OBJECT (GTK_FILE_SELECTION (filebox)->cancel_button),
 
1144
                     "clicked",
 
1145
                     G_CALLBACK (gtk_widget_destroy),
 
1146
                     (gpointer)filebox);
 
1147
 
 
1148
  gtk_widget_show (filebox);
 
1149
}
 
1150
 
 
1151
/*
 
1152
  srcreread ()
 
1153
 
 
1154
  rereads the source of a panel- this is useful if, for example,
 
1155
  one changes a list and wants the new list used
 
1156
*/
 
1157
static void srcreread (KKamPanel *p)
 
1158
{
 
1159
  g_free (p->source);
 
1160
  p->source = gtk_editable_get_chars (GTK_EDITABLE (p->sourcebox), 0, -1);
 
1161
  create_sources_list (p);
 
1162
  p->count = get_period (p);
 
1163
  update_image (p);
 
1164
}
 
1165
 
 
1166
/*
 
1167
  create_configpanel_tab ()
 
1168
 
 
1169
  Creates a GtkVbox for the configuration of one of the panels
 
1170
*/
 
1171
static GtkWidget *create_configpanel_tab (int i)
 
1172
{
 
1173
  GtkWidget *vbox, *hbox, *button, *sourcelabel;
 
1174
 
 
1175
  vbox = gtk_vbox_new (FALSE, 0);
 
1176
 
 
1177
  gkrellm_gtk_spin_button (vbox, &panels[i].period_spinner,
 
1178
                       (gfloat) panels[i].default_period,
 
1179
                       1.0, (gfloat)MAX_SECONDS, 1.0, 10.0, 0, 0, NULL, NULL,
 
1180
                       FALSE, _("Default number of seconds per update"));
 
1181
 
 
1182
  gkrellm_gtk_spin_button (vbox, &panels[i].height_spinner,
 
1183
                       (gfloat) panels[i].height,
 
1184
                       10.0, 100.0, 1.0, 5.0, 0, 0, cb_height_spinner, &panels[i],
 
1185
                       FALSE, _("Height of viewer, in pixels"));
 
1186
 
 
1187
  hbox = gtk_hbox_new (FALSE, 0);
 
1188
  gkrellm_gtk_spin_button (hbox, &panels[i].boundary_spinner,
 
1189
                       (gfloat) panels[i].boundary,
 
1190
                       0.0, 20.0, 1.0, 1.0, 0, 0, cb_boundary_spinner, &panels[i],
 
1191
                       FALSE, _("Border size"));
 
1192
  
 
1193
  gkrellm_gtk_check_button (hbox, &panels[i].aspect_box,
 
1194
                        panels[i].maintain_aspect,
 
1195
                        TRUE, 0, _("Maintain aspect ratio"));
 
1196
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
 
1197
  
 
1198
  g_signal_connect_swapped (G_OBJECT (panels[i].aspect_box), "toggled",
 
1199
                        G_CALLBACK (cb_aspect_box), (gpointer)&panels[i]);
 
1200
 
 
1201
  gkrellm_gtk_check_button (vbox, &panels[i].random_box,
 
1202
                        panels[i].random,
 
1203
                        TRUE, 0, _("Select list images at random"));
 
1204
 
 
1205
  hbox = gtk_hbox_new (FALSE, 0);
 
1206
  sourcelabel = gtk_label_new (_("Image source:  "));
 
1207
  panels[i].sourcebox = gtk_entry_new ();
 
1208
  gtk_entry_set_text (GTK_ENTRY (panels[i].sourcebox), panels[i].source);
 
1209
  button = gtk_button_new_with_label (_("Browse.."));
 
1210
  g_signal_connect_swapped (G_OBJECT (button), "clicked",
 
1211
                             G_CALLBACK (srcbrowse), (gpointer)&panels[i]);
 
1212
  gtk_box_pack_start (GTK_BOX (hbox), sourcelabel, FALSE, FALSE, 0);
 
1213
  gtk_box_pack_start (GTK_BOX (hbox), panels[i].sourcebox, TRUE, TRUE, 0);
 
1214
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
 
1215
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
 
1216
 
 
1217
  hbox = gtk_hbox_new (FALSE, 5);
 
1218
  button = gtk_button_new_with_label (_("Reread source"));
 
1219
  g_signal_connect_swapped (G_OBJECT (button), "clicked",
 
1220
                             G_CALLBACK (srcreread), (gpointer)&panels[i]);
 
1221
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
 
1222
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
 
1223
 
 
1224
  gtk_widget_show_all (vbox);
 
1225
 
 
1226
  return vbox;
 
1227
}
 
1228
 
 
1229
/*
 
1230
  insert_configpanel_tab ()
 
1231
 
 
1232
  Creates a new configuration tab and inserts it into the config box
 
1233
*/
 
1234
static void insert_configpanel_tab (int i)
 
1235
{
 
1236
  GtkWidget *label, *configpanel;
 
1237
  gchar *labeltxt;
 
1238
  
 
1239
  if (!GTK_IS_OBJECT (tabs))
 
1240
    return;
 
1241
 
 
1242
  configpanel = create_configpanel_tab (i);
 
1243
  
 
1244
  labeltxt = g_strdup_printf (_("Panel #%i"), i + 1);
 
1245
  label = gtk_label_new (labeltxt);
 
1246
  g_free (labeltxt);
 
1247
  
 
1248
  gtk_notebook_insert_page (GTK_NOTEBOOK (tabs), configpanel, label, i + 1);
 
1249
}
 
1250
 
 
1251
/*
 
1252
  remove_configpanel_tab ()
 
1253
 
 
1254
  Removes a configuration tab from the config box
 
1255
*/
 
1256
static void remove_configpanel_tab (int i)
 
1257
{
 
1258
  if (!GTK_IS_OBJECT (tabs))
 
1259
    return;
 
1260
 
 
1261
  gtk_notebook_remove_page (GTK_NOTEBOOK (tabs), i + 1);
 
1262
}
 
1263
 
 
1264
/*
 
1265
  change_num_panels ()
 
1266
 
 
1267
  called to move the number in `newnumpanels' into
 
1268
  `numpanels' with all associated changes to the monitor
 
1269
*/
 
1270
static void change_num_panels ()
 
1271
{
 
1272
  int i;
 
1273
  
 
1274
  if (numpanels == newnumpanels)
 
1275
    return;
 
1276
 
 
1277
  if (created)
 
1278
  {
 
1279
    for (i = numpanels - 1; i >= newnumpanels; i--)
 
1280
    {
 
1281
      remove_configpanel_tab (i);
 
1282
      if (panels[i].cmd_pipe)
 
1283
      {
 
1284
        pclose (panels[i].cmd_pipe);
 
1285
        panels[i].cmd_pipe = NULL;
 
1286
      }
 
1287
    }
 
1288
 
 
1289
    for (i = 0; i < MAX_NUMPANELS; i++)
 
1290
    {
 
1291
      gkrellm_panel_enable_visibility (panels[i].panel, i < newnumpanels,
 
1292
                                       &(panels[i].visible));
 
1293
    }
 
1294
 
 
1295
    for (i = numpanels; i < newnumpanels; i++)
 
1296
    {
 
1297
      insert_configpanel_tab (i);
 
1298
      update_image (&panels[i]);
 
1299
    }
 
1300
  }
 
1301
 
 
1302
  numpanels = newnumpanels;
 
1303
  gkrellm_config_modified ();
 
1304
}
 
1305
 
 
1306
static void kkam_create_plugin (GtkWidget *vbox, gint first_create)
 
1307
{
 
1308
  int i;
 
1309
 
 
1310
  kkam_vbox = vbox;
 
1311
  
 
1312
  if (first_create)
 
1313
  {
 
1314
    change_num_panels ();
 
1315
    created = 1;
 
1316
 
 
1317
    for (i = 0; i < MAX_NUMPANELS; i++)
 
1318
      panels[i].panel = gkrellm_panel_new0 ();
 
1319
 
 
1320
    tooltipobj = gtk_tooltips_new ();
 
1321
 
 
1322
    srand (time (NULL)); /* randomize from timer */
 
1323
  }
 
1324
 
 
1325
  img_style = gkrellm_meter_style (style_id);
 
1326
 
 
1327
  for (i = 0; i < MAX_NUMPANELS; i++)
 
1328
  {
 
1329
    gkrellm_panel_configure_add_height (panels[i].panel, panels[i].height);
 
1330
    gkrellm_panel_create (vbox, monitor, panels[i].panel);
 
1331
    gkrellm_panel_keep_lists (panels[i].panel, TRUE);
 
1332
 
 
1333
    panels[i].visible = TRUE;
 
1334
    if (i >= numpanels)
 
1335
    {
 
1336
      gkrellm_panel_enable_visibility (panels[i].panel, FALSE,
 
1337
                                       &(panels[i].visible));
 
1338
    }
 
1339
  }
 
1340
 
 
1341
  if (first_create)
 
1342
  {
 
1343
    for (i = 0; i < MAX_NUMPANELS; i++)
 
1344
    {
 
1345
      g_signal_connect (G_OBJECT (panels[i].panel->drawing_area),
 
1346
            "expose_event", G_CALLBACK (panel_expose_event),
 
1347
            GINT_TO_POINTER (i));
 
1348
      g_signal_connect (G_OBJECT (panels[i].panel->drawing_area),
 
1349
            "button_press_event", G_CALLBACK (click_callback),
 
1350
            GINT_TO_POINTER (i));
 
1351
      g_signal_connect (G_OBJECT (panels[i].panel->drawing_area),
 
1352
            "scroll_event", G_CALLBACK (wheel_callback), NULL);
 
1353
            
 
1354
      gkrellm_draw_panel_layers (panels[i].panel);
 
1355
      if (i < numpanels)
 
1356
        update_image (&panels[i]);
 
1357
    }
 
1358
  }
 
1359
  else
 
1360
  {
 
1361
    for (i = 0; i < numpanels; i++)
 
1362
      if (panels[i].decal && panels[i].decal->pixmap)
 
1363
      {
 
1364
        gkrellm_draw_decal_pixmap (panels[i].panel, panels[i].decal, 0);
 
1365
        gkrellm_draw_panel_layers (panels[i].panel);
 
1366
      }
 
1367
  }
 
1368
}
 
1369
 
 
1370
static KKamSource *kkam_source_new ()
 
1371
{
 
1372
  return g_new0 (KKamSource, 1);
 
1373
}
 
1374
 
 
1375
static void kkam_source_free (KKamSource *ks)
 
1376
{
 
1377
  if (ks->tfile)
 
1378
    tfile_release (ks);
 
1379
  g_free (ks->img_name);
 
1380
  g_free (ks->tooltip);
 
1381
  g_free (ks);
 
1382
}
 
1383
 
 
1384
static void destroy_sources_list (KKamPanel *p)
 
1385
{
 
1386
  g_list_foreach (p->sources, (GFunc)kkam_source_free, NULL);
 
1387
  g_list_free (p->sources);
 
1388
  p->sources = NULL;
 
1389
}
 
1390
 
 
1391
/*
 
1392
  addto_sources_list ()
 
1393
 
 
1394
  Adds a component to the sources list for panel p. The name
 
1395
  and type are copied from name and type. The new component
 
1396
  is returned, in case it needs to be modified further.
 
1397
*/
 
1398
static KKamSource *addto_sources_list (KKamPanel *p, char *name, SourceEnum type)
 
1399
{
 
1400
  KKamSource *ks;
 
1401
 
 
1402
  ks = kkam_source_new ();
 
1403
  ks->type = type;
 
1404
  ks->img_name = g_strdup (name);
 
1405
  ks->tfile = NULL;
 
1406
  ks->next_dl = 0;
 
1407
  p->sources = g_list_append (p->sources, (gpointer)ks);
 
1408
 
 
1409
  return ks;
 
1410
}
 
1411
 
 
1412
/*
 
1413
  endswith ()
 
1414
 
 
1415
  returns TRUE if the end of str matches endstr
 
1416
*/
 
1417
static gboolean endswith (char *str, char *endstr)
 
1418
{
 
1419
  int len, lenend;
 
1420
 
 
1421
  if ((len = strlen (str)) < (lenend = strlen (endstr)))
 
1422
    return FALSE;
 
1423
  return !strcmp (&str[len - lenend], endstr);
 
1424
}
 
1425
 
 
1426
/*
 
1427
  source_type_of ()
 
1428
 
 
1429
  determines what type of source is represented by the given
 
1430
  string, according to these rules, checked in order:
 
1431
 
 
1432
  - A source beginning with 'http:' or 'ftp:' and ending in '.list'
 
1433
     is a SOURCE_LISTURL.
 
1434
  - A source beginning with 'http:' or 'ftp:' is a SOURCE_URL.
 
1435
  - A source that begins with '-x' is a SOURCE_SCRIPT.
 
1436
  - A source with an extension in IMG_EXTENSIONS is a SOURCE_FILE.
 
1437
  - A source that matches a filename that is executable by the user is
 
1438
     a SOURCE_SCRIPT.
 
1439
  - A source ending in [.-]list is a SOURCE_LIST.
 
1440
  - If none of the above apply, and the source is a filename, the
 
1441
     file is opened. If it 'looks' like a GKrellKam list configuration
 
1442
     file, it is a SOURCE_LIST.
 
1443
  - Otherwise, it is a SOURCE_FILE.
 
1444
 
 
1445
FIXME: spaghetti!
 
1446
*/
 
1447
static SourceEnum source_type_of (char *def)
 
1448
{
 
1449
  int i, p, len;
 
1450
  FILE *test;
 
1451
  gchar **words;
 
1452
  unsigned char buf[BUFLEN];
 
1453
  
 
1454
  words = g_strsplit (def, " ", 2);
 
1455
  if (!words || !words[0]) /* wish I didn't need this */
 
1456
    return SOURCE_FILE;
 
1457
 
 
1458
  if (!strncmp (words[0], "http:", 5) || !strncmp (words[0], "ftp:", 4))
 
1459
  {
 
1460
    if (endswith (words[0], ".list") || endswith (words[0], "-list"))
 
1461
    {
 
1462
      g_strfreev (words);
 
1463
      return SOURCE_LISTURL;
 
1464
    }
 
1465
 
 
1466
    g_strfreev (words);
 
1467
    return SOURCE_URL;
 
1468
  }
 
1469
  if (!strcmp (words[0], "-x"))
 
1470
  {
 
1471
    g_strfreev (words);
 
1472
    return SOURCE_SCRIPT;
 
1473
  }
 
1474
  for (i = 0; i < (sizeof (IMG_EXTENSIONS) / sizeof (IMG_EXTENSIONS[0])); i++)
 
1475
  {
 
1476
    if (endswith (words[0], IMG_EXTENSIONS[i]))
 
1477
    {
 
1478
      g_strfreev (words);
 
1479
      return SOURCE_FILE;
 
1480
    }
 
1481
  }
 
1482
  if (access (words[0], X_OK) == 0)
 
1483
  {
 
1484
    g_strfreev (words);
 
1485
    return SOURCE_SCRIPT;
 
1486
  }
 
1487
  if (endswith (words[0], ".list") || endswith (words[0], "-list"))
 
1488
  {
 
1489
    g_strfreev (words);
 
1490
    return SOURCE_LIST;
 
1491
  }
 
1492
  
 
1493
  if ((test = fopen (words[0], "r")) != NULL)
 
1494
  {
 
1495
    len = fread (buf, sizeof (buf[0]), BUFLEN, test);
 
1496
    for (p = 0; p < len; p++)
 
1497
      if (!isgraph (buf[p]) && !isspace (buf[p]))
 
1498
      {
 
1499
        fclose (test);
 
1500
        g_strfreev (words);
 
1501
        return SOURCE_FILE;
 
1502
      }
 
1503
    g_strfreev (words);
 
1504
    fclose (test);
 
1505
    return SOURCE_LIST;
 
1506
  }
 
1507
 
 
1508
  g_strfreev (words);
 
1509
  return SOURCE_FILE;
 
1510
}
 
1511
 
 
1512
/*
 
1513
  nextword ()
 
1514
 
 
1515
  convenience function for the list reader; returns a pointer in s
 
1516
  to the beginning of the next parameter (non-space following a colon)
 
1517
*/
 
1518
char *nextword (char *s)
 
1519
{
 
1520
  char *ret;
 
1521
 
 
1522
  for (ret = s; *ret != ':'; ret++) ;
 
1523
  ret++;
 
1524
  for ( ; isspace (*ret); ret++) ;
 
1525
  return ret;
 
1526
}
 
1527
 
 
1528
/*
 
1529
  kkam_read_list ()
 
1530
 
 
1531
  reads a GKrellKam list configuration file and loads its
 
1532
  data into the panel's source GList
 
1533
*/
 
1534
static void kkam_read_list (KKamPanel *p, char *listname, int depth)
 
1535
{
 
1536
  KKamSource *ks = NULL;
 
1537
  SourceEnum typ;
 
1538
  FILE *listfile;
 
1539
  char buf[BUFLEN];
 
1540
  int thislist_error = 0;
 
1541
 
 
1542
  if (depth > MAX_DEPTH)
 
1543
  {
 
1544
    report_error (p, _("Maximum recursion depth exceeded reading list %s; "
 
1545
                  "perhaps a list is trying to load itself?"), listname);
 
1546
    return;
 
1547
  }
 
1548
 
 
1549
  if ((listfile = fopen (listname, "r")) == NULL)
 
1550
    return;
 
1551
  
 
1552
  while (fgets (buf, BUFLEN, listfile))
 
1553
  {
 
1554
    g_strchomp (buf);
 
1555
 
 
1556
    switch (buf[0])
 
1557
    {
 
1558
    case '\0':
 
1559
    case '#':
 
1560
      ks = NULL;
 
1561
      break;
 
1562
 
 
1563
    case '\t':
 
1564
      if (ks != NULL)
 
1565
      {
 
1566
        if (!strncmp (&buf[1], "tooltip:", 8))
 
1567
          ks->tooltip = g_strdup (nextword (&buf[1]));
 
1568
        else if (!strncmp (&buf[1], "seconds:", 8))
 
1569
          ks->seconds = CLAMP (atoi (nextword (&buf[1])), 1, MAX_SECONDS);
 
1570
        else if (!strncmp (&buf[1], "refresh:", 8))
 
1571
          ks->tlife = CLAMP (atoi (nextword (&buf[1])), 1, MAX_SECONDS);
 
1572
      }
 
1573
      else if (thislist_error == 0) /* only show one error per file */
 
1574
      {
 
1575
        thislist_error = 1;
 
1576
        report_error (p, _("In list %s, property line \"%s\" isn't associated"
 
1577
                           " with any source!"), listname, &buf[1]);
 
1578
      }
 
1579
      break;
 
1580
      
 
1581
    default:
 
1582
      if (!strncmp (buf, "image:", 6))
 
1583
        ks = addto_sources_list (p, nextword (buf), SOURCE_FILE);
 
1584
      else if (!strncmp (buf, "script:", 7))
 
1585
        ks = addto_sources_list (p, nextword (buf), SOURCE_SCRIPT);
 
1586
      else if (!strncmp (buf, "url:", 4))
 
1587
        ks = addto_sources_list (p, nextword (buf), SOURCE_URL);
 
1588
      else if (!strncmp (buf, "list:", 5))
 
1589
      {
 
1590
        kkam_read_list (p, nextword (buf), depth + 1);
 
1591
        ks = NULL;
 
1592
      }
 
1593
      else
 
1594
      {
 
1595
        typ = source_type_of (buf);
 
1596
        if (typ == SOURCE_LIST)
 
1597
        {
 
1598
          kkam_read_list (p, buf, depth + 1);
 
1599
          ks = NULL;
 
1600
        }
 
1601
        else
 
1602
          ks = addto_sources_list (p, buf, typ);
 
1603
      }
 
1604
    }
 
1605
  }
 
1606
}
 
1607
 
 
1608
static void kkam_read_listurl (KKamPanel *p, char *source)
 
1609
{
 
1610
  gchar *wget_str;
 
1611
  char tmpfile[] = TEMPTEMPLATE "-urllistXXXXXX";
 
1612
  int tmpfd;
 
1613
 
 
1614
  if (p->listurl_pipe) /* already open */
 
1615
    return;
 
1616
 
 
1617
  tmpfd = mkstemp (tmpfile); /* this will create the file, perm 0600 */
 
1618
  if (tmpfd == -1)
 
1619
  {
 
1620
    report_error (p, _("Couldn't create temporary file for list download: %s"),
 
1621
                  strerror (errno));
 
1622
    return;
 
1623
  }
 
1624
  close (tmpfd);
 
1625
 
 
1626
  wget_str = g_strdup_printf ("wget -q %s -O %s \"%s\"",
 
1627
                              wget_opts, tmpfile, source);
 
1628
 
 
1629
  p->listurl_pipe = popen (wget_str, "r");
 
1630
  g_free (wget_str);
 
1631
  if (p->listurl_pipe == NULL)
 
1632
  {
 
1633
    unlink (tmpfile);
 
1634
    report_error (p, _("Couldn't start wget for list download: %s"),
 
1635
                  strerror (errno));
 
1636
    return;
 
1637
  }
 
1638
  
 
1639
  p->listurl_file = g_strdup (tmpfile);
 
1640
  fcntl (fileno (p->listurl_pipe), F_SETFL, O_NONBLOCK);
 
1641
 
 
1642
  gtk_tooltips_set_tip (tooltipobj, p->panel->drawing_area,
 
1643
                        _("Downloading list.."), NULL);
 
1644
}
 
1645
 
 
1646
/*
 
1647
  create_sources_list ()
 
1648
 
 
1649
  Takes the 'source' of a panel- which can be a web address,
 
1650
  local image, script, list, etc.. and translates that into
 
1651
  a GList of sources. For most source types, obviously, the
 
1652
  sources GList will only have one entry.
 
1653
*/
 
1654
static void create_sources_list (KKamPanel *p)
 
1655
{
 
1656
  SourceEnum s;
 
1657
 
 
1658
  if (p->sources)
 
1659
    destroy_sources_list (p);
 
1660
 
 
1661
  if (p->source && p->source[0])
 
1662
  {
 
1663
    switch (s = source_type_of (p->source))
 
1664
    {
 
1665
    case SOURCE_URL:
 
1666
    case SOURCE_FILE:
 
1667
    case SOURCE_SCRIPT:
 
1668
      addto_sources_list (p, p->source, s);
 
1669
      break;
 
1670
    case SOURCE_LIST:
 
1671
      kkam_read_list (p, p->source, 0);
 
1672
      break;
 
1673
    case SOURCE_LISTURL:
 
1674
      kkam_read_listurl (p, p->source);
 
1675
      break;
 
1676
    }
 
1677
  }
 
1678
}
 
1679
 
 
1680
/*
 
1681
  update_source_config ()
 
1682
 
 
1683
  Translates a 0.2.2[bcd]-style config into an appropriate source item
 
1684
*/
 
1685
static void update_source_config (KKamPanel *p, char *val)
 
1686
{
 
1687
  SourceEnum t;
 
1688
  gchar **words;
 
1689
  gchar *scr;
 
1690
  int i;
 
1691
 
 
1692
  g_strdelimit (val, " \t\n", '\n');
 
1693
  words = g_strsplit (val, "\n", 0);
 
1694
  
 
1695
  for (i = 0; words[i]; i++)
 
1696
  {
 
1697
    if (!strcmp (words[i], "-l") || !strcmp (words[i], "--list"))
 
1698
    {
 
1699
      g_free (words[i]);
 
1700
      words[i] = g_strdup ("");
 
1701
    }
 
1702
    else if (!strcmp (words[i], "-x") || !strcmp (words[i], "--execute"))
 
1703
    {
 
1704
      g_free (words[i]);
 
1705
      words[i] = g_strdup ("-x");
 
1706
      scr = g_strjoinv (" ", &words[i]);
 
1707
      addto_sources_list (p, scr, SOURCE_SCRIPT);
 
1708
      g_free (p->source);
 
1709
      p->source = scr;
 
1710
      break;
 
1711
    }
 
1712
    else if (!strcmp (words[i], "-r") || !strcmp (words[i], "--random"))
 
1713
      p->random = TRUE;
 
1714
    else
 
1715
    {
 
1716
      t = source_type_of (words[i]);
 
1717
      g_free (p->source);
 
1718
      p->source = g_strdup (words[i]);
 
1719
      if (t == SOURCE_LIST)
 
1720
        kkam_read_list (p, words[i], 0);
 
1721
      else
 
1722
        addto_sources_list (p, words[i], source_type_of (val));
 
1723
    }
 
1724
  }
 
1725
  g_strfreev (words);
 
1726
}
 
1727
 
 
1728
/*
 
1729
  update_script_config ()
 
1730
 
 
1731
  Translates a pre-0.2.2-style config into an appropriate source item
 
1732
*/
 
1733
static void update_script_config (KKamPanel *p, char *val)
 
1734
{
 
1735
  gchar *chopmeup;
 
1736
  char *firstword, *rest;
 
1737
 
 
1738
  chopmeup = g_strdup_printf ("%s\n \n", g_strstrip (val));
 
1739
  firstword = strtok (chopmeup, " \n");
 
1740
  if (!firstword)
 
1741
    return;
 
1742
  rest = strtok (NULL, "\n");
 
1743
  if (!rest)
 
1744
    return;
 
1745
  g_strstrip (rest);
 
1746
  
 
1747
  /* If the old update_script item was using krellkam_load, parse
 
1748
     its parameters. If it was using a different script, put it with
 
1749
     the appropriate type into the source list */
 
1750
 
 
1751
  if (!strcmp (basename (firstword), "krellkam_load"))
 
1752
    update_source_config (p, rest);
 
1753
  else
 
1754
  {
 
1755
    g_free (p->source);
 
1756
    p->source = g_strdup_printf ("-x %s", val);
 
1757
    addto_sources_list (p, val, SOURCE_SCRIPT);
 
1758
  }
 
1759
  g_free (chopmeup);
 
1760
}
 
1761
 
 
1762
static void kkam_save_config (FILE *f)
 
1763
{
 
1764
  int i;
 
1765
 
 
1766
  if (viewer_prog && viewer_prog[0])
 
1767
    fprintf (f, "%s viewer_prog %s\n", PLUGIN_KEYWORD, viewer_prog);
 
1768
 
 
1769
  fprintf (f, "%s popup_errors %d\n", PLUGIN_KEYWORD, popup_errors);
 
1770
  fprintf (f, "%s numpanels %d\n", PLUGIN_KEYWORD, numpanels);
 
1771
 
 
1772
  for (i = 0; i < MAX_NUMPANELS; i++)
 
1773
  {
 
1774
    fprintf (f, "%s %d sourcedef %s\n",
 
1775
             PLUGIN_KEYWORD, i + 1, panels[i].source);
 
1776
    fprintf (f, "%s %d options %d.%d.%d.%d.%d\n",
 
1777
             PLUGIN_KEYWORD, i + 1,
 
1778
             panels[i].height,
 
1779
             panels[i].default_period,
 
1780
             panels[i].boundary,
 
1781
             panels[i].maintain_aspect,
 
1782
             panels[i].random);
 
1783
  }
 
1784
}
 
1785
 
 
1786
static void kkam_load_config (gchar *arg)
 
1787
{
 
1788
  gchar *config_item, *value;
 
1789
  int which;
 
1790
 
 
1791
  config_item = strtok (arg, " \n");
 
1792
  if (!config_item)
 
1793
    return;
 
1794
  which = atoi (config_item);
 
1795
  if (which)
 
1796
  {
 
1797
    config_item = strtok (NULL, " \n");
 
1798
    if (!config_item)
 
1799
      return;
 
1800
    which--;
 
1801
  }
 
1802
  value = strtok (NULL, "\n");
 
1803
  if (value == NULL)
 
1804
    value = "";
 
1805
 
 
1806
  if (!strcmp (config_item, "options"))
 
1807
  {
 
1808
    if (validnum (which))
 
1809
    {
 
1810
      sscanf (value, "%d.%d.%d.%d.%d",
 
1811
              &(panels[which].height),
 
1812
              &(panels[which].default_period),
 
1813
              &(panels[which].boundary),
 
1814
              &(panels[which].maintain_aspect),
 
1815
              &(panels[which].random));
 
1816
      
 
1817
      panels[which].height = CLAMP (panels[which].height, 10, 100);
 
1818
      panels[which].default_period =
 
1819
                           CLAMP (panels[which].default_period, 1, MAX_SECONDS);
 
1820
      panels[which].boundary = CLAMP (panels[which].boundary, 0, 20);
 
1821
      panels[which].maintain_aspect =
 
1822
                          (gboolean)CLAMP (panels[which].maintain_aspect, 0, 1);
 
1823
      panels[which].random = (gboolean)CLAMP (panels[which].random, 0, 1);
 
1824
    }
 
1825
  }
 
1826
  else if (!strcmp (config_item, "sourcedef"))
 
1827
  {
 
1828
    if (validnum (which))
 
1829
    {
 
1830
      g_free (panels[which].source);
 
1831
      panels[which].source = g_strstrip (g_strdup (value));
 
1832
      create_sources_list (&panels[which]);
 
1833
    }
 
1834
  }
 
1835
  else if (!strcmp (config_item, "viewer_prog"))
 
1836
  {
 
1837
    g_free (viewer_prog);
 
1838
    viewer_prog = g_strdup (value);
 
1839
  }
 
1840
  else if (!strcmp (config_item, "popup_errors"))
 
1841
  {
 
1842
    popup_errors = atoi (value);
 
1843
  }
 
1844
  else if (!strcmp (config_item, "numpanels"))
 
1845
  {
 
1846
    newnumpanels = CLAMP (atoi (value), MIN_NUMPANELS, MAX_NUMPANELS);
 
1847
    change_num_panels ();
 
1848
  }
 
1849
  /* backwards compatibility */
 
1850
  else if (!strcmp (config_item, "img_height"))
 
1851
  {
 
1852
    if (validnum (which))
 
1853
      panels[which].height = CLAMP (atoi (value), 10, 100);
 
1854
  }
 
1855
  else if (!strcmp (config_item, "period"))
 
1856
  {
 
1857
    if (validnum (which))
 
1858
      panels[which].default_period = CLAMP (atoi (value), 1, MAX_SECONDS);
 
1859
  }
 
1860
  else if (!strcmp (config_item, "maintain_aspect"))
 
1861
  {
 
1862
    if (validnum (which))
 
1863
      panels[which].maintain_aspect = (gboolean)CLAMP (atoi (value), 0, 1);
 
1864
  }
 
1865
  else if (!strcmp (config_item, "boundary"))
 
1866
  {
 
1867
    if (validnum (which))
 
1868
      panels[which].boundary = CLAMP (atoi (value), 0, 20);
 
1869
  }
 
1870
  else if (!strcmp (config_item, "update_period"))
 
1871
  {
 
1872
    /* this is in minutes */
 
1873
    if (validnum (which))
 
1874
      panels[which].default_period = MAX (atoi (value) * 60, 1);
 
1875
  }
 
1876
  else if (!strcmp (config_item, "update_script"))
 
1877
  {
 
1878
    /* update old config item */
 
1879
    if (validnum (which))
 
1880
      update_script_config (&panels[which], value);
 
1881
  }
 
1882
  else if (!strcmp (config_item, "source"))
 
1883
  {
 
1884
    /* backwards compat for 0.2.2[bcd] */
 
1885
    if (validnum (which))
 
1886
      update_source_config (&panels[which], value);
 
1887
  }
 
1888
}
 
1889
 
 
1890
static void cb_numpanel_spinner ()
 
1891
{
 
1892
  newnumpanels = gtk_spin_button_get_value_as_int
 
1893
                                       (GTK_SPIN_BUTTON (numpanel_spinner));
 
1894
  change_num_panels ();
 
1895
}
 
1896
 
 
1897
static void kkam_create_tab (GtkWidget *tab_vbox)
 
1898
{
 
1899
  GtkWidget *vbox, *tablabel;
 
1900
  GtkWidget *text;
 
1901
  GtkWidget *hbox, *configpanel, *about;
 
1902
  GtkAdjustment *numadj;
 
1903
  gchar *tabname;
 
1904
  int i;
 
1905
 
 
1906
  if (tabs)
 
1907
    g_object_unref (G_OBJECT (tabs));
 
1908
  
 
1909
  tabs = gtk_notebook_new ();  
 
1910
  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (tabs), GTK_POS_TOP);
 
1911
  gtk_box_pack_start (GTK_BOX (tab_vbox), tabs, TRUE, TRUE, 0);
 
1912
  g_object_ref (G_OBJECT (tabs));
 
1913
 
 
1914
  /* main options tab */
 
1915
  vbox = gkrellm_gtk_framed_notebook_page (tabs, _("Options"));
 
1916
  
 
1917
  hbox = gtk_hbox_new (FALSE, 0);
 
1918
  viewerbox = gtk_entry_new ();
 
1919
  if (viewer_prog)
 
1920
    gtk_entry_set_text (GTK_ENTRY (viewerbox), viewer_prog);
 
1921
 
 
1922
  gtk_box_pack_start (GTK_BOX (hbox),
 
1923
                      gtk_label_new (_("Path to image viewer program:")),
 
1924
                      FALSE, FALSE, 10);
 
1925
  gtk_box_pack_start (GTK_BOX (hbox), viewerbox, FALSE, FALSE, 0);
 
1926
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
 
1927
  
 
1928
  hbox = gtk_hbox_new (FALSE, 0);
 
1929
  popup_errors_box = gtk_check_button_new_with_label (_("Popup errors"));
 
1930
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (popup_errors_box),
 
1931
                                popup_errors);
 
1932
  gtk_box_pack_start (GTK_BOX (hbox), popup_errors_box, FALSE, FALSE, 10);
 
1933
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
 
1934
 
 
1935
  numadj = (GtkAdjustment *) gtk_adjustment_new ((gfloat) numpanels,
 
1936
                             (gfloat) MIN_NUMPANELS,
 
1937
                             (gfloat) MAX_NUMPANELS,
 
1938
                             1.0, 1.0, 0);
 
1939
  numpanel_spinner = gtk_spin_button_new (numadj, 1.0, 0);
 
1940
  g_signal_connect (G_OBJECT (numpanel_spinner), "changed",
 
1941
                      G_CALLBACK (cb_numpanel_spinner), NULL);
 
1942
  hbox = gtk_hbox_new (FALSE, 0);
 
1943
  gtk_box_pack_start (GTK_BOX (hbox), numpanel_spinner, FALSE, FALSE, 10);
 
1944
  gtk_box_pack_start (GTK_BOX (hbox),
 
1945
                      gtk_label_new (_("Number of panels")),
 
1946
                      FALSE, FALSE, 0);
 
1947
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
 
1948
  
 
1949
  /* individual panel options tabs */
 
1950
  for (i = 0; i < MAX_NUMPANELS; i++)
 
1951
  {
 
1952
    configpanel = create_configpanel_tab (i);
 
1953
 
 
1954
    tabname = g_strdup_printf (_("Panel #%d"), i + 1);
 
1955
    tablabel = gtk_label_new (tabname);
 
1956
    g_free (tabname);
 
1957
    
 
1958
    if (i < numpanels)
 
1959
      gtk_notebook_append_page (GTK_NOTEBOOK (tabs), configpanel, tablabel);
 
1960
  }
 
1961
 
 
1962
  /* Info tab */
 
1963
  vbox = gkrellm_gtk_framed_notebook_page (tabs, _("Info"));
 
1964
 
 
1965
  text = gkrellm_gtk_scrolled_text_view(vbox, NULL,
 
1966
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
1967
 
 
1968
  for (i = 0; i < sizeof(kkam_info_text) / sizeof(gchar *); ++i)
 
1969
     gkrellm_gtk_text_view_append(text, _(kkam_info_text[i]));
 
1970
 
 
1971
  /* About tab */
 
1972
  vbox = gkrellm_gtk_framed_notebook_page (tabs, _("About"));
 
1973
  about = gtk_label_new (kkam_about_text);
 
1974
  gtk_box_pack_start (GTK_BOX (vbox), about, TRUE, TRUE, 0);
 
1975
}
 
1976
 
 
1977
static void kkam_apply_config ()
 
1978
{
 
1979
  int i, diff;
 
1980
  gchar *newsource;
 
1981
 
 
1982
  for (i = 0; i < numpanels; i++)
 
1983
  {
 
1984
    newsource = gtk_editable_get_chars
 
1985
                         (GTK_EDITABLE (panels[i].sourcebox), 0, -1);
 
1986
    diff = strcmp (newsource, panels[i].source);
 
1987
    g_free (panels[i].source);
 
1988
    panels[i].source = newsource;
 
1989
    if (diff)
 
1990
      create_sources_list (&panels[i]);
 
1991
 
 
1992
    panels[i].default_period = gtk_spin_button_get_value_as_int
 
1993
                               (GTK_SPIN_BUTTON (panels[i].period_spinner));
 
1994
    panels[i].maintain_aspect = GTK_TOGGLE_BUTTON (panels[i].aspect_box)->active;
 
1995
    panels[i].random = GTK_TOGGLE_BUTTON (panels[i].random_box)->active;
 
1996
    panels[i].boundary = gtk_spin_button_get_value_as_int
 
1997
                               (GTK_SPIN_BUTTON (panels[i].boundary_spinner));
 
1998
  }
 
1999
 
 
2000
  newnumpanels = gtk_spin_button_get_value_as_int
 
2001
                                 (GTK_SPIN_BUTTON (numpanel_spinner));
 
2002
  change_num_panels ();
 
2003
 
 
2004
  if (viewer_prog)
 
2005
    g_free (viewer_prog);
 
2006
  viewer_prog = g_strdup
 
2007
                   (gtk_editable_get_chars (GTK_EDITABLE (viewerbox), 0, -1));
 
2008
 
 
2009
  popup_errors = gtk_toggle_button_get_active
 
2010
                                       (GTK_TOGGLE_BUTTON (popup_errors_box));
 
2011
}
 
2012
 
 
2013
void kkam_cleanup ()
 
2014
{
 
2015
  int i;
 
2016
 
 
2017
  for (i = 0; i < MAX_NUMPANELS; i++)
 
2018
    destroy_sources_list (&panels[i]);
 
2019
}
 
2020
 
 
2021
static GkrellmMonitor kam_mon  =
 
2022
{
 
2023
  PLUGIN_NAME,         /* Name, for config tab.                    */
 
2024
  0,                   /* Id,  0 if a plugin                       */
 
2025
  kkam_create_plugin,  /* The create_plugin() function             */
 
2026
  kkam_update_plugin,  /* The update_plugin() function             */
 
2027
  kkam_create_tab,     /* The create_plugin_tab() config function  */
 
2028
  kkam_apply_config,   /* The apply_plugin_config() function       */
 
2029
 
 
2030
  kkam_save_config,    /* The save_plugin_config() function        */
 
2031
  kkam_load_config,    /* The load_plugin_config() function        */
 
2032
  PLUGIN_KEYWORD,      /* config keyword                           */
 
2033
 
 
2034
  NULL,                /* Undefined 2                              */
 
2035
  NULL,                /* Undefined 1                              */
 
2036
  NULL,                /* private                                  */
 
2037
 
 
2038
  MON_CPU,             /* Insert plugin before this monitor.       */
 
2039
  NULL,                /* Handle if a plugin, filled in by GKrellM */
 
2040
  NULL                 /* path if a plugin, filled in by GKrellM   */
 
2041
};
 
2042
 
 
2043
 
 
2044
#if defined(WIN32)
 
2045
    __declspec(dllexport) GkrellmMonitor *
 
2046
    gkrellm_init_plugin(win32_plugin_callbacks* calls)
 
2047
#else
 
2048
    GkrellmMonitor *
 
2049
    gkrellm_init_plugin()
 
2050
#endif
 
2051
{
 
2052
  int i;
 
2053
  
 
2054
  pGK = gkrellm_ticks();
 
2055
  style_id = gkrellm_add_meter_style (&kam_mon, PLUGIN_STYLE);
 
2056
  panels = g_new0 (KKamPanel, MAX_NUMPANELS);
 
2057
  
 
2058
  /* the g_new0 initialized everything to 0- pretty convenient for
 
2059
     almost everything.. */
 
2060
 
 
2061
  for (i = 0; i < MAX_NUMPANELS; i++)
 
2062
  {
 
2063
    panels[i].height = 50;
 
2064
    panels[i].source = g_strdup (default_source[i]);
 
2065
    panels[i].default_period = 60;
 
2066
  }
 
2067
 
 
2068
  /* gkrellm now hooks INT and QUIT and exits nicely. This will work. */
 
2069
 
 
2070
  g_atexit (kkam_cleanup);
 
2071
 
 
2072
  return (monitor = &kam_mon);
 
2073
}