~ubuntu-core-dev/update-notifier/ubuntu

« back to all changes in this revision

Viewing changes to src/hooks.c

  • Committer: Julian Andres Klode
  • Date: 2020-01-28 09:54:52 UTC
  • Revision ID: juliank@ubuntu.com-20200128095452-g0q3a1xg75a7k56j
Replace pep8 with pycodestyle

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
#include "hooks.h"
19
19
#include "rfc822.h"
20
20
#include "assert.h"
21
 
 
 
21
#include "trayappletui.h"
22
22
 
23
23
/* relative to the home dir */
24
 
#define HOOKS_SEEN ".update-notifier/hooks_seen"
 
24
#define HOOKS_SEEN_DEPRECATED ".update-notifier/hooks_seen"
 
25
/* relative to the XDG_CONFIG_HOME dir */
 
26
#define HOOKS_SEEN "update-notifier/hooks_seen"
25
27
 
26
28
/* used by e.g. the installer to mark stuff that's already done */
27
29
#define GLOBAL_HOOKS_SEEN "/etc/update-notifier/hooks_seen"
39
41
   va_end(va);
40
42
}
41
43
 
42
 
void hooks_trayicon_update_tooltip (TrayApplet *un, int num_hooks)
43
 
{
44
 
   g_debug_hooks("update_tooltip: %p ", un);
45
 
   gchar *updates;
46
 
 
47
 
   updates = _("Information available");
48
 
 
49
 
   gtk_status_icon_set_tooltip(un->tray_icon, updates);
50
 
}
51
 
 
52
 
 
53
44
// compare a HookFile with a filename and find the HookFile that matches
54
 
gint compare_hook_func(gconstpointer a, gconstpointer b)
 
45
static gint
 
46
compare_hook_func(gconstpointer a, gconstpointer b)
55
47
{
56
 
   //g_debug_hooks("compare: %s %s\n",(char*)(((HookFileSeen*)a)->filename),(char*)b);
 
48
   //g_debug_hooks("compare: %s %s",(char*)(((HookFileSeen*)a)->filename),(char*)b);
57
49
   g_assert(a);
58
50
   g_assert(b);
59
51
 
61
53
}
62
54
 
63
55
// return the most recent mtime or ctime of the file
64
 
time_t hook_file_time(const gchar *filename)
 
56
static time_t
 
57
hook_file_time(const gchar *filename)
65
58
{
66
59
   struct stat buf;
67
60
   char *file = g_strdup_printf("%s/%s",HOOKS_DIR, filename);
68
61
   if(g_stat(file, &buf) <0) {
69
 
      g_warning("can't stat %s\n",file);
 
62
      g_warning("can't stat %s",file);
70
63
      g_free(file);
71
64
      return 0;
72
65
   }
78
71
   return mtime > ctime ? mtime : ctime;
79
72
}
80
73
 
81
 
gboolean hook_file_md5(const gchar *filename, guint8 *md5)
 
74
static gboolean
 
