1 /*
2  * overviewscintilla.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 "overviewscintilla.h"
28 #include "overviewplugin.h"
29 #include <string.h>
30 
31 #define OVERVIEW_SCINTILLA_CURSOR        GDK_ARROW
32 #define OVERVIEW_SCINTILLA_CURSOR_CLICK  GDK_ARROW
33 #define OVERVIEW_SCINTILLA_CURSOR_SCROLL GDK_SB_V_DOUBLE_ARROW
34 #define OVERVIEW_SCINTILLA_ZOOM_MIN      -100
35 #define OVERVIEW_SCINTILLA_ZOOM_MAX      100
36 #define OVERVIEW_SCINTILLA_ZOOM_DEF      -20
37 #define OVERVIEW_SCINTILLA_WIDTH_MIN     16
38 #define OVERVIEW_SCINTILLA_WIDTH_MAX     512
39 #define OVERVIEW_SCINTILLA_WIDTH_DEF     120
40 #define OVERVIEW_SCINTILLA_SCROLL_LINES  1
41 
42 #ifndef SC_MAX_MARGIN
43 # define SC_MAX_MARGIN 4
44 #endif
45 
46 #define sci_send(sci, msg, wParam, lParam) \
47   scintilla_send_message (SCINTILLA (sci), SCI_##msg, (uptr_t)(wParam), (sptr_t)(lParam))
48 
49 static const OverviewColor def_overlay_color         = { 0.0, 0.0, 0.0, 0.25 };
50 static const OverviewColor def_overlay_outline_color = { 0.0, 0.0, 0.0, 0.0 };
51 
52 enum
53 {
54   PROP_0,
55   PROP_SCINTILLA,
56   PROP_CURSOR,
57   PROP_VISIBLE_RECT,
58   PROP_WIDTH,
59   PROP_ZOOM,
60   PROP_SHOW_TOOLTIP,
61   PROP_OVERLAY_ENABLED,
62   PROP_OVERLAY_COLOR,
63   PROP_OVERLAY_OUTLINE_COLOR,
64   PROP_OVERLAY_INVERTED,
65   PROP_DOUBLE_BUFFERED,
66   PROP_SCROLL_LINES,
67   PROP_SHOW_SCROLLBAR,
68   N_PROPERTIES,
69 };
70 
71 struct OverviewScintilla_
72 {
73   ScintillaObject  parent;
74   ScintillaObject *sci;             // source scintilla
75   GtkWidget       *canvas;          // internal GtkDrawingArea of scintilla
76   GdkCursorType    cursor;          // the chosen mouse cursor to use over the scintilla
77   GdkCursorType    active_cursor;   // the cursor to draw
78   GdkRectangle     visible_rect;    // visible region rectangle
79   guint            width;           // width of the overview
80   gint             zoom;            // zoom level of scintilla
81   gboolean         show_tooltip;    // whether tooltip shown on hover
82   gboolean         overlay_enabled; // whether the visible overlay is drawn
83   OverviewColor    overlay_color;   // the color of the visible overlay
84   OverviewColor    overlay_outline_color; // the color of the outline of the overlay
85   gboolean         overlay_inverted;// draw overlay over the visible area instead of around it
86   gboolean         double_buffered; // whether to enable double-buffering on internal scintilla canvas
87   gint             scroll_lines;    // number of lines to scroll each scroll-event
88   gboolean         show_scrollbar;  // show the main scintilla's scrollbar
89   gboolean         mouse_down;      // whether the mouse is down
90   gulong           update_rect;     // signal id of idle rect handler
91   gulong           conf_event;      // signal id of the configure event on scintilla internal drawing area
92   GtkWidget       *src_canvas;      // internal drawing area of main scintilla
93 };
94 
95 struct OverviewScintillaClass_
96 {
97   ScintillaClass parent_class;
98 };
99 
100 static GParamSpec *pspecs[N_PROPERTIES] = { NULL };
101 
102 static void overview_scintilla_finalize     (GObject           *object);
103 static void overview_scintilla_set_property (GObject           *object,
104                                              guint              prop_id,
105                                              const GValue      *value,
106                                              GParamSpec        *pspec);
107 static void overview_scintilla_get_property (GObject           *object,
108                                              guint              prop_id,
109                                              GValue            *value,
110                                              GParamSpec        *pspec);
111 static void overview_scintilla_set_src_sci  (OverviewScintilla *self,
112                                              ScintillaObject   *sci);
113 
114 #if GTK_CHECK_VERSION (3, 0, 0)
115 static gboolean overview_scintilla_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data);
116 #else
117 static gboolean overview_scintilla_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
118 #endif
119 
G_DEFINE_TYPE(OverviewScintilla,overview_scintilla,scintilla_get_type ())120 G_DEFINE_TYPE (OverviewScintilla, overview_scintilla, scintilla_get_type())
121 
122 static void
123 overview_scintilla_class_init (OverviewScintillaClass *klass)
124 {
125   GObjectClass   *g_object_class;
126 
127   g_object_class = G_OBJECT_CLASS (klass);
128 
129   g_object_class->finalize     = overview_scintilla_finalize;
130   g_object_class->set_property = overview_scintilla_set_property;
131   g_object_class->get_property = overview_scintilla_get_property;
132 
133   pspecs[PROP_SCINTILLA] =
134     g_param_spec_object ("scintilla",
135                          "Scintilla",
136                          "The source ScintillaObject",
137                          scintilla_get_type (),
138                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
139 
140   pspecs[PROP_CURSOR] =
141     g_param_spec_enum ("cursor",
142                        "Cursor",
143                        "The GdkCursorType to use for the mouse cursor",
144                        GDK_TYPE_CURSOR_TYPE,
145                        OVERVIEW_SCINTILLA_CURSOR,
146                        G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
147 
148   pspecs[PROP_VISIBLE_RECT] =
149     g_param_spec_boxed ("visible-rect",
150                        "VisibleRect",
151                        "The visible area indication rectangle to draw",
152                        GDK_TYPE_RECTANGLE,
153                        G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
154 
155   pspecs[PROP_WIDTH] =
156     g_param_spec_uint ("width",
157                        "Width",
158                        "Width of the overview",
159                        OVERVIEW_SCINTILLA_WIDTH_MIN,
160                        OVERVIEW_SCINTILLA_WIDTH_MAX,
161                        OVERVIEW_SCINTILLA_WIDTH_DEF,
162                        G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
163 
164   pspecs[PROP_ZOOM] =
165     g_param_spec_int ("zoom",
166                       "Zoom",
167                       "The zoom-level of the overview",
168                       OVERVIEW_SCINTILLA_ZOOM_MIN,
169                       OVERVIEW_SCINTILLA_ZOOM_MAX,
170                       OVERVIEW_SCINTILLA_ZOOM_DEF,
171                       G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
172 
173   pspecs[PROP_SHOW_TOOLTIP] =
174     g_param_spec_boolean ("show-tooltip",
175                           "ShowTooltip",
176                           "Whether to show a tooltip with addition info on mouse over",
177                           TRUE,
178                           G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
179 
180   pspecs[PROP_OVERLAY_ENABLED] =
181     g_param_spec_boolean ("overlay-enabled",
182                           "OverlayEnabled",
183                           "Whether an overlay is drawn ontop of the overview",
184                           TRUE,
185                           G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
186 
187   pspecs[PROP_OVERLAY_COLOR] =
188     g_param_spec_boxed ("overlay-color",
189                         "OverlayColor",
190                         "The color of the overlay, if enabled",
191                         OVERVIEW_TYPE_COLOR,
192                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
193 
194   pspecs[PROP_OVERLAY_OUTLINE_COLOR] =
195     g_param_spec_boxed ("overlay-outline-color",
196                         "OverlayOutlineColor",
197                         "The color of the overlay's outline, if enabled",
198                         OVERVIEW_TYPE_COLOR,
199                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
200 
201   pspecs[PROP_OVERLAY_INVERTED] =
202     g_param_spec_boolean ("overlay-inverted",
203                           "OverlayInverted",
204                           "Whether to draw the overlay over the visible area",
205                           TRUE,
206                           G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
207 
208   pspecs[PROP_DOUBLE_BUFFERED] =
209     g_param_spec_boolean ("double-buffered",
210                           "DoubleBuffered",
211                           "Whether the overview Scintilla's internal canvas is double-buffered",
212                           TRUE,
213                           G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
214 
215   pspecs[PROP_SCROLL_LINES] =
216     g_param_spec_int ("scroll-lines",
217                       "ScrollLines",
218                       "The number of lines to move each scroll, -1 for default, 0 to disable.",
219                       -1, 100, OVERVIEW_SCINTILLA_SCROLL_LINES,
220                       G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
221 
222   pspecs[PROP_SHOW_SCROLLBAR] =
223     g_param_spec_boolean ("show-scrollbar",
224                           "ShowScrollbar",
225                           "Whether to show the scrollbar in the main Scintilla",
226                           TRUE,
227                           G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
228 
229   g_object_class_install_properties (g_object_class, N_PROPERTIES, pspecs);
230 }
231 
232 static void
overview_scintilla_finalize(GObject * object)233 overview_scintilla_finalize (GObject *object)
234 {
235   OverviewScintilla *self;
236 
237   g_return_if_fail (OVERVIEW_IS_SCINTILLA (object));
238 
239   self = OVERVIEW_SCINTILLA (object);
240 
241   if (GTK_IS_WIDGET (self->src_canvas) && self->conf_event != 0)
242     g_signal_handler_disconnect (self->src_canvas, self->conf_event);
243 
244   g_object_unref (self->sci);
245 
246   G_OBJECT_CLASS (overview_scintilla_parent_class)->finalize (object);
247 }
248 
249 static inline void
cairo_set_source_overlay_color_(cairo_t * cr,const OverviewColor * color)250 cairo_set_source_overlay_color_ (cairo_t             *cr,
251                                  const OverviewColor *color)
252 {
253   cairo_set_source_rgba (cr, color->red, color->green, color->blue, color->alpha);
254 }
255 
256 static inline void
overview_scintilla_draw_real(OverviewScintilla * self,cairo_t * cr)257 overview_scintilla_draw_real (OverviewScintilla *self,
258                               cairo_t           *cr)
259 {
260   if (! self->overlay_enabled)
261     return;
262 
263   GtkAllocation alloc;
264 
265   gtk_widget_get_allocation (GTK_WIDGET (self->canvas), &alloc);
266 
267   cairo_save (cr);
268 
269   cairo_set_line_width (cr, 1.0);
270 
271 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
272   cairo_set_antialias (cr, CAIRO_ANTIALIAS_GOOD);
273 #endif
274 
275   if (self->overlay_inverted)
276     {
277       cairo_set_source_overlay_color_ (cr, &self->overlay_color);
278       cairo_rectangle (cr,
279                        0,
280                        self->visible_rect.y,
281                        alloc.width,
282                        self->visible_rect.height);
283       cairo_fill (cr);
284     }
285   else
286     {
287       // draw a rectangle at the top and bottom to obscure the non-visible area
288       cairo_set_source_overlay_color_ (cr, &self->overlay_color);
289       cairo_rectangle (cr, 0, 0, alloc.width, self->visible_rect.y);
290       cairo_rectangle (cr,
291                        0,
292                        self->visible_rect.y + self->visible_rect.height,
293                        alloc.width,
294                        alloc.height - (self->visible_rect.y + self->visible_rect.height));
295       cairo_fill (cr);
296     }
297 
298     // draw a highlighting border at top and bottom of visible rect
299     cairo_set_source_overlay_color_ (cr, &self->overlay_outline_color);
300     cairo_move_to (cr, self->visible_rect.x + 0.5, self->visible_rect.y + 0.5);
301     cairo_line_to (cr, self->visible_rect.width, self->visible_rect.y + 0.5);
302     cairo_move_to (cr, self->visible_rect.x + 0.5, self->visible_rect.y + 0.5 + self->visible_rect.height);
303     cairo_line_to (cr, self->visible_rect.width, self->visible_rect.y + 0.5 + self->visible_rect.height);
304     cairo_stroke (cr);
305 
306     // draw a left border if there's no scrollbar
307     if (! overview_scintilla_get_show_scrollbar (self))
308       {
309         cairo_move_to (cr, 0.5, 0.5);
310         cairo_line_to (cr, 0.5, alloc.height);
311         cairo_stroke (cr);
312       }
313 
314   cairo_restore (cr);
315 }
316 
317 #if GTK_CHECK_VERSION (3, 0, 0)
318 static gboolean
overview_scintilla_draw(GtkWidget * widget,cairo_t * cr,gpointer user_data)319 overview_scintilla_draw (GtkWidget *widget,
320                          cairo_t   *cr,
321                          gpointer   user_data)
322 {
323   overview_scintilla_draw_real (OVERVIEW_SCINTILLA (user_data), cr);
324   return FALSE;
325 }
326 #else
327 static gboolean
overview_scintilla_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)328 overview_scintilla_expose_event (GtkWidget      *widget,
329                                  GdkEventExpose *event,
330                                  gpointer        user_data)
331 {
332   cairo_t *cr;
333   cr = gdk_cairo_create (gtk_widget_get_window (widget));
334   overview_scintilla_draw_real (OVERVIEW_SCINTILLA (user_data), cr);
335   cairo_destroy (cr);
336   return FALSE;
337 }
338 #endif
339 
340 static gboolean
on_focus_in_event(OverviewScintilla * self,GdkEventFocus * event,ScintillaObject * sci)341 on_focus_in_event (OverviewScintilla *self,
342                    GdkEventFocus     *event,
343                    ScintillaObject   *sci)
344 {
345   sci_send (self, SETREADONLY, 1, 0);
346   return TRUE;
347 }
348 
349 static gboolean
on_focus_out_event(OverviewScintilla * self,GdkEventFocus * event,ScintillaObject * sci)350 on_focus_out_event (OverviewScintilla *self,
351                     GdkEventFocus     *event,
352                     ScintillaObject   *sci)
353 {
354   GeanyDocument *doc = g_object_get_data (G_OBJECT (sci), "document");
355   if (DOC_VALID (doc))
356     sci_send (self, SETREADONLY, doc->readonly, 0);
357   else
358     sci_send (self, SETREADONLY, 0, 0);
359   return TRUE;
360 }
361 
362 static gboolean
on_enter_notify_event(OverviewScintilla * self,GdkEventCrossing * event,ScintillaObject * sci)363 on_enter_notify_event (OverviewScintilla *self,
364                        GdkEventCrossing  *event,
365                        ScintillaObject   *sci)
366 {
367   return TRUE;
368 }
369 
370 static gboolean
on_leave_notify_event(OverviewScintilla * self,GdkEventCrossing * event,ScintillaObject * sci)371 on_leave_notify_event (OverviewScintilla *self,
372                        GdkEventCrossing  *event,
373                        ScintillaObject   *sci)
374 {
375   return TRUE;
376 }
377 
378 static void
on_each_child(GtkWidget * child,GList ** list)379 on_each_child (GtkWidget *child,
380                GList    **list)
381 {
382   *list = g_list_prepend (*list, child);
383 }
384 
385 static GList *
gtk_container_get_internal_children_(GtkContainer * cont)386 gtk_container_get_internal_children_ (GtkContainer *cont)
387 {
388   GList *list = NULL;
389   gtk_container_forall (cont, (GtkCallback) on_each_child, &list);
390   return g_list_reverse (list);
391 }
392 
393 static GtkWidget *
overview_scintilla_find_drawing_area(GtkWidget * root)394 overview_scintilla_find_drawing_area (GtkWidget *root)
395 {
396   GtkWidget *da = NULL;
397   if (GTK_IS_DRAWING_AREA (root))
398     da = root;
399   else if (GTK_IS_CONTAINER (root))
400     {
401       GList *children = gtk_container_get_internal_children_ (GTK_CONTAINER (root));
402       for (GList *iter = children; iter != NULL; iter = g_list_next (iter))
403         {
404           GtkWidget *wid = overview_scintilla_find_drawing_area (iter->data);
405           if (GTK_IS_DRAWING_AREA (wid))
406             {
407               da = wid;
408               break;
409             }
410         }
411       g_list_free (children);
412     }
413   return da;
414 }
415 
416 static gboolean
on_scroll_event(OverviewScintilla * self,GdkEventScroll * event,GtkWidget * widget)417 on_scroll_event (OverviewScintilla *self,
418                  GdkEventScroll    *event,
419                  GtkWidget         *widget)
420 {
421   gint delta = 0;
422 
423   if (self->scroll_lines == 0)
424     return TRUE;
425 
426   if (event->direction == GDK_SCROLL_UP)
427     delta = -(self->scroll_lines);
428   else if (event->direction == GDK_SCROLL_DOWN)
429     delta = self->scroll_lines;
430 
431   if (delta != 0)
432     sci_send (self->sci, LINESCROLL, 0, delta);
433 
434   return TRUE;
435 }
436 
437 static void
overview_scintilla_goto_point(OverviewScintilla * self,gint x,gint y)438 overview_scintilla_goto_point (OverviewScintilla *self,
439                                gint               x,
440                                gint               y)
441 {
442   gint pos;
443   pos = sci_send (self, POSITIONFROMPOINT, x, y);
444   if (pos >= 0)
445     sci_send (self->sci, GOTOPOS, pos, 0);
446 }
447 
448 static void
overview_scintilla_update_cursor(OverviewScintilla * self)449 overview_scintilla_update_cursor (OverviewScintilla *self)
450 {
451   if (GTK_IS_WIDGET (self->canvas) && gtk_widget_get_mapped (self->canvas))
452     {
453       GdkCursor *cursor = gdk_cursor_new (self->active_cursor);
454       gdk_window_set_cursor (gtk_widget_get_window (self->canvas), cursor);
455       gdk_cursor_unref (cursor);
456     }
457 }
458 
459 static gboolean
on_button_press_event(OverviewScintilla * self,GdkEventButton * event,GtkWidget * widget)460 on_button_press_event (OverviewScintilla *self,
461                        GdkEventButton    *event,
462                        GtkWidget         *widget)
463 {
464   self->mouse_down = TRUE;
465   self->active_cursor = OVERVIEW_SCINTILLA_CURSOR_CLICK;
466   overview_scintilla_update_cursor (self);
467   return TRUE;
468 }
469 
470 static gboolean
on_button_release_event(OverviewScintilla * self,GdkEventButton * event,GtkWidget * widget)471 on_button_release_event (OverviewScintilla *self,
472                          GdkEventButton    *event,
473                          GtkWidget         *widget)
474 {
475   self->mouse_down = FALSE;
476   self->active_cursor = OVERVIEW_SCINTILLA_CURSOR;
477   overview_scintilla_update_cursor (self);
478   overview_scintilla_goto_point (self, event->x, event->y);
479   return TRUE;
480 }
481 
482 static gboolean
on_motion_notify_event(OverviewScintilla * self,GdkEventMotion * event,GtkWidget * widget)483 on_motion_notify_event (OverviewScintilla *self,
484                         GdkEventMotion    *event,
485                         GtkWidget         *widget)
486 {
487   if (self->mouse_down)
488     {
489       if (self->active_cursor != OVERVIEW_SCINTILLA_CURSOR_SCROLL)
490         {
491           self->active_cursor = OVERVIEW_SCINTILLA_CURSOR_SCROLL;
492           overview_scintilla_update_cursor (self);
493         }
494       overview_scintilla_goto_point (self, event->x, event->y);
495     }
496   return TRUE;
497 }
498 
499 static gboolean
overview_scintilla_setup_tooltip(OverviewScintilla * self,gint x,gint y,GtkTooltip * tooltip)500 overview_scintilla_setup_tooltip (OverviewScintilla *self,
501                                   gint               x,
502                                   gint               y,
503                                   GtkTooltip        *tooltip)
504 {
505   gint pos;
506 
507   if (!self->show_tooltip)
508     return FALSE;
509 
510   pos = sci_send (self, POSITIONFROMPOINT, x, y);
511   if (pos >= 0)
512     {
513       gint line = sci_send (self, LINEFROMPOSITION, pos, 0);
514       gint column = sci_send (self, GETCOLUMN, pos, 0);
515       gchar *text =
516         g_strdup_printf (_("Line <b>%d</b>, Column <b>%d</b>, Position <b>%d</b>"),
517                          line, column, pos);
518       gtk_tooltip_set_markup (tooltip, text);
519       g_free (text);
520     }
521   else
522     gtk_tooltip_set_text (tooltip, NULL);
523 
524   return TRUE;
525 }
526 
527 static gboolean
on_query_tooltip(OverviewScintilla * self,gint x,gint y,gboolean kbd_mode,GtkTooltip * tooltip,GtkWidget * widget)528 on_query_tooltip (OverviewScintilla *self,
529                   gint               x,
530                   gint               y,
531                   gboolean           kbd_mode,
532                   GtkTooltip        *tooltip,
533                   GtkWidget         *widget)
534 {
535   if (!kbd_mode)
536     return overview_scintilla_setup_tooltip (self, x, y, tooltip);
537   return FALSE;
538 }
539 
540 static void
overview_scintilla_setup_canvas(OverviewScintilla * self)541 overview_scintilla_setup_canvas (OverviewScintilla *self)
542 {
543   if (!GTK_IS_WIDGET (self->canvas))
544     {
545       self->canvas = overview_scintilla_find_drawing_area (GTK_WIDGET (self));
546 
547       gtk_widget_add_events (self->canvas,
548                              GDK_EXPOSURE_MASK |
549                               GDK_BUTTON_PRESS_MASK |
550                               GDK_BUTTON_RELEASE_MASK |
551                               GDK_POINTER_MOTION_MASK |
552                               GDK_SCROLL_MASK);
553 
554       g_signal_connect_swapped (self->canvas,
555                                 "scroll-event",
556                                 G_CALLBACK (on_scroll_event),
557                                 self);
558       g_signal_connect_swapped (self->canvas,
559                                 "button-press-event",
560                                 G_CALLBACK (on_button_press_event),
561                                 self);
562       g_signal_connect_swapped (self->canvas,
563                                 "button-release-event",
564                                 G_CALLBACK (on_button_release_event),
565                                 self);
566       g_signal_connect_swapped (self->canvas,
567                                 "motion-notify-event",
568                                 G_CALLBACK (on_motion_notify_event),
569                                 self);
570       g_signal_connect_swapped (self->canvas,
571                                 "query-tooltip",
572                                 G_CALLBACK (on_query_tooltip),
573                                 self);
574 
575       gtk_widget_set_has_tooltip (self->canvas, self->show_tooltip);
576 
577 #if GTK_CHECK_VERSION (3, 0, 0)
578       g_signal_connect_after (self->canvas,
579                               "draw",
580                               G_CALLBACK (overview_scintilla_draw),
581                               self);
582 #else
583       g_signal_connect_after (self->canvas,
584                               "expose-event",
585                               G_CALLBACK (overview_scintilla_expose_event),
586                               self);
587 #endif
588 
589     }
590 }
591 
592 static void
overview_scintilla_update_rect(OverviewScintilla * self)593 overview_scintilla_update_rect (OverviewScintilla *self)
594 {
595   GtkAllocation alloc;
596   GdkRectangle  rect;
597   gint          first_line, n_lines, last_line;
598   gint          pos_start, pos_end;
599   gint          ystart, yend;
600 
601   gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
602 
603   first_line = sci_send (self->sci, GETFIRSTVISIBLELINE, 0, 0);
604   n_lines = sci_send (self->sci, LINESONSCREEN, 0, 0);
605   last_line = first_line + n_lines;
606 
607   pos_start = sci_send (self, POSITIONFROMLINE, first_line, 0);
608   pos_end = sci_send (self, POSITIONFROMLINE, last_line, 0);
609 
610   ystart = sci_send (self, POINTYFROMPOSITION, 0, pos_start);
611   yend = sci_send (self, POINTYFROMPOSITION, 0, pos_end);
612 
613   if (yend >= alloc.height || yend == 0)
614     {
615       n_lines = sci_send (self, GETLINECOUNT, 0, 0);
616       last_line = n_lines - 1;
617       pos_end = sci_send (self, POSITIONFROMLINE, last_line, 0);
618       yend = sci_send (self, POINTYFROMPOSITION, 0, pos_end);
619     }
620 
621   rect.x      = 0;
622   rect.width  = alloc.width - 1;
623   rect.y      = ystart;
624   rect.height = yend - ystart;
625 
626   overview_scintilla_set_visible_rect (self, &rect);
627 }
628 
629 static gint
sci_get_midline_(gpointer sci)630 sci_get_midline_ (gpointer sci)
631 {
632   gint first, count;
633   first = sci_send (sci, GETFIRSTVISIBLELINE, 0, 0);
634   count = sci_send (sci, LINESONSCREEN, 0, 0);
635   return first + (count / 2);
636 }
637 
638 static void
overview_scintilla_sync_center(OverviewScintilla * self)639 overview_scintilla_sync_center (OverviewScintilla *self)
640 {
641   gint mid_src = sci_get_midline_ (self->sci);
642   gint mid_dst = sci_get_midline_ (self);
643   gint delta = mid_src - mid_dst;
644   sci_send (self, LINESCROLL, 0, delta);
645   overview_scintilla_update_rect (self);
646 }
647 
648 static gboolean
on_map_event(OverviewScintilla * self,GdkEventAny * event,ScintillaObject * sci)649 on_map_event (OverviewScintilla *self,
650               GdkEventAny       *event,
651               ScintillaObject   *sci)
652 {
653   overview_scintilla_setup_canvas (self);
654 
655   if (GTK_IS_WIDGET (self->canvas) &&
656       gtk_widget_get_double_buffered (self->canvas) != self->double_buffered)
657     {
658       gtk_widget_set_double_buffered (self->canvas, self->double_buffered);
659       self->double_buffered = gtk_widget_get_double_buffered (self->canvas);
660     }
661 
662   overview_scintilla_update_cursor (self);
663   overview_scintilla_update_rect (self);
664 
665   return FALSE;
666 }
667 
668 #define self_connect(name, cb) \
669   g_signal_connect_swapped (self, name, G_CALLBACK (cb), self)
670 
671 static void
overview_scintilla_init(OverviewScintilla * self)672 overview_scintilla_init (OverviewScintilla *self)
673 {
674   self->sci             = NULL;
675   self->canvas          = NULL;
676   self->cursor          = OVERVIEW_SCINTILLA_CURSOR;
677   self->active_cursor   = OVERVIEW_SCINTILLA_CURSOR;
678   self->update_rect     = 0;
679   self->conf_event      = 0;
680   self->src_canvas      = NULL;
681   self->width           = OVERVIEW_SCINTILLA_WIDTH_DEF;
682   self->zoom            = OVERVIEW_SCINTILLA_ZOOM_DEF;
683   self->mouse_down      = FALSE;
684   self->show_tooltip    = TRUE;
685   self->double_buffered = TRUE;
686   self->scroll_lines    = OVERVIEW_SCINTILLA_SCROLL_LINES;
687   self->show_scrollbar  = TRUE;
688   self->overlay_inverted = TRUE;
689 
690   memset (&self->visible_rect, 0, sizeof (GdkRectangle));
691   memcpy (&self->overlay_color, &def_overlay_color, sizeof (OverviewColor));
692   memcpy (&self->overlay_outline_color, &def_overlay_outline_color, sizeof (OverviewColor));
693 
694   gtk_widget_add_events (GTK_WIDGET (self),
695                          GDK_EXPOSURE_MASK |
696                          GDK_FOCUS_CHANGE_MASK |
697                          GDK_ENTER_NOTIFY_MASK |
698                          GDK_STRUCTURE_MASK);
699 
700   self_connect ("focus-in-event", on_focus_in_event);
701   self_connect ("focus-out-event", on_focus_out_event);
702   self_connect ("enter-notify-event", on_enter_notify_event);
703   self_connect ("leave-notify-event", on_leave_notify_event);
704   self_connect ("map-event", on_map_event);
705 }
706 
707 static void
overview_scintilla_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)708 overview_scintilla_set_property (GObject      *object,
709                                  guint         prop_id,
710                                  const GValue *value,
711                                  GParamSpec   *pspec)
712 {
713   OverviewScintilla *self = OVERVIEW_SCINTILLA (object);
714 
715   switch (prop_id)
716     {
717     case PROP_SCINTILLA:
718       overview_scintilla_set_src_sci (self, g_value_get_object (value));
719       break;
720     case PROP_CURSOR:
721       overview_scintilla_set_cursor (self, g_value_get_enum (value));
722       break;
723     case PROP_VISIBLE_RECT:
724       overview_scintilla_set_visible_rect (self, g_value_get_boxed (value));
725       break;
726     case PROP_WIDTH:
727       overview_scintilla_set_width (self, g_value_get_uint (value));
728       break;
729     case PROP_ZOOM:
730       overview_scintilla_set_zoom (self, g_value_get_int (value));
731       break;
732     case PROP_SHOW_TOOLTIP:
733       overview_scintilla_set_show_tooltip (self, g_value_get_boolean (value));
734       break;
735     case PROP_OVERLAY_ENABLED:
736       overview_scintilla_set_overlay_enabled (self, g_value_get_boolean (value));
737       break;
738     case PROP_OVERLAY_COLOR:
739       overview_scintilla_set_overlay_color (self, g_value_get_boxed (value));
740       break;
741     case PROP_OVERLAY_OUTLINE_COLOR:
742       overview_scintilla_set_overlay_outline_color (self, g_value_get_boxed (value));
743       break;
744     case PROP_OVERLAY_INVERTED:
745       overview_scintilla_set_overlay_inverted (self, g_value_get_boolean (value));
746       break;
747     case PROP_DOUBLE_BUFFERED:
748       overview_scintilla_set_double_buffered (self, g_value_get_boolean (value));
749       break;
750     case PROP_SCROLL_LINES:
751       overview_scintilla_set_scroll_lines (self, g_value_get_int (value));
752       break;
753     case PROP_SHOW_SCROLLBAR:
754       overview_scintilla_set_show_scrollbar (self, g_value_get_boolean (value));
755       break;
756     default:
757       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
758       break;
759     }
760 }
761 
762 static void
overview_scintilla_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)763 overview_scintilla_get_property (GObject      *object,
764                                  guint         prop_id,
765                                  GValue       *value,
766                                  GParamSpec   *pspec)
767 {
768   OverviewScintilla *self = OVERVIEW_SCINTILLA (object);
769 
770   switch (prop_id)
771     {
772     case PROP_SCINTILLA:
773       g_value_set_object (value, self->sci);
774       break;
775     case PROP_CURSOR:
776       g_value_set_enum (value, overview_scintilla_get_cursor (self));
777       break;
778     case PROP_VISIBLE_RECT:
779       {
780         GdkRectangle rect;
781         overview_scintilla_get_visible_rect (self, &rect);
782         g_value_set_boxed (value, &rect);
783         break;
784       }
785     case PROP_WIDTH:
786       g_value_set_uint (value, overview_scintilla_get_width (self));
787       break;
788     case PROP_ZOOM:
789       g_value_set_int (value, overview_scintilla_get_zoom (self));
790       break;
791     case PROP_SHOW_TOOLTIP:
792       g_value_set_boolean (value, overview_scintilla_get_show_tooltip (self));
793       break;
794     case PROP_OVERLAY_ENABLED:
795       g_value_set_boolean (value, overview_scintilla_get_overlay_enabled (self));
796       break;
797     case PROP_OVERLAY_COLOR:
798       {
799         OverviewColor color = { 0 };
800         overview_scintilla_get_overlay_color (self, &color);
801         g_value_set_boxed (value, &color);
802         break;
803       }
804     case PROP_OVERLAY_OUTLINE_COLOR:
805       {
806         OverviewColor color = { 0 };
807         overview_scintilla_get_overlay_outline_color (self, &color);
808         g_value_set_boxed (value, &color);
809         break;
810       }
811     case PROP_OVERLAY_INVERTED:
812       g_value_set_boolean (value, overview_scintilla_get_overlay_inverted (self));
813       break;
814     case PROP_DOUBLE_BUFFERED:
815       g_value_set_boolean (value, overview_scintilla_get_double_buffered (self));
816       break;
817     case PROP_SCROLL_LINES:
818       g_value_set_int (value, overview_scintilla_get_scroll_lines (self));
819       break;
820     case PROP_SHOW_SCROLLBAR:
821       g_value_set_boolean (value, overview_scintilla_get_show_scrollbar (self));
822       break;
823     default:
824       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
825       break;
826     }
827 }
828 
829 GtkWidget *
overview_scintilla_new(ScintillaObject * src_sci)830 overview_scintilla_new (ScintillaObject *src_sci)
831 {
832   return g_object_new (OVERVIEW_TYPE_SCINTILLA, "scintilla", src_sci, NULL);
833 }
834 
835 static gchar *
sci_get_font(ScintillaObject * sci,gint style)836 sci_get_font (ScintillaObject *sci,
837               gint             style)
838 {
839   gsize  len   = sci_send (sci, STYLEGETFONT, style, 0);
840   gchar *name  = g_malloc0 (len + 1);
841   sci_send (sci, STYLEGETFONT, style, name);
842   return name;
843 }
844 
845 static gboolean
on_src_sci_configure_event(GtkWidget * widget,GdkEventConfigure * event,OverviewScintilla * self)846 on_src_sci_configure_event (GtkWidget         *widget,
847                             GdkEventConfigure *event,
848                             OverviewScintilla *self)
849 {
850   overview_scintilla_sync_center (self);
851   return FALSE;
852 }
853 
854 static gboolean
on_src_sci_map_event(ScintillaObject * sci,GdkEvent * event,OverviewScintilla * self)855 on_src_sci_map_event (ScintillaObject   *sci,
856                       GdkEvent          *event,
857                       OverviewScintilla *self)
858 {
859   if (self->conf_event == 0)
860     {
861       GtkWidget *internal;
862       internal = overview_scintilla_find_drawing_area (GTK_WIDGET (sci));
863       if (GTK_IS_DRAWING_AREA (internal))
864         {
865           self->src_canvas = internal;
866           self->conf_event = g_signal_connect (self->src_canvas,
867                                                "configure-event",
868                                                G_CALLBACK (on_src_sci_configure_event),
869                                                self);
870         }
871     }
872   return FALSE;
873 }
874 
875 static void
on_src_sci_notify(ScintillaObject * sci,gpointer unused,SCNotification * nt,OverviewScintilla * self)876 on_src_sci_notify (ScintillaObject   *sci,
877                    gpointer           unused,
878                    SCNotification    *nt,
879                    OverviewScintilla *self)
880 {
881   if (nt->nmhdr.code == SCN_UPDATEUI && nt->updated & SC_UPDATE_V_SCROLL)
882     {
883       overview_scintilla_sync_center (self);
884       if (GTK_IS_WIDGET (self->canvas))
885         gtk_widget_queue_draw (self->canvas);
886     }
887 }
888 
889 static void
overview_scintilla_clone_styles(OverviewScintilla * self)890 overview_scintilla_clone_styles (OverviewScintilla *self)
891 {
892   ScintillaObject *sci     = SCINTILLA (self);
893   ScintillaObject *src_sci = self->sci;
894 
895   for (gint i = 0; i < STYLE_MAX; i++)
896     {
897       gchar   *font_name = sci_get_font (src_sci, i);
898       gint     font_size = sci_send (src_sci, STYLEGETSIZE, i, 0);
899       gint     weight    = sci_send (src_sci, STYLEGETWEIGHT, i, 0);
900       gboolean italic    = sci_send (src_sci, STYLEGETITALIC, i, 0);
901       gint     fg_color  = sci_send (src_sci, STYLEGETFORE, i, 0);
902       gint     bg_color  = sci_send (src_sci, STYLEGETBACK, i, 0);
903 
904       sci_send (sci, STYLESETFONT, i, font_name);
905       sci_send (sci, STYLESETSIZE, i, font_size);
906       sci_send (sci, STYLESETWEIGHT, i, weight);
907       sci_send (sci, STYLESETITALIC, i, italic);
908       sci_send (sci, STYLESETFORE, i, fg_color);
909       sci_send (sci, STYLESETBACK, i, bg_color);
910       sci_send (sci, STYLESETCHANGEABLE, i, 0);
911 
912       g_free (font_name);
913     }
914 }
915 
916 static void
overview_scintilla_queue_draw(OverviewScintilla * self)917 overview_scintilla_queue_draw (OverviewScintilla *self)
918 {
919   if (GTK_IS_WIDGET (self->canvas))
920     gtk_widget_queue_draw (self->canvas);
921   else
922     gtk_widget_queue_draw (GTK_WIDGET (self));
923 }
924 
925 void
overview_scintilla_sync(OverviewScintilla * self)926 overview_scintilla_sync (OverviewScintilla *self)
927 {
928   sptr_t doc_ptr;
929 
930   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
931 
932   doc_ptr = sci_send (self->sci, GETDOCPOINTER, 0, 0);
933   sci_send (self, SETDOCPOINTER, 0, doc_ptr);
934 
935   overview_scintilla_clone_styles (self);
936 
937   for (gint i = 0; i < SC_MAX_MARGIN; i++)
938     sci_send (self, SETMARGINWIDTHN, i, 0);
939 
940   sci_send (self, SETVIEWEOL, 0, 0);
941   sci_send (self, SETVIEWWS, 0, 0);
942   sci_send (self, SETHSCROLLBAR, 0, 0);
943   sci_send (self, SETVSCROLLBAR, 0, 0);
944   sci_send (self, SETZOOM, self->zoom, 0);
945   sci_send (self, SETCURSOR, SC_CURSORARROW, 0);
946   sci_send (self, SETENDATLASTLINE, sci_send (self->sci, GETENDATLASTLINE, 0, 0), 0);
947   sci_send (self, SETMOUSEDOWNCAPTURES, 0, 0);
948   sci_send (self, SETCARETPERIOD, 0, 0);
949   sci_send (self, SETCARETWIDTH, 0, 0);
950   sci_send (self, SETEXTRAASCENT, 0, 0);
951   sci_send (self, SETEXTRADESCENT, 0, 0);
952 
953   sci_send (self->sci, SETVSCROLLBAR, self->show_scrollbar, 0);
954 
955   overview_scintilla_update_cursor (self);
956   overview_scintilla_update_rect (self);
957   overview_scintilla_sync_center (self);
958 
959   overview_scintilla_queue_draw (self);
960 }
961 
962 static void
overview_scintilla_set_src_sci(OverviewScintilla * self,ScintillaObject * sci)963 overview_scintilla_set_src_sci (OverviewScintilla *self,
964                                 ScintillaObject   *sci)
965 {
966 
967   g_assert (! IS_SCINTILLA (self->sci));
968 
969   self->sci = g_object_ref (sci);
970 
971   overview_scintilla_sync (self);
972   sci_send (self->sci, SETVSCROLLBAR, self->show_scrollbar, 0);
973 
974   gtk_widget_add_events (GTK_WIDGET (self->sci), GDK_STRUCTURE_MASK);
975   plugin_signal_connect (geany_plugin,
976                          G_OBJECT (self->sci),
977                          "map-event",
978                          TRUE,
979                          G_CALLBACK (on_src_sci_map_event),
980                          self);
981 
982   plugin_signal_connect (geany_plugin,
983                          G_OBJECT (self->sci),
984                          "sci-notify",
985                          TRUE,
986                          G_CALLBACK (on_src_sci_notify),
987                          self);
988 
989   g_object_notify (G_OBJECT (self), "scintilla");
990 }
991 
992 GdkCursorType
overview_scintilla_get_cursor(OverviewScintilla * self)993 overview_scintilla_get_cursor (OverviewScintilla *self)
994 {
995   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), GDK_ARROW);
996   return self->cursor;
997 }
998 
999 void
overview_scintilla_set_cursor(OverviewScintilla * self,GdkCursorType cursor_type)1000 overview_scintilla_set_cursor (OverviewScintilla *self,
1001                                GdkCursorType      cursor_type)
1002 {
1003   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1004   if (cursor_type != self->cursor)
1005     {
1006       self->cursor = cursor_type;
1007       self->active_cursor = cursor_type;
1008       overview_scintilla_update_cursor (self);
1009       g_object_notify (G_OBJECT (self), "cursor");
1010     }
1011 }
1012 
1013 void
overview_scintilla_get_visible_rect(OverviewScintilla * self,GdkRectangle * rect)1014 overview_scintilla_get_visible_rect (OverviewScintilla *self,
1015                                      GdkRectangle      *rect)
1016 {
1017   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1018   g_return_if_fail (rect != NULL);
1019 
1020   memcpy (rect, &self->visible_rect, sizeof (GdkRectangle));
1021 }
1022 
1023 void
overview_scintilla_set_visible_rect(OverviewScintilla * self,const GdkRectangle * rect)1024 overview_scintilla_set_visible_rect (OverviewScintilla  *self,
1025                                      const GdkRectangle *rect)
1026 {
1027   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1028 
1029   if (rect == NULL)
1030     {
1031       memset (&self->visible_rect, 0, sizeof (GdkRectangle));
1032       g_object_notify (G_OBJECT (self), "visible-rect");
1033       return;
1034     }
1035 
1036   if (rect->x != self->visible_rect.x ||
1037       rect->y != self->visible_rect.y ||
1038       rect->width != self->visible_rect.width ||
1039       rect->height != self->visible_rect.height)
1040     {
1041       memcpy (&self->visible_rect, rect, sizeof (GdkRectangle));
1042       if (GTK_IS_WIDGET (self->canvas))
1043         gtk_widget_queue_draw (self->canvas);
1044       g_object_notify (G_OBJECT (self), "visible-rect");
1045     }
1046 }
1047 
1048 guint
overview_scintilla_get_width(OverviewScintilla * self)1049 overview_scintilla_get_width (OverviewScintilla *self)
1050 {
1051   GtkAllocation alloc;
1052   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), 0);
1053   gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
1054   return alloc.width;
1055 }
1056 
1057 void
overview_scintilla_set_width(OverviewScintilla * self,guint width)1058 overview_scintilla_set_width (OverviewScintilla *self,
1059                               guint              width)
1060 {
1061   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1062   gtk_widget_set_size_request (GTK_WIDGET (self), width, -1);
1063 }
1064 
1065 gint
overview_scintilla_get_zoom(OverviewScintilla * self)1066 overview_scintilla_get_zoom (OverviewScintilla *self)
1067 {
1068   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), 0);
1069   self->zoom = sci_send (self, GETZOOM, 0, 0);
1070   return self->zoom;
1071 }
1072 
1073 void
overview_scintilla_set_zoom(OverviewScintilla * self,gint zoom)1074 overview_scintilla_set_zoom (OverviewScintilla *self,
1075                              gint               zoom)
1076 {
1077   gint old_zoom;
1078 
1079   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1080   g_return_if_fail (zoom >= OVERVIEW_SCINTILLA_ZOOM_MIN &&
1081                     zoom <= OVERVIEW_SCINTILLA_ZOOM_MAX);
1082 
1083   old_zoom = sci_send (self, GETZOOM, 0, 0);
1084   if (zoom != old_zoom)
1085     {
1086       sci_send (self, SETZOOM, zoom, 0);
1087       self->zoom = sci_send (self, GETZOOM, 0, 0);
1088       if (self->zoom != old_zoom)
1089         {
1090           overview_scintilla_sync_center (self);
1091           g_object_notify (G_OBJECT (self), "zoom");
1092         }
1093     }
1094 }
1095 
1096 gboolean
overview_scintilla_get_show_tooltip(OverviewScintilla * self)1097 overview_scintilla_get_show_tooltip (OverviewScintilla *self)
1098 {
1099   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), FALSE);
1100   return self->show_tooltip;
1101 }
1102 
1103 void
overview_scintilla_set_show_tooltip(OverviewScintilla * self,gboolean show)1104 overview_scintilla_set_show_tooltip (OverviewScintilla *self,
1105                                      gboolean           show)
1106 {
1107   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1108 
1109   if (show != self->show_tooltip)
1110     {
1111       self->show_tooltip = show;
1112       if (GTK_IS_WIDGET (self->canvas))
1113         gtk_widget_set_has_tooltip (self->canvas, self->show_tooltip);
1114       g_object_notify (G_OBJECT (self), "show-tooltip");
1115     }
1116 }
1117 
1118 gboolean
overview_scintilla_get_overlay_enabled(OverviewScintilla * self)1119 overview_scintilla_get_overlay_enabled (OverviewScintilla *self)
1120 {
1121   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), FALSE);
1122   return self->overlay_enabled;
1123 }
1124 
1125 void
overview_scintilla_set_overlay_enabled(OverviewScintilla * self,gboolean enabled)1126 overview_scintilla_set_overlay_enabled (OverviewScintilla *self,
1127                                         gboolean           enabled)
1128 {
1129   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1130 
1131   if (enabled != self->overlay_enabled)
1132     {
1133       self->overlay_enabled = enabled;
1134       if (GTK_IS_WIDGET (self->canvas))
1135         gtk_widget_queue_draw (self->canvas);
1136       g_object_notify (G_OBJECT (self), "overlay-enabled");
1137     }
1138 }
1139 
1140 void
overview_scintilla_get_overlay_color(OverviewScintilla * self,OverviewColor * color)1141 overview_scintilla_get_overlay_color (OverviewScintilla *self,
1142                                       OverviewColor     *color)
1143 {
1144   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1145   g_return_if_fail (color != NULL);
1146   memcpy (color, &self->overlay_color, sizeof (OverviewColor));
1147 }
1148 
1149 void
overview_scintilla_set_overlay_color(OverviewScintilla * self,const OverviewColor * color)1150 overview_scintilla_set_overlay_color (OverviewScintilla   *self,
1151                                       const OverviewColor *color)
1152 {
1153   gboolean changed = FALSE;
1154   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1155 
1156   if (color == NULL)
1157     {
1158       memcpy (&self->overlay_color, &def_overlay_color, sizeof (OverviewColor));
1159       changed = TRUE;
1160     }
1161   else if (!overview_color_equal (color, &self->overlay_color))
1162     {
1163       memcpy (&self->overlay_color, color, sizeof (OverviewColor));
1164       changed = TRUE;
1165     }
1166 
1167   if (changed)
1168     {
1169       if (GTK_IS_WIDGET (self->canvas))
1170         gtk_widget_queue_draw (self->canvas);
1171       g_object_notify (G_OBJECT (self), "overlay-color");
1172     }
1173 }
1174 
1175 void
overview_scintilla_get_overlay_outline_color(OverviewScintilla * self,OverviewColor * color)1176 overview_scintilla_get_overlay_outline_color (OverviewScintilla *self,
1177                                               OverviewColor     *color)
1178 {
1179   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1180   g_return_if_fail (color != NULL);
1181   memcpy (color, &self->overlay_outline_color, sizeof (OverviewColor));
1182 }
1183 
1184 void
overview_scintilla_set_overlay_outline_color(OverviewScintilla * self,const OverviewColor * color)1185 overview_scintilla_set_overlay_outline_color (OverviewScintilla   *self,
1186                                               const OverviewColor *color)
1187 {
1188   gboolean changed = FALSE;
1189   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1190 
1191   if (color == NULL)
1192     {
1193       memcpy (&self->overlay_outline_color, &def_overlay_outline_color, sizeof (OverviewColor));
1194       changed = TRUE;
1195     }
1196   else if (!overview_color_equal (color, &self->overlay_outline_color))
1197     {
1198       memcpy (&self->overlay_outline_color, color, sizeof (OverviewColor));
1199       changed = TRUE;
1200     }
1201 
1202   if (changed)
1203     {
1204       if (GTK_IS_WIDGET (self->canvas))
1205         gtk_widget_queue_draw (self->canvas);
1206       g_object_notify (G_OBJECT (self), "overlay-outline-color");
1207     }
1208 }
1209 
1210 gboolean
overview_scintilla_get_overlay_inverted(OverviewScintilla * self)1211 overview_scintilla_get_overlay_inverted (OverviewScintilla *self)
1212 {
1213   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), FALSE);
1214   return self->overlay_inverted;
1215 }
1216 
1217 void
overview_scintilla_set_overlay_inverted(OverviewScintilla * self,gboolean inverted)1218 overview_scintilla_set_overlay_inverted (OverviewScintilla *self,
1219                                          gboolean           inverted)
1220 {
1221   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1222 
1223   if (inverted != self->overlay_inverted)
1224     {
1225       self->overlay_inverted = inverted;
1226       overview_scintilla_queue_draw (self);
1227       g_object_notify (G_OBJECT (self), "overlay-inverted");
1228     }
1229 }
1230 
1231 gboolean
overview_scintilla_get_double_buffered(OverviewScintilla * self)1232 overview_scintilla_get_double_buffered (OverviewScintilla *self)
1233 {
1234   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), FALSE);
1235   if (GTK_IS_WIDGET (self->canvas))
1236     self->double_buffered = gtk_widget_get_double_buffered (self->canvas);
1237   return self->double_buffered;
1238 }
1239 
1240 void
overview_scintilla_set_double_buffered(OverviewScintilla * self,gboolean enabled)1241 overview_scintilla_set_double_buffered (OverviewScintilla *self,
1242                                         gboolean           enabled)
1243 {
1244   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1245 
1246   if (enabled != self->double_buffered)
1247     {
1248       self->double_buffered = enabled;
1249       if (GTK_IS_WIDGET (self->canvas))
1250         {
1251           gtk_widget_set_double_buffered (self->canvas, self->double_buffered);
1252           self->double_buffered = gtk_widget_get_double_buffered (self->canvas);
1253         }
1254       if (self->double_buffered == enabled)
1255         g_object_notify (G_OBJECT (self), "double-buffered");
1256     }
1257 }
1258 
1259 gint
overview_scintilla_get_scroll_lines(OverviewScintilla * self)1260 overview_scintilla_get_scroll_lines (OverviewScintilla *self)
1261 {
1262   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), -1);
1263   return self->scroll_lines;
1264 }
1265 
1266 void
overview_scintilla_set_scroll_lines(OverviewScintilla * self,gint lines)1267 overview_scintilla_set_scroll_lines (OverviewScintilla *self,
1268                                      gint               lines)
1269 {
1270   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1271 
1272   if (lines < 0)
1273     lines = OVERVIEW_SCINTILLA_SCROLL_LINES;
1274 
1275   if (lines != self->scroll_lines)
1276     {
1277       self->scroll_lines = lines;
1278       g_object_notify (G_OBJECT (self), "scroll-lines");
1279     }
1280 }
1281 
1282 gboolean
overview_scintilla_get_show_scrollbar(OverviewScintilla * self)1283 overview_scintilla_get_show_scrollbar (OverviewScintilla *self)
1284 {
1285   g_return_val_if_fail (OVERVIEW_IS_SCINTILLA (self), FALSE);
1286   return self->show_scrollbar;
1287 }
1288 
1289 void
overview_scintilla_set_show_scrollbar(OverviewScintilla * self,gboolean show)1290 overview_scintilla_set_show_scrollbar (OverviewScintilla *self,
1291                                        gboolean           show)
1292 {
1293   g_return_if_fail (OVERVIEW_IS_SCINTILLA (self));
1294 
1295   if (show != self->show_scrollbar)
1296     {
1297       self->show_scrollbar = show;
1298       sci_send (self->sci, SETVSCROLLBAR, self->show_scrollbar, 0);
1299       gtk_widget_queue_draw (GTK_WIDGET (self->sci));
1300       g_object_notify (G_OBJECT (self), "show-scrollbar");
1301     }
1302 }
1303