1 /*
2  * marker-source-view.c
3  *
4  * Copyright (C) 2017 - 2018 Fabio Colacio
5  *
6  * Marker is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * Marker 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with Marker; see the file LICENSE.md. If not,
18  * see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <string.h>
23 #include <stdlib.h>
24 
25 #include "marker-source-view.h"
26 #include "marker-prefs.h"
27 #include "marker-utils.h"
28 
29 #include <glib.h>
30 #include <gtkspell/gtkspell.h>
31 
32 struct _MarkerSourceView
33 {
34   GtkSourceView           parent_instance;
35   GSettings              *settings;
36   GtkSpellChecker        *spell;
37   GtkSourceSearchContext *search_context;
38 };
39 
G_DEFINE_TYPE(MarkerSourceView,marker_source_view,GTK_SOURCE_TYPE_VIEW)40 G_DEFINE_TYPE(MarkerSourceView, marker_source_view, GTK_SOURCE_TYPE_VIEW)
41 
42 void
43 marker_source_view_surround_selection_with (MarkerSourceView *source_view,
44                                             const char       *insertion)
45 {
46   GtkTextBuffer* buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
47   GtkTextIter start, end;
48   gint start_index, end_index, selection_len;
49   gboolean selected;
50   size_t len = strlen (insertion);
51 
52   selected = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
53 
54   start_index = gtk_text_iter_get_line_offset (&start);
55   end_index = gtk_text_iter_get_line_offset (&end);
56   selection_len = end_index - start_index;
57 
58   gtk_text_buffer_insert (buffer, &start, insertion, len);
59   gtk_text_iter_forward_chars (&start, selection_len);
60   gtk_text_buffer_insert (buffer, &start, insertion, len);
61 
62   if (!selected)
63   {
64     gtk_text_iter_backward_chars (&start, len);
65     gtk_text_buffer_place_cursor (buffer, &start);
66   }
67 }
68 
69 void
marker_source_view_insert_link(MarkerSourceView * source_view)70 marker_source_view_insert_link (MarkerSourceView   *source_view)
71 {
72   GtkTextBuffer* buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view));
73   GtkTextIter start, end;
74   gint start_index, end_index, selection_len;
75   gboolean selected;
76 
77   selected = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
78   if (selected) {
79     start_index = gtk_text_iter_get_line_offset (&start);
80     end_index = gtk_text_iter_get_line_offset (&end);
81     selection_len = end_index - start_index;
82 
83     gchar * selected = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
84     if (!marker_utils_is_url(selected)) {
85       gtk_text_buffer_insert (buffer, &start, "[", 1);
86       gtk_text_iter_forward_chars (&start, selection_len);
87       gtk_text_buffer_insert (buffer, &start, "]()", 3);
88       gtk_text_iter_backward_chars(&start, 1);
89       gtk_text_buffer_place_cursor(buffer, &start);
90 
91     } else {
92       gtk_text_buffer_insert (buffer, &start, "[](", 3);
93       gtk_text_iter_forward_chars (&start, selection_len);
94       gtk_text_buffer_insert (buffer, &start, ")", 1);
95       gtk_text_iter_backward_chars(&start, selection_len + 3);
96       gtk_text_buffer_place_cursor(buffer, &start);
97     }
98   } else {
99     gchar * link = g_strdup("[]()");
100     GtkTextMark * mark = gtk_text_buffer_get_insert(buffer);
101     gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
102     size_t len = strlen(link);
103 
104     gtk_text_buffer_insert(buffer, &start, link, len);
105     gtk_text_iter_backward_chars(&start, 3);
106     gtk_text_buffer_place_cursor(buffer, &start);
107     g_free(link);
108   }
109 }
110 
111 void
marker_source_view_insert_image(MarkerSourceView * source_view,const char * image_path)112 marker_source_view_insert_image (MarkerSourceView   *source_view,
113                                  const char         *image_path)
114 {
115   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
116   GtkTextIter start ;
117 
118   gtk_text_buffer_get_iter_at_mark(buffer, &start, gtk_text_buffer_get_insert(buffer));
119 
120   gchar * img = g_strdup_printf("![](%s)", image_path);
121 
122   size_t len = strlen(img);
123 
124   gtk_text_buffer_insert(buffer, &start, img, len);
125   g_free(img);
126 }
127 
128 void
marker_source_view_set_spell_check(MarkerSourceView * source_view,gboolean state)129 marker_source_view_set_spell_check(MarkerSourceView *source_view,
130                                    gboolean          state)
131 {
132   g_assert (MARKER_IS_SOURCE_VIEW (source_view));
133 
134   gboolean is_attached =
135     source_view->spell == gtk_spell_checker_get_from_text_view (GTK_TEXT_VIEW (source_view));
136 
137   if (state && !is_attached)
138   {
139     gtk_spell_checker_attach((GtkSpellChecker*)source_view->spell, GTK_TEXT_VIEW(source_view));
140   }
141   else if (!state && is_attached)
142   {
143     g_object_ref (source_view->spell);
144     gtk_spell_checker_detach((GtkSpellChecker*)source_view->spell);
145   }
146 }
147 
148 void
marker_source_view_set_spell_check_lang(MarkerSourceView * source_view,const gchar * lang)149 marker_source_view_set_spell_check_lang (MarkerSourceView *source_view,
150                                          const gchar      *lang)
151 {
152   g_assert (MARKER_IS_SOURCE_VIEW (source_view));
153   gtk_spell_checker_set_language (source_view->spell, lang, NULL);
154 }
155 
156 void
marker_source_view_set_syntax_theme(MarkerSourceView * source_view,const char * theme)157 marker_source_view_set_syntax_theme(MarkerSourceView* source_view,
158                                     const char*       theme)
159 {
160   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
161   GtkSourceStyleSchemeManager* style_manager =
162     gtk_source_style_scheme_manager_get_default();
163   GtkSourceStyleScheme* scheme =
164     gtk_source_style_scheme_manager_get_scheme(style_manager, theme);
165   gtk_source_buffer_set_style_scheme(GTK_SOURCE_BUFFER(buffer), scheme);
166 }
167 
168 gboolean
marker_source_view_get_modified(MarkerSourceView * source_view)169 marker_source_view_get_modified(MarkerSourceView* source_view)
170 {
171   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
172   return gtk_text_buffer_get_modified(buffer);
173 }
174 
175 void
marker_source_view_set_modified(MarkerSourceView * source_view,gboolean modified)176 marker_source_view_set_modified(MarkerSourceView* source_view,
177                                 gboolean          modified)
178 {
179   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
180   gtk_text_buffer_set_modified(buffer, modified);
181 }
182 
183 gchar*
marker_source_view_get_text(MarkerSourceView * source_view,gboolean include_position)184 marker_source_view_get_text(MarkerSourceView* source_view,
185                             gboolean          include_position)
186 {
187   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
188   GtkTextIter start, end;
189   gtk_text_buffer_get_start_iter(buffer, &start);
190   gtk_text_buffer_get_end_iter(buffer, &end);
191   if (include_position && !gtk_text_iter_equal(&start, &end)) {
192     gchar * identifier = g_strdup("<span id=\"cursor_pos\"></span>");
193     GtkTextIter pos;
194     gtk_text_buffer_get_selection_bounds (buffer, &pos, NULL);
195     gchar * beginning = gtk_text_buffer_get_text(buffer, &start, &pos, FALSE);
196     gchar * ending = gtk_text_buffer_get_text(buffer, &pos, &end, FALSE);
197     return g_strconcat(beginning, identifier, ending, NULL);
198   }
199   return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
200 }
201 
202 int
marker_source_view_get_cursor_position(MarkerSourceView * source_view)203 marker_source_view_get_cursor_position (MarkerSourceView   *source_view)
204 {
205   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
206   GtkTextIter pos;
207   gtk_text_buffer_get_selection_bounds (buffer, &pos, NULL);
208   return gtk_text_iter_get_offset(&pos);
209 }
210 
211 
212 void
marker_source_view_set_text(MarkerSourceView * source_view,const char * text,size_t size)213 marker_source_view_set_text(MarkerSourceView* source_view,
214                             const char*       text,
215                             size_t            size)
216 {
217   GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view));
218   gtk_text_buffer_set_text(buffer, text, size);
219 }
220 
221 void
marker_source_view_set_language(MarkerSourceView * source_view,const gchar * language_name)222 marker_source_view_set_language(MarkerSourceView* source_view,
223                                 const gchar*      language_name)
224 {
225   if (GTK_SOURCE_IS_VIEW(source_view))
226   {
227     GtkSourceLanguageManager* manager =
228       gtk_source_language_manager_get_default();
229 
230     GtkSourceLanguage* language =
231       gtk_source_language_manager_get_language(manager, language_name);
232 
233     GtkSourceBuffer* buffer =
234       GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view)));
235 
236     gtk_source_buffer_set_language(buffer, language);
237 
238     g_object_unref(manager);
239   }
240 }
241 
242 static void
default_font_changed(GSettings * settings,const gchar * key,gpointer user_data)243 default_font_changed(GSettings*   settings,
244                      const gchar* key,
245                      gpointer     user_data)
246 {
247   MarkerSourceView* source_view = (MarkerSourceView*) user_data;
248   gchar* fontname = g_settings_get_string(settings, key);
249   PangoFontDescription* font = pango_font_description_from_string(fontname);
250   gtk_widget_modify_font(GTK_WIDGET(source_view), font);
251   pango_font_description_free(font);
252   g_free(fontname);
253 }
254 
255 static void
marker_source_view_init(MarkerSourceView * source_view)256 marker_source_view_init (MarkerSourceView *source_view)
257 {
258   GtkSourceSearchContext * search_context = gtk_source_search_context_new(GTK_SOURCE_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(source_view))),
259                                                                           NULL);
260   source_view->search_context = search_context;
261   marker_source_view_set_language (source_view, "markdown");
262   source_view->settings = g_settings_new ("org.gnome.desktop.interface");
263   g_signal_connect (source_view->settings, "changed::monospace-font-name", G_CALLBACK (default_font_changed), source_view);
264   gchar *fontname = g_settings_get_string (source_view->settings, "monospace-font-name");
265   PangoFontDescription* font = pango_font_description_from_string (fontname);
266   gtk_widget_modify_font (GTK_WIDGET (source_view), font);
267   pango_font_description_free (font);
268   g_free (fontname);
269 
270   gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (source_view), marker_prefs_get_replace_tabs ());
271   gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (source_view), marker_prefs_get_tab_width ());
272   gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (source_view), marker_prefs_get_auto_indent ());
273 
274   source_view->spell = gtk_spell_checker_new ();
275   gchar* lang = marker_prefs_get_spell_check_language();
276   gtk_spell_checker_set_language (source_view->spell, lang, NULL);
277   if (marker_prefs_get_spell_check ()){
278     gtk_spell_checker_attach (source_view->spell, GTK_TEXT_VIEW (source_view));
279   }
280 }
281 
282 static void
marker_source_view_class_init(MarkerSourceViewClass * class)283 marker_source_view_class_init(MarkerSourceViewClass* class)
284 {
285 
286 }
287 
288 MarkerSourceView*
marker_source_view_new(void)289 marker_source_view_new(void)
290 {
291   return g_object_new(MARKER_TYPE_SOURCE_VIEW, NULL);
292 }
293 
294 GtkSourceSearchContext*
marker_source_get_search_context(MarkerSourceView * source_view)295 marker_source_get_search_context (MarkerSourceView   *source_view)
296 {
297   return source_view->search_context;
298 }
299