75
hook_file_md5(const gchar *filename, guint8 *md5)
82
76
{
83
77
   guchar buf[512];
84
78
   FILE *f;
89
83
   file = g_strdup_printf("%s/%s",HOOKS_DIR, filename);
90
84
   f = fopen(file,"r");
91
85
   if(f == NULL) {
92
 
      g_warning("can't read %s\n",file);
 
86
      g_warning("can't read %s",file);
93
87
      g_free(file);
94
88
      return FALSE;
95
89
   }
101
95
   
102
96
   n=DIGEST_SIZE;
103
97
   g_checksum_get_digest(checksum, md5, &n);
104
 
   //g_debug_hooks("md5: %s -> '%s' ", filename, md5);
 
98
   //g_debug_hooks("md5: %s -> '%s'", filename, md5);
105
99
 
106
100
   g_free(file);
107
101
   g_checksum_free(checksum);
111
105
/* mark a given hook file as seen 
112
106
  (actually implemented to write out all the information we have)
113
107
*/
114
 
gboolean hook_file_mark_as_seen(HookTrayAppletPrivate *priv, 
115
 
                                HookFile *hf)
 
108
static gboolean
 
109
hook_file_mark_as_seen(HookTrayAppletPrivate *priv, HookFile *hf)
116
110
{
117
111
   g_debug_hooks("mark_hook_file_as_seen: %s", hf->filename);
118
112
 
127
121
   hf->mtime = hook_file_time(hf->filename);
128
122
 
129
123
   // write out the list of known files
130
 
   gchar *filename = g_strdup_printf("%s/%s",g_get_home_dir(),HOOKS_SEEN);
 
124
   gchar *filename = g_strdup_printf("%s/%s",
 
125
                                     g_get_user_config_dir(),
 
126
                                     HOOKS_SEEN);
131
127
   FILE *f = fopen(filename, "w");
132
128
   if(f==NULL) {
133
 
      g_warning("Something went wrong writing the users hook file");
 
129
      g_warning("Something went wrong writing the user's hook file");
134
130
      return FALSE;
135
131
   }
136
132
 
137
133
   // write out all the hooks that are seen 
138
134
   //
139
135
   // and any hooks that are not yet seen but have the same md5sum 
140
 
   // as the one just marked (avoids showing duplicated hooks effecivly). 
 
136
   // as the one just marked (avoids showing duplicated hooks effectively). 
141
137
   //
142
138
   // For hooks with the same md5sum use the mtime of the just displayed hook
143
139
   GList *elm = g_list_first(priv->hook_files);
161
157
}
162
158
 
163
159
/* mark a given HookFile as run */
164
 
gboolean mark_hook_file_as_run(HookTrayAppletPrivate *priv, HookFile *hf)
 
160
static gboolean
 
161
mark_hook_file_as_run(HookTrayAppletPrivate *priv, HookFile *hf)
165
162
{
166
163
   if(hf == NULL)
167
164
      return FALSE;
177
174
 * short_form: only return the languagecode if true, otherwise
178
175
 *             languagecode_countrycode
179
176
 */
180
 
char* get_lang_code(gboolean short_form)
 
177
static char *
 
178
get_lang_code(gboolean short_form)
181
179
{
182
180
   /* make own private copy */
183
181
   static gchar locale[51];
198
196
 * get a i18n field of the rfc822 header
199
197
 * Return Value: a pointer that must not be freed (part of the rfc822 struct)
200
198
 */
201
 
char *hook_file_lookup_i18n(struct rfc822_header *rfc822, char *field)
 
199
static char *
 
200
hook_file_lookup_i18n(struct rfc822_header *rfc822, char *field)
202
201
{
203
202
   gchar *s, *entry, *text;
204
203
 
214
213
   /* try $field-$languagecode_$countrycode.$codeset first */
215
214
   s = g_strdup_printf("%s-%s.%s", field, get_lang_code(FALSE), nl_langinfo(CODESET));
216
215
   entry = rfc822_header_lookup(rfc822, s);
217
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
216
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
218
217
   g_free(s);
219
218
   if(entry != NULL)
220
219
      return entry;
222
221
   /* try $field-$languagecode_$countrycode (and assume utf-8) */
223
222
   s = g_strdup_printf("%s-%s", field, get_lang_code(FALSE));
224
223
   entry = rfc822_header_lookup(rfc822, s);
225
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
224
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
226
225
   g_free(s);
227
226
   if(entry != NULL)
228
227
      return entry;
229
228
 
230
229
   /* try $field-$languagecode.$codeset next */
231
230
   s = g_strdup_printf("%s-%s.%s", field, get_lang_code(TRUE), nl_langinfo(CODESET));
232
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
231
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
233
232
   entry = rfc822_header_lookup(rfc822, s);
234
233
   g_free(s);
235
234
   if(entry != NULL)
237
236
 
238
237
   /* try $field-$languagecode (and assume utf-8 codeset) */
239
238
   s = g_strdup_printf("%s-%s", field, get_lang_code(TRUE));
240
 
   //g_debug_hooks("Looking for: %s ; found: %s\n",s,entry);
 
239
   //g_debug_hooks("Looking for: %s ; found: %s",s,entry);
241
240
   entry = rfc822_header_lookup(rfc822, s);
242
241
   g_free(s);
243
242
   if(entry != NULL)
248
247
   return entry;
249
248
}
250
249
 
251
 
char* hook_description_get_summary(struct rfc822_header *rfc822)
 
250
static char *
 
251
hook_description_get_summary(struct rfc822_header *rfc822)
252
252
{
253
253
   char *summary = hook_file_lookup_i18n(rfc822, "Name");
254
254
   return summary;
255
255
}
256
256
 
257
 
char* hook_description_get_description(struct rfc822_header *rfc822)
 
257
static char *
 
258
hook_description_get_description(struct rfc822_header *rfc822)
258
259
{
259
260
   char *description = hook_file_lookup_i18n(rfc822, "Description");
260
261
   return description;
263
264
/*
264
265
 * show the given hook file
265
266
 */
266
 
gboolean show_next_hook(TrayApplet *ta, GList *hooks)
 
267
static gboolean
 
268
show_next_hook(TrayApplet *ta, GList *hooks)
267
269
{
268
 
   //g_debug_hooks("show_next_hook()\n");
 
270
   //g_debug_hooks("show_next_hook()");
269
271
 
270
272
   HookTrayAppletPrivate *priv = (HookTrayAppletPrivate *)ta->user_data;
271
 
   GtkBuilder *builder = priv->gtk_builder;
272
 
 
273
 
   GtkWidget *title = GTK_WIDGET (gtk_builder_get_object(builder, "label_title"));
274
 
   assert(title);
275
 
   GtkWidget *text = GTK_WIDGET (gtk_builder_get_object(builder, "textview_hook"));
276
 
   assert(text);
277
 
   GtkWidget *button_next = GTK_WIDGET (gtk_builder_get_object(builder, "button_next"));
278
 
   assert(button_next);
279
 
   GtkWidget *button_run = GTK_WIDGET (gtk_builder_get_object(builder, "button_run"));
280
 
   assert(button_run);
281
273
 
282
274
   // init some vars
283
275
   HookFile *hf = NULL;
289
281
      hf = (HookFile*)elm->data;
290
282
      if(hf->seen == FALSE) {
291
283
         hook_file = hf->filename;
292
 
         //g_debug_hooks("next_hook is: %s\n",hook_file);
 
284
         //g_debug_hooks("next_hook is: %s",hook_file);
293
285
         break;
294
286
      }
295
287
   }
300
292
      return FALSE;
301
293
   }
302
294
 
 
295
   /* if there's another unseen hook, show the "next" button */
 
296
   gboolean show_next_button = FALSE;
 
297
   if(elm) {
 
298
      for(elm = g_list_next(elm); elm != NULL; elm = g_list_next(elm)) {
 
299
         if(((HookFile*)elm->data)->seen == FALSE) {
 
300
            show_next_button = TRUE;
 
301
            break;
 
302
         }
 
303
      }
 
304
   }
 
305
   if(show_next_button)
 
306
      gtk_widget_show(priv->button_next);
 
307
   else
 
308
      gtk_widget_hide(priv->button_next);
 
309
 
303
310
   char *filename = g_strdup_printf("%s%s",HOOKS_DIR,hook_file);
304
311
 
305
312
   /* setup the message */
306
 
   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
 
313
   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview_hook));
307
314
   FILE *f = fopen(filename, "r");
308
315
   if(f == NULL) {
309
316
      g_warning("can't open %s", filename);
313
320
   struct rfc822_header *rfc822 = rfc822_parse_stanza(f);
314
321
 
315
322
   char *cmd = rfc822_header_lookup(rfc822, "Command");
316
 
   char *term = g_strstrip(rfc822_header_lookup(rfc822, "Terminal"));
317
 
   g_object_set_data(G_OBJECT(button_run),"cmd", g_strdup(cmd));
318
 
   g_object_set_data(G_OBJECT(button_run),"term", g_strdup(term));
319
 
   g_object_set_data(G_OBJECT(button_run),"hook_file", hf);
 
323
   char *term = rfc822_header_lookup(rfc822, "Terminal");
 
324
   if(term)
 
325
      term = g_strstrip(term);
 
326
   g_object_set_data(G_OBJECT(priv->button_run),"cmd", g_strdup(cmd));
 
327
   g_object_set_data(G_OBJECT(priv->button_run),"term", g_strdup(term));
 
328
   g_object_set_data(G_OBJECT(priv->button_run),"hook_file", hf);
320
329
   if(cmd != NULL) {
321
 
      gtk_widget_show(button_run);
 
330
      gtk_widget_show(priv->button_run);
322
331
   } else {
323
 
      gtk_widget_hide(button_run);
 
332
      gtk_widget_hide(priv->button_run);
324
333
   }
325
334
   char *title_str = hook_file_lookup_i18n(rfc822, "Title");
326
335
   if(title_str != NULL) {
327
336
      gchar *s = g_strdup_printf("<span weight=\"bold\" size=\"larger\">%s</span>", title_str);
328
 
      gtk_label_set_markup(GTK_LABEL(title), s);
 
337
      gtk_label_set_markup(GTK_LABEL(priv->label_title), s);
329
338
      g_free(s);
330
339
   }
331
340
   char *summary = hook_description_get_summary(rfc822);
348
357
   // set button name (if needed)
349
358
   char *b = hook_file_lookup_i18n(rfc822, "ButtonText");
350
359
   if(b)
351
 
      gtk_button_set_label(GTK_BUTTON (button_run), b);
 
360
      gtk_button_set_label(GTK_BUTTON (priv->button_run), b);
352
361
   else
353
 
      gtk_button_set_label(GTK_BUTTON (button_run), _("_Run this action now"));
 
362
      gtk_button_set_label(GTK_BUTTON (priv->button_run), _("_Run this action now"));
354
363
 
355
364
   // clean up
356
365
   fclose(f);
359
368
   rfc822_header_free_all(rfc822);
360
369
 
361
370
   /* mark the current hook file as seen */
362
 
   g_object_set_data(G_OBJECT(button_next), "HookFile", hf);
 
371
   g_object_set_data(G_OBJECT(priv->button_next), "HookFile", hf);
363
372
 
364
373
   return TRUE;
365
374
}
367
376
 
368
377
void on_button_run_clicked(GtkWidget *self, gpointer *data)
369
378
{
370
 
   //g_debug_hooks("cb_button_run()\n");
 
379
   //g_debug_hooks("cb_button_run()");
371
380
   gchar *cmdline;
372
381
 
373
382
   TrayApplet *ta = (TrayApplet *)data;
381
390
   gchar *term = g_object_get_data(G_OBJECT(self), "term");
382
391
 
383
392
   if(cmd == NULL) {
384
 
      g_warning("cmd is NULL\n");
 
393
      g_warning("cmd is NULL");
385
394
      return;
386
395
   }
387
396
 
388
397
   if(term != NULL && !g_ascii_strncasecmp(term, "true",-1)) {
389
 
      cmdline = g_strdup_printf("gnome-terminal -e %s",cmd);
 
398
      cmdline = g_strdup_printf("x-terminal-emulator -e %s",cmd);
390
399
   } else 
391
400
      cmdline = g_strdup(cmd);
392
401
 
403
412
   HookFile *hf;
404
413
   hf = (HookFile*)g_object_get_data(G_OBJECT(self),"HookFile");
405
414
   if(hf == NULL) {
406
 
      g_warning("button_next called without HookFile\n");
 
415
      g_warning("button_next called without HookFile");
407
416
      return;
408
417
   }
409
418
   
410
419
   // mark as seen 
411
420
   hook_file_mark_as_seen(priv, hf);
412
 
   check_update_hooks(ta);
413
421
 
414
422
   if(priv->hook_files != NULL)
415
423
      show_next_hook(ta, priv->hook_files);
416
424
}
417
425
 
418
 
void show_hooks(TrayApplet *ta, gboolean focus_on_map)
419
 
{
 
426
static void
 
427
on_insert_text(GtkTextBuffer *buffer,
 
428
               GtkTextIter   *iter_end,
 
429
               gchar         *text,
 
430
               gint           arg3,
 
431
               gpointer       user_data)
 
432
{
 
433
   GtkTextIter iter, match_start, match_end, match_tmp;
 
434
 
 
435
   // get where we start
 
436
   gtk_text_buffer_get_iter_at_offset(buffer, &iter,
 
437
                                      gtk_text_iter_get_offset(iter_end) - g_utf8_strlen(text,-1));
 
438
   // search for http:// uris
 
439
   while(gtk_text_iter_forward_search(&iter, 
 
440
                                      "http://",  GTK_TEXT_SEARCH_VISIBLE_ONLY, 
 
441
                                      &match_start, &match_end, iter_end))  {
 
442
      match_tmp = match_end;
 
443
      // we found a uri, iterate it until the end is found
 
444
      while(gtk_text_iter_forward_char(&match_tmp)) {
 
445
         gchar *s = gtk_text_iter_get_text(&match_end,&match_tmp);
 
446
         if(strlen(s) < 1)
 
447
            break;
 
448
         char c = s[strlen(s)-1];
 
449
         if(c == ' ' || c == ')' || c == ']' || c == '\n' || c == '\t' || c == '>')
 
450
            break;
 
451
         match_end = match_tmp;
 
452
      }
 
453
      gchar *url = gtk_text_iter_get_text(&match_start, &match_end);
 
454
      //g_print("url: '%s'\n",url);
 
455
      GtkTextTag *tag;
 
456
      tag = gtk_text_buffer_create_tag(buffer, NULL,
 
457
                                       "foreground","blue",
 
458
                                       "underline", PANGO_UNDERLINE_SINGLE,
 
459
                                       NULL);
 
460
      g_object_set_data(G_OBJECT(tag), "url", url);
 
461
      gtk_text_buffer_apply_tag(buffer, tag, &match_start, &match_end);
 
462
      iter = match_end;
 
463
   }
 
464
}
 
465
 
 
466
static void
 
467
on_event_after(GtkWidget *widget,
 
468
               GdkEventButton  *event,
 
469
               gpointer   user_data)      
 
470
{
 
471
   if(event->type != GDK_BUTTON_RELEASE)
 
472
      return;
 
473
   if(event->button != 1)
 
474
      return;
 
475
   gint x,y;
 
476
   gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
 
477
                                         GTK_TEXT_WINDOW_WIDGET,
 
478
                                         event->x, event->y,
 
479
                                         &x, &y);
 
480
   GtkTextIter iter;
 
481
   gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y);
 
482
   GSList *tags = gtk_text_iter_get_tags(&iter);
 
483
   for( ; tags != NULL ; tags = tags->next) {
 
484
      gchar *url = g_object_get_data(G_OBJECT(tags->data), "url");
 
485
      if(url != NULL) {
 
486
         //g_print("click: '%s'\n",url);
 
487
         char *argv[] = { "/usr/bin/gnome-open", url, NULL };
 
488
         g_spawn_async(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL);
 
489
         break;
 
490
      }
 
491
   }
 
492
}
 
