1 #include <config.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <gtk/gtk.h>
5 #include <gdk/gdkkeysyms.h>
6 #include <libpeas/peas-extension-set.h>
7 #include <glib/gi18n.h>
8 
9 #include "xed-view.h"
10 #include "xed-view-gutter-renderer.h"
11 #include "xed-view-activatable.h"
12 #include "xed-plugins-engine.h"
13 #include "xed-debug.h"
14 #include "xed-marshal.h"
15 #include "xed-utils.h"
16 #include "xed-settings.h"
17 #include "xed-app.h"
18 
19 #define XED_VIEW_SCROLL_MARGIN 0.02
20 
21 enum
22 {
23     TARGET_URI_LIST = 100
24 };
25 
26 struct _XedViewPrivate
27 {
28     GSettings *editor_settings;
29     GtkTextBuffer *current_buffer;
30     PeasExtensionSet *extensions;
31     GtkSourceGutterRenderer *renderer;
32     guint view_realized : 1;
33 };
34 
35 G_DEFINE_TYPE_WITH_PRIVATE (XedView, xed_view, GTK_SOURCE_TYPE_VIEW)
36 
37 /* Signals */
38 enum
39 {
40     DROP_URIS,
41     LAST_SIGNAL
42 };
43 
44 static guint view_signals[LAST_SIGNAL] = { 0 };
45 
46 static void
document_read_only_notify_handler(XedDocument * document,GParamSpec * pspec,XedView * view)47 document_read_only_notify_handler (XedDocument *document,
48                                    GParamSpec *pspec,
49                                    XedView *view)
50 {
51     xed_debug (DEBUG_VIEW);
52     gtk_text_view_set_editable (GTK_TEXT_VIEW (view), !xed_document_get_readonly (document));
53 }
54 
55 static void
current_buffer_removed(XedView * view)56 current_buffer_removed (XedView *view)
57 {
58     if (view->priv->current_buffer != NULL)
59     {
60         g_signal_handlers_disconnect_by_func(view->priv->current_buffer, document_read_only_notify_handler, view);
61         g_object_unref (view->priv->current_buffer);
62         view->priv->current_buffer = NULL;
63     }
64 }
65 
66 static void
extension_added(PeasExtensionSet * extensions,PeasPluginInfo * info,PeasExtension * exten,XedView * view)67 extension_added (PeasExtensionSet *extensions,
68                  PeasPluginInfo   *info,
69                  PeasExtension    *exten,
70                  XedView          *view)
71 {
72     peas_extension_call (exten, "activate");
73 }
74 
75 static void
extension_removed(PeasExtensionSet * extensions,PeasPluginInfo * info,PeasExtension * exten,XedView * view)76 extension_removed (PeasExtensionSet *extensions,
77                    PeasPluginInfo   *info,
78                    PeasExtension    *exten,
79                    XedView          *view)
80 {
81     peas_extension_call (exten, "deactivate");
82 }
83 
84 static void
on_notify_buffer_cb(XedView * view,GParamSpec * arg1,gpointer userdata)85 on_notify_buffer_cb (XedView *view,
86                      GParamSpec *arg1,
87                      gpointer userdata)
88 {
89     GtkTextBuffer *buffer;
90 
91     current_buffer_removed (view);
92     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
93 
94     if (buffer == NULL || !XED_IS_DOCUMENT (buffer))
95     {
96         return;
97     }
98 
99     view->priv->current_buffer = g_object_ref (buffer);
100     g_signal_connect(buffer, "notify::read-only", G_CALLBACK (document_read_only_notify_handler), view);
101 
102     gtk_text_view_set_editable (GTK_TEXT_VIEW (view), !xed_document_get_readonly (XED_DOCUMENT(buffer)));
103 }
104 
105 static void
xed_view_init(XedView * view)106 xed_view_init (XedView *view)
107 {
108     GtkTargetList *tl;
109 
110     xed_debug (DEBUG_VIEW);
111 
112     view->priv = xed_view_get_instance_private (view);
113 
114     view->priv->editor_settings = g_settings_new ("org.x.editor.preferences.editor");
115 
116     /* Drag and drop support */
117     tl = gtk_drag_dest_get_target_list (GTK_WIDGET(view));
118 
119     if (tl != NULL)
120     {
121         gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST);
122     }
123 
124     view->priv->extensions = peas_extension_set_new (PEAS_ENGINE (xed_plugins_engine_get_default ()),
125                                                      XED_TYPE_VIEW_ACTIVATABLE, "view", view, NULL);
126 
127     g_signal_connect (view->priv->extensions, "extension-added",
128                       G_CALLBACK (extension_added), view);
129     g_signal_connect (view->priv->extensions, "extension-removed",
130                       G_CALLBACK (extension_removed), view);
131 
132     /* Act on buffer change */
133     g_signal_connect(view, "notify::buffer", G_CALLBACK (on_notify_buffer_cb), NULL);
134 }
135 
136 static void
xed_view_dispose(GObject * object)137 xed_view_dispose (GObject *object)
138 {
139     XedView *view = XED_VIEW (object);
140 
141     g_clear_object (&view->priv->extensions);
142     g_clear_object (&view->priv->editor_settings);
143     g_clear_object (&view->priv->renderer);
144 
145     current_buffer_removed (view);
146 
147     /* Disconnect notify buffer because the destroy of the textview will set
148      * the buffer to NULL, and we call get_buffer in the notify which would
149      * reinstate a buffer which we don't want.
150      * There is no problem calling g_signal_handlers_disconnect_by_func()
151      * several times (if dispose() is called several times).
152      */
153     g_signal_handlers_disconnect_by_func (view, on_notify_buffer_cb, NULL);
154 
155     G_OBJECT_CLASS (xed_view_parent_class)->dispose (object);
156 }
157 
158 static void
xed_view_constructed(GObject * object)159 xed_view_constructed (GObject *object)
160 {
161     XedView *view;
162     XedViewPrivate *priv;
163     gboolean use_default_font;
164     GtkSourceGutter *gutter;
165     gboolean draw_whitespace;
166 
167     view = XED_VIEW (object);
168     priv = view->priv;
169 
170     /* Get setting values */
171     use_default_font = g_settings_get_boolean (view->priv->editor_settings, XED_SETTINGS_USE_DEFAULT_FONT);
172 
173     /*
174      *  Set tab, fonts, wrap mode, colors, etc. according to preferences
175      */
176     if (!use_default_font)
177     {
178         gchar *editor_font;
179 
180         editor_font = g_settings_get_string (view->priv->editor_settings, XED_SETTINGS_EDITOR_FONT);
181 
182         xed_view_set_font (view, FALSE, editor_font);
183 
184         g_free (editor_font);
185     }
186     else
187     {
188         xed_view_set_font (view, TRUE, NULL);
189     }
190 
191     g_settings_bind (priv->editor_settings,
192                      XED_SETTINGS_DISPLAY_LINE_NUMBERS,
193                      view,
194                      "show-line-numbers",
195                      G_SETTINGS_BIND_GET);
196 
197     g_settings_bind (priv->editor_settings,
198                      XED_SETTINGS_AUTO_INDENT,
199                      view,
200                      "auto-indent",
201                      G_SETTINGS_BIND_GET);
202 
203     g_settings_bind (priv->editor_settings,
204                      XED_SETTINGS_TABS_SIZE,
205                      view,
206                      "tab-width",
207                      G_SETTINGS_BIND_GET);
208 
209     g_settings_bind (priv->editor_settings,
210                      XED_SETTINGS_INSERT_SPACES,
211                      view,
212                      "insert-spaces-instead-of-tabs",
213                      G_SETTINGS_BIND_GET);
214 
215     g_settings_bind (priv->editor_settings,
216                      XED_SETTINGS_DISPLAY_RIGHT_MARGIN,
217                      view,
218                      "show-right-margin",
219                      G_SETTINGS_BIND_GET);
220 
221     g_settings_bind (priv->editor_settings,
222                      XED_SETTINGS_RIGHT_MARGIN_POSITION,
223                      view,
224                      "right-margin-position",
225                      G_SETTINGS_BIND_GET);
226 
227     g_settings_bind (priv->editor_settings,
228                      XED_SETTINGS_HIGHLIGHT_CURRENT_LINE,
229                      view,
230                      "highlight-current-line",
231                      G_SETTINGS_BIND_GET);
232 
233     g_settings_bind (priv->editor_settings,
234                      XED_SETTINGS_WRAP_MODE,
235                      view,
236                      "wrap-mode",
237                      G_SETTINGS_BIND_GET);
238 
239     g_settings_bind (priv->editor_settings,
240                      XED_SETTINGS_SMART_HOME_END,
241                      view,
242                      "smart-home-end",
243                      G_SETTINGS_BIND_GET);
244 
245     draw_whitespace = g_settings_get_boolean (priv->editor_settings, XED_SETTINGS_DRAW_WHITESPACE);
246 
247     if (draw_whitespace)
248     {
249         xed_view_set_draw_whitespace (view, draw_whitespace);
250     }
251 
252     xed_view_update_draw_whitespace_locations_and_types (view);
253 
254     g_object_set (G_OBJECT (view),
255                   "indent_on_tab", TRUE,
256                   NULL);
257 
258     gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (view), GTK_TEXT_WINDOW_LEFT);
259     priv->renderer = g_object_new (XED_TYPE_VIEW_GUTTER_RENDERER,
260                                    "size", 2,
261                                    NULL);
262     g_object_ref (priv->renderer);
263     gtk_source_gutter_insert (gutter, priv->renderer, 0);
264 
265 #if GTK_CHECK_VERSION (3, 18, 0)
266     gtk_text_view_set_top_margin (GTK_TEXT_VIEW (view), 2);
267 #endif
268 
269     G_OBJECT_CLASS (xed_view_parent_class)->constructed (object);
270 }
271 
272 static gint
xed_view_focus_out(GtkWidget * widget,GdkEventFocus * event)273 xed_view_focus_out (GtkWidget     *widget,
274                     GdkEventFocus *event)
275 {
276     gtk_widget_queue_draw (widget);
277 
278     GTK_WIDGET_CLASS (xed_view_parent_class)->focus_out_event (widget, event);
279 
280     return FALSE;
281 }
282 
283 static GdkAtom
drag_get_uri_target(GtkWidget * widget,GdkDragContext * context)284 drag_get_uri_target (GtkWidget      *widget,
285                      GdkDragContext *context)
286 {
287     GdkAtom target;
288     GtkTargetList *tl;
289 
290     tl = gtk_target_list_new (NULL, 0);
291     gtk_target_list_add_uri_targets (tl, 0);
292 
293     target = gtk_drag_dest_find_target (widget, context, tl);
294     gtk_target_list_unref (tl);
295 
296     return target;
297 }
298 
299 static gboolean
xed_view_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint timestamp)300 xed_view_drag_motion (GtkWidget      *widget,
301                       GdkDragContext *context,
302                       gint            x,
303                       gint            y,
304                       guint           timestamp)
305 {
306     gboolean result;
307 
308     /* Chain up to allow textview to scroll and position dnd mark, note
309      * that this needs to be checked if gtksourceview or gtktextview
310      * changes drag_motion behaviour */
311     result = GTK_WIDGET_CLASS (xed_view_parent_class)->drag_motion (widget, context, x, y, timestamp);
312 
313     /* If this is a URL, deal with it here */
314     if (drag_get_uri_target (widget, context) != GDK_NONE)
315     {
316         gdk_drag_status (context, gdk_drag_context_get_suggested_action (context), timestamp);
317         result = TRUE;
318     }
319 
320     return result;
321 }
322 
323 static void
xed_view_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint timestamp)324 xed_view_drag_data_received (GtkWidget        *widget,
325                              GdkDragContext   *context,
326                              gint              x,
327                              gint              y,
328                              GtkSelectionData *selection_data,
329                              guint             info,
330                              guint             timestamp)
331 {
332     gchar **uri_list;
333 
334     /* If this is an URL emit DROP_URIS, otherwise chain up the signal */
335     if (info == TARGET_URI_LIST)
336     {
337         uri_list = xed_utils_drop_get_uris (selection_data);
338 
339         if (uri_list != NULL)
340         {
341             g_signal_emit (widget, view_signals[DROP_URIS], 0, uri_list);
342             g_strfreev (uri_list);
343             gtk_drag_finish (context, TRUE, FALSE, timestamp);
344         }
345     }
346     else
347     {
348         GTK_WIDGET_CLASS (xed_view_parent_class)->drag_data_received (widget, context, x, y, selection_data, info,
349                                                                       timestamp);
350     }
351 }
352 
353 static gboolean
xed_view_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint timestamp)354 xed_view_drag_drop (GtkWidget      *widget,
355                     GdkDragContext *context,
356                     gint            x,
357                     gint            y,
358                     guint           timestamp)
359 {
360     gboolean result;
361     GdkAtom target;
362 
363     /* If this is a URL, just get the drag data */
364     target = drag_get_uri_target (widget, context);
365 
366     if (target != GDK_NONE)
367     {
368         gtk_drag_get_data (widget, context, target, timestamp);
369         result = TRUE;
370     }
371     else
372     {
373         /* Chain up */
374         result = GTK_WIDGET_CLASS (xed_view_parent_class)->drag_drop (widget, context, x, y, timestamp);
375     }
376 
377     return result;
378 }
379 
380 static GtkWidget *
create_line_numbers_menu(GtkWidget * view)381 create_line_numbers_menu (GtkWidget *view)
382 {
383     GtkWidget *menu;
384     GtkWidget *item;
385 
386     menu = gtk_menu_new ();
387 
388     item = gtk_check_menu_item_new_with_mnemonic (_("_Display line numbers"));
389     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
390                                     gtk_source_view_get_show_line_numbers (GTK_SOURCE_VIEW (view)));
391 
392     g_settings_bind (XED_VIEW (view)->priv->editor_settings,
393                      XED_SETTINGS_DISPLAY_LINE_NUMBERS,
394                      item,
395                      "active",
396                      G_SETTINGS_BIND_SET);
397 
398     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
399 
400     gtk_widget_show_all (menu);
401 
402     return menu;
403 }
404 
405 static void
show_line_numbers_menu(GtkWidget * view,GdkEventButton * event)406 show_line_numbers_menu (GtkWidget      *view,
407                         GdkEventButton *event)
408 {
409     GtkWidget *menu;
410 
411     menu = create_line_numbers_menu (view);
412     gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *) event);
413 }
414 
415 static gboolean
xed_view_button_press_event(GtkWidget * widget,GdkEventButton * event)416 xed_view_button_press_event (GtkWidget      *widget,
417                              GdkEventButton *event)
418 {
419     if ((event->type == GDK_BUTTON_PRESS) &&
420         (event->button == 3) &&
421         (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_LEFT)))
422     {
423         show_line_numbers_menu (widget, event);
424         return TRUE;
425     }
426 
427     return GTK_WIDGET_CLASS (xed_view_parent_class)->button_press_event (widget, event);
428 }
429 
430 static void
xed_view_realize(GtkWidget * widget)431 xed_view_realize (GtkWidget *widget)
432 {
433     XedView *view = XED_VIEW (widget);
434 
435     if (!view->priv->view_realized)
436     {
437         peas_extension_set_call (view->priv->extensions, "activate");
438         view->priv->view_realized = TRUE;
439     }
440 
441     GTK_WIDGET_CLASS (xed_view_parent_class)->realize (widget);
442 }
443 
444 static void
delete_line(GtkTextView * text_view,gint count)445 delete_line (GtkTextView *text_view,
446              gint         count)
447 {
448     GtkTextIter start;
449     GtkTextIter end;
450     GtkTextBuffer *buffer;
451 
452     buffer = gtk_text_view_get_buffer (text_view);
453 
454     gtk_text_view_reset_im_context (text_view);
455 
456     /* If there is a selection delete the selected lines and
457      * ignore count */
458     if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
459     {
460         gtk_text_iter_order (&start, &end);
461         if (gtk_text_iter_starts_line (&end))
462         {
463             /* Do no delete the line with the cursor if the cursor
464              * is at the beginning of the line */
465             count = 0;
466         }
467         else
468         {
469             count = 1;
470         }
471     }
472 
473     gtk_text_iter_set_line_offset (&start, 0);
474 
475     if (count > 0)
476     {
477         gtk_text_iter_forward_lines (&end, count);
478         if (gtk_text_iter_is_end (&end))
479         {
480             if (gtk_text_iter_backward_line (&start) && !gtk_text_iter_ends_line (&start))
481             {
482                 gtk_text_iter_forward_to_line_end (&start);
483             }
484         }
485     }
486     else if (count < 0)
487     {
488         if (!gtk_text_iter_ends_line (&end))
489         {
490             gtk_text_iter_forward_to_line_end (&end);
491         }
492 
493         while (count < 0)
494         {
495             if (!gtk_text_iter_backward_line (&start))
496             {
497                 break;
498             }
499             ++count;
500         }
501 
502         if (count == 0)
503         {
504             if (!gtk_text_iter_ends_line (&start))
505             {
506                 gtk_text_iter_forward_to_line_end (&start);
507             }
508         }
509         else
510         {
511             gtk_text_iter_forward_line (&end);
512         }
513     }
514 
515     if (!gtk_text_iter_equal (&start, &end))
516     {
517         GtkTextIter cur = start;
518         gtk_text_iter_set_line_offset (&cur, 0);
519         gtk_text_buffer_begin_user_action (buffer);
520         gtk_text_buffer_place_cursor (buffer, &cur);
521         gtk_text_buffer_delete_interactive (buffer, &start, &end, gtk_text_view_get_editable (text_view));
522         gtk_text_buffer_end_user_action (buffer);
523         gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (buffer));
524     }
525     else
526     {
527         gtk_widget_error_bell (GTK_WIDGET(text_view));
528     }
529 }
530 
531 static void
xed_view_delete_from_cursor(GtkTextView * text_view,GtkDeleteType type,gint count)532 xed_view_delete_from_cursor (GtkTextView  *text_view,
533                              GtkDeleteType type,
534                              gint          count)
535 {
536     /* We override the standard handler for delete_from_cursor since
537      the GTK_DELETE_PARAGRAPHS case is not implemented as we like (i.e. it
538      does not remove the carriage return in the previous line)
539      */
540     switch (type)
541     {
542         case GTK_DELETE_PARAGRAPHS:
543             delete_line (text_view, count);
544             break;
545         default:
546             GTK_TEXT_VIEW_CLASS (xed_view_parent_class)->delete_from_cursor (text_view, type, count);
547             break;
548     }
549 }
550 
551 static GtkTextBuffer *
xed_view_create_buffer(GtkTextView * text_view)552 xed_view_create_buffer (GtkTextView *text_view)
553 {
554     return GTK_TEXT_BUFFER (xed_document_new ());
555 }
556 
557 static void
xed_view_class_init(XedViewClass * klass)558 xed_view_class_init (XedViewClass *klass)
559 {
560     GObjectClass *object_class = G_OBJECT_CLASS (klass);
561     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
562     GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass);
563     GtkBindingSet *binding_set;
564 
565     object_class->dispose = xed_view_dispose;
566     object_class->constructed = xed_view_constructed;
567 
568     widget_class->focus_out_event = xed_view_focus_out;
569 
570     /*
571      * Override the gtk_text_view_drag_motion and drag_drop
572      * functions to get URIs
573      *
574      * If the mime type is text/uri-list, then we will accept
575      * the potential drop, or request the data (depending on the
576      * function).
577      *
578      * If the drag context has any other mime type, then pass the
579      * information onto the GtkTextView's standard handlers.
580      * (widget_class->function_name).
581      *
582      * See bug #89881 for details
583      */
584     widget_class->drag_motion = xed_view_drag_motion;
585     widget_class->drag_data_received = xed_view_drag_data_received;
586     widget_class->drag_drop = xed_view_drag_drop;
587     widget_class->button_press_event = xed_view_button_press_event;
588     widget_class->realize = xed_view_realize;
589 
590     text_view_class->delete_from_cursor = xed_view_delete_from_cursor;
591     text_view_class->create_buffer = xed_view_create_buffer;
592 
593     /* A new signal DROP_URIS has been added to allow plugins to intercept
594      * the default dnd behaviour of 'text/uri-list'. XedView now handles
595      * dnd in the default handlers of drag_drop, drag_motion and
596      * drag_data_received. The view emits drop_uris from drag_data_received
597      * if valid uris have been dropped. Plugins should connect to
598      * drag_motion, drag_drop and drag_data_received to change this
599      * default behaviour. They should _NOT_ use this signal because this
600      * will not prevent xed from loading the uri
601      */
602     view_signals[DROP_URIS] =
603         g_signal_new ("drop_uris",
604                       G_TYPE_FROM_CLASS (object_class),
605                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
606                       G_STRUCT_OFFSET (XedViewClass, drop_uris),
607                       NULL, NULL,
608                       g_cclosure_marshal_VOID__BOXED,
609                       G_TYPE_NONE, 1, G_TYPE_STRV);
610 
611     binding_set = gtk_binding_set_by_class (klass);
612 
613     gtk_binding_entry_add_signal (binding_set,
614                                   GDK_KEY_d,
615                                   GDK_CONTROL_MASK,
616                                   "delete_from_cursor", 2,
617                                   G_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
618                                   G_TYPE_INT, 1);
619 
620     gtk_binding_entry_add_signal (binding_set,
621                                   GDK_KEY_u,
622                                   GDK_CONTROL_MASK,
623                                   "change_case", 1,
624                                   G_TYPE_ENUM, GTK_SOURCE_CHANGE_CASE_UPPER);
625 
626     gtk_binding_entry_add_signal (binding_set,
627                                   GDK_KEY_l,
628                                   GDK_CONTROL_MASK,
629                                   "change_case", 1,
630                                   G_TYPE_ENUM, GTK_SOURCE_CHANGE_CASE_LOWER);
631 
632     gtk_binding_entry_add_signal (binding_set,
633                                   GDK_KEY_asciitilde,
634                                   GDK_CONTROL_MASK,
635                                   "change_case", 1,
636                                   G_TYPE_ENUM, GTK_SOURCE_CHANGE_CASE_TOGGLE);
637 
638     gtk_binding_entry_add_signal (binding_set,
639                                   GDK_KEY_t,
640                                   GDK_CONTROL_MASK,
641                                   "change_case", 1,
642                                   G_TYPE_ENUM, GTK_SOURCE_CHANGE_CASE_TITLE);
643 }
644 
645 /**
646  * xed_view_new:
647  * @doc: a #XedDocument
648  *
649  * Creates a new #XedView object displaying the @doc document.
650  * @doc cannot be %NULL.
651  *
652  * Return value: a new #XedView
653  **/
654 GtkWidget *
xed_view_new(XedDocument * doc)655 xed_view_new (XedDocument *doc)
656 {
657     GtkWidget *view;
658 
659     xed_debug_message (DEBUG_VIEW, "START");
660 
661     g_return_val_if_fail(XED_IS_DOCUMENT (doc), NULL);
662 
663     view = GTK_WIDGET(g_object_new (XED_TYPE_VIEW, "buffer", doc, NULL));
664 
665     xed_debug_message (DEBUG_VIEW, "END: %d", G_OBJECT (view)->ref_count);
666 
667     gtk_widget_show_all (view);
668 
669     return view;
670 }
671 
672 void
xed_view_cut_clipboard(XedView * view)673 xed_view_cut_clipboard (XedView *view)
674 {
675     GtkTextBuffer *buffer;
676     GtkClipboard *clipboard;
677 
678     xed_debug (DEBUG_VIEW);
679     g_return_if_fail(XED_IS_VIEW (view));
680 
681     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
682     g_return_if_fail(buffer != NULL);
683 
684     clipboard = gtk_widget_get_clipboard (GTK_WIDGET(view), GDK_SELECTION_CLIPBOARD);
685 
686     /* FIXME: what is default editability of a buffer? */
687     gtk_text_buffer_cut_clipboard (buffer, clipboard, !xed_document_get_readonly (XED_DOCUMENT(buffer)));
688 
689     gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW(view), gtk_text_buffer_get_insert (buffer), XED_VIEW_SCROLL_MARGIN,
690                                   FALSE, 0.0, 0.0);
691 }
692 
693 void
xed_view_copy_clipboard(XedView * view)694 xed_view_copy_clipboard (XedView *view)
695 {
696     GtkTextBuffer *buffer;
697     GtkClipboard *clipboard;
698 
699     xed_debug (DEBUG_VIEW);
700     g_return_if_fail(XED_IS_VIEW (view));
701 
702     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
703     g_return_if_fail(buffer != NULL);
704 
705     clipboard = gtk_widget_get_clipboard (GTK_WIDGET(view), GDK_SELECTION_CLIPBOARD);
706     gtk_text_buffer_copy_clipboard (buffer, clipboard);
707 }
708 
709 void
xed_view_paste_clipboard(XedView * view)710 xed_view_paste_clipboard (XedView *view)
711 {
712     GtkTextBuffer *buffer;
713     GtkClipboard *clipboard;
714 
715     xed_debug (DEBUG_VIEW);
716 
717     g_return_if_fail(XED_IS_VIEW (view));
718 
719     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
720     g_return_if_fail(buffer != NULL);
721 
722     clipboard = gtk_widget_get_clipboard (GTK_WIDGET(view), GDK_SELECTION_CLIPBOARD);
723 
724     /* FIXME: what is default editability of a buffer? */
725     gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, !xed_document_get_readonly (XED_DOCUMENT(buffer)));
726 
727     gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW(view), gtk_text_buffer_get_insert (buffer), XED_VIEW_SCROLL_MARGIN,
728                                   FALSE, 0.0, 0.0);
729 }
730 
731 /**
732  * xed_view_delete_selection:
733  * @view: a #XedView
734  *
735  * Deletes the text currently selected in the #GtkTextBuffer associated
736  * to the view and scroll to the cursor position.
737  **/
738 void
xed_view_delete_selection(XedView * view)739 xed_view_delete_selection (XedView *view)
740 {
741     GtkTextBuffer *buffer = NULL;
742 
743     xed_debug (DEBUG_VIEW);
744 
745     g_return_if_fail(XED_IS_VIEW (view));
746 
747     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
748     g_return_if_fail(buffer != NULL);
749 
750     /* FIXME: what is default editability of a buffer? */
751     gtk_text_buffer_delete_selection (buffer, TRUE, !xed_document_get_readonly (XED_DOCUMENT(buffer)));
752 
753     gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW(view), gtk_text_buffer_get_insert (buffer), XED_VIEW_SCROLL_MARGIN,
754                                   FALSE, 0.0, 0.0);
755 }
756 
757 /**
758  * xed_view_select_all:
759  * @view: a #XedView
760  *
761  * Selects all the text displayed in the @view.
762  **/
763 void
xed_view_select_all(XedView * view)764 xed_view_select_all (XedView *view)
765 {
766     GtkTextBuffer *buffer = NULL;
767     GtkTextIter start, end;
768 
769     xed_debug (DEBUG_VIEW);
770 
771     g_return_if_fail(XED_IS_VIEW (view));
772 
773     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
774     g_return_if_fail(buffer != NULL);
775 
776     gtk_text_buffer_get_bounds (buffer, &start, &end);
777     gtk_text_buffer_select_range (buffer, &start, &end);
778 }
779 
780 /**
781  * xed_view_scroll_to_cursor:
782  * @view: a #XedView
783  *
784  * Scrolls the @view to the cursor position.
785  **/
786 void
xed_view_scroll_to_cursor(XedView * view)787 xed_view_scroll_to_cursor (XedView *view)
788 {
789     GtkTextBuffer* buffer = NULL;
790 
791     xed_debug (DEBUG_VIEW);
792 
793     g_return_if_fail(XED_IS_VIEW (view));
794 
795     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
796     g_return_if_fail(buffer != NULL);
797 
798     gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW(view), gtk_text_buffer_get_insert (buffer), 0.25, FALSE, 0.0, 0.0);
799 }
800 
801 /* FIXME this is an issue for introspection */
802 /**
803  * xed_view_set_font:
804  * @view: a #XedView
805  * @def: whether to reset the default font
806  * @font_name: the name of the font to use
807  *
808  * If @def is #TRUE, resets the font of the @view to the default font
809  * otherwise sets it to @font_name.
810  **/
811 void
xed_view_set_font(XedView * view,gboolean def,const gchar * font_name)812 xed_view_set_font (XedView     *view,
813                    gboolean     def,
814                    const gchar *font_name)
815 {
816     PangoFontDescription *font_desc = NULL;
817 
818     xed_debug (DEBUG_VIEW);
819 
820     g_return_if_fail(XED_IS_VIEW (view));
821 
822     if (def)
823     {
824         GObject *settings;
825         gchar *font;
826 
827         settings = _xed_app_get_settings (XED_APP (g_application_get_default ()));
828         font = xed_settings_get_system_font (XED_SETTINGS (settings));
829         font_desc = pango_font_description_from_string (font);
830 
831         g_free (font);
832     }
833     else
834     {
835         g_return_if_fail (font_name != NULL);
836         font_desc = pango_font_description_from_string (font_name);
837     }
838 
839     g_return_if_fail (font_desc != NULL);
840     gtk_widget_modify_font (GTK_WIDGET (view), font_desc);
841     pango_font_description_free (font_desc);
842 }
843 
844 static guint
xed_view_get_draw_whitespace_locations_from_settings(GSettings * settings)845 xed_view_get_draw_whitespace_locations_from_settings (GSettings* settings)
846 {
847     guint locations;
848 
849     locations = 0;
850 
851     locations |= g_settings_get_boolean (settings, XED_SETTINGS_DRAW_WHITESPACE_LEADING)
852                                 ? GTK_SOURCE_SPACE_LOCATION_LEADING : 0;
853     locations |= g_settings_get_boolean (settings, XED_SETTINGS_DRAW_WHITESPACE_INSIDE)
854                                 ? GTK_SOURCE_SPACE_LOCATION_INSIDE_TEXT : 0;
855     locations |= g_settings_get_boolean (settings, XED_SETTINGS_DRAW_WHITESPACE_TRAILING)
856                                 ? GTK_SOURCE_SPACE_LOCATION_TRAILING : 0;
857 
858     return locations;
859 }
860 
861 static guint
xed_view_get_draw_whitespace_types_from_settings(GSettings * settings)862 xed_view_get_draw_whitespace_types_from_settings (GSettings* settings)
863 {
864     if (!g_settings_get_boolean (settings, XED_SETTINGS_DRAW_WHITESPACE_NEWLINE))
865     {
866         return GTK_SOURCE_SPACE_TYPE_ALL & ~GTK_SOURCE_SPACE_TYPE_NEWLINE;
867     }
868 
869     return GTK_SOURCE_SPACE_TYPE_ALL;
870 }
871 
872 /**
873  * xed_view_set_draw_whitespace:
874  * @view: a #XedView
875  * @enable: whether whitespace should be drawn
876  *
877  * Enables or disables rendering of any whitespace.
878  * The locations and types of whitespace to render is set by
879  * xed_view_update_draw_whitespace_locations_and_types()
880  *
881  **/
882 
883 void
xed_view_set_draw_whitespace(XedView * view,gboolean enable)884 xed_view_set_draw_whitespace (XedView *view, gboolean enable)
885 {
886     GtkSourceSpaceDrawer *spacedrawer;
887 
888     spacedrawer = gtk_source_view_get_space_drawer (GTK_SOURCE_VIEW (view));
889     gtk_source_space_drawer_set_enable_matrix (spacedrawer, enable);
890 }
891 
892 
893 /**
894  * xed_view_update_draw_whitespace_locations_and_types:
895  * @view: a #XedView
896  *
897  * Updates the view to render whitespace at the locations and for types
898  * set in the preferences
899  *
900  **/
901 
902 void
xed_view_update_draw_whitespace_locations_and_types(XedView * view)903 xed_view_update_draw_whitespace_locations_and_types (XedView *view)
904 {
905     GtkSourceSpaceDrawer *spacedrawer;
906     guint locations, types;
907 
908     spacedrawer = gtk_source_view_get_space_drawer (GTK_SOURCE_VIEW (view));
909     locations = xed_view_get_draw_whitespace_locations_from_settings (view->priv->editor_settings);
910     types = xed_view_get_draw_whitespace_types_from_settings (view->priv->editor_settings);
911 
912     // disable other locations
913     gtk_source_space_drawer_set_types_for_locations (spacedrawer,
914                                                      GTK_SOURCE_SPACE_LOCATION_ALL & ~locations,
915                                                      GTK_SOURCE_SPACE_TYPE_NONE);
916     // enable chosen locations and types
917     gtk_source_space_drawer_set_types_for_locations (spacedrawer, locations, types);
918 }
919