1 /*
2
3 Copyright (c) 2001-2007 Michael Terry
4 Copyright (c) 2011 Sergei Riaguzov
5 Copyright (c) 2013-2014 Arthur Borsboom
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21 */
22
23 #include "../config.h"
24
25 #include <gtk/gtk.h>
26 #include <gtksourceview/gtksource.h>
27
28 #include "xpad-text-buffer.h"
29 #include "xpad-pad.h"
30 #include "xpad-undo.h"
31
32 struct XpadTextBufferPrivate
33 {
34 XpadUndo *undo;
35 XpadPad *pad;
36 };
37
38 G_DEFINE_TYPE_WITH_PRIVATE (XpadTextBuffer, xpad_text_buffer, GTK_SOURCE_TYPE_BUFFER)
39
40 /* Unicode chars in the Private Use Area. */
41 static gunichar TAG_CHAR = 0xe000;
42
43 static GtkTextTagTable *create_tag_table (void);
44
45 enum
46 {
47 PROP_0,
48 PROP_PAD,
49 LAST_PROP
50 };
51
52 static void xpad_text_buffer_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
53 static void xpad_text_buffer_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
54 static void xpad_text_buffer_dispose (GObject *object);
55 static void xpad_text_buffer_finalize (GObject *object);
56
57 XpadTextBuffer *
xpad_text_buffer_new(XpadPad * pad)58 xpad_text_buffer_new (XpadPad *pad)
59 {
60 return g_object_new (XPAD_TYPE_TEXT_BUFFER, "tag_table", create_tag_table(), "pad", pad, NULL);
61 }
62
63 static void
xpad_text_buffer_class_init(XpadTextBufferClass * klass)64 xpad_text_buffer_class_init (XpadTextBufferClass *klass)
65 {
66 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
67
68 gobject_class->dispose = xpad_text_buffer_dispose;
69 gobject_class->finalize = xpad_text_buffer_finalize;
70 gobject_class->set_property = xpad_text_buffer_set_property;
71 gobject_class->get_property = xpad_text_buffer_get_property;
72
73 g_object_class_install_property (gobject_class,
74 PROP_PAD,
75 g_param_spec_pointer ("pad",
76 "Pad",
77 "Pad connected to this buffer",
78 G_PARAM_READWRITE));
79 }
80
81 static void
xpad_text_buffer_init(XpadTextBuffer * buffer)82 xpad_text_buffer_init (XpadTextBuffer *buffer)
83 {
84 buffer->priv = xpad_text_buffer_get_instance_private (buffer);
85
86 buffer->priv->undo = xpad_undo_new (buffer);
87 }
88
89 static void
xpad_text_buffer_dispose(GObject * object)90 xpad_text_buffer_dispose (GObject *object)
91 {
92 XpadTextBuffer *buffer = XPAD_TEXT_BUFFER (object);
93
94 g_clear_object (&buffer->priv->pad);
95 g_clear_object (&buffer->priv->undo);
96
97 G_OBJECT_CLASS (xpad_text_buffer_parent_class)->dispose (object);
98 }
99
100 static void
xpad_text_buffer_finalize(GObject * object)101 xpad_text_buffer_finalize (GObject *object)
102 {
103 G_OBJECT_CLASS (xpad_text_buffer_parent_class)->finalize (object);
104 }
105
106 static void
xpad_text_buffer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)107 xpad_text_buffer_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
108 {
109 XpadTextBuffer *buffer = XPAD_TEXT_BUFFER (object);
110
111 switch (prop_id)
112 {
113 case PROP_PAD:
114 buffer->priv->pad = g_value_get_pointer (value);
115 g_object_ref (buffer->priv->pad);
116 break;
117
118 default:
119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
120 break;
121 }
122 }
123
124 static void
xpad_text_buffer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)125 xpad_text_buffer_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
126 {
127 XpadTextBuffer *buffer = XPAD_TEXT_BUFFER (object);
128
129 switch (prop_id)
130 {
131 case PROP_PAD:
132 g_value_set_pointer (value, buffer->priv->pad);
133 break;
134
135 default:
136 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
137 break;
138 }
139 }
140
141 void
xpad_text_buffer_set_text_with_tags(XpadTextBuffer * buffer,const gchar * text)142 xpad_text_buffer_set_text_with_tags (XpadTextBuffer *buffer, const gchar *text)
143 {
144 GtkTextIter start, end;
145 GList *tags = NULL;
146 gchar **tokens;
147 gint count;
148 gchar tag_char_utf8[7] = {0};
149
150 if (!text)
151 return;
152
153 GtkSourceBuffer *buffer_tb = GTK_SOURCE_BUFFER (buffer);
154
155 gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer_tb));
156
157 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer_tb), &start, &end);
158 gtk_text_buffer_delete (GTK_TEXT_BUFFER (buffer_tb), &start, &end);
159 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer_tb), &start, &end);
160
161 g_unichar_to_utf8 (TAG_CHAR, tag_char_utf8);
162
163 tokens = g_strsplit (text, tag_char_utf8, 0);
164
165 for (count = 0; tokens[count]; count++)
166 {
167 if (count % 2 == 0)
168 {
169 gint offset;
170 GList *j;
171
172 offset = gtk_text_iter_get_offset (&end);
173 gtk_text_buffer_insert (GTK_TEXT_BUFFER (buffer_tb), &end, tokens[count], -1);
174 gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer_tb), &start, offset);
175
176 for (j = tags; j; j = j->next)
177 {
178 gtk_text_buffer_apply_tag_by_name (GTK_TEXT_BUFFER (buffer_tb), j->data, &start, &end);
179 }
180 }
181 else
182 {
183 if (tokens[count][0] != '/')
184 {
185 tags = g_list_prepend (tags, tokens[count]);
186 }
187 else
188 {
189 GList *element = g_list_find_custom (tags, &(tokens[count][1]), (GCompareFunc) g_ascii_strcasecmp);
190
191 if (element)
192 {
193 tags = g_list_delete_link (tags, element);
194 }
195 }
196 }
197 }
198
199 gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer_tb));
200
201 g_strfreev (tokens);
202 }
203
204
205 gchar *
xpad_text_buffer_get_text_with_tags(XpadTextBuffer * buffer)206 xpad_text_buffer_get_text_with_tags (XpadTextBuffer *buffer)
207 {
208 GtkTextIter start, prev;
209 GSList *tags = NULL, *i;
210 gchar tag_char_utf8[7] = {0};
211 gchar *text = g_strdup (""), *oldtext = NULL, *tmp;
212 gboolean done = FALSE;
213 GtkSourceBuffer *buffer_tb = GTK_SOURCE_BUFFER (buffer);
214
215 gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer_tb), &start);
216
217 g_unichar_to_utf8 (TAG_CHAR, tag_char_utf8);
218
219 prev = start;
220
221 while (!done)
222 {
223 tmp = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer_tb), &prev, &start, TRUE);
224 oldtext = text;
225 text = g_strconcat (text, tmp, NULL);
226 g_free (oldtext);
227 g_free (tmp);
228
229 tags = gtk_text_iter_get_toggled_tags (&start, TRUE);
230 for (i = tags; i; i = i->next)
231 {
232 gchar *name;
233 g_object_get (G_OBJECT (i->data), "name", &name, NULL);
234
235 if (name) {
236 oldtext = text;
237 text = g_strconcat (text, tag_char_utf8, name, tag_char_utf8, NULL);
238 g_free (oldtext);
239 }
240
241 g_free (name);
242 }
243 g_slist_free (tags);
244
245 tags = gtk_text_iter_get_toggled_tags (&start, FALSE);
246 for (i = tags; i; i = i->next)
247 {
248 gchar *name;
249 g_object_get (G_OBJECT (i->data), "name", &name, NULL);
250
251 if (name) {
252 oldtext = text;
253 text = g_strconcat (text, tag_char_utf8, "/", name, tag_char_utf8, NULL);
254 g_free (oldtext);
255 }
256
257 g_free (name);
258 }
259 g_slist_free (tags);
260
261 if (gtk_text_iter_is_end (&start))
262 done = TRUE;
263 prev = start;
264 gtk_text_iter_forward_to_tag_toggle (&start, NULL);
265 }
266
267 return text;
268 }
269
270 void
xpad_text_buffer_insert_text(XpadTextBuffer * buffer,gint pos,const gchar * text,gint len)271 xpad_text_buffer_insert_text (XpadTextBuffer *buffer, gint pos, const gchar *text, gint len)
272 {
273 GtkSourceBuffer *parent = (GtkSourceBuffer*) buffer;
274 GtkTextIter iter;
275 gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (parent), &iter, pos);
276 gtk_text_buffer_insert (GTK_TEXT_BUFFER (parent), &iter, text, len);
277 gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (parent), &iter);
278 }
279
280 void
xpad_text_buffer_delete_range(XpadTextBuffer * buffer,gint start,gint end)281 xpad_text_buffer_delete_range (XpadTextBuffer *buffer, gint start, gint end)
282 {
283 GtkSourceBuffer *parent = (GtkSourceBuffer*) buffer;
284
285 GtkTextIter start_iter;
286 GtkTextIter end_iter;
287
288 gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (parent), &start_iter, start);
289
290 if (end < 0)
291 gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (parent), &end_iter);
292 else
293 gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (parent), &end_iter, end);
294
295 gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (parent), &start_iter);
296 gtk_text_buffer_delete (GTK_TEXT_BUFFER (parent), &start_iter, &end_iter);
297 }
298
299 void
xpad_text_buffer_toggle_tag(XpadTextBuffer * buffer,const gchar * name)300 xpad_text_buffer_toggle_tag (XpadTextBuffer *buffer, const gchar *name)
301 {
302 GtkTextTagTable *table;
303 GtkTextTag *tag;
304 GtkTextIter start, end, i;
305 gboolean all_tagged;
306 GtkSourceBuffer *buffer_tb = GTK_SOURCE_BUFFER (buffer);
307
308 table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer_tb));
309 tag = gtk_text_tag_table_lookup (table, name);
310 gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer_tb), &start, &end);
311
312 if (!tag)
313 {
314 g_printerr ("Tag not found in table %p\n", (void *) table);
315 return;
316 }
317
318 for (all_tagged = TRUE, i = start; !gtk_text_iter_equal (&i, &end); gtk_text_iter_forward_char (&i))
319 {
320 if (!gtk_text_iter_has_tag (&i, tag))
321 {
322 all_tagged = FALSE;
323 break;
324 }
325 }
326
327 if (all_tagged)
328 {
329 gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer_tb), tag, &start, &end);
330 xpad_undo_remove_tag (buffer->priv->undo, name, &start, &end);
331 }
332 else
333 {
334 gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer_tb), tag, &start, &end);
335 xpad_undo_apply_tag (buffer->priv->undo, name, &start, &end);
336 }
337 }
338
339 static GtkTextTagTable *
create_tag_table(void)340 create_tag_table (void)
341 {
342 GtkTextTagTable *table;
343 GtkTextTag *tag;
344
345 table = gtk_text_tag_table_new ();
346
347 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "bold", "weight", PANGO_WEIGHT_BOLD, NULL));
348 gtk_text_tag_table_add (table, tag);
349 g_clear_object (&tag);
350
351 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "italic", "style", PANGO_STYLE_ITALIC, NULL));
352 gtk_text_tag_table_add (table, tag);
353 g_clear_object (&tag);
354
355 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "strikethrough", "strikethrough", TRUE, NULL));
356 gtk_text_tag_table_add (table, tag);
357 g_clear_object (&tag);
358
359 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "underline", "underline", PANGO_UNDERLINE_SINGLE, NULL));
360 gtk_text_tag_table_add (table, tag);
361 g_clear_object (&tag);
362
363 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "small-xx", "scale", PANGO_SCALE_XX_SMALL, NULL));
364 gtk_text_tag_table_add (table, tag);
365 g_clear_object (&tag);
366
367 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "small-x", "scale", PANGO_SCALE_X_SMALL, NULL));
368 gtk_text_tag_table_add (table, tag);
369 g_clear_object (&tag);
370
371 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "small", "scale", PANGO_SCALE_SMALL, NULL));
372 gtk_text_tag_table_add (table, tag);
373 g_clear_object (&tag);
374
375 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "medium", "scale", PANGO_SCALE_MEDIUM, NULL));
376 gtk_text_tag_table_add (table, tag);
377 g_clear_object (&tag);
378
379 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "large", "scale", PANGO_SCALE_LARGE, NULL));
380 gtk_text_tag_table_add (table, tag);
381 g_clear_object (&tag);
382
383 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "large-x", "scale", PANGO_SCALE_X_LARGE, NULL));
384 gtk_text_tag_table_add (table, tag);
385 g_clear_object (&tag);
386
387 tag = GTK_TEXT_TAG (g_object_new (GTK_TYPE_TEXT_TAG, "name", "large-xx", "scale", PANGO_SCALE_XX_LARGE, NULL));
388 gtk_text_tag_table_add (table, tag);
389 g_clear_object (&tag);
390
391 return table;
392 }
393
394 gboolean
xpad_text_buffer_undo_available(XpadTextBuffer * buffer)395 xpad_text_buffer_undo_available (XpadTextBuffer *buffer)
396 {
397 return xpad_undo_undo_available (buffer->priv->undo);
398 }
399
400 gboolean
xpad_text_buffer_redo_available(XpadTextBuffer * buffer)401 xpad_text_buffer_redo_available (XpadTextBuffer *buffer)
402 {
403 return xpad_undo_redo_available (buffer->priv->undo);
404 }
405
406 void
xpad_text_buffer_undo(XpadTextBuffer * buffer)407 xpad_text_buffer_undo (XpadTextBuffer *buffer)
408 {
409 xpad_undo_exec_undo (buffer->priv->undo);
410 }
411
412 void
xpad_text_buffer_redo(XpadTextBuffer * buffer)413 xpad_text_buffer_redo (XpadTextBuffer *buffer)
414 {
415 xpad_undo_exec_redo (buffer->priv->undo);
416 }
417
xpad_text_buffer_freeze_undo(XpadTextBuffer * buffer)418 void xpad_text_buffer_freeze_undo (XpadTextBuffer *buffer)
419 {
420 xpad_undo_freeze (buffer->priv->undo);
421 }
422
xpad_text_buffer_thaw_undo(XpadTextBuffer * buffer)423 void xpad_text_buffer_thaw_undo (XpadTextBuffer *buffer)
424 {
425 xpad_undo_thaw (buffer->priv->undo);
426 }
427
xpad_text_buffer_get_pad(XpadTextBuffer * buffer)428 XpadPad *xpad_text_buffer_get_pad (XpadTextBuffer *buffer)
429 {
430 if (buffer == NULL)
431 return NULL;
432
433 XpadPad *pad = NULL;
434 g_object_get (G_OBJECT (buffer), "pad", &pad, NULL);
435 return pad;
436 }
437
xpad_text_buffer_set_pad(XpadTextBuffer * buffer,XpadPad * pad)438 void xpad_text_buffer_set_pad (XpadTextBuffer *buffer, XpadPad *pad)
439 {
440 g_return_if_fail (buffer);
441
442 g_object_set (G_OBJECT (buffer), "pad", pad, NULL);
443 }
444