493
 
 
494
static void
 
495
show_hooks(TrayApplet *ta, gboolean focus_on_map)
 
496
{
 
497
   HookTrayAppletPrivate *priv = (HookTrayAppletPrivate *)ta->user_data;
 
498
   GtkBuilder *builder;
 
499
   GError *error = NULL;
 
500
 
420
501
   g_debug_hooks("show_hooks()");
421
 
   HookTrayAppletPrivate* priv = (HookTrayAppletPrivate *)ta->user_data;
422
 
 
423
 
   GtkBuilder *builder = priv->gtk_builder;
424
 
   GtkWidget *dia = GTK_WIDGET (gtk_builder_get_object(builder, "dialog_hooks"));
425
 
   gtk_window_set_title(GTK_WINDOW(dia), _("Information available"));
426
 
   assert(dia);
427
 
   GtkWidget *button_run = GTK_WIDGET (gtk_builder_get_object(builder, "button_run"));
428
 
   assert(button_run);
429
 
   GtkWidget *button_next = GTK_WIDGET (gtk_builder_get_object(builder, "button_next"));
430
 
   assert(button_next);
431
 
 
432
 
   gtk_window_set_focus_on_map(GTK_WINDOW(dia), focus_on_map);
 
502
 
 
503
   builder = gtk_builder_new ();
 
504
   assert (builder);
 
505
 
 
506
   if (!gtk_builder_add_from_file (builder, UIDIR"hooks-dialog.ui", &error)) {
 
507
      g_warning ("Couldn't load builder file: %s", error->message);
 
508
      g_error_free (error);
 
509
   }
 
510
 
 
511
   gtk_builder_connect_signals (builder, (gpointer)ta);
 
512
 
 
513
   priv->dialog_hooks = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_hooks"));
 
514
   gtk_window_set_title (GTK_WINDOW (priv->dialog_hooks), _("Information available"));
 
515
   assert (priv->dialog_hooks);
 
516
   priv->label_title = GTK_WIDGET (gtk_builder_get_object (builder, "label_title"));
 
517
   assert (priv->label_title);
 
518
   priv->textview_hook = GTK_WIDGET (gtk_builder_get_object (builder, "textview_hook"));
 
519
   assert (priv->textview_hook);
 
520
   priv->button_run = GTK_WIDGET (gtk_builder_get_object (builder, "button_run"));
 
521
   assert (priv->button_run);
 
522
   priv->button_next = GTK_WIDGET (gtk_builder_get_object (builder, "button_next"));
 
523
   assert (priv->button_next);
 
524
 
 
525
   g_object_unref (G_OBJECT (builder));
 
526
 
 
527
   // create a bold tag
 
528
   GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->textview_hook));
 
