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