1 /* Sticky Notes
2  * Copyright (C) 2002-2003 Loban A Rahman
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 #include <config.h>
21 #include <libxml/parser.h>
22 #include <X11/Xatom.h>
23 #include <gdk/gdkx.h>
24 #include <gtk/gtk.h>
25 #define WNCK_I_KNOW_THIS_IS_UNSTABLE 1
26 #include <libwnck/libwnck.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 
30 #include <gtksourceview/gtksource.h>
31 
32 #include "stickynotes.h"
33 #include "stickynotes_callbacks.h"
34 #include "util.h"
35 #include "stickynotes_applet.h"
36 
37 /* Stop gcc complaining about xmlChar's signedness */
38 #define XML_CHAR(str) ((xmlChar *) (str))
39 #define STICKYNOTES_ICON_SIZE 8
40 
41 static gboolean save_scheduled = FALSE;
42 
43 static void response_cb (GtkWidget *dialog, gint id, gpointer data);
44 
45 /* Based on a function found in wnck */
46 static void
set_icon_geometry(GdkWindow * window,int x,int y,int width,int height)47 set_icon_geometry (GdkWindow *window,
48                    int        x,
49                    int        y,
50                    int        width,
51                    int        height)
52 {
53     gulong data[4];
54     Display *dpy;
55 
56     dpy = gdk_x11_display_get_xdisplay (gdk_window_get_display (window));
57 
58     data[0] = x;
59     data[1] = y;
60     data[2] = width;
61     data[3] = height;
62 
63     XChangeProperty (dpy,
64                      GDK_WINDOW_XID (window),
65                      gdk_x11_get_xatom_by_name_for_display (gdk_window_get_display (window),
66                                                             "_NET_WM_ICON_GEOMETRY"),
67                      XA_CARDINAL, 32, PropModeReplace,
68                      (guchar *)&data, 4);
69 }
70 
71 /* Called when a timeout occurs.  */
72 static gboolean
timeout_happened(gpointer data)73 timeout_happened (gpointer data)
74 {
75     if (GPOINTER_TO_UINT (data) == stickynotes->last_timeout_data)
76         stickynotes_save ();
77 
78     return FALSE;
79 }
80 
81 /* Called when a text buffer is changed.  */
82 static void
buffer_changed(GtkTextBuffer * buffer,StickyNote * note)83 buffer_changed (GtkTextBuffer *buffer,
84                 StickyNote    *note)
85 {
86     if ( (note->h + note->y) > stickynotes->max_height )
87         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (note->w_scroller),
88                                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
89 
90     /* When a buffer is changed, we set a 10 second timer.  When
91        the timer triggers, we will save the buffer if there have
92        been no subsequent changes.  */
93     ++stickynotes->last_timeout_data;
94     g_timeout_add_seconds (10, (GSourceFunc) timeout_happened,
95                            GUINT_TO_POINTER (stickynotes->last_timeout_data));
96 }
97 
98 /* Create a new (empty) Sticky Note at a specific position
99    and with specific size */
100 static StickyNote *
stickynote_new_aux(GdkScreen * screen,gint x,gint y,gint w,gint h)101 stickynote_new_aux (GdkScreen *screen,
102                     gint       x,
103                     gint       y,
104                     gint       w,
105                     gint       h)
106 {
107     StickyNote *note;
108     GtkBuilder *builder;
109 
110     note = g_new (StickyNote, 1);
111 
112     builder = gtk_builder_new ();
113     gtk_builder_add_from_resource (builder,
114                                    GRESOURCE "sticky-notes-note.ui",
115                                    NULL);
116     gtk_builder_add_from_resource (builder,
117                                    GRESOURCE "sticky-notes-properties.ui",
118                                    NULL);
119 
120     note->w_window = GTK_WIDGET (gtk_builder_get_object (builder,
121                                                          "stickynote_window"));
122     gtk_window_set_screen (GTK_WINDOW (note->w_window),screen);
123     gtk_window_set_decorated (GTK_WINDOW (note->w_window), FALSE);
124     gtk_window_set_skip_taskbar_hint (GTK_WINDOW (note->w_window), TRUE);
125     gtk_window_set_skip_pager_hint (GTK_WINDOW (note->w_window), TRUE);
126     gtk_widget_add_events (note->w_window, GDK_BUTTON_PRESS_MASK);
127 
128     note->w_title = GTK_WIDGET (gtk_builder_get_object (builder,
129                                                         "title_label"));
130     note->w_body = GTK_WIDGET (gtk_builder_get_object (builder,
131                                                        "body_text"));
132     note->w_scroller = GTK_WIDGET (gtk_builder_get_object (builder,
133                                                            "body_scroller"));
134     note->w_lock = GTK_WIDGET (gtk_builder_get_object (builder,
135                                                        "lock_button"));
136     gtk_widget_add_events (note->w_lock, GDK_BUTTON_PRESS_MASK);
137 
138     note->buffer =
139         GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (note->w_body)));
140 
141     note->w_close =
142         GTK_WIDGET (gtk_builder_get_object (builder, "close_button"));
143     gtk_widget_add_events (note->w_close, GDK_BUTTON_PRESS_MASK);
144     note->w_resize_se =
145         GTK_WIDGET (gtk_builder_get_object (builder, "resize_se_box"));
146     gtk_widget_add_events (note->w_resize_se, GDK_BUTTON_PRESS_MASK);
147     note->w_resize_sw =
148         GTK_WIDGET (gtk_builder_get_object (builder, "resize_sw_box"));
149     gtk_widget_add_events (note->w_resize_sw, GDK_BUTTON_PRESS_MASK);
150 
151     note->img_lock =
152         GTK_IMAGE (gtk_builder_get_object (builder, "lock_img"));
153     note->img_close =
154         GTK_IMAGE (gtk_builder_get_object (builder, "close_img"));
155     note->img_resize_se =
156         GTK_IMAGE (gtk_builder_get_object (builder, "resize_se_img"));
157     note->img_resize_sw =
158         GTK_IMAGE (gtk_builder_get_object (builder, "resize_sw_img"));
159 
160     /* deal with RTL environments */
161     gtk_widget_set_direction (GTK_WIDGET (gtk_builder_get_object (builder, "resize_bar")),
162                               GTK_TEXT_DIR_LTR);
163 
164     note->w_menu = GTK_WIDGET (gtk_builder_get_object (builder,
165                                                        "stickynote_menu"));
166     note->ta_lock_toggle_item =
167         GTK_TOGGLE_ACTION (gtk_builder_get_object (builder,
168                                                    "popup_toggle_lock"));
169 
170     note->w_properties =
171         GTK_WIDGET (gtk_builder_get_object (builder,
172                                             "stickynote_properties"));
173     gtk_window_set_screen (GTK_WINDOW (note->w_properties), screen);
174 
175     note->w_entry =
176         GTK_WIDGET (gtk_builder_get_object (builder, "title_entry"));
177     note->w_color =
178         GTK_WIDGET (gtk_builder_get_object (builder, "note_color"));
179     note->w_color_label =
180         GTK_WIDGET (gtk_builder_get_object (builder, "color_label"));
181     note->w_font_color =
182         GTK_WIDGET (gtk_builder_get_object (builder, "font_color"));
183     note->w_font_color_label =
184         GTK_WIDGET (gtk_builder_get_object (builder,
185                                             "font_color_label"));
186     note->w_font =
187         GTK_WIDGET (gtk_builder_get_object (builder, "note_font"));
188     note->w_font_label =
189         GTK_WIDGET (gtk_builder_get_object (builder, "font_label"));
190     note->w_def_color =
191         GTK_WIDGET (&GTK_CHECK_BUTTON (gtk_builder_get_object (builder,
192                                                                "def_color_check"))->toggle_button);
193     note->w_def_font =
194         GTK_WIDGET (&GTK_CHECK_BUTTON (gtk_builder_get_object (builder,
195                                                                "def_font_check"))->toggle_button);
196 
197     note->color = NULL;
198     note->font_color = NULL;
199     note->font = NULL;
200     note->locked = FALSE;
201     note->x = x;
202     note->y = y;
203     note->w = w;
204     note->h = h;
205 
206     /* Customize the window */
207     if (g_settings_get_boolean (stickynotes->settings, "sticky"))
208         gtk_window_stick (GTK_WINDOW (note->w_window));
209 
210     if (w == 0 || h == 0)
211         gtk_window_resize (GTK_WINDOW (note->w_window),
212                                        g_settings_get_int (stickynotes->settings,
213                                        "default-width"),
214                                        g_settings_get_int (stickynotes->settings,
215                                        "default-height"));
216     else
217         gtk_window_resize (GTK_WINDOW (note->w_window),
218                                        note->w,
219                                        note->h);
220 
221     if (x != -1 && y != -1)
222         gtk_window_move (GTK_WINDOW (note->w_window),
223                                      note->x,
224                                      note->y);
225 
226     /* Set the button images */
227     gtk_image_set_from_icon_name (note->img_close,
228                                   STICKYNOTES_STOCK_CLOSE,
229                                   GTK_ICON_SIZE_MENU);
230     gtk_image_set_pixel_size (note->img_close,
231                               STICKYNOTES_ICON_SIZE);
232 
233     gtk_image_set_from_icon_name (note->img_resize_se,
234                                   STICKYNOTES_STOCK_RESIZE_SE,
235                                   GTK_ICON_SIZE_MENU);
236     gtk_image_set_pixel_size (note->img_resize_se,
237                               STICKYNOTES_ICON_SIZE);
238 
239     gtk_image_set_from_icon_name (note->img_resize_sw,
240                                   STICKYNOTES_STOCK_RESIZE_SW,
241                                   GTK_ICON_SIZE_MENU);
242     gtk_image_set_pixel_size (note->img_resize_sw,
243                               STICKYNOTES_ICON_SIZE);
244 
245     gtk_widget_show (note->w_lock);
246     gtk_widget_show (note->w_close);
247     gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (builder,
248                                                          "resize_bar")));
249 
250     /* Customize the title and colors, hide and unlock */
251     stickynote_set_title (note, NULL);
252     stickynote_set_color (note, NULL, NULL, TRUE);
253     stickynote_set_font (note, NULL, TRUE);
254     stickynote_set_locked (note, FALSE);
255 
256     gtk_widget_realize (note->w_window);
257 
258     /* Connect a popup menu to all buttons and title */
259     /* GtkBuilder holds and drops the references to all the widgets it
260      * creates for as long as it exist (GtkBuilder). Hence in our callback
261      * we would have an invalid GtkMenu. We need to ref it.
262      */
263     g_object_ref (note->w_menu);
264 
265     g_signal_connect (note->w_window, "button-press-event",
266                       G_CALLBACK (stickynote_show_popup_menu),
267                       note->w_menu);
268 
269     g_signal_connect (note->w_lock, "button-press-event",
270                       G_CALLBACK (stickynote_show_popup_menu),
271                       note->w_menu);
272 
273     g_signal_connect (note->w_close, "button-press-event",
274                       G_CALLBACK (stickynote_show_popup_menu),
275                       note->w_menu);
276 
277     g_signal_connect (note->w_resize_se, "button-press-event",
278                       G_CALLBACK (stickynote_show_popup_menu),
279                       note->w_menu);
280 
281     g_signal_connect (note->w_resize_sw, "button-press-event",
282                       G_CALLBACK (stickynote_show_popup_menu),
283                       note->w_menu);
284 
285     /* Connect a properties dialog to the note */
286     gtk_window_set_transient_for (GTK_WINDOW (note->w_properties),
287                                   GTK_WINDOW (note->w_window));
288     gtk_dialog_set_default_response (GTK_DIALOG (note->w_properties),
289                                      GTK_RESPONSE_CLOSE);
290     g_signal_connect (note->w_properties, "response",
291                       G_CALLBACK (response_cb),
292                       note);
293 
294     /* Connect signals to the sticky note */
295     g_signal_connect (note->w_lock, "clicked",
296                       G_CALLBACK (stickynote_toggle_lock_cb),
297                       note);
298 
299     g_signal_connect (note->w_close, "clicked",
300                       G_CALLBACK (stickynote_close_cb),
301                       note);
302 
303     g_signal_connect (note->w_resize_se, "button-press-event",
304                       G_CALLBACK (stickynote_resize_cb),
305                       note);
306 
307     g_signal_connect (note->w_resize_sw, "button-press-event",
308                       G_CALLBACK (stickynote_resize_cb),
309                       note);
310 
311     g_signal_connect (note->w_window, "button-press-event",
312                       G_CALLBACK (stickynote_move_cb),
313                       note);
314 
315     g_signal_connect (note->w_window, "configure-event",
316                       G_CALLBACK (stickynote_configure_cb),
317                       note);
318 
319     g_signal_connect (note->w_window, "delete-event",
320                       G_CALLBACK (stickynote_delete_cb),
321                       note);
322 
323     g_signal_connect (gtk_builder_get_object (builder, "popup_create"), "activate",
324                       G_CALLBACK (popup_create_cb),
325                       note);
326 
327     g_signal_connect (gtk_builder_get_object (builder, "popup_destroy"), "activate",
328                       G_CALLBACK (popup_destroy_cb),
329                       note);
330 
331     g_signal_connect (gtk_builder_get_object (builder, "popup_toggle_lock"), "toggled",
332                       G_CALLBACK (popup_toggle_lock_cb),
333                       note);
334 
335     g_signal_connect (gtk_builder_get_object (builder, "popup_properties"), "activate",
336                       G_CALLBACK (popup_properties_cb),
337                       note);
338 
339     g_signal_connect_swapped (note->w_entry, "changed",
340                               G_CALLBACK (properties_apply_title_cb),
341                               note);
342 
343     g_signal_connect (note->w_color, "color-set",
344                       G_CALLBACK (properties_color_cb),
345                       note);
346 
347     g_signal_connect (note->w_font_color, "color-set",
348                       G_CALLBACK (properties_color_cb),
349                       note);
350 
351     g_signal_connect_swapped (note->w_def_color, "toggled",
352                               G_CALLBACK (properties_apply_color_cb),
353                               note);
354 
355     g_signal_connect (note->w_font, "font-set",
356                       G_CALLBACK (properties_font_cb),
357                       note);
358 
359     g_signal_connect_swapped (note->w_def_font, "toggled",
360                               G_CALLBACK (properties_apply_font_cb),
361                               note);
362 
363     g_signal_connect (note->w_entry, "activate",
364                       G_CALLBACK (properties_activate_cb),
365                       note);
366 
367     g_signal_connect (note->w_properties, "delete-event",
368                       G_CALLBACK (gtk_widget_hide),
369                       note);
370 
371     g_object_unref (builder);
372 
373     g_signal_connect_after (note->w_body, "button-press-event",
374                             G_CALLBACK (gtk_true),
375                             note);
376 
377     g_signal_connect (gtk_text_view_get_buffer (GTK_TEXT_VIEW (note->w_body)), "changed",
378                       G_CALLBACK (buffer_changed),
379                       note);
380 
381     return note;
382 }
383 
384 /* Create a new (empty) Sticky Note */
385 StickyNote *
stickynote_new(GdkScreen * screen)386 stickynote_new (GdkScreen *screen)
387 {
388     return stickynote_new_aux (screen, -1, -1, 0, 0);
389 }
390 
391 /* Destroy a Sticky Note */
stickynote_free(StickyNote * note)392 void stickynote_free (StickyNote *note)
393 {
394     gtk_widget_destroy (note->w_properties);
395     gtk_widget_destroy (note->w_menu);
396     gtk_widget_destroy (note->w_window);
397 
398     g_free (note->color);
399     g_free (note->font_color);
400     g_free (note->font);
401 
402     g_free (note);
403 }
404 
405 /* Change the sticky note title and color */
stickynote_change_properties(StickyNote * note)406 void stickynote_change_properties (StickyNote *note)
407 {
408     char *color_str = NULL;
409 
410     gtk_entry_set_text (GTK_ENTRY (note->w_entry),
411                         gtk_label_get_text (GTK_LABEL (note->w_title)));
412 
413     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (note->w_def_color),
414                                   note->color == NULL);
415 
416     if (note->color)
417         color_str = g_strdup (note->color);
418     else {
419         color_str = g_settings_get_string (stickynotes->settings,
420                                            "default-color");
421     }
422 
423     if (color_str) {
424         GdkRGBA color;
425         gdk_rgba_parse (&color, color_str);
426         gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (note->w_color),
427                                     &color);
428         g_free (color_str);
429     }
430 
431     if (note->font_color)
432         color_str = g_strdup (note->font_color);
433     else {
434         color_str = g_settings_get_string (stickynotes->settings,
435                                            "default-font-color");
436     }
437 
438     if (color_str) {
439         GdkRGBA font_color;
440         gdk_rgba_parse (&font_color, color_str);
441         gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (note->w_font_color),
442                                     &font_color);
443         g_free (color_str);
444     }
445 
446     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (note->w_def_font),
447                                   note->font == NULL);
448     if (note->font)
449         gtk_font_button_set_font_name (GTK_FONT_BUTTON (note->w_font),
450                                        note->font);
451 
452     gtk_widget_show (note->w_properties);
453 
454     stickynotes_save ();
455 }
456 
457 static void
response_cb(GtkWidget * dialog,gint id,gpointer data)458 response_cb (GtkWidget *dialog,
459              gint id,   gpointer data)
460 {
461     if (id == GTK_RESPONSE_HELP)
462         gtk_show_uri_on_window (GTK_WINDOW (dialog),
463                                 "help:mate-stickynotes-applet/stickynotes-settings-individual",
464                                 gtk_get_current_event_time (),
465                                 NULL);
466     else if (id == GTK_RESPONSE_CLOSE)
467         gtk_widget_hide (dialog);
468 }
469 
470 /* Check if a sticky note is empty */
471 gboolean
stickynote_get_empty(const StickyNote * note)472 stickynote_get_empty (const StickyNote *note)
473 {
474     return gtk_text_buffer_get_char_count (gtk_text_view_get_buffer (GTK_TEXT_VIEW (note->w_body))) == 0;
475 }
476 
477 /* Set the sticky note title */
478 void
stickynote_set_title(StickyNote * note,const gchar * title)479 stickynote_set_title (StickyNote  *note,
480                       const gchar *title)
481 {
482     /* If title is NULL, use the current date as the title. */
483     if (!title) {
484         GDateTime *now;
485         gchar *date_title;
486         gchar *date_format;
487 
488         date_format = g_settings_get_string (stickynotes->settings, "date-format");
489         if (!date_format)
490             date_format = g_strdup ("%x");
491 
492         now = g_date_time_new_now_local ();
493         date_title = g_date_time_format (now, date_format);
494 
495         gtk_window_set_title (GTK_WINDOW (note->w_window), date_title);
496         gtk_label_set_text (GTK_LABEL (note->w_title), date_title);
497 
498         g_date_time_unref (now);
499         g_free (date_title);
500         g_free (date_format);
501     }
502     else {
503         gtk_window_set_title (GTK_WINDOW (note->w_window), title);
504         gtk_label_set_text (GTK_LABEL (note->w_title), title);
505     }
506 }
507 
508 /* Set the sticky note color */
509 void
stickynote_set_color(StickyNote * note,const gchar * color_str,const gchar * font_color_str,gboolean save)510 stickynote_set_color (StickyNote  *note,
511                       const gchar *color_str,
512                       const gchar *font_color_str,
513                       gboolean     save)
514 {
515     char *color_str_actual, *font_color_str_actual;
516     gboolean force_default, use_system_color;
517 
518     if (save) {
519         if (note->color)
520             g_free (note->color);
521         if (note->font_color)
522             g_free (note->font_color);
523 
524         note->color = color_str ? g_strdup (color_str) : NULL;
525         note->font_color = font_color_str ? g_strdup (font_color_str) : NULL;
526 
527         gtk_widget_set_sensitive (note->w_color_label,
528                                   note->color != NULL);
529         gtk_widget_set_sensitive (note->w_font_color_label,
530                                   note->font_color != NULL);
531         gtk_widget_set_sensitive (note->w_color,
532                                   note->color != NULL);
533         gtk_widget_set_sensitive (note->w_font_color,
534                                   note->color != NULL);
535     }
536 
537     force_default = g_settings_get_boolean (stickynotes->settings,
538                                             "force-default");
539     use_system_color = g_settings_get_boolean (stickynotes->settings,
540                                                "use-system-color");
541 
542     /* If "force_default" is enabled or color_str is NULL,
543      * then we use the default color instead of color_str. */
544     if (!color_str || force_default) {
545         if (use_system_color)
546             color_str_actual = NULL;
547         else
548             color_str_actual = g_settings_get_string (stickynotes->settings,
549                                                       "default-color");
550     } else
551         color_str_actual = g_strdup (color_str);
552 
553     if (!font_color_str || force_default) {
554         if (use_system_color)
555             font_color_str_actual = NULL;
556         else
557             font_color_str_actual = g_settings_get_string (stickynotes->settings,
558                                                            "default-font-color");
559     } else
560         font_color_str_actual = g_strdup (font_color_str);
561 
562     /* Do not use custom colors if "use_system_color" is enabled */
563     if (color_str_actual) {
564         GdkRGBA colors[4];
565         gint i;
566 
567         for (i = 0; i <= 3; i++) {
568             gdk_rgba_parse (&colors[i], color_str_actual);
569             colors[i].red = (colors[i].red * (10 - i)) / 10;
570             colors[i].green = (colors[i].green * (10 - i)) / 10;
571             colors[i].blue = (colors[i].blue * (10 - i)) / 10;
572         }
573 
574         gtk_widget_override_background_color (note->w_window,
575                                               GTK_STATE_FLAG_NORMAL,
576                                               &colors[0]);
577         gtk_widget_override_background_color (note->w_body,
578                                               GTK_STATE_FLAG_NORMAL,
579                                               &colors[0]);
580         gtk_widget_override_background_color (note->w_lock,
581                                               GTK_STATE_FLAG_NORMAL,
582                                               &colors[0]);
583         gtk_widget_override_background_color (note->w_close,
584                                               GTK_STATE_FLAG_NORMAL,
585                                               &colors[0]);
586         gtk_widget_override_background_color (note->w_resize_se,
587                                               GTK_STATE_FLAG_NORMAL,
588                                               &colors[0]);
589         gtk_widget_override_background_color (note->w_resize_sw,
590                                               GTK_STATE_FLAG_NORMAL,
591                                               &colors[0]);
592     } else {
593         gtk_widget_override_background_color (note->w_window,
594                                               GTK_STATE_FLAG_NORMAL,
595                                               NULL);
596         gtk_widget_override_background_color (note->w_body,
597                                               GTK_STATE_FLAG_NORMAL,
598                                               NULL);
599         gtk_widget_override_background_color (note->w_lock,
600                                               GTK_STATE_FLAG_NORMAL,
601                                               NULL);
602         gtk_widget_override_background_color (note->w_close,
603                                               GTK_STATE_FLAG_NORMAL,
604                                               NULL);
605         gtk_widget_override_background_color (note->w_resize_se,
606                                               GTK_STATE_FLAG_NORMAL,
607                                               NULL);
608         gtk_widget_override_background_color (note->w_resize_sw,
609                                               GTK_STATE_FLAG_NORMAL,
610                                               NULL);
611     }
612 
613     if (font_color_str_actual) {
614         GdkRGBA color;
615 
616         gdk_rgba_parse (&color, font_color_str_actual);
617 
618         gtk_widget_override_color (note->w_window,
619                                    GTK_STATE_FLAG_NORMAL,
620                                    &color);
621         gtk_widget_override_color (note->w_body,
622                                    GTK_STATE_FLAG_NORMAL,
623                                    &color);
624     } else {
625         gtk_widget_override_color (note->w_window,
626                                    GTK_STATE_FLAG_NORMAL,
627                                    NULL);
628         gtk_widget_override_color (note->w_body,
629                                    GTK_STATE_FLAG_NORMAL,
630                                    NULL);
631     }
632 
633     if (color_str_actual)
634         g_free (color_str_actual);
635     if (font_color_str_actual)
636         g_free (font_color_str_actual);
637 }
638 
639 /* Set the sticky note font */
640 void
stickynote_set_font(StickyNote * note,const gchar * font_str,gboolean save)641 stickynote_set_font (StickyNote  *note,
642                      const gchar *font_str,
643                      gboolean     save)
644 {
645     PangoFontDescription *font_desc;
646     gchar *font_str_actual;
647 
648     if (save) {
649         g_free (note->font);
650         note->font = font_str ? g_strdup (font_str) : NULL;
651 
652         gtk_widget_set_sensitive (note->w_font_label,
653                                   note->font != NULL);
654         gtk_widget_set_sensitive (note->w_font,
655                                   note->font != NULL);
656     }
657 
658     /* If "force_default" is enabled or font_str is NULL,
659      * then we use the default font instead of font_str. */
660     if (!font_str || g_settings_get_boolean (stickynotes->settings,
661                                              "force-default")) {
662         if (g_settings_get_boolean (stickynotes->settings,
663                                     "use-system-font"))
664             font_str_actual = NULL;
665         else
666             font_str_actual = g_settings_get_string (stickynotes->settings,
667                                                      "default-font");
668     } else
669         font_str_actual = g_strdup (font_str);
670 
671     /* Do not use custom fonts if "use_system_font" is enabled */
672     font_desc = font_str_actual ?
673         pango_font_description_from_string (font_str_actual): NULL;
674 
675     /* Apply the style to the widgets */
676     gtk_widget_override_font (note->w_window, font_desc);
677     gtk_widget_override_font (note->w_body, font_desc);
678 
679     g_free (font_str_actual);
680     pango_font_description_free (font_desc);
681 }
682 
683 /* Lock/Unlock a sticky note from editing */
684 void
stickynote_set_locked(StickyNote * note,gboolean locked)685 stickynote_set_locked (StickyNote *note,
686                        gboolean    locked)
687 {
688     note->locked = locked;
689 
690     /* Set cursor visibility and editability */
691     gtk_text_view_set_editable (GTK_TEXT_VIEW (note->w_body),
692                                 !locked);
693     gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (note->w_body),
694                                       !locked);
695 
696     /* Show appropriate icon and tooltip */
697     if (locked) {
698         gtk_image_set_from_icon_name (note->img_lock,
699                                       STICKYNOTES_STOCK_LOCKED,
700                                       GTK_ICON_SIZE_MENU);
701         gtk_widget_set_tooltip_text (note->w_lock,
702                                      _("This note is locked."));
703     } else {
704         gtk_image_set_from_icon_name (note->img_lock,
705                                       STICKYNOTES_STOCK_UNLOCKED,
706                                       GTK_ICON_SIZE_MENU);
707         gtk_widget_set_tooltip_text (note->w_lock,
708                                      _("This note is unlocked."));
709     }
710 
711     gtk_image_set_pixel_size (note->img_lock,
712                               STICKYNOTES_ICON_SIZE);
713 
714     gtk_toggle_action_set_active (note->ta_lock_toggle_item,
715                                   locked);
716 
717     stickynotes_applet_update_menus ();
718 }
719 
720 /* Show/Hide a sticky note */
721 void
stickynote_set_visible(StickyNote * note,gboolean visible)722 stickynote_set_visible (StickyNote *note,
723                         gboolean    visible)
724 {
725     if (visible) {
726         gtk_window_present (GTK_WINDOW (note->w_window));
727 
728         if (note->x != -1 || note->y != -1)
729             gtk_window_move (GTK_WINDOW (note->w_window),
730                              note->x, note->y);
731 
732         /* Put the note on all workspaces if necessary. */
733         if (g_settings_get_boolean (stickynotes->settings, "sticky"))
734             gtk_window_stick (GTK_WINDOW (note->w_window));
735 
736         else if (note->workspace > 0)
737             xstuff_change_workspace (GTK_WINDOW (note->w_window),
738                                      note->workspace - 1);
739     } else {
740         /* Hide sticky note */
741         int x, y, width, height;
742 
743         stickynotes_applet_panel_icon_get_geometry (&x, &y,
744                                                     &width, &height);
745         set_icon_geometry (gtk_widget_get_window (GTK_WIDGET (note->w_window)),
746                            x, y, width, height);
747         gtk_window_iconify (GTK_WINDOW (note->w_window));
748     }
749 }
750 
751 /* Add a sticky note */
752 void
stickynotes_add(GdkScreen * screen)753 stickynotes_add (GdkScreen *screen)
754 {
755     StickyNote *note;
756 
757     note = stickynote_new (screen);
758 
759     stickynotes->notes = g_list_append (stickynotes->notes, note);
760     stickynotes_applet_update_tooltips ();
761     stickynotes_save ();
762     stickynote_set_visible (note, TRUE);
763 }
764 
765 /* Remove a sticky note with confirmation, if needed */
766 void
stickynotes_remove(StickyNote * note)767 stickynotes_remove (StickyNote *note)
768 {
769     GtkBuilder *builder;
770     GtkWidget  *dialog;
771 
772     builder = gtk_builder_new ();
773     gtk_builder_add_from_resource (builder,
774                                    GRESOURCE "sticky-notes-delete.ui",
775                                    NULL);
776 
777     dialog = GTK_WIDGET (gtk_builder_get_object (builder,
778                                                  "delete_dialog"));
779 
780     gtk_window_set_transient_for (GTK_WINDOW (dialog),
781                                   GTK_WINDOW (note->w_window));
782 
783     if (stickynote_get_empty (note)
784         || !g_settings_get_boolean (stickynotes->settings,
785                                     "confirm-deletion")
786         || gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
787 
788         /* Remove the note from the linked-list of all notes */
789         stickynotes->notes = g_list_remove_all (stickynotes->notes,
790                                                 note);
791 
792         /* Destroy the note */
793         stickynote_free (note);
794 
795         /* Update tooltips */
796         stickynotes_applet_update_tooltips ();
797 
798         /* Save notes */
799         stickynotes_save ();
800     }
801 
802     gtk_widget_destroy (dialog);
803     g_object_unref (builder);
804 }
805 
806 /* Save all sticky notes in an XML configuration file */
807 gboolean
stickynotes_save_now(void)808 stickynotes_save_now (void)
809 {
810     WnckScreen *wnck_screen;
811     const gchar *title;
812     GtkTextBuffer *buffer;
813     GtkTextIter start, end;
814     gchar *body;
815 
816     gint i;
817 
818     /* Create a new XML document */
819     xmlDocPtr doc = xmlNewDoc (XML_CHAR ("1.0"));
820     xmlNodePtr root = xmlNewDocNode (doc, NULL, XML_CHAR ("stickynotes"), NULL);
821 
822     xmlDocSetRootElement (doc, root);
823     xmlNewProp (root, XML_CHAR ("version"), XML_CHAR (VERSION));
824 
825     wnck_screen = wnck_screen_get_default ();
826     wnck_screen_force_update (wnck_screen);
827 
828     /* For all sticky notes */
829     for (i = 0; i < g_list_length (stickynotes->notes); i++) {
830         WnckWindow *wnck_win;
831         gulong xid = 0;
832 
833         /* Access the current note in the list */
834         StickyNote *note = g_list_nth_data (stickynotes->notes, i);
835 
836         /* Retrieve the window size of the note */
837         gchar *w_str = g_strdup_printf ("%d", note->w);
838         gchar *h_str = g_strdup_printf ("%d", note->h);
839 
840         /* Retrieve the window position of the note */
841         gchar *x_str = g_strdup_printf ("%d", note->x);
842         gchar *y_str = g_strdup_printf ("%d", note->y);
843 
844         xid = GDK_WINDOW_XID (gtk_widget_get_window (note->w_window));
845         wnck_win = wnck_window_get (xid);
846 
847         if (!g_settings_get_boolean (stickynotes->settings, "sticky") && wnck_win)
848             note->workspace = 1 +
849                 wnck_workspace_get_number (wnck_window_get_workspace (wnck_win));
850         else
851             note->workspace = 0;
852 
853         /* Retrieve the title of the note */
854         title = gtk_label_get_text (GTK_LABEL (note->w_title));
855 
856         /* Retrieve body contents of the note */
857         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (note->w_body));
858 
859         gtk_text_buffer_get_bounds (buffer, &start, &end);
860         body = gtk_text_iter_get_text (&start, &end);
861 
862         /* Save the note as a node in the XML document */
863         {
864             xmlNodePtr node = xmlNewTextChild (root, NULL,
865                                                XML_CHAR ("note"),
866                                                XML_CHAR (body));
867 
868             xmlNewProp (node, XML_CHAR ("title"),
869                         XML_CHAR (title));
870 
871             if (note->color)
872                 xmlNewProp (node, XML_CHAR ("color"),
873                             XML_CHAR (note->color));
874 
875             if (note->font_color)
876                 xmlNewProp (node, XML_CHAR ("font_color"),
877                             XML_CHAR (note->font_color));
878 
879             if (note->font)
880                 xmlNewProp (node, XML_CHAR ("font"),
881                             XML_CHAR (note->font));
882 
883             if (note->locked)
884                 xmlNewProp (node, XML_CHAR ("locked"),
885                             XML_CHAR ("true"));
886 
887             xmlNewProp (node, XML_CHAR ("x"), XML_CHAR (x_str));
888             xmlNewProp (node, XML_CHAR ("y"), XML_CHAR (y_str));
889             xmlNewProp (node, XML_CHAR ("w"), XML_CHAR (w_str));
890             xmlNewProp (node, XML_CHAR ("h"), XML_CHAR (h_str));
891             if (note->workspace > 0) {
892                 char *workspace_str;
893 
894                 workspace_str = g_strdup_printf ("%i", note->workspace);
895                 xmlNewProp (node, XML_CHAR ("workspace"), XML_CHAR (workspace_str));
896                 g_free (workspace_str);
897             }
898         }
899 
900         /* Now that it has been saved, reset the modified flag */
901         gtk_text_buffer_set_modified (buffer, FALSE);
902 
903         g_free (x_str);
904         g_free (y_str);
905         g_free (w_str);
906         g_free (h_str);
907         g_free (body);
908     }
909 
910     /* The XML file is $HOME/.config/mate/stickynotes-applet,
911        most probably */
912     {
913         gchar* path = g_build_filename (g_get_user_config_dir (),
914                                         "mate", NULL);
915         gchar* file = g_build_filename (path, "stickynotes-applet.xml",
916                                         NULL);
917         g_mkdir_with_parents (path, S_IRWXU);
918         g_free (path);
919 
920         xmlSaveFormatFile (file, doc, 1);
921 
922         g_free (file);
923     }
924 
925     xmlFreeDoc (doc);
926 
927     save_scheduled = FALSE;
928 
929     return FALSE;
930 }
931 
932 void
stickynotes_save(void)933 stickynotes_save (void)
934 {
935     /* If a save isn't already scheduled, save everything a minute from now. */
936     if (!save_scheduled) {
937         g_timeout_add_seconds (60, (GSourceFunc) stickynotes_save_now, NULL);
938         save_scheduled = TRUE;
939     }
940 }
941 
942 /* Load all sticky notes from an XML configuration file */
943 void
stickynotes_load(GdkScreen * screen)944 stickynotes_load (GdkScreen *screen)
945 {
946     xmlDocPtr doc = NULL;
947     xmlNodePtr root;
948     xmlNodePtr node;
949     /* WnckScreen *wnck_screen; */
950     GList *new_notes, *tmp1;  /* Lists of StickyNote*'s */
951     GList *new_nodes; /* Lists of xmlNodePtr's */
952     int x, y, w, h;
953 
954     /* The XML file is $HOME/.config/mate/stickynotes-applet, most probably */
955     gchar* file = g_build_filename (g_get_user_config_dir (),
956                                     "mate", "stickynotes-applet.xml", NULL);
957 
958     if (g_file_test (file, G_FILE_TEST_EXISTS)) {
959         /* load file */
960         doc = xmlParseFile (file);
961     } else {
962         /* old one */
963         g_free (file);
964 
965         file = g_build_filename (g_get_home_dir (),
966                                  ".mate2", "stickynotes_applet", NULL);
967 
968         if (g_file_test (file, G_FILE_TEST_EXISTS)) {
969             /* load file */
970             doc = xmlParseFile (file);
971         }
972     }
973     g_free (file);
974 
975     /* If the XML file does not exist, create a blank one */
976     if (!doc) {
977         stickynotes_save ();
978         return;
979     }
980 
981     /* If the XML file is corrupted/incorrect, create a blank one */
982     root = xmlDocGetRootElement (doc);
983     if (!root || xmlStrcmp (root->name, XML_CHAR ("stickynotes"))) {
984         xmlFreeDoc (doc);
985         stickynotes_save ();
986         return;
987     }
988 
989     node = root->xmlChildrenNode;
990 
991     /* For all children of the root node (ie all sticky notes) */
992     new_notes = NULL;
993     new_nodes = NULL;
994     while (node) {
995         if (!xmlStrcmp (node->name, (const xmlChar *) "note")) {
996             StickyNote *note;
997 
998             /* Retrieve and set the window size of the note */
999             {
1000                 gchar *w_str = (gchar *)xmlGetProp (node, XML_CHAR ("w"));
1001                 gchar *h_str = (gchar *)xmlGetProp (node, XML_CHAR ("h"));
1002                 if (w_str && h_str) {
1003                     w = atoi (w_str);
1004                     h = atoi (h_str);
1005                 } else {
1006                     w = 0;
1007                     h = 0;
1008                 }
1009 
1010                 g_free (w_str);
1011                 g_free (h_str);
1012             }
1013 
1014             /* Retrieve and set the window position of the note */
1015             {
1016                 gchar *x_str = (gchar *)xmlGetProp (node, XML_CHAR ("x"));
1017                 gchar *y_str = (gchar *)xmlGetProp (node, XML_CHAR ("y"));
1018 
1019                 if (x_str && y_str) {
1020                     x = atoi (x_str);
1021                     y = atoi (y_str);
1022                 } else {
1023                     x = -1;
1024                     y = -1;
1025                 }
1026 
1027                 g_free (x_str);
1028                 g_free (y_str);
1029             }
1030 
1031             /* Create a new note */
1032             note = stickynote_new_aux (screen, x, y, w, h);
1033             stickynotes->notes = g_list_append (stickynotes->notes,
1034                                                 note);
1035             new_notes = g_list_append (new_notes, note);
1036             new_nodes = g_list_append (new_nodes, node);
1037 
1038             /* Retrieve and set title of the note */
1039             {
1040                 gchar *title = (gchar *)xmlGetProp (node,
1041                                                     XML_CHAR ("title"));
1042                 if (title)
1043                     stickynote_set_title (note, title);
1044 
1045                 g_free (title);
1046             }
1047 
1048             /* Retrieve and set the color of the note */
1049             {
1050                 gchar *color_str;
1051                 gchar *font_color_str;
1052 
1053                 color_str = (gchar *)xmlGetProp (node, XML_CHAR ("color"));
1054                 font_color_str = (gchar *)xmlGetProp (node, XML_CHAR ("font_color"));
1055 
1056                 if (color_str || font_color_str)
1057                     stickynote_set_color (note,
1058                                           color_str,
1059                                           font_color_str,
1060                                           TRUE);
1061 
1062                 g_free (color_str);
1063                 g_free (font_color_str);
1064             }
1065 
1066             /* Retrieve and set the font of the note */
1067             {
1068                 gchar *font_str = (gchar *)xmlGetProp (node,
1069                                                        XML_CHAR ("font"));
1070                 if (font_str)
1071                     stickynote_set_font (note, font_str, TRUE);
1072 
1073                 g_free (font_str);
1074             }
1075 
1076             /* Retrieve the workspace */
1077             {
1078                 char *workspace_str;
1079 
1080                 workspace_str = (gchar *)xmlGetProp (node,
1081                                                      XML_CHAR ("workspace"));
1082                 if (workspace_str) {
1083                     note->workspace = atoi (workspace_str);
1084                     g_free (workspace_str);
1085                 }
1086             }
1087 
1088             /* Retrieve and set (if any) the body contents of the
1089              * note */
1090             {
1091                 gchar *body = (gchar *)xmlNodeListGetString (doc,
1092                                                              node->xmlChildrenNode,
1093                                                               1);
1094                 if (body) {
1095                     GtkTextBuffer *buffer;
1096                     GtkTextIter start, end;
1097 
1098                     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (note->w_body));
1099                     gtk_text_buffer_get_bounds (buffer,
1100                                                 &start, &end);
1101                     gtk_text_buffer_insert (buffer,
1102                                             &start, body, -1);
1103                 }
1104                 g_free (body);
1105             }
1106 
1107             /* Retrieve and set the locked state of the note,
1108              * by default unlocked */
1109             {
1110                 gchar *locked = (gchar *)xmlGetProp (node,
1111                                                      XML_CHAR ("locked"));
1112                 if (locked)
1113                     stickynote_set_locked (note,
1114                                            !strcmp (locked,
1115                                            "true"));
1116                 g_free (locked);
1117             }
1118         }
1119 
1120         node = node->next;
1121     }
1122 
1123     tmp1 = new_notes;
1124     /*
1125     wnck_screen = wnck_screen_get_default ();
1126     wnck_screen_force_update (wnck_screen);
1127     */
1128 
1129     while (tmp1) {
1130         StickyNote *note = tmp1->data;
1131 
1132         stickynote_set_visible (note, stickynotes->visible);
1133         tmp1 = tmp1->next;
1134     }
1135 
1136     g_list_free (new_notes);
1137     g_list_free (new_nodes);
1138 
1139     xmlFreeDoc (doc);
1140 }
1141