529
   gtk_text_buffer_create_tag (buf, "bold_tag",
 
530
                               "scale", PANGO_SCALE_LARGE, 
 
531
                               "weight", PANGO_WEIGHT_BOLD,
 
532
                               NULL);  
 
533
   g_signal_connect_after (G_OBJECT (buf), "insert-text",
 
534
                           G_CALLBACK (on_insert_text), NULL);
 
535
   g_signal_connect_after (G_OBJECT (priv->textview_hook), "event-after",
 
536
                           G_CALLBACK (on_event_after), NULL);
 
537
 
 
538
   gtk_window_set_focus_on_map (GTK_WINDOW (priv->dialog_hooks), focus_on_map);
433
539
 
434
540
   // if show_next_hook() fails for some reason don't do anything
435
541
   if(!show_next_hook(ta, priv->hook_files))
436
 
      return;
 
542
      goto out;
437
543
 
438
 
   int res = gtk_dialog_run(GTK_DIALOG(dia));
 
544
   int res = gtk_dialog_run (GTK_DIALOG (priv->dialog_hooks));
439
545
   if(res == GTK_RESPONSE_CLOSE) {
440
546
      // mark the currently current hookfile as seen
441
547
      HookFile *hf;
442
 
      hf = (HookFile*)g_object_get_data(G_OBJECT(button_next),"HookFile");
 
548
      hf = (HookFile*)g_object_get_data (G_OBJECT (priv->button_next), "HookFile");
443
549
      if(hf == NULL) {
444
 
         g_warning("show_hooks called without HookFile\n");
445
 
         return;
 
550
         g_warning("show_hooks called without HookFile");
 
551
         goto out;
446
552
      }
447
553
   
448
554
      // mark as seen 
450
556
      check_update_hooks(ta);
451
557
   }
