1 /*
2  * overviewprefs.c - This file is part of the Geany Overview plugin
3  *
4  * Copyright (c) 2015 Matthew Brush <mbrush@codebrainz.ca>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  *
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #include "overviewprefs.h"
28 #include "overviewcolor.h"
29 #include "overviewscintilla.h"
30 #include <string.h>
31 #include <stdarg.h>
32 
33 enum
34 {
35   PROP_0,
36   PROP_WIDTH,
37   PROP_ZOOM,
38   PROP_SHOW_TOOLTIP,
39   PROP_SHOW_SCROLLBAR,
40   PROP_DOUBLE_BUFFERED,
41   PROP_SCROLL_LINES,
42   PROP_OVERLAY_ENABLED,
43   PROP_OVERLAY_COLOR,
44   PROP_OVERLAY_OUTLINE_COLOR,
45   PROP_OVERLAY_INVERTED,
46   PROP_POSITION,
47   PROP_VISIBLE,
48   N_PROPERTIES
49 };
50 
51 struct OverviewPrefs_
52 {
53   GObject         parent;
54   guint           width;
55   gint            zoom;
56   gboolean        show_tt;
57   gboolean        show_sb;
58   gboolean        dbl_buf;
59   gint            scr_lines;
60   gboolean        ovl_en;
61   OverviewColor   ovl_clr;
62   OverviewColor   out_clr;
63   gboolean        ovl_inv;
64   GtkPositionType position;
65   gboolean        visible;
66 };
67 
68 struct OverviewPrefsClass_
69 {
70   GObjectClass parent_class;
71 };
72 
73 static void overview_prefs_finalize     (GObject      *object);
74 static void overview_prefs_set_property (GObject      *object,
75                                          guint         prop_id,
76                                          const GValue *value,
77                                          GParamSpec   *pspec);
78 static void overview_prefs_get_property (GObject      *object,
79                                          guint         prop_id,
80                                          GValue       *value,
81                                          GParamSpec   *pspec);
82 
83 
84 static GParamSpec *pspecs[N_PROPERTIES] = { NULL };
85 
G_DEFINE_TYPE(OverviewPrefs,overview_prefs,G_TYPE_OBJECT)86 G_DEFINE_TYPE (OverviewPrefs, overview_prefs, G_TYPE_OBJECT)
87 
88 static void
89 overview_prefs_class_init (OverviewPrefsClass *klass)
90 {
91   GObjectClass *g_object_class;
92 
93   g_object_class = G_OBJECT_CLASS (klass);
94 
95   g_object_class->finalize     = overview_prefs_finalize;
96   g_object_class->set_property = overview_prefs_set_property;
97   g_object_class->get_property = overview_prefs_get_property;
98 
99   pspecs[PROP_WIDTH] = g_param_spec_uint ("width", "Width", "Width of the overview", 16, 512, 120, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
100   pspecs[PROP_ZOOM] = g_param_spec_int ("zoom", "Zoom", "Zoom level of the view", -10, 20, -10, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
101   pspecs[PROP_SHOW_TOOLTIP] = g_param_spec_boolean ("show-tooltip", "ShowTooltip", "Whether to show informational tooltip over the overview", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
102   pspecs[PROP_SHOW_SCROLLBAR] = g_param_spec_boolean ("show-scrollbar", "ShowScrollbar", "Whether to show the normal editor scrollbar", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
103   pspecs[PROP_DOUBLE_BUFFERED] = g_param_spec_boolean ("double-buffered", "DoubleBuffered", "Whether the overview drawing is double-buffered", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
104   pspecs[PROP_SCROLL_LINES] = g_param_spec_uint ("scroll-lines", "ScrollLines", "The number of lines to scroll the overview by", 1, 512, 1, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
105   pspecs[PROP_OVERLAY_ENABLED] = g_param_spec_boolean ("overlay-enabled", "OverlayEnabled", "Whether an overlay is drawn overtop the overview", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
106   pspecs[PROP_OVERLAY_COLOR] = g_param_spec_boxed ("overlay-color", "OverlayColor", "The color of the overlay", OVERVIEW_TYPE_COLOR, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
107   pspecs[PROP_OVERLAY_OUTLINE_COLOR] = g_param_spec_boxed ("overlay-outline-color", "OverlayOutlineColor", "The color of the outlines drawn around the overlay", OVERVIEW_TYPE_COLOR, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
108   pspecs[PROP_OVERLAY_INVERTED] = g_param_spec_boolean ("overlay-inverted", "OverlayInverted", "Whether to invert the drawing of the overlay", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
109   pspecs[PROP_POSITION] = g_param_spec_enum ("position", "Position", "Where to draw the overview", GTK_TYPE_POSITION_TYPE, GTK_POS_RIGHT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
110   pspecs[PROP_VISIBLE] = g_param_spec_boolean ("visible", "Visible", "Whether the overview is shown", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
111 
112   g_object_class_install_properties (g_object_class, N_PROPERTIES, pspecs);
113 }
114 
115 static void
overview_prefs_finalize(GObject * object)116 overview_prefs_finalize (GObject *object)
117 {
118   g_return_if_fail (OVERVIEW_IS_PREFS (object));
119   G_OBJECT_CLASS (overview_prefs_parent_class)->finalize (object);
120 }
121 
122 static void
overview_prefs_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)123 overview_prefs_set_property (GObject      *object,
124                              guint         prop_id,
125                              const GValue *value,
126                              GParamSpec   *pspec)
127 {
128   OverviewPrefs *self = OVERVIEW_PREFS (object);
129 
130   switch (prop_id)
131     {
132     case PROP_WIDTH:
133       self->width = g_value_get_uint (value);
134       g_object_notify (object, "width");
135       break;
136     case PROP_ZOOM:
137       self->zoom = g_value_get_int (value);
138       g_object_notify (object, "zoom");
139       break;
140     case PROP_SHOW_TOOLTIP:
141       self->show_tt = g_value_get_boolean (value);
142       g_object_notify (object, "show-tooltip");
143       break;
144     case PROP_SHOW_SCROLLBAR:
145       self->show_sb = g_value_get_boolean (value);
146       g_object_notify (object, "show-scrollbar");
147       break;
148     case PROP_DOUBLE_BUFFERED:
149       self->dbl_buf = g_value_get_boolean (value);
150       g_object_notify (object, "double-buffered");
151       break;
152     case PROP_SCROLL_LINES:
153       self->scr_lines = g_value_get_uint (value);
154       g_object_notify (object, "scroll-lines");
155       break;
156     case PROP_OVERLAY_ENABLED:
157       self->ovl_en = g_value_get_boolean (value);
158       g_object_notify (object, "overlay-enabled");
159       break;
160     case PROP_OVERLAY_COLOR:
161       {
162         OverviewColor *src = g_value_get_boxed (value);
163         if (src != NULL)
164           memcpy (&self->ovl_clr, src, sizeof (OverviewColor));
165         g_object_notify (object, "overlay-color");
166         break;
167       }
168     case PROP_OVERLAY_OUTLINE_COLOR:
169       {
170         OverviewColor *src = g_value_get_boxed (value);
171         if (src != NULL)
172           memcpy (&self->out_clr, src, sizeof (OverviewColor));
173         g_object_notify (object, "overlay-outline-color");
174         break;
175       }
176     case PROP_OVERLAY_INVERTED:
177       self->ovl_inv = g_value_get_boolean (value);
178       g_object_notify (G_OBJECT (self), "overlay-inverted");
179       break;
180     case PROP_POSITION:
181       self->position = g_value_get_enum (value);
182       g_object_notify (G_OBJECT (self), "position");
183       break;
184     case PROP_VISIBLE:
185       self->visible = g_value_get_boolean (value);
186       g_object_notify (G_OBJECT (self), "visible");
187       break;
188     default:
189       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
190       break;
191     }
192 }
193 
194 static void
overview_prefs_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)195 overview_prefs_get_property (GObject      *object,
196                              guint         prop_id,
197                              GValue       *value,
198                              GParamSpec   *pspec)
199 {
200   OverviewPrefs *self = OVERVIEW_PREFS (object);
201 
202   switch (prop_id)
203     {
204     case PROP_WIDTH:
205       g_value_set_uint (value, self->width);
206       break;
207     case PROP_ZOOM:
208       g_value_set_int (value, self->zoom);
209       break;
210     case PROP_SHOW_TOOLTIP:
211       g_value_set_boolean (value, self->show_tt);
212       break;
213     case PROP_SHOW_SCROLLBAR:
214       g_value_set_boolean (value, self->show_sb);
215       break;
216     case PROP_DOUBLE_BUFFERED:
217       g_value_set_boolean (value, self->dbl_buf);
218       break;
219     case PROP_SCROLL_LINES:
220       g_value_set_uint (value, self->scr_lines);
221       break;
222     case PROP_OVERLAY_ENABLED:
223       g_value_set_boolean (value, self->ovl_en);
224       break;
225     case PROP_OVERLAY_COLOR:
226       g_value_set_boxed (value, &self->ovl_clr);
227       break;
228     case PROP_OVERLAY_OUTLINE_COLOR:
229       g_value_set_boxed (value, &self->out_clr);
230       break;
231     case PROP_OVERLAY_INVERTED:
232       g_value_set_boolean (value, self->ovl_inv);
233       break;
234     case PROP_POSITION:
235       g_value_set_enum (value, self->position);
236       break;
237     case PROP_VISIBLE:
238       g_value_set_boolean (value, self->visible);
239       break;
240     default:
241       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
242       break;
243     }
244 }
245 
246 static void
overview_prefs_init(OverviewPrefs * self)247 overview_prefs_init (OverviewPrefs *self)
248 {
249   self->position = GTK_POS_RIGHT;
250   self->visible  = TRUE;
251 }
252 
253 OverviewPrefs *
overview_prefs_new(void)254 overview_prefs_new (void)
255 {
256   return g_object_new (OVERVIEW_TYPE_PREFS, NULL);
257 }
258 
259 gboolean
overview_prefs_load(OverviewPrefs * self,const gchar * filename,GError ** error)260 overview_prefs_load (OverviewPrefs *self,
261                      const gchar   *filename,
262                      GError       **error)
263 {
264   gchar  *contents = NULL;
265   gsize   size = 0;
266   g_return_val_if_fail (OVERVIEW_IS_PREFS (self), FALSE);
267   g_return_val_if_fail (filename != NULL, FALSE);
268   if (! g_file_get_contents (filename, &contents, &size, error))
269     return FALSE;
270   if (! overview_prefs_from_data (self, contents, size, error))
271     {
272       g_free (contents);
273       return FALSE;
274     }
275   g_free (contents);
276   return TRUE;
277 }
278 
279 gboolean
overview_prefs_save(OverviewPrefs * self,const gchar * filename,GError ** error)280 overview_prefs_save (OverviewPrefs *self,
281                      const gchar   *filename,
282                      GError       **error)
283 
284 {
285   gchar *contents = NULL;
286   gsize  size = 0;
287   g_return_val_if_fail (OVERVIEW_IS_PREFS (self), FALSE);
288   g_return_val_if_fail (filename != NULL, FALSE);
289   contents = overview_prefs_to_data (self, &size, error);
290   if (contents == NULL)
291     return FALSE;
292   if (! g_file_set_contents (filename, contents, size, error))
293     {
294       g_free (contents);
295       return FALSE;
296     }
297   g_free (contents);
298   return TRUE;
299 }
300 
301 #define GET(T, k, v)                                     \
302   do {                                                   \
303     if (g_key_file_has_key (kf, "overview", k, NULL)) {  \
304       v = g_key_file_get_##T (kf, "overview", k, error); \
305       if (error != NULL && *error != NULL) {             \
306         g_key_file_free (kf);                            \
307         return FALSE;                                    \
308       } else {                                           \
309         g_object_notify (G_OBJECT (self), k);            \
310       }                                                  \
311     }                                                    \
312   } while (0)
313 
314 gboolean
overview_prefs_from_data(OverviewPrefs * self,const gchar * contents,gssize size,GError ** error)315 overview_prefs_from_data (OverviewPrefs *self,
316                           const gchar   *contents,
317                           gssize         size,
318                           GError       **error)
319 {
320   GKeyFile *kf;
321   gchar    *pos;
322 
323   g_return_val_if_fail (OVERVIEW_IS_PREFS (self), FALSE);
324   g_return_val_if_fail (contents != NULL, FALSE);
325 
326   kf = g_key_file_new ();
327 
328   if (! g_key_file_load_from_data (kf, contents, size,
329                                    G_KEY_FILE_KEEP_COMMENTS |
330                                      G_KEY_FILE_KEEP_TRANSLATIONS,
331                                   error))
332     {
333       g_key_file_free (kf);
334       return FALSE;
335     }
336 
337   GET (uint64,  "width",            self->width);
338   GET (integer, "zoom",             self->zoom);
339   GET (boolean, "show-tooltip",     self->show_tt);
340   GET (boolean, "show-scrollbar",   self->show_sb);
341   GET (boolean, "double-buffered",  self->dbl_buf);
342   GET (uint64,  "scroll-lines",     self->scr_lines);
343   GET (boolean, "overlay-enabled",  self->ovl_en);
344   GET (boolean, "overlay-inverted", self->ovl_inv);
345   GET (boolean, "visible",          self->visible);
346 
347   if (g_key_file_has_key (kf, "overview", "position", NULL))
348     {
349       pos = g_key_file_get_string (kf, "overview", "position", error);
350       if (error != NULL && *error != NULL)
351         return FALSE;
352       if (g_ascii_strcasecmp (pos, "right") == 0)
353         self->position = GTK_POS_RIGHT;
354       else if (g_ascii_strcasecmp (pos, "left") == 0)
355         self->position = GTK_POS_LEFT;
356       else
357         {
358           g_warning ("unknown value '%s' for 'position' key", pos);
359           self->position = GTK_POS_RIGHT;
360         }
361       g_free (pos);
362     }
363 
364   if (g_key_file_has_key (kf, "overview", "overlay-color", NULL) &&
365       g_key_file_has_key (kf, "overview", "overlay-alpha", NULL))
366     {
367       if (! overview_color_from_keyfile (&self->ovl_clr, kf, "overview", "overlay", error))
368         {
369           g_key_file_free (kf);
370           return FALSE;
371         }
372       g_object_notify (G_OBJECT (self), "overlay-color");
373     }
374 
375   if (g_key_file_has_key (kf, "overview", "overlay-outline-color", NULL) &&
376       g_key_file_has_key (kf, "overview", "overlay-outline-alpha", NULL))
377     {
378       if (! overview_color_from_keyfile (&self->out_clr, kf, "overview", "overlay-outline", error))
379         {
380           g_key_file_free (kf);
381           return FALSE;
382         }
383       g_object_notify (G_OBJECT (self), "overlay-outline-color");
384     }
385 
386   g_key_file_free (kf);
387 
388   return TRUE;
389 }
390 
391 #define SET(T, k, v) g_key_file_set_##T (kf, "overview", k, v)
392 
393 gchar *
overview_prefs_to_data(OverviewPrefs * self,gsize * size,GError ** error)394 overview_prefs_to_data (OverviewPrefs *self,
395                         gsize         *size,
396                         GError       **error)
397 {
398   GKeyFile *kf;
399   gchar    *contents;
400 
401   g_return_val_if_fail (OVERVIEW_IS_PREFS (self), NULL);
402 
403   kf = g_key_file_new ();
404 
405   SET (uint64,  "width",            self->width);
406   SET (integer, "zoom",             self->zoom);
407   SET (boolean, "show-tooltip",     self->show_tt);
408   SET (boolean, "show-scrollbar",   self->show_sb);
409   SET (boolean, "double-buffered",  self->dbl_buf);
410   SET (uint64,  "scroll-lines",     self->scr_lines);
411   SET (boolean, "overlay-enabled",  self->ovl_en);
412   SET (boolean, "overlay-inverted", self->ovl_inv);
413   SET (boolean, "visible",          self->visible);
414 
415   g_key_file_set_string (kf, "overview", "position",
416                          self->position == GTK_POS_LEFT ? "left" : "right");
417 
418   overview_color_to_keyfile (&self->ovl_clr, kf, "overview", "overlay");
419   overview_color_to_keyfile (&self->out_clr, kf, "overview", "overlay-outline");
420 
421   contents = g_key_file_to_data (kf, size, error);
422   g_key_file_free (kf);
423   return contents;
424 }
425 
426 #define BIND(prop) \
427   g_object_bind_property (self, prop, sci, prop, G_BINDING_SYNC_CREATE)
428 
429 void
overview_prefs_bind_scintilla(OverviewPrefs * self,GObject * sci)430 overview_prefs_bind_scintilla (OverviewPrefs *self,
431                                GObject       *sci)
432 {
433   g_return_if_fail (OVERVIEW_IS_PREFS (self));
434   g_return_if_fail (OVERVIEW_IS_SCINTILLA (sci));
435 
436   BIND ("width");
437   BIND ("zoom");
438   BIND ("show-tooltip");
439   BIND ("show-scrollbar");
440   BIND ("double-buffered");
441   BIND ("scroll-lines");
442   BIND ("overlay-enabled");
443   BIND ("overlay-color");
444   BIND ("overlay-outline-color");
445   BIND ("overlay-inverted");
446   BIND ("visible");
447 }
448