452
558
 
453
 
   gtk_widget_hide(dia);
 
559
out:
 
560
   g_clear_pointer (&priv->dialog_hooks, gtk_widget_destroy);
 
561
   priv->label_title = NULL;
 
562
   priv->textview_hook = NULL;
 
563
   priv->button_run = NULL;
 
564
   priv->button_next = NULL;
454
565
}
455
566
 
456
567
static gboolean
461
572
 
462
573
   if(priv->active_notification != NULL) {
463
574
      notify_notification_close(priv->active_notification, NULL);
464
 
      priv->active_notification = NULL;
 
575
      g_clear_object(&priv->active_notification);
465
576
   }
466
577
   
467
 
   //g_debug_hooks("left click on hook applet\n");
 
578
   //g_debug_hooks("left click on hook applet");
468
579
   show_hooks(ta, TRUE);
469
580
   return TRUE;
470
581
}
471
582
 
472
 
gboolean is_hook_relevant(const gchar *hook_file)
 
583
static gboolean
 
584
is_hook_relevant(const gchar *hook_file)
473
585
{
474
586
   g_debug_hooks("is_hook_relevant(): %s", hook_file);
475
587
   gboolean res = TRUE;
539
651
   TrayApplet *ta = (TrayApplet*)data;
540
652
   HookTrayAppletPrivate *priv = (HookTrayAppletPrivate*)ta->user_data;
541
653
 
542
 
   if(!gtk_status_icon_get_visible(ta->tray_icon))
543
 
      return FALSE;
544
 
 
545
 
   GtkWidget *d = GTK_WIDGET (gtk_builder_get_object(priv->gtk_builder,"dialog_hooks"));
546
 
   if((d && GTK_WIDGET_VISIBLE(d)) || priv->active_notification != NULL)
547
 
      return FALSE;
548
 
 
549
 
   GdkRectangle area;
550
 
   gtk_status_icon_get_geometry(ta->tray_icon, NULL, &area, NULL);
551
 
   // no usefull coordiante yet, do another timeout
552
 
   if(area.x <= 0 || area.y <= 0 || area.width <= 0 || area.height <= 0)
553
 
      return TRUE;
 
654
   if(!tray_applet_ui_get_visible(ta))
 
655
      return FALSE;
 
656
 
 
657
   if((priv->dialog_hooks && gtk_widget_get_visible(priv->dialog_hooks)) ||
 
658
      priv->active_notification != NULL)
 
659
      return FALSE;
554
660
 
555
661
   NotifyNotification *n;
556
662
   GdkPixbuf* pix;
557
 
   n = notify_notification_new_with_status_icon(
 
663
   n = notify_notification_new(
558
664
                               _("Information available"),
559
665
                               _("Click on the notification icon"
560
666
                                 " to show the available information.\n"),
561
 
                               NULL, 
562
 
                               ta->tray_icon);
 
667
                               NULL);
563
668
   
564
669
   pix = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), 
565
670
                                  GTK_STOCK_DIALOG_INFO, 48,0,NULL);
567
672
   g_object_unref(pix);
568
673
   notify_notification_set_timeout (n, 60*1000);
569
674
   notify_notification_show(n, NULL);
 
675
   if (priv->active_notification)
 
676
      g_object_unref(priv->active_notification);
570
677
   priv->active_notification = n;
571
678
 
572
679
   return FALSE;
584
691
 
585
692
   dir=g_dir_open(HOOKS_DIR, 0, NULL);
586
693
   if(dir == NULL) {
587
 
      g_warning("can't read %s directory\n",HOOKS_DIR);
 
694
      g_warning("can't read %s directory",HOOKS_DIR);
588
695
      return FALSE;
589
696
   }
590
697
 
591
698
   g_debug_hooks("reading '%s' dir", HOOKS_DIR);
592
699
   int unseen_count = 0;
593
700
   while((hook_file=g_dir_read_name(dir)) != NULL) {
594
 
      g_debug_hooks("investigating file '%s' ", hook_file); 
 
701
      g_debug_hooks("investigating file '%s'", hook_file); 
595
702
 
596
703
      // check if the hook still applies (for e.g. DontShowAfterReboot)
597
704
      if(!is_hook_relevant(hook_file)) {
651
758
               (e->seen == FALSE) &&
652
759
               (memcmp(hf->md5,e->md5,DIGEST_SIZE)==0) )
653
760
              {
654
 
                 g_debug_hooks("%s (%s) was seen also in %s (%s)\n",
 
761
                 g_debug_hooks("%s (%s) was seen also in %s (%s)",
655
762
                         hf->filename, hf->md5, e->filename, e->md5);
656
763
                 md5match = TRUE;
657
764
              }
658
765
            x = g_list_next(x);
659
766
         }
660
767
         if(!md5match) {
661
 
            g_debug_hooks("%s increases unseen_count\n",hf->filename);
 
768
            g_debug_hooks("%s increases unseen_count",hf->filename);
662
769
            unseen_count++;
663
770
         }
664
771
      } else 
668
775
 
669
776
   //g_debug_hooks("hooks: %i (new: %i)", g_list_length(hook_files), unseen_count);
670
777
 
671
 
   GtkBuilder *builder = priv->gtk_builder;
672
 
   GtkWidget *button_next = GTK_WIDGET (gtk_builder_get_object(builder, "button_next"));
673
 
   assert(button_next);
674
 
 
675
 
   // check if we do a notification or a dialog
676
 
   GConfClient *gconf = gconf_client_get_default();
677
 
   if(gconf_client_get_bool(gconf, GCONF_KEY_AUTO_LAUNCH, NULL)) {
 
778
   if (unseen_count > 0) {
 
779
      // we only do a dialog
678
780
      g_debug_hooks("showing hooks with focus on map == FALSE");
679
 
      if (unseen_count < 2)
680
 
         gtk_widget_hide(button_next);
681
 
      else
682
 
         gtk_widget_show(button_next);
683
781
      show_hooks(ta, FALSE);
684
782
      return TRUE;
685
 
   }
686
 
 
687
 
   // no dialog, do the notification
688
 
   if((unseen_count > 0) && !gtk_status_icon_get_visible (ta->tray_icon))
689
 
      g_timeout_add(5000, show_notification, ta);
690
 
 
691
 
   switch(unseen_count) {
692
 
   case 0:
693
 
      gtk_status_icon_set_visible (ta->tray_icon, FALSE);
694
 
      gtk_widget_hide(button_next);
695
 
      break;
696
 
   case 1:
697
 
      gtk_status_icon_set_visible (ta->tray_icon, TRUE);
698
 
      gtk_widget_hide(button_next);
699
 
      break;
700
 
   default:
701
 
      gtk_status_icon_set_visible (ta->tray_icon, TRUE);
702
 
      gtk_widget_show(button_next);
703
 
      break;
704
 
   }
705
 
   hooks_trayicon_update_tooltip (ta, unseen_count);
 
783
   } else
 
784
      tray_applet_ui_destroy (ta);
706
785
 
707
786
   return TRUE;
708
787
}
709
788
 
710
 
void hook_read_seen_file(HookTrayAppletPrivate *priv, const char* filename)
 
789
static void
 
790
hook_read_seen_file(HookTrayAppletPrivate *priv, const char* filename)
711
791
{
712
792
   HookFile *t;
713
793
   char buf[512];
739
819
         HookFile *exisiting = (HookFile *)elm->data;
740
820
         // and it's more current
741
821
         if(exisiting->mtime > time) {
742
 
            g_debug_hooks("existing is newer, ignoring the read one\n");
 
822
            g_debug_hooks("existing is newer, ignoring the read one");
743
823
            continue;
744
824
         }
745
825
 
768
848
   fclose(f);
769
849
}
770
850
 
771
 
gboolean 
 
851
static gboolean 
772
852
init_already_seen_hooks(TrayApplet *ta)
773
853
{
774
854
   g_debug_hooks("init_already_seen_hooks");
775
855
 
776
856
   HookTrayAppletPrivate* priv = (HookTrayAppletPrivate*)ta->user_data;
777
 
   char *filename;
 
857
   char *filename, *old_filename;
778
858
 
779
859
   // init with default value
780
860
   priv->hook_files = NULL;  
781
861
 
782
862
   // read global hook file
783
 
   hook_read_seen_file(priv,GLOBAL_HOOKS_SEEN);
 
863
   hook_read_seen_file(priv, GLOBAL_HOOKS_SEEN);
784
864
   
 
865
   filename = g_strdup_printf("%s/%s", 
 
866
                              g_get_user_config_dir(),
 
867
                              HOOKS_SEEN);
 
868
   // compat with the previous version
 
869
   old_filename = g_strdup_printf("%s/%s", 
 
870
                                  g_get_home_dir(),
 
871
                                  HOOKS_SEEN_DEPRECATED);
 
872
   if (g_file_test(old_filename, G_FILE_TEST_IS_REGULAR)) {
 
873
     g_rename(old_filename, filename);
 
874
   }
785
875
   // read user hook file
786
 
   filename = g_strdup_printf("%s/%s", g_get_home_dir(),HOOKS_SEEN);
787
876
   hook_read_seen_file(priv,filename);
788
877
   g_free(filename);
 
878
   g_free(old_filename);
789
879
 
790
880
   return TRUE;
791
881
}
792
882
 
793
 
static void
794
 
on_insert_text(GtkTextBuffer *buffer,
795
 
               GtkTextIter   *iter_end,
796
 
               gchar         *text,
797
 
               gint           arg3,
798
 
               gpointer       user_data)
799
 
{
800
 
   GtkTextIter iter, match_start, match_end, match_tmp;
801
 
 
802
 
   // get where we start
803
 
   gtk_text_buffer_get_iter_at_offset(buffer, &iter,
804
 
                                      gtk_text_iter_get_offset(iter_end) - g_utf8_strlen(text,-1));
805
 
   // search for http:// uris
806
 
   while(gtk_text_iter_forward_search(&iter, 
807
 
                                      "http://",  GTK_TEXT_SEARCH_VISIBLE_ONLY, 
808
 
                                      &match_start, &match_end, iter_end))  {
809
 
      match_tmp = match_end;
810
 
      // we found a uri, iterate it until the end is found
811
 
      while(gtk_text_iter_forward_char(&match_tmp)) {
812
 
         gchar *s = gtk_text_iter_get_text(&match_end,&match_tmp);
813
 
         if(strlen(s) < 1)
814
 
            break;
815
 
         char c = s[strlen(s)-1];
816
 
         if(c == ' ' || c == ')' || c == ']' || c == '\n' || c == '\t' || c == '>')
817
 
            break;
818
 
         match_end = match_tmp;
819
 
      }
820
 
      gchar *url = gtk_text_iter_get_text(&match_start, &match_end);
821
 
      //g_print("url: '%s'\n",url);
822
 
      GtkTextTag *tag;
823
 
      tag = gtk_text_buffer_create_tag(buffer, NULL,
824
 
                                       "foreground","blue",
825
 
                                       "underline", PANGO_UNDERLINE_SINGLE,
826
 
                                       NULL);
827
 
      g_object_set_data(G_OBJECT(tag), "url", url);
828
 
      gtk_text_buffer_apply_tag(buffer, tag, &match_start, &match_end);
829
 
      iter = match_end;
830
 
   }
831
 
}
832
 
 
833
 
static void
834
 
on_event_after(GtkWidget *widget,
835
 
               GdkEventButton  *event,
836
 
               gpointer   user_data)      
837
 
{
838
 
   if(event->type != GDK_BUTTON_RELEASE)
839
 
      return;
840
 
   if(event->button != 1)
841
 
      return;
842
 
   gint x,y;
843
 
   gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
844
 
                                         GTK_TEXT_WINDOW_WIDGET,
845
 
                                         event->x, event->y,
846
 
                                         &x, &y);
847
 
   GtkTextIter iter;
848
 
   gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y);
849
 
   GSList *tags = gtk_text_iter_get_tags(&iter);
850
 
   for( ; tags != NULL ; tags = tags->next) {
851
 
      gchar *url = g_object_get_data(G_OBJECT(tags->data), "url");
852
 
      if(url != NULL) {
853
 
         //g_print("click: '%s'\n",url);
854
 
         char *argv[] = { "/usr/bin/gnome-open", url, NULL };
855
 
         g_spawn_async(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL);
856
 
         break;
857
 
      }
858
 
   }
859
 
}
860
 
 
861
883
void hook_tray_icon_init(TrayApplet *ta)
862
884
{
863
 
   GError* error = NULL;
864
 
   GtkBuilder* builder = gtk_builder_new ();
865
 
 
866
885
   HookTrayAppletPrivate *priv = g_new0(HookTrayAppletPrivate, 1);
867
886
   ta->user_data = priv;
868
887
 
869
 
   if (!gtk_builder_add_from_file (builder, UIDIR"hooks-dialog.ui", &error)) {
870
 
      g_warning ("Couldn't load builder file: %s", error->message);
871
 
      g_error_free (error);
872
 
   }
873
 
 
874
 
   assert(builder);
875
 
   priv->gtk_builder = builder;
876
 
   gtk_builder_connect_signals (builder, (gpointer)ta);
877
 
 
878
 
   /* show dialog on click */
879
 
   g_signal_connect (G_OBJECT(ta->tray_icon),
880
 
                     "activate",
881
 
                     G_CALLBACK (button_release_cb),
882
 
                     ta);
883
 
 
884
 
   // create a bold tag
885
 
   GtkWidget *text = GTK_WIDGET (gtk_builder_get_object(builder, "textview_hook"));
886
 
   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
887
 
   gtk_text_buffer_create_tag (buf, "bold_tag",
888
 
                               "scale", PANGO_SCALE_LARGE, 
889
 
                               "weight", PANGO_WEIGHT_BOLD,
890
 
                               NULL);  
891
 
   g_signal_connect_after(G_OBJECT(buf), "insert-text", 
892
 
                          G_CALLBACK(on_insert_text), NULL);
893
 
   g_signal_connect_after(G_OBJECT(text), "event-after", 
894
 
                          G_CALLBACK(on_event_after), NULL);
895
 
 
896
 
 
897
888
   /* read already seen hooks */
898
889
   init_already_seen_hooks(ta);
899
890