1 /*
2 * Copyright (C) 2010 - 2012 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <string.h>
22 #include <glib/gi18n-lib.h>
23 #include <gdk-pixbuf/gdk-pixdata.h>
24 #include "bullet.h"
25 #include "bulleth.h"
26 #define MAX_BULLETS 2
27 //gchar * bullet_strings[] = {"•", "◦"};
28 #include <libgda-ui.h>
29
30 GdkPixbuf *bullet_pix[MAX_BULLETS] = {NULL};
31 gchar *lists_tokens[MAX_BULLETS] = {"- ", " - "};
32
33 static void gdaui_rt_editor_class_init (GdauiRtEditorClass *klass);
34 static void gdaui_rt_editor_init (GdauiRtEditor *wid);
35 static void gdaui_rt_editor_dispose (GObject *object);
36
37 static void gdaui_rt_editor_set_property (GObject *object,
38 guint param_id,
39 const GValue *value,
40 GParamSpec *pspec);
41 static void gdaui_rt_editor_get_property (GObject *object,
42 guint param_id,
43 GValue *value,
44 GParamSpec *pspec);
45 static void gdaui_rt_editor_show_all (GtkWidget *widget);
46
47 static void _gdaui_rt_editor_set_show_markup (GdauiRtEditor *editor, gboolean show_markup);
48
49 /* tag types */
50 enum {
51 TEXT_TAG_ITALIC,
52 TEXT_TAG_BOLD,
53 TEXT_TAG_TT,
54 TEXT_TAG_VERBATIM,
55 TEXT_TAG_UNDERLINE,
56 TEXT_TAG_STRIKE,
57 TEXT_TAG_TITLE1,
58 TEXT_TAG_TITLE2,
59 TEXT_TAG_LIST1,
60 TEXT_TAG_LIST2,
61
62 TEXT_TAG_LAST
63 };
64
65 typedef struct {
66 GtkTextTag *tag;
67 gchar *action_name;
68 } TagData;
69
70 struct _GdauiRtEditorPriv
71 {
72 GtkTextView *textview;
73 gdouble vadj_value;
74 GtkTextBuffer *textbuffer;
75 GtkWidget *toolbar;
76 GtkActionGroup *actions_group;
77 GtkUIManager *uimanager;
78 TagData tags[TEXT_TAG_LAST];
79 gboolean selection_changing;
80 gboolean show_markup;
81 gchar *saved_for_help;
82 gboolean enable_changed_signal;
83 gboolean no_background;
84 gint insert_offset;
85
86 gboolean contents_setting; /* TRUE if whole contents is being changed */
87 GtkWidget *sw; /* swrolled window in which the contents is */
88 };
89
90 /* get a pointer to the parents to be able to call their destructor */
91 static GObjectClass *parent_class = NULL;
92 static gchar *help_str=N_("\"\"\"= Title level 1 =\n"
93 "== Title level 2 ==\n"
94 "\"\"\"= Title level 1 =\n"
95 "== Title level 2 ==\n\n"
96 "\"\"\""
97 "For beautifiers we have **bold**\n"
98 "and //italic//.\n"
99 "There is also __underline__, --strike--\n"
100 "and ``monospaced``.\n"
101 "\"\"\"\n"
102 "For beautifiers we have **bold**\n"
103 "and //italic//.\n"
104 "There is also __underline__, --strike--\n"
105 "and ``monospaced``.\n\n"
106 "\"\"\""
107 "- This is a list of items\n"
108 "- Just use hyphens\n"
109 " - And starting space for indenting\n"
110 "\"\"\"- This is a list of items\n"
111 "- Just use hyphens\n"
112 " - And starting space for indenting\n"
113 "\nRaw areas are enclosed inside three doublequotes and no markup is interpreted");
114
115 /* signals */
116 enum {
117 CHANGED,
118 LAST_SIGNAL
119 };
120
121 static gint gdaui_rt_editor_signals[LAST_SIGNAL] = { 0 };
122
123 /* properties */
124 enum {
125 PROP_0,
126 PROP_NO_BACKGROUND,
127 PROP_SHOW_MARKUP,
128 PROP_TEXTBUFFER,
129 PROP_SCROLLED_WINDOW
130 };
131
132 /* global pixbufs */
133 static gint spaces_since_start_of_line (GtkTextIter *iter);
134 static gchar *real_gdaui_rt_editor_get_contents (GdauiRtEditor *editor);
135
136 static GtkTextTag *iter_begins_list (GdauiRtEditor *rte, GtkTextIter *iter, gint *out_list_level);
137 static void mark_set_cb (GtkTextBuffer *textbuffer, GtkTextIter *location,
138 GtkTextMark *mark, GdauiRtEditor *rte);
139 static void insert_text_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, GdauiRtEditor *rte);
140 static void insert_text_after_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, GdauiRtEditor *rte);
141 static void text_buffer_changed_cb (GtkTextBuffer *textbuffer, GdauiRtEditor *rte);
142 static void populate_popup_cb (GtkTextView *entry, GtkMenu *menu, GdauiRtEditor *rte);
143
144 static void show_hide_toolbar (GdauiRtEditor *editor);
145
146 static gchar *add_newlines_to_base64 (gchar *base64);
147 static gchar *remove_newlines_from_base64 (gchar *base64);
148
149 static void italic_cb (GtkToggleAction *action, GdauiRtEditor *rte);
150 static void strike_cb (GtkToggleAction *action, GdauiRtEditor *rte);
151 static void underline_cb (GtkToggleAction *action, GdauiRtEditor *rte);
152 static void bold_cb (GtkToggleAction *action, GdauiRtEditor *rte);
153 static void reset_all_cb (GtkAction *action, GdauiRtEditor *rte);
154 static void add_image_cb (GtkAction *action, GdauiRtEditor *rte);
155 static void help_cb (GtkToggleAction *action, GdauiRtEditor *rte);
156
157 static const GtkToggleActionEntry ui_toggle_actions [] =
158 {
159 { "ActionBold", GTK_STOCK_BOLD, N_("_Bold"), NULL, N_("Bold text"), G_CALLBACK (bold_cb), FALSE},
160 { "ActionItalic", GTK_STOCK_ITALIC, N_("_Italic"), NULL, N_("Italic text"), G_CALLBACK (italic_cb), FALSE},
161 { "ActionUnderline", GTK_STOCK_UNDERLINE, N_("_Underline"), NULL, N_("Underline text"), G_CALLBACK (underline_cb), FALSE},
162 { "ActionStrike", GTK_STOCK_STRIKETHROUGH, N_("_Strike through"), NULL, N_("Strike through text"), G_CALLBACK (strike_cb), FALSE},
163 { "ActionHelp", GTK_STOCK_HELP, N_("_Syntax help"), NULL, N_("Show syntax help"), G_CALLBACK (help_cb), FALSE}
164 };
165
166 static const GtkActionEntry ui_actions[] = {
167 { "ActionAddImage", "insert-image", N_("_Add image"), NULL, N_("Insert image"), G_CALLBACK (add_image_cb)},
168 { "ActionReset", GTK_STOCK_CLEAR, N_("_Normal text"), NULL, N_("Reset to normal text"), G_CALLBACK (reset_all_cb)},
169 };
170
171 static const gchar *ui_actions_info =
172 "<ui>"
173 " <toolbar name='ToolBar'>"
174 " <toolitem action='ActionBold'/>"
175 " <toolitem action='ActionItalic'/>"
176 " <toolitem action='ActionUnderline'/>"
177 " <toolitem action='ActionStrike'/>"
178 " <toolitem action='ActionAddImage'/>"
179 " <toolitem action='ActionReset'/>"
180 " <toolitem action='ActionHelp'/>"
181 " </toolbar>"
182 "</ui>";
183
184 GType
gdaui_rt_editor_get_type(void)185 gdaui_rt_editor_get_type (void)
186 {
187 static GType type = 0;
188
189 if (G_UNLIKELY (type == 0)) {
190 static const GTypeInfo info = {
191 sizeof (GdauiRtEditorClass),
192 (GBaseInitFunc) NULL,
193 (GBaseFinalizeFunc) NULL,
194 (GClassInitFunc) gdaui_rt_editor_class_init,
195 NULL,
196 NULL,
197 sizeof (GdauiRtEditor),
198 0,
199 (GInstanceInitFunc) gdaui_rt_editor_init,
200 0
201 };
202
203 type = g_type_register_static (GTK_TYPE_BOX, "GdauiRtEditor", &info, 0);
204 }
205
206 return type;
207 }
208
209 static void
gdaui_rt_editor_class_init(GdauiRtEditorClass * klass)210 gdaui_rt_editor_class_init (GdauiRtEditorClass *klass)
211 {
212 GObjectClass *object_class = G_OBJECT_CLASS (klass);
213 parent_class = g_type_class_peek_parent (klass);
214
215 object_class->dispose = gdaui_rt_editor_dispose;
216
217 GTK_WIDGET_CLASS (klass)->show_all = gdaui_rt_editor_show_all;
218
219 /* signals */
220 gdaui_rt_editor_signals[CHANGED] =
221 g_signal_new ("changed",
222 G_TYPE_FROM_CLASS (object_class),
223 G_SIGNAL_RUN_LAST,
224 G_STRUCT_OFFSET (GdauiRtEditorClass, changed),
225 NULL, NULL,
226 g_cclosure_marshal_VOID__VOID,
227 G_TYPE_NONE, 0);
228
229 /* Properties */
230 object_class->set_property = gdaui_rt_editor_set_property;
231 object_class->get_property = gdaui_rt_editor_get_property;
232 /**
233 * GdauiRtEditor:no-background:
234 *
235 * If set to %TRUE, then the default text background is removed
236 * and thus the textbackground is the default widget's background.
237 *
238 * This property has to be set before the widget is realized, and is taken into account only
239 * if the widget is not editable (when it's realized).
240 **/
241 g_object_class_install_property (object_class, PROP_NO_BACKGROUND,
242 g_param_spec_boolean ("no-background",
243 _("Don't display a specific background for the text"),
244 NULL, FALSE,
245 G_PARAM_READABLE | G_PARAM_WRITABLE));
246 /**
247 * GdauiRtEditor:show-markup:
248 *
249 * Instead of showing the formatted text, display the raw text (in the txt2tags syntax)
250 **/
251 g_object_class_install_property (object_class, PROP_SHOW_MARKUP,
252 g_param_spec_boolean ("show-markup",
253 _("Display raw markup text instead of formatted text"),
254 NULL, FALSE,
255 G_PARAM_READABLE | G_PARAM_WRITABLE));
256
257 /**
258 * GdauiRtEditor:buffer:
259 *
260 * Get access to the actual #GtkTextBuffer used. Do not modify it!
261 **/
262 g_object_class_install_property (object_class, PROP_TEXTBUFFER,
263 g_param_spec_object ("buffer",
264 _("The buffer which is displayed"),
265 NULL, GTK_TYPE_TEXT_BUFFER,
266 G_PARAM_READABLE));
267
268 /**
269 * GdauiRtEditor:in-scrolled-window:
270 *
271 * Determines if the contents of the widget appears in a scrolled window or not.
272 **/
273 g_object_class_install_property (object_class, PROP_SCROLLED_WINDOW,
274 g_param_spec_boolean ("in-scrolled-window",
275 _("Determines if the contents appears in a scrolled window"),
276 NULL, TRUE,
277 G_PARAM_READABLE | G_PARAM_WRITABLE));
278 }
279
280 static void
text_view_realized_cb(GtkWidget * tv,GdauiRtEditor * rte)281 text_view_realized_cb (GtkWidget *tv, GdauiRtEditor *rte)
282 {
283 if (rte->priv->no_background && ! gtk_text_view_get_editable (GTK_TEXT_VIEW (tv))) {
284 GdkWindow *win;
285 GtkStyleContext* style_context = gtk_widget_get_style_context (tv);
286 GdkRGBA color;
287 win = gtk_text_view_get_window (GTK_TEXT_VIEW (tv), GTK_TEXT_WINDOW_TEXT);
288 gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_NORMAL, &color);
289 gdk_window_set_background_rgba (win, &color);
290 }
291 }
292
293 static gboolean
focus_changed_cb(GtkWidget * textview,GdkEventFocus * ev,GdauiRtEditor * rte)294 focus_changed_cb (GtkWidget *textview, GdkEventFocus *ev, GdauiRtEditor *rte)
295 {
296 show_hide_toolbar (rte);
297 return FALSE;
298 }
299
300 static void
gdaui_rt_editor_init(GdauiRtEditor * rte)301 gdaui_rt_editor_init (GdauiRtEditor *rte)
302 {
303 GtkWidget *sw, *textview, *toolbar;
304
305 gtk_orientable_set_orientation (GTK_ORIENTABLE (rte), GTK_ORIENTATION_VERTICAL);
306
307 sw = gtk_scrolled_window_new (NULL, NULL);
308 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
309 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
310 gtk_box_pack_end (GTK_BOX (rte), sw, TRUE, TRUE, 0);
311
312 textview = gtk_text_view_new ();
313 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (textview), GTK_WRAP_WORD);
314 gtk_container_add (GTK_CONTAINER (sw), textview);
315 g_signal_connect (textview, "realize",
316 G_CALLBACK (text_view_realized_cb), rte);
317
318 gtk_widget_show_all (sw);
319
320 rte->priv = g_new0 (GdauiRtEditorPriv, 1);
321 rte->priv->sw = sw;
322 rte->priv->vadj_value = 0.;
323 rte->priv->saved_for_help = NULL;
324 rte->priv->enable_changed_signal = TRUE;
325 rte->priv->no_background = FALSE;
326 rte->priv->insert_offset = -1;
327 rte->priv->textview = GTK_TEXT_VIEW (textview);
328 rte->priv->textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
329 rte->priv->contents_setting = FALSE;
330 g_signal_connect (rte->priv->textbuffer, "changed",
331 G_CALLBACK (text_buffer_changed_cb), rte);
332 g_signal_connect (rte->priv->textbuffer, "mark-set",
333 G_CALLBACK (mark_set_cb), rte);
334 g_signal_connect (rte->priv->textbuffer, "insert-text",
335 G_CALLBACK (insert_text_cb), rte);
336 g_signal_connect_after (rte->priv->textbuffer, "insert-text",
337 G_CALLBACK (insert_text_after_cb), rte);
338 g_signal_connect (rte->priv->textview, "populate-popup",
339 G_CALLBACK (populate_popup_cb), rte);
340 g_signal_connect (rte->priv->textview, "focus-in-event",
341 G_CALLBACK (focus_changed_cb), rte);
342 g_signal_connect (rte->priv->textview, "focus-out-event",
343 G_CALLBACK (focus_changed_cb), rte);
344
345 /* tags. REM: leave the LIST* and BULLET tags defined 1st as they will be with less priority
346 * and it affects the result returned by gtk_text_iter_get_tags() */
347 rte->priv->show_markup = FALSE;
348 memset (rte->priv->tags, 0, sizeof (rte->priv->tags));
349
350 rte->priv->tags[TEXT_TAG_LIST1].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
351 "indent", -5,
352 "left_margin", 15,
353 /*"background", "#cbbcbc",*/
354 NULL);
355 rte->priv->tags[TEXT_TAG_LIST2].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
356 "indent", -10,
357 "left_margin", 25,
358 /*"background", "#dcbcbc",*/
359 NULL);
360 rte->priv->tags[TEXT_TAG_ITALIC].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
361 "style", PANGO_STYLE_ITALIC, NULL);
362 rte->priv->tags[TEXT_TAG_ITALIC].action_name = "/ToolBar/ActionItalic";
363
364 rte->priv->tags[TEXT_TAG_BOLD].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
365 "weight", PANGO_WEIGHT_BOLD, NULL);
366 rte->priv->tags[TEXT_TAG_BOLD].action_name = "/ToolBar/ActionBold";
367
368 rte->priv->tags[TEXT_TAG_TT].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
369 "family", "Monospace", NULL);
370 rte->priv->tags[TEXT_TAG_VERBATIM].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
371 "background", "#e5e2e2",
372 NULL);
373
374 rte->priv->tags[TEXT_TAG_UNDERLINE].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
375 "underline", PANGO_UNDERLINE_SINGLE, NULL);
376 rte->priv->tags[TEXT_TAG_UNDERLINE].action_name = "/ToolBar/ActionUnderline";
377
378 rte->priv->tags[TEXT_TAG_STRIKE].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
379 "strikethrough", TRUE, NULL);
380 rte->priv->tags[TEXT_TAG_STRIKE].action_name = "/ToolBar/ActionStrike";
381
382 rte->priv->tags[TEXT_TAG_TITLE1].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
383 "size", 15 * PANGO_SCALE,
384 "weight", PANGO_WEIGHT_SEMIBOLD,
385 NULL);
386 rte->priv->tags[TEXT_TAG_TITLE2].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
387 "size", 13 * PANGO_SCALE,
388 "weight", PANGO_WEIGHT_SEMIBOLD,
389 NULL);
390
391 /* action group */
392 rte->priv->actions_group = gtk_action_group_new ("Actions");
393 gtk_action_group_set_translation_domain (rte->priv->actions_group, GETTEXT_PACKAGE);
394 gtk_action_group_add_toggle_actions (rte->priv->actions_group, ui_toggle_actions,
395 G_N_ELEMENTS (ui_toggle_actions), rte);
396 gtk_action_group_add_actions (rte->priv->actions_group, ui_actions, G_N_ELEMENTS (ui_actions),
397 rte);
398
399 /* ui manager */
400 rte->priv->uimanager = gtk_ui_manager_new ();
401 gtk_ui_manager_insert_action_group (rte->priv->uimanager, rte->priv->actions_group, 0);
402 gtk_ui_manager_add_ui_from_string (rte->priv->uimanager, ui_actions_info, -1, NULL);
403
404 /* toolbar */
405 toolbar = gtk_ui_manager_get_widget (rte->priv->uimanager, "/ToolBar");
406 gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
407 rte->priv->toolbar = toolbar;
408 gtk_box_pack_end (GTK_BOX (rte), toolbar, FALSE, FALSE, 0);
409
410 show_hide_toolbar (rte);
411 }
412
413 /**
414 * gdaui_rt_editor_new:
415 *
416 * Creates a new #GdauiRtEditor widget
417 *
418 * Returns: (transfer full): the new widget
419 *
420 * Since: 4.2.2
421 */
422 GtkWidget *
gdaui_rt_editor_new()423 gdaui_rt_editor_new ()
424 {
425 GtkWidget *rte;
426
427 rte = (GtkWidget *) g_object_new (GDAUI_TYPE_RT_EDITOR, NULL);
428
429 return rte;
430 }
431
432 static void
gdaui_rt_editor_dispose(GObject * object)433 gdaui_rt_editor_dispose (GObject *object)
434 {
435 GdauiRtEditor *rte;
436
437 g_return_if_fail (GDAUI_IS_RT_EDITOR (object));
438 rte = GDAUI_RT_EDITOR (object);
439
440 if (rte->priv) {
441 if (rte->priv->actions_group) {
442 g_object_unref (G_OBJECT (rte->priv->actions_group));
443 rte->priv->actions_group = NULL;
444 }
445
446 if (rte->priv->uimanager)
447 g_object_unref (rte->priv->uimanager);
448
449 g_free (rte->priv->saved_for_help);
450 /* NB: GtkTextTags are owned by the GtkTextBuffer's text tags table */
451
452 /* the private area itself */
453 g_free (rte->priv);
454 rte->priv = NULL;
455 }
456
457 /* for the parent class */
458 parent_class->dispose (object);
459 }
460
461 static void
gdaui_rt_editor_show_all(GtkWidget * widget)462 gdaui_rt_editor_show_all (GtkWidget *widget)
463 {
464 GdauiRtEditor *editor;
465 editor = GDAUI_RT_EDITOR (widget);
466 GTK_WIDGET_CLASS (parent_class)->show_all (widget);
467 show_hide_toolbar (editor);
468 }
469
470 static void
gdaui_rt_editor_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)471 gdaui_rt_editor_set_property (GObject *object,
472 guint param_id,
473 const GValue *value,
474 GParamSpec *pspec)
475 {
476 GdauiRtEditor *editor;
477
478 editor = GDAUI_RT_EDITOR (object);
479 if (editor->priv) {
480 switch (param_id) {
481 case PROP_NO_BACKGROUND:
482 editor->priv->no_background = g_value_get_boolean (value);
483 break;
484 case PROP_SHOW_MARKUP:
485 _gdaui_rt_editor_set_show_markup (editor, g_value_get_boolean (value));
486 break;
487 case PROP_SCROLLED_WINDOW: {
488 gboolean setting;
489 setting = g_value_get_boolean (value);
490 if ((editor->priv->sw && setting) ||
491 (! editor->priv->sw && ! setting))
492 break; /* nothing to change */
493 if (editor->priv->sw) {
494 /* remove scrolled window */
495 gtk_widget_reparent (GTK_WIDGET (editor->priv->textview), GTK_WIDGET (editor));
496 gtk_widget_destroy (editor->priv->sw);
497 editor->priv->sw = NULL;
498 }
499 else {
500 /* add scrolled window */
501 GtkWidget *sw;
502 sw = gtk_scrolled_window_new (NULL, NULL);
503 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
504 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
505 gtk_box_pack_end (GTK_BOX (editor), sw, TRUE, TRUE, 0);
506 editor->priv->sw = sw;
507 gtk_widget_show (sw);
508 gtk_widget_reparent (GTK_WIDGET (editor->priv->textview), GTK_WIDGET (sw));
509 }
510 break;
511 }
512 default:
513 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
514 break;
515 }
516 }
517 }
518
519 static void
gdaui_rt_editor_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)520 gdaui_rt_editor_get_property (GObject *object,
521 guint param_id,
522 GValue *value,
523 GParamSpec *pspec)
524 {
525 GdauiRtEditor *editor;
526
527 editor = GDAUI_RT_EDITOR (object);
528 if (editor->priv) {
529 switch (param_id) {
530 case PROP_NO_BACKGROUND:
531 g_value_set_boolean (value, editor->priv->no_background);
532 break;
533 case PROP_SHOW_MARKUP:
534 g_value_set_boolean (value, editor->priv->show_markup);
535 break;
536 case PROP_TEXTBUFFER:
537 g_value_set_object (value, editor->priv->textbuffer);
538 break;
539 case PROP_SCROLLED_WINDOW:
540 g_value_set_boolean (value, editor->priv->sw ? TRUE : FALSE);
541 break;
542 default:
543 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
544 break;
545 }
546 }
547 }
548
549 static GtkTextTag *
iter_begins_list(GdauiRtEditor * rte,GtkTextIter * iter,gint * out_list_level)550 iter_begins_list (GdauiRtEditor *rte, GtkTextIter *iter, gint *out_list_level)
551 {
552 gint idx = -1;
553 GtkTextTag *tag = NULL;
554 if (gtk_text_iter_has_tag (iter, rte->priv->tags[TEXT_TAG_LIST1].tag)) {
555 tag = rte->priv->tags[TEXT_TAG_LIST1].tag;
556 idx = 0;
557 }
558 else if (gtk_text_iter_has_tag (iter, rte->priv->tags[TEXT_TAG_LIST2].tag)) {
559 tag = rte->priv->tags[TEXT_TAG_LIST2].tag;
560 idx = 1;
561 }
562 if (out_list_level)
563 *out_list_level = idx;
564 return tag;
565 }
566
567 /* tags management */
568 static void
apply_tag(GdauiRtEditor * rte,gboolean reverse,GtkTextTag * tag)569 apply_tag (GdauiRtEditor *rte, gboolean reverse, GtkTextTag *tag)
570 {
571 GtkTextIter start;
572 GtkTextIter end;
573
574 g_return_if_fail (rte->priv->textbuffer);
575
576 if (rte->priv->selection_changing)
577 return;
578
579 if (gtk_text_buffer_get_selection_bounds (rte->priv->textbuffer, &start, &end)) {
580 if (tag) {
581 if (reverse)
582 gtk_text_buffer_remove_tag (rte->priv->textbuffer, tag, &start, &end);
583 else {
584 gtk_text_buffer_apply_tag (rte->priv->textbuffer, tag, &start, &end);
585 /* if there are LIST tags, then remove the applied tag */
586 GtkTextIter iter;
587 for (iter = start; gtk_text_iter_compare (&iter, &end) < 0; ) {
588 GtkTextTag *ltag;
589 gint idx;
590 ltag = iter_begins_list (rte, &iter, &idx);
591 if (ltag) {
592 GtkTextIter liter;
593 liter = iter;
594 gtk_text_iter_forward_to_tag_toggle (&liter, ltag);
595 gtk_text_iter_backward_char (&iter);
596 gtk_text_buffer_remove_tag (rte->priv->textbuffer,
597 tag, &iter, &liter);
598 gtk_text_iter_forward_char (&iter);
599 }
600 if (! gtk_text_iter_forward_char (&iter))
601 break;
602 }
603 }
604 }
605 else {
606 GtkTextIter iter;
607 for (iter = start; gtk_text_iter_compare (&iter, &end) < 0; ) {
608
609 GSList *tags, *list;
610 tags = gtk_text_iter_get_tags (&iter);
611 if (tags) {
612 for (list = tags; list; list = list->next) {
613 GtkTextTag *tag = (GtkTextTag *) list->data;
614 if ((tag != rte->priv->tags[TEXT_TAG_LIST1].tag) &&
615 (tag != rte->priv->tags[TEXT_TAG_LIST2].tag))
616 gtk_text_buffer_remove_tag (rte->priv->textbuffer,
617 tag, &start, &end);
618 }
619 g_slist_free (tags);
620 }
621 if (! gtk_text_iter_forward_char (&iter))
622 break;
623 }
624 }
625 }
626
627 if (rte->priv->enable_changed_signal)
628 g_signal_emit (rte, gdaui_rt_editor_signals[CHANGED], 0, NULL);
629
630 }
631
632 static void
help_cb(GtkToggleAction * action,GdauiRtEditor * rte)633 help_cb (GtkToggleAction *action, GdauiRtEditor *rte)
634 {
635 if (gtk_toggle_action_get_active (action)) {
636 rte->priv->enable_changed_signal = FALSE;
637 g_free (rte->priv->saved_for_help);
638 rte->priv->saved_for_help = gdaui_rt_editor_get_contents (rte);
639 gdaui_rt_editor_set_contents (rte, help_str, -1);
640 }
641 else {
642 gdaui_rt_editor_set_contents (rte, rte->priv->saved_for_help, -1);
643 rte->priv->enable_changed_signal = TRUE;
644 g_free (rte->priv->saved_for_help);
645 rte->priv->saved_for_help = NULL;
646 }
647 }
648
649 static void
italic_cb(GtkToggleAction * action,GdauiRtEditor * rte)650 italic_cb (GtkToggleAction *action, GdauiRtEditor *rte)
651 {
652 apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
653 rte->priv->tags [TEXT_TAG_ITALIC].tag);
654 }
655
656 static void
bold_cb(GtkToggleAction * action,GdauiRtEditor * rte)657 bold_cb (GtkToggleAction *action, GdauiRtEditor *rte)
658 {
659 apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
660 rte->priv->tags [TEXT_TAG_BOLD].tag);
661 }
662
663 static void
strike_cb(GtkToggleAction * action,GdauiRtEditor * rte)664 strike_cb (GtkToggleAction *action, GdauiRtEditor *rte)
665 {
666 apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
667 rte->priv->tags [TEXT_TAG_STRIKE].tag);
668 }
669
670 static void
underline_cb(GtkToggleAction * action,GdauiRtEditor * rte)671 underline_cb (GtkToggleAction *action, GdauiRtEditor *rte)
672 {
673 apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
674 rte->priv->tags [TEXT_TAG_UNDERLINE].tag);
675 }
676
677 static void
reset_all_cb(G_GNUC_UNUSED GtkAction * action,GdauiRtEditor * rte)678 reset_all_cb (G_GNUC_UNUSED GtkAction *action, GdauiRtEditor *rte)
679 {
680 apply_tag (rte, FALSE, NULL);
681 }
682
683 static void
add_image_cb(G_GNUC_UNUSED GtkAction * action,GdauiRtEditor * rte)684 add_image_cb (G_GNUC_UNUSED GtkAction *action, GdauiRtEditor *rte)
685 {
686 GtkWidget *dlg;
687 GtkFileFilter *filter;
688
689 dlg = gtk_file_chooser_dialog_new (_("Select image to load"),
690 GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) rte)),
691 GTK_FILE_CHOOSER_ACTION_OPEN,
692 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
693 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
694 NULL);
695 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg),
696 gdaui_get_default_path ());
697 filter = gtk_file_filter_new ();
698 gtk_file_filter_add_pixbuf_formats (filter);
699 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dlg), filter);
700
701 if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
702 char *filename;
703 GError *error = NULL;
704
705 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
706 gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg)));
707
708 GdkPixbuf *pixbuf;
709 pixbuf = gdk_pixbuf_new_from_file (filename, &error);
710 if (pixbuf) {
711 GtkTextIter start;
712 GtkTextIter end;
713 gboolean ret = FALSE;
714
715 ret = gtk_text_buffer_get_selection_bounds (rte->priv->textbuffer, &start, &end);
716 if (ret)
717 gtk_text_buffer_delete (rte->priv->textbuffer, &start, &end);
718
719 gtk_text_buffer_get_iter_at_mark (rte->priv->textbuffer, &start,
720 gtk_text_buffer_get_insert (rte->priv->textbuffer));
721 gtk_text_buffer_insert_pixbuf (rte->priv->textbuffer, &start, pixbuf);
722 g_object_unref (pixbuf);
723 }
724 else {
725 GtkWidget *msg;
726
727 msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) rte)),
728 GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
729 GTK_BUTTONS_CLOSE,
730 _("Could not load the contents of '%s':\n %s"),
731 filename,
732 error && error->message ? error->message : _("No detail"));
733 g_clear_error (&error);
734 gtk_widget_destroy (dlg);
735 dlg = NULL;
736
737 gtk_dialog_run (GTK_DIALOG (msg));
738 gtk_widget_destroy (msg);
739 }
740 }
741
742 gtk_widget_destroy (dlg);
743 }
744
745 static void
mark_set_cb(GtkTextBuffer * textbuffer,GtkTextIter * location,GtkTextMark * mark,GdauiRtEditor * rte)746 mark_set_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, GtkTextMark *mark, GdauiRtEditor *rte)
747 {
748 if (mark == gtk_text_buffer_get_insert (textbuffer)) {
749 GtkAction *action;
750 gboolean act;
751 gint i;
752
753 rte->priv->selection_changing = TRUE;
754
755 for (i = 0; i < TEXT_TAG_LAST; i++) {
756 if (! rte->priv->tags[i].action_name)
757 continue;
758
759 action = gtk_ui_manager_get_action (rte->priv->uimanager,
760 rte->priv->tags[i].action_name);
761 if (gtk_text_buffer_get_has_selection (textbuffer))
762 act = FALSE;
763 else
764 act = gtk_text_iter_has_tag (location, rte->priv->tags [i].tag);
765 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), act);
766 }
767
768 rte->priv->selection_changing = FALSE;
769 }
770 }
771
772 static void
insert_text_cb(GtkTextBuffer * textbuffer,GtkTextIter * location,G_GNUC_UNUSED gchar * text,G_GNUC_UNUSED gint len,GdauiRtEditor * rte)773 insert_text_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, G_GNUC_UNUSED gchar *text, G_GNUC_UNUSED gint len, GdauiRtEditor *rte)
774 {
775 /* if inserting is before a bullet, then insert right after */
776 GtkTextTag *tag;
777 tag = iter_begins_list (rte, location, NULL);
778 if (tag) {
779 gtk_text_iter_forward_char (location);
780 gtk_text_buffer_place_cursor (textbuffer, location);
781 }
782 rte->priv->insert_offset = gtk_text_iter_get_offset (location);
783 }
784
785 static void
insert_text_after_cb(GtkTextBuffer * textbuffer,GtkTextIter * location,gchar * text,G_GNUC_UNUSED gint len,GdauiRtEditor * rte)786 insert_text_after_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, G_GNUC_UNUSED gint len, GdauiRtEditor *rte)
787 {
788 GtkTextIter start, end;
789
790 if ((rte->priv->insert_offset < 0) || rte->priv->show_markup)
791 return;
792
793 /* disable any extra modification while text is being set using gdaui_rt_editor_set_contents() */
794 if (rte->priv->contents_setting)
795 return;
796
797 /* apply selected tag in toolbar if any */
798 gtk_text_buffer_get_iter_at_offset (textbuffer, &start, rte->priv->insert_offset);
799 end = *location;
800 if (gtk_text_iter_backward_chars (&end, g_utf8_strlen (text, -1))) {
801 gint i;
802 for (i = 0; i < TEXT_TAG_LAST; i++) {
803 GtkAction *action;
804 if (! rte->priv->tags[i].action_name)
805 continue;
806 action = gtk_ui_manager_get_action (rte->priv->uimanager,
807 rte->priv->tags[i].action_name);
808 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
809 gtk_text_buffer_apply_tag (rte->priv->textbuffer,
810 rte->priv->tags[i].tag, location, &end);
811 }
812 }
813 rte->priv->insert_offset = -1;
814
815 gtk_text_iter_set_line_offset (&start, 0);
816 /* add new bullet if already in list */
817 if (*text == '\n') {
818 gchar *text_to_insert = NULL;
819 GtkTextTag *tag;
820 gint index;
821 tag = iter_begins_list (rte, &start, &index);
822 if (tag)
823 text_to_insert = lists_tokens [index];
824
825 if (text_to_insert) {
826 if (! gtk_text_iter_forward_char (&end))
827 gtk_text_buffer_get_end_iter (textbuffer, &end);
828 gtk_text_buffer_insert (textbuffer, &end, text_to_insert, -1);
829 }
830 }
831 else {
832 GtkTextTag *tag = NULL;
833 end = *location;
834
835 if (gtk_text_iter_begins_tag (&start, rte->priv->tags[TEXT_TAG_TITLE1].tag))
836 tag = rte->priv->tags[TEXT_TAG_TITLE1].tag;
837 else if (gtk_text_iter_begins_tag (&start, rte->priv->tags[TEXT_TAG_TITLE2].tag))
838 tag = rte->priv->tags[TEXT_TAG_TITLE2].tag;
839 if (tag)
840 gtk_text_buffer_apply_tag (rte->priv->textbuffer, tag, &start, &end);
841 }
842 }
843
844 static void
show_markup_item_activate_cb(GtkCheckMenuItem * checkmenuitem,GdauiRtEditor * rte)845 show_markup_item_activate_cb (GtkCheckMenuItem *checkmenuitem, GdauiRtEditor *rte)
846 {
847 gboolean show;
848 show = gtk_check_menu_item_get_active (checkmenuitem);
849 _gdaui_rt_editor_set_show_markup (rte, show);
850 }
851
852 static void
bigger_font_item_activate_cb(G_GNUC_UNUSED GtkCheckMenuItem * checkmenuitem,GdauiRtEditor * rte)853 bigger_font_item_activate_cb (G_GNUC_UNUSED GtkCheckMenuItem *checkmenuitem,
854 GdauiRtEditor *rte)
855 {
856 PangoContext *pcontext;
857 PangoFontDescription *fd, *nfd;
858 pcontext = gtk_widget_get_pango_context (GTK_WIDGET (rte->priv->textview));
859 fd = pango_context_get_font_description (pcontext);
860 nfd = pango_font_description_copy_static (fd);
861 pango_font_description_set_size (nfd, pango_font_description_get_size (fd) * 1.2);
862 gtk_widget_override_font (GTK_WIDGET (rte->priv->textview), nfd);
863 pango_font_description_free (nfd);
864 }
865
866 static void
smaller_font_item_activate_cb(G_GNUC_UNUSED GtkCheckMenuItem * checkmenuitem,GdauiRtEditor * rte)867 smaller_font_item_activate_cb (G_GNUC_UNUSED GtkCheckMenuItem *checkmenuitem,
868 GdauiRtEditor *rte)
869 {
870 PangoContext *pcontext;
871 PangoFontDescription *fd, *nfd;
872 pcontext = gtk_widget_get_pango_context (GTK_WIDGET (rte->priv->textview));
873 fd = pango_context_get_font_description (pcontext);
874 nfd = pango_font_description_copy_static (fd);
875 pango_font_description_set_size (nfd, pango_font_description_get_size (fd) / 1.2);
876 gtk_widget_override_font (GTK_WIDGET (rte->priv->textview), nfd);
877 pango_font_description_free (nfd);
878 }
879
880 static void
reset_font_item_activate_cb(G_GNUC_UNUSED GtkCheckMenuItem * checkmenuitem,GdauiRtEditor * rte)881 reset_font_item_activate_cb (G_GNUC_UNUSED GtkCheckMenuItem *checkmenuitem,
882 GdauiRtEditor *rte)
883 {
884 gtk_widget_override_font (GTK_WIDGET (rte->priv->textview), NULL);
885 }
886
887 static void
populate_popup_cb(G_GNUC_UNUSED GtkTextView * entry,GtkMenu * menu,GdauiRtEditor * rte)888 populate_popup_cb (G_GNUC_UNUSED GtkTextView *entry, GtkMenu *menu, GdauiRtEditor *rte)
889 {
890 GtkWidget *item;
891
892 item = gtk_separator_menu_item_new ();
893 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
894 gtk_widget_show (item);
895
896 item = gtk_menu_item_new_with_label (_("Reset font size"));
897 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
898 g_signal_connect (G_OBJECT (item), "activate",
899 G_CALLBACK (reset_font_item_activate_cb), rte);
900 gtk_widget_show (item);
901
902 item = gtk_menu_item_new_with_label (_("Decrease font size (zoom out)"));
903 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
904 g_signal_connect (G_OBJECT (item), "activate",
905 G_CALLBACK (smaller_font_item_activate_cb), rte);
906 gtk_widget_show (item);
907
908 item = gtk_menu_item_new_with_label (_("Increase font size (zoom in)"));
909 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
910 g_signal_connect (G_OBJECT (item), "activate",
911 G_CALLBACK (bigger_font_item_activate_cb), rte);
912 gtk_widget_show (item);
913
914 item = gtk_check_menu_item_new_with_label (_("Show source markup"));
915 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
916 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), rte->priv->show_markup);
917 g_signal_connect (G_OBJECT (item), "toggled",
918 G_CALLBACK (show_markup_item_activate_cb), rte);
919 gtk_widget_show (item);
920 }
921
922 /* RTE markup analysis */
923 typedef enum {
924 MARKUP_NONE, /* 0 */
925 MARKUP_BOLD,
926 MARKUP_TT,
927 MARKUP_VERBATIM,
928 MARKUP_ITALIC,
929 MARKUP_STRIKE, /* 5 */
930 MARKUP_UNDERLINE,
931
932 MARKUP_TITLE1_S,
933 MARKUP_TITLE1_E,
934 MARKUP_TITLE2_S,
935 MARKUP_TITLE2_E, /* 10 */
936
937 MARKUP_PICTURE_S,
938 MARKUP_PICTURE_E,
939
940 MARKUP_LIST_S,
941 MARKUP_LIST_E,
942
943 MARKUP_EOF
944 } MarkupTag;
945
946 typedef struct {
947 GtkTextMark *m_start;
948 GtkTextMark *m_end;
949 gboolean init;
950 MarkupTag markup;
951 } TextTag;
952
953
954 static MarkupTag get_markup_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
955 TextTag *start_tag, GdauiRtEditor *rte);
956 static MarkupTag get_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
957 TextTag *start_tag, GdauiRtEditor *rte);
958 static gchar get_char_at_iter (GtkTextIter *iter, gboolean move_forward_first);
959 static gboolean markup_tag_match (MarkupTag tag1, gint tagline1, MarkupTag tag2, gint tagline2);
960
961 static void
apply_markup(GdauiRtEditor * rte,GtkTextBuffer * textbuffer,TextTag * current,GtkTextMark * mark_start,GtkTextMark * mark_end)962 apply_markup (GdauiRtEditor *rte, GtkTextBuffer *textbuffer, TextTag *current, GtkTextMark *mark_start, GtkTextMark *mark_end)
963 {
964 gint ssol;
965 GtkTextIter start, end;
966
967 rte->priv->insert_offset = -1;
968
969 gtk_text_buffer_get_iter_at_mark (textbuffer, &start, mark_start);
970 gtk_text_buffer_get_iter_at_mark (textbuffer, &end, mark_end);
971
972 /* apply markup */
973 GtkTextIter astart;
974 gtk_text_buffer_get_iter_at_mark (textbuffer, &astart, current->m_start);
975 switch (current->markup) {
976 case MARKUP_BOLD:
977 gtk_text_buffer_apply_tag (textbuffer,
978 rte->priv->tags[TEXT_TAG_BOLD].tag,
979 &astart, &end);
980 break;
981 case MARKUP_VERBATIM:
982 gtk_text_buffer_apply_tag (textbuffer,
983 rte->priv->tags[TEXT_TAG_VERBATIM].tag,
984 &astart, &end);
985 break;
986 case MARKUP_TT:
987 gtk_text_buffer_apply_tag (textbuffer,
988 rte->priv->tags[TEXT_TAG_TT].tag,
989 &astart, &end);
990 break;
991 case MARKUP_ITALIC:
992 gtk_text_buffer_apply_tag (textbuffer,
993 rte->priv->tags[TEXT_TAG_ITALIC].tag,
994 &astart, &end);
995 break;
996 case MARKUP_STRIKE:
997 gtk_text_buffer_apply_tag (textbuffer,
998 rte->priv->tags[TEXT_TAG_STRIKE].tag,
999 &astart, &end);
1000 break;
1001 case MARKUP_UNDERLINE:
1002 gtk_text_buffer_apply_tag (textbuffer,
1003 rte->priv->tags[TEXT_TAG_UNDERLINE].tag,
1004 &astart, &end);
1005 break;
1006 case MARKUP_TITLE1_S:
1007 gtk_text_buffer_apply_tag (textbuffer,
1008 rte->priv->tags[TEXT_TAG_TITLE1].tag,
1009 &astart, &end);
1010 break;
1011 case MARKUP_TITLE2_S:
1012 gtk_text_buffer_apply_tag (textbuffer,
1013 rte->priv->tags[TEXT_TAG_TITLE2].tag,
1014 &astart, &end);
1015 break;
1016 case MARKUP_LIST_S: {
1017 GtkTextIter ps, pe;
1018 gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_start);
1019 ssol = spaces_since_start_of_line (&ps);
1020 if (ssol > 0) {
1021 GtkTextIter diter;
1022 diter = ps;
1023 if (gtk_text_iter_backward_chars (&diter, ssol))
1024 gtk_text_buffer_delete (textbuffer, &diter, &ps);
1025 }
1026
1027 GtkTextTag *tag;
1028 gint bindex = 0;
1029 gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_end);
1030 if (ssol > 0) {
1031 bindex = 1;
1032 tag = rte->priv->tags[TEXT_TAG_LIST2].tag;
1033 }
1034 else
1035 tag = rte->priv->tags[TEXT_TAG_LIST1].tag;
1036
1037 if (! bullet_pix[0]) {
1038 bullet_pix[0] = gdk_pixbuf_new_from_inline (-1, bullet_pixdata,
1039 FALSE, NULL);
1040 bullet_pix[1] = gdk_pixbuf_new_from_inline (-1, bulleth_pixdata,
1041 FALSE, NULL);
1042 }
1043 gtk_text_buffer_insert_pixbuf (textbuffer, &ps, bullet_pix[bindex]);
1044 gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_end);
1045 pe = ps;
1046 gtk_text_iter_forward_char (&pe);
1047 gtk_text_buffer_apply_tag (rte->priv->textbuffer, tag, &ps, &pe);
1048
1049 /* remove all other tags */
1050 gint i;
1051 gtk_text_iter_set_line_index (&ps, 0);
1052 gtk_text_iter_backward_char (&ps); /* to catch the previous line's '\n' */
1053 for (i = 0; i < TEXT_TAG_LAST; i++) {
1054 if (rte->priv->tags[i].tag == tag)
1055 continue;
1056 else
1057 gtk_text_buffer_remove_tag (rte->priv->textbuffer,
1058 rte->priv->tags[i].tag, &ps, &pe);
1059 }
1060
1061 break;
1062 }
1063 case MARKUP_PICTURE_S: {
1064 gchar *data;
1065 GtkTextIter ps, pe;
1066 gsize length;
1067 GdkPixdata pixdata;
1068 gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_end);
1069 pe = start;
1070 data = remove_newlines_from_base64 (gtk_text_buffer_get_text (textbuffer, &ps, &pe, FALSE));
1071 /*g_print ("{{{%s}}}\n", data);*/
1072 g_base64_decode_inplace (data, &length);
1073 if (gdk_pixdata_deserialize (&pixdata, length, (guint8*) data, NULL)) {
1074 GdkPixbuf *pixbuf;
1075 pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, NULL);
1076 if (pixbuf) {
1077 gtk_text_buffer_delete (textbuffer, &ps, &pe);
1078 gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, mark_end);
1079 gtk_text_buffer_insert_pixbuf (textbuffer, &ps, pixbuf);
1080 g_object_unref (pixbuf);
1081 }
1082 }
1083 g_free (data);
1084 break;
1085 }
1086 default:
1087 g_warning ("Unhandled marker (type %d)", current->markup);
1088 break;
1089 }
1090 /* remove markup text */
1091 gtk_text_buffer_get_iter_at_mark (textbuffer, &start, mark_start);
1092 gtk_text_buffer_get_iter_at_mark (textbuffer, &end, mark_end);
1093 if (! gtk_text_iter_equal (&start, &end))
1094 gtk_text_buffer_delete (textbuffer, &start, &end);
1095 gtk_text_buffer_get_iter_at_mark (textbuffer, &start, current->m_start);
1096 gtk_text_buffer_get_iter_at_mark (textbuffer, &end, current->m_end);
1097 if (! gtk_text_iter_equal (&start, &end))
1098 gtk_text_buffer_delete (textbuffer, &start, &end);
1099 }
1100
1101 static void
text_buffer_changed_cb(GtkTextBuffer * textbuffer,GdauiRtEditor * rte)1102 text_buffer_changed_cb (GtkTextBuffer *textbuffer, GdauiRtEditor *rte)
1103 {
1104 GSList *queue = NULL;
1105 MarkupTag mt;
1106 TextTag *current = NULL;
1107 GtkTextIter start, end;
1108 gint ssol;
1109
1110 if (rte->priv->show_markup) {
1111 if (rte->priv->enable_changed_signal)
1112 g_signal_emit (rte, gdaui_rt_editor_signals[CHANGED], 0, NULL);
1113 return;
1114 }
1115
1116 gtk_text_buffer_get_start_iter (textbuffer, &start);
1117
1118 g_signal_handlers_block_by_func (textbuffer,
1119 G_CALLBACK (text_buffer_changed_cb), rte);
1120
1121 for (mt = get_token (&start, &ssol, &end, current, rte);
1122 mt != MARKUP_EOF;
1123 mt = get_token (&start, &ssol, &end, current, rte)) {
1124 /*
1125 gchar *text= gtk_text_iter_get_text (&start, &end);
1126 g_print ("Token %d [%s] with SSOL %d\n", mt, text, ssol);
1127 g_free (text);
1128 */
1129 if (mt == MARKUP_NONE) {
1130 start = end;
1131 continue;
1132 }
1133 if (! current) {
1134 current = g_new (TextTag, 1);
1135 current->markup = mt;
1136 current->m_start = gtk_text_buffer_create_mark (textbuffer, NULL, &start, TRUE);
1137 current->m_end = gtk_text_buffer_create_mark (textbuffer, NULL, &end, TRUE);
1138
1139 queue = g_slist_prepend (queue, current);
1140 }
1141 else {
1142 GtkTextIter liter;
1143 gtk_text_buffer_get_iter_at_mark (textbuffer, &liter, current->m_start);
1144 if (markup_tag_match (current->markup, gtk_text_iter_get_line (&liter),
1145 mt, gtk_text_iter_get_line (&start))) {
1146 /* save iters as marks */
1147 GtkTextMark *mark_start, *mark_end;
1148 mark_start = gtk_text_buffer_create_mark (textbuffer, NULL, &start, TRUE);
1149 mark_end = gtk_text_buffer_create_mark (textbuffer, NULL, &end, TRUE);
1150
1151 /* apply markup */
1152 apply_markup (rte, textbuffer, current, mark_start, mark_end);
1153
1154 /* get rid of @current */
1155 gtk_text_buffer_delete_mark (textbuffer, current->m_start);
1156 gtk_text_buffer_delete_mark (textbuffer, current->m_end);
1157 g_free (current);
1158 queue = g_slist_remove (queue, current);
1159 current = NULL;
1160
1161 if (queue) {
1162 current = (TextTag*) queue->data;
1163 gtk_text_buffer_get_iter_at_mark (textbuffer, &end, current->m_end);
1164 }
1165 else {
1166 /* restore iter from marks */
1167 gtk_text_buffer_get_iter_at_mark (textbuffer, &end, mark_end);
1168 }
1169
1170 /* delete marks */
1171 gtk_text_buffer_delete_mark (textbuffer, mark_end);
1172 gtk_text_buffer_delete_mark (textbuffer, mark_start);
1173 }
1174 else {
1175 current = g_new (TextTag, 1);
1176 current->markup = mt;
1177 current->m_start = gtk_text_buffer_create_mark (textbuffer, NULL, &start,
1178 TRUE);
1179 current->m_end = gtk_text_buffer_create_mark (textbuffer, NULL, &end, TRUE);
1180
1181 queue = g_slist_prepend (queue, current);
1182 }
1183 }
1184
1185 start = end;
1186 }
1187
1188 while (queue) {
1189 current = (TextTag*) queue->data;
1190 gtk_text_buffer_delete_mark (textbuffer, current->m_start);
1191 gtk_text_buffer_delete_mark (textbuffer, current->m_end);
1192 g_free (current);
1193 queue = g_slist_delete_link (queue, queue);
1194 }
1195
1196 g_signal_handlers_unblock_by_func (textbuffer,
1197 G_CALLBACK (text_buffer_changed_cb), rte);
1198
1199 if (rte->priv->enable_changed_signal)
1200 g_signal_emit (rte, gdaui_rt_editor_signals[CHANGED], 0, NULL);
1201 }
1202
1203 /*
1204 * get_token
1205 *
1206 * returns the token type starting from @iter, and positions @out_end to the last used position
1207 * position.
1208 *
1209 * Returns: a #MarkupTag
1210 */
1211 static MarkupTag
get_token(GtkTextIter * iter,gint * out_nb_spaces_before,GtkTextIter * out_end,TextTag * start_tag,GdauiRtEditor * rte)1212 get_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
1213 TextTag *start_tag, GdauiRtEditor *rte)
1214 {
1215 MarkupTag retval;
1216 GtkTextIter inti;
1217 inti = *iter;
1218
1219 retval = get_markup_token (&inti, out_nb_spaces_before, out_end, start_tag, rte);
1220 if ((retval != MARKUP_NONE) || (retval == MARKUP_EOF))
1221 return retval;
1222
1223 for (; gtk_text_iter_forward_char (&inti);) {
1224 retval = get_markup_token (&inti, NULL, NULL, start_tag, rte);
1225 if ((retval != MARKUP_NONE) || (retval == MARKUP_EOF))
1226 break;
1227 }
1228 *out_end = inti;
1229 return MARKUP_NONE;
1230 }
1231
1232 /*
1233 * spaces_since_start_of_line
1234 * @iter: an iterator, __not modified__
1235 *
1236 * Computes the number of spaces since start of line in case there is no other
1237 * character on the line except for spaces
1238 *
1239 * Returns: number of spaces, or -1 if there is not only some spaces on the line before @iter
1240 */
1241 static gint
spaces_since_start_of_line(GtkTextIter * iter)1242 spaces_since_start_of_line (GtkTextIter *iter)
1243 {
1244 gint i = 0;
1245 GtkTextIter inti = *iter;
1246 gunichar u;
1247 for (; !gtk_text_iter_starts_line (&inti) && gtk_text_iter_backward_char (&inti); i++) {
1248 u = gtk_text_iter_get_char (&inti);
1249 if (! g_unichar_isspace (u))
1250 return -1;
1251 }
1252
1253 #ifdef GDA_DEBUG_NO
1254 gchar *data;
1255 data = gtk_text_buffer_get_slice (gtk_text_iter_get_buffer (iter), &inti, iter, TRUE);
1256 g_print ("FOUND %d spaces for [%s]\n", i, data);
1257 g_free (data);
1258 #endif
1259 return i;
1260 }
1261
1262 /**
1263 * get_markup_token:
1264 * @iter: starting position
1265 * @out_nb_spaces_before: a place to set the value returned by spaces_since_start_of_line() if called
1266 * @out_end: (allow-none): place to put the last used position, or %NULL
1267 * @rte: the #GdauiRtEditor
1268 *
1269 * Parses marking tokens, nothing else
1270 *
1271 * Returns: a markup token, or MARKUP_NONE or MARKUP_EOF otherwise
1272 */
1273 static MarkupTag
get_markup_token(GtkTextIter * iter,gint * out_nb_spaces_before,GtkTextIter * out_end,TextTag * start_tag,GdauiRtEditor * rte)1274 get_markup_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
1275 TextTag *start_tag, GdauiRtEditor *rte)
1276 {
1277 GtkTextIter inti;
1278 gchar c;
1279 gint ssol = -1; /* spaces since start of line */
1280 MarkupTag start_markup = MARKUP_EOF;
1281 gint linestart = 0;
1282
1283 #define SET_OUT \
1284 if (out_end) { \
1285 gtk_text_iter_forward_char (&inti); \
1286 *out_end = inti; \
1287 } \
1288 if (out_nb_spaces_before) \
1289 *out_nb_spaces_before = ssol
1290
1291 if (start_tag) {
1292 start_markup = start_tag->markup;
1293 gtk_text_buffer_get_iter_at_mark (gtk_text_iter_get_buffer (iter), &inti, start_tag->m_start);
1294 linestart = gtk_text_iter_get_line (&inti);
1295 }
1296
1297 inti = *iter;
1298 if (out_end)
1299 *out_end = inti;
1300
1301 c = get_char_at_iter (&inti, FALSE);
1302
1303 /* tests involving starting markup before anything else */
1304 if (start_markup == MARKUP_PICTURE_S) {
1305 if (c == ']') {
1306 c = get_char_at_iter (&inti, TRUE);
1307 if (c == ']') {
1308 c = get_char_at_iter (&inti, TRUE);
1309 if (c == ']') {
1310 SET_OUT;
1311 return MARKUP_PICTURE_E;
1312 }
1313 }
1314 }
1315 if (!c)
1316 return MARKUP_EOF;
1317 else
1318 return MARKUP_NONE;
1319 }
1320 else if (start_markup == MARKUP_VERBATIM) {
1321 if (c == '"') {
1322 c = get_char_at_iter (&inti, TRUE);
1323 if (c == '"') {
1324 c = get_char_at_iter (&inti, TRUE);
1325 if (c == '"') {
1326 SET_OUT;
1327 return MARKUP_VERBATIM;
1328 }
1329 }
1330 }
1331 if (!c)
1332 return MARKUP_EOF;
1333 else
1334 return MARKUP_NONE;
1335 }
1336 else if (gtk_text_iter_has_tag (&inti, rte->priv->tags[TEXT_TAG_VERBATIM].tag)) {
1337 if (!c)
1338 return MARKUP_EOF;
1339 else
1340 return MARKUP_NONE;
1341 }
1342
1343 if (gtk_text_iter_ends_line (&inti) && (start_markup == MARKUP_LIST_S) &&
1344 (linestart == gtk_text_iter_get_line (&inti)))
1345 return MARKUP_LIST_E;
1346
1347 if (!c)
1348 return MARKUP_EOF;
1349
1350 /* other tests */
1351 ssol = spaces_since_start_of_line (&inti);
1352 if (ssol >= 0) {
1353 /* we are on a line with only spaces since its start */
1354 if (c == '=') {
1355 c = get_char_at_iter (&inti, TRUE);
1356 if (c == ' ') {
1357 SET_OUT;
1358 return MARKUP_TITLE1_S;
1359 }
1360 else if (c == '=') {
1361 c = get_char_at_iter (&inti, TRUE);
1362 if (c == ' ') {
1363 SET_OUT;
1364 return MARKUP_TITLE2_S;
1365 }
1366 }
1367 }
1368 else if (c == '-') {
1369 c = get_char_at_iter (&inti, TRUE);
1370 if (c == ' ') {
1371 SET_OUT;
1372 return MARKUP_LIST_S;
1373 }
1374 }
1375 }
1376
1377 if (c == '*') {
1378 c = get_char_at_iter (&inti, TRUE);
1379 if (c == '*') {
1380 SET_OUT;
1381 return MARKUP_BOLD;
1382 }
1383 }
1384 else if (c == '/') {
1385 c = get_char_at_iter (&inti, TRUE);
1386 if (c == '/') {
1387 GtkTextIter previ;
1388 previ = inti;
1389 if (gtk_text_iter_backward_char (&previ) &&
1390 gtk_text_iter_backward_char (&previ) &&
1391 (get_char_at_iter (&previ, FALSE) == ':')) {}
1392 else {
1393 SET_OUT;
1394 return MARKUP_ITALIC;
1395 }
1396 }
1397 }
1398 else if (c == '_') {
1399 c = get_char_at_iter (&inti, TRUE);
1400 if (c == '_') {
1401 SET_OUT;
1402 return MARKUP_UNDERLINE;
1403 }
1404 }
1405 else if (c == '-') {
1406 c = get_char_at_iter (&inti, TRUE);
1407 if (c == '-') {
1408 SET_OUT;
1409 return MARKUP_STRIKE;
1410 }
1411 }
1412 else if (c == '`') {
1413 c = get_char_at_iter (&inti, TRUE);
1414 if (c == '`') {
1415 SET_OUT;
1416 return MARKUP_TT;
1417 }
1418 }
1419 else if (c == '"') {
1420 c = get_char_at_iter (&inti, TRUE);
1421 if (c == '"') {
1422 c = get_char_at_iter (&inti, TRUE);
1423 if (c == '"') {
1424 SET_OUT;
1425 return MARKUP_VERBATIM;
1426 }
1427 }
1428 }
1429 else if (c == ' ') {
1430 gtk_text_iter_starts_line (&inti);
1431
1432 c = get_char_at_iter (&inti, TRUE);
1433 if (c == '=') {
1434 if (start_markup == MARKUP_TITLE1_S) {
1435 GtkTextIter it = inti;
1436 gtk_text_iter_forward_char (&it);
1437 if (gtk_text_iter_ends_line (&it) &&
1438 (linestart == gtk_text_iter_get_line (&inti))) {
1439 SET_OUT;
1440 return MARKUP_TITLE1_E;
1441 }
1442 }
1443 else {
1444 c = get_char_at_iter (&inti, TRUE);
1445 if (c == '=') {
1446 GtkTextIter it = inti;
1447 gtk_text_iter_forward_char (&it);
1448 if ((start_markup == MARKUP_TITLE2_S) && gtk_text_iter_ends_line (&it) &&
1449 (linestart == gtk_text_iter_get_line (&inti))) {
1450 SET_OUT;
1451 return MARKUP_TITLE2_E;
1452 }
1453 }
1454 }
1455 }
1456 }
1457 else if (c == '[') {
1458 c = get_char_at_iter (&inti, TRUE);
1459 if (c == '[') {
1460 c = get_char_at_iter (&inti, TRUE);
1461 if (c == '[') {
1462 SET_OUT;
1463 return MARKUP_PICTURE_S;
1464 }
1465 }
1466 }
1467 return MARKUP_NONE;
1468 }
1469
1470 /**
1471 * get_char_at_iter:
1472 * @iter: an iter
1473 * @move_forward_first: %TRUE if @iter should be moved forward first
1474 *
1475 * Returns: 0 for EOF, 1 for the "unknown" unicode char (usually pixbufs) or for chars represented
1476 * by more than one byte
1477 */
1478 static gchar
get_char_at_iter(GtkTextIter * iter,gboolean move_forward_first)1479 get_char_at_iter (GtkTextIter *iter, gboolean move_forward_first)
1480 {
1481 if (!move_forward_first ||
1482 (move_forward_first && gtk_text_iter_forward_char (iter))) {
1483 gunichar uc;
1484 gchar buf1[6];
1485 uc = gtk_text_iter_get_char (iter);
1486 if (!uc)
1487 return 0;
1488 if (g_unichar_to_utf8 (uc, buf1) == 1)
1489 return *buf1;
1490 return 1;
1491 }
1492 return 0;
1493 }
1494
1495 static gboolean
markup_tag_match(MarkupTag tag1,gint tagline1,MarkupTag tag2,gint tagline2)1496 markup_tag_match (MarkupTag tag1, gint tagline1, MarkupTag tag2, gint tagline2)
1497 {
1498 gboolean retval;
1499 switch (tag1) {
1500 case MARKUP_BOLD:
1501 case MARKUP_TT:
1502 case MARKUP_VERBATIM:
1503 case MARKUP_ITALIC:
1504 case MARKUP_STRIKE:
1505 case MARKUP_UNDERLINE:
1506 retval = (tag1 == tag2) ? TRUE : FALSE;
1507 break;
1508 case MARKUP_TITLE1_S:
1509 retval = (tag2 == MARKUP_TITLE1_E) ? TRUE : FALSE;
1510 break;
1511 case MARKUP_TITLE2_S:
1512 retval = (tag2 == MARKUP_TITLE2_E) ? TRUE : FALSE;
1513 break;
1514 case MARKUP_PICTURE_S:
1515 retval = (tag2 == MARKUP_PICTURE_E) ? TRUE : FALSE;
1516 break;
1517 case MARKUP_LIST_S:
1518 retval = (tag2 == MARKUP_LIST_E) ? TRUE : FALSE;
1519 break;
1520 default:
1521 retval = FALSE;
1522 break;
1523 }
1524
1525 if (retval) {
1526 if ((tag1 != MARKUP_PICTURE_S) && (tag1 != MARKUP_VERBATIM))
1527 retval = (tagline1 == tagline2) ? TRUE : FALSE;
1528 }
1529 return retval;
1530 }
1531
1532 static guint8 *serialize_as_txt2tag (GtkTextBuffer *register_buffer,
1533 GtkTextBuffer *content_buffer,
1534 const GtkTextIter *start,
1535 const GtkTextIter *end,
1536 gsize *length,
1537 GdauiRtEditor *editor);
1538
1539 /**
1540 * gdaui_rt_editor_get_contents:
1541 * @editor: a #GdauiRtEditor
1542 *
1543 * Get the contents of @editor, using the markup syntax
1544 *
1545 * Returns: (transfer full): a new string, or %NULL if there was an error
1546 *
1547 * Since: 4.2.2
1548 */
1549 gchar *
gdaui_rt_editor_get_contents(GdauiRtEditor * editor)1550 gdaui_rt_editor_get_contents (GdauiRtEditor *editor)
1551 {
1552 g_return_val_if_fail (GDAUI_IS_RT_EDITOR (editor), NULL);
1553
1554 if (editor->priv->saved_for_help)
1555 return g_strdup (editor->priv->saved_for_help);
1556 else
1557 return real_gdaui_rt_editor_get_contents (editor);
1558 }
1559
1560 static gchar *
real_gdaui_rt_editor_get_contents(GdauiRtEditor * editor)1561 real_gdaui_rt_editor_get_contents (GdauiRtEditor *editor)
1562 {
1563 GtkTextIter start, end;
1564
1565 gtk_text_buffer_get_bounds (editor->priv->textbuffer, &start, &end);
1566
1567 if (editor->priv->show_markup)
1568 return gtk_text_buffer_get_text (editor->priv->textbuffer, &start, &end, FALSE);
1569 else {
1570 GdkAtom format;
1571 guint8 *data;
1572 gsize length;
1573 format = gtk_text_buffer_register_serialize_format (editor->priv->textbuffer, "txt/rte",
1574 (GtkTextBufferSerializeFunc) serialize_as_txt2tag,
1575 editor, NULL);
1576 data = gtk_text_buffer_serialize (editor->priv->textbuffer, editor->priv->textbuffer, format,
1577 &start, &end, &length);
1578 return (gchar*) data;
1579 }
1580 }
1581
1582 /*
1583 * Serialization as txt2tag
1584 */
1585 typedef struct
1586 {
1587 GString *text_str;
1588 GHashTable *tags;
1589 GtkTextIter start, end;
1590
1591 gint tag_id;
1592 GHashTable *tag_id_tags;
1593 } SerializationContext;
1594
1595 /*
1596 * serialize_tag:
1597 * @tag: a #GtkTextTag
1598 * @starting: %TRUE if serialization has to be done for the opening part
1599 *
1600 * Returns: a static string, never %NULL
1601 */
1602 static const gchar *
serialize_tag(GtkTextTag * tag,gboolean starting,GdauiRtEditor * editor)1603 serialize_tag (GtkTextTag *tag, gboolean starting, GdauiRtEditor *editor)
1604 {
1605 if (tag == editor->priv->tags[TEXT_TAG_ITALIC].tag)
1606 return "//";
1607 else if (tag == editor->priv->tags[TEXT_TAG_BOLD].tag)
1608 return "**";
1609 else if (tag == editor->priv->tags[TEXT_TAG_UNDERLINE].tag)
1610 return "__";
1611 else if (tag == editor->priv->tags[TEXT_TAG_STRIKE].tag)
1612 return "--";
1613 else if (tag == editor->priv->tags[TEXT_TAG_TT].tag)
1614 return "``";
1615 else if (tag == editor->priv->tags[TEXT_TAG_VERBATIM].tag)
1616 return "\"\"\"";
1617 else if (tag == editor->priv->tags[TEXT_TAG_TITLE1].tag) {
1618 if (starting)
1619 return "= ";
1620 else
1621 return " =";
1622 }
1623 else if (tag == editor->priv->tags[TEXT_TAG_TITLE2].tag) {
1624 if (starting)
1625 return "== ";
1626 else
1627 return " ==";
1628 }
1629 else if (tag == editor->priv->tags[TEXT_TAG_LIST1].tag) {
1630 if (starting)
1631 return lists_tokens [0];
1632 else
1633 return "";
1634 }
1635 else if (tag == editor->priv->tags[TEXT_TAG_LIST2].tag) {
1636 if (starting)
1637 return lists_tokens [1];
1638 else
1639 return "";
1640 }
1641 else {
1642 gchar *tagname;
1643 g_object_get ((GObject*) tag, "name", &tagname, NULL);
1644 g_warning ("Unknown tag '%s'\n", tagname);
1645 g_free (tagname);
1646 return "";
1647 }
1648 }
1649
1650 /*
1651 * steals @base64
1652 */
1653 static gchar *
add_newlines_to_base64(gchar * base64)1654 add_newlines_to_base64 (gchar *base64)
1655 {
1656 GString *string;
1657 gint i;
1658 gchar *ptr;
1659 string = g_string_new ("");
1660 for (i = 0, ptr = base64; *ptr; i++, ptr++) {
1661 if (i && ! (i % 100))
1662 g_string_append_c (string, '\n');
1663 g_string_append_c (string, *ptr);
1664 }
1665 g_free (base64);
1666 return g_string_free (string, FALSE);
1667 }
1668
1669 /*
1670 * steals @base64
1671 */
1672 static gchar *
remove_newlines_from_base64(gchar * base64)1673 remove_newlines_from_base64 (gchar *base64)
1674 {
1675 GString *string;
1676 gchar *ptr;
1677 string = g_string_new ("");
1678 for (ptr = base64; *ptr; ptr++) {
1679 if (*ptr != '\n')
1680 g_string_append_c (string, *ptr);
1681 }
1682 g_free (base64);
1683 return g_string_free (string, FALSE);
1684 }
1685
1686 static void
serialize_text(G_GNUC_UNUSED GtkTextBuffer * buffer,SerializationContext * context,GdauiRtEditor * editor)1687 serialize_text (G_GNUC_UNUSED GtkTextBuffer *buffer, SerializationContext *context,
1688 GdauiRtEditor *editor)
1689 {
1690 GtkTextIter iter, old_iter;
1691 GList *opened_tags = NULL; /* 1st element of the list is the last opened tag (ie. the one
1692 * which should be closed 1st */
1693
1694 /*g_string_append (context->text_str, "###");*/
1695 iter = context->start;
1696 do {
1697 GSList *new_tag_list, *list;
1698 GList *dlist;
1699 gboolean tags_needs_reopened = TRUE;
1700 new_tag_list = gtk_text_iter_get_tags (&iter);
1701
1702 /*
1703 * Close tags which need closing
1704 */
1705 /* Find the last element in @opened_tags which is not opened anymore */
1706 for (dlist = g_list_last (opened_tags); dlist; dlist = dlist->prev) {
1707 if (! g_slist_find (new_tag_list, dlist->data))
1708 break;
1709 }
1710
1711 if (dlist) {
1712 /* close all the tags up to dlist->data and at the same time remove them
1713 from @opened_tags */
1714 for (; opened_tags != dlist;
1715 opened_tags = g_list_delete_link (opened_tags, opened_tags)) {
1716 g_string_append (context->text_str,
1717 serialize_tag ((GtkTextTag*) opened_tags->data,
1718 FALSE, editor));
1719 }
1720 g_string_append (context->text_str,
1721 serialize_tag ((GtkTextTag*) opened_tags->data,
1722 FALSE, editor));
1723 opened_tags = g_list_delete_link (opened_tags, opened_tags);
1724 }
1725
1726 /* Now try to go to either the next tag toggle, or if a pixbuf appears */
1727 old_iter = iter;
1728 while (1) {
1729 gunichar ch = gtk_text_iter_get_char (&iter);
1730
1731 if (ch == 0xFFFC) {
1732 GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
1733
1734 if (pixbuf) {
1735 /* Append the text before the pixbuf */
1736 gchar *tmp_text;
1737 tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
1738 g_string_append (context->text_str, tmp_text);
1739 g_free (tmp_text);
1740
1741 if ((pixbuf != bullet_pix[0]) &&
1742 (pixbuf != bullet_pix[1])) {
1743 GdkPixdata pixdata;
1744 guint8 *tmp;
1745 guint len;
1746 gchar *data;
1747
1748 gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
1749 tmp = gdk_pixdata_serialize (&pixdata, &len);
1750 data = add_newlines_to_base64 (g_base64_encode (tmp, len));
1751 g_free (tmp);
1752 g_string_append (context->text_str, "[[[");
1753 g_string_append (context->text_str, data);
1754 g_free (data);
1755 g_string_append (context->text_str, "]]]");
1756 }
1757
1758 /* Forward so we don't get the 0xfffc char */
1759 gtk_text_iter_forward_char (&iter);
1760 old_iter = iter;
1761 }
1762 }
1763 else if (ch == 0) {
1764 break;
1765 }
1766 else
1767 gtk_text_iter_forward_char (&iter);
1768
1769 if (gtk_text_iter_toggles_tag (&iter, NULL)) {
1770 /*g_print ("Toggle @pos %d:%d\n", gtk_text_iter_get_line (&iter),
1771 gtk_text_iter_get_line_offset (&iter));*/
1772 break;
1773 }
1774 }
1775
1776 /* We might have moved too far */
1777 if (gtk_text_iter_compare (&iter, &context->end) > 0)
1778 iter = context->end;
1779
1780 /* Append the text, except if there is the TEXT_TAG_BULLET tag */
1781 GtkTextTag *ltag;
1782 ltag = iter_begins_list (editor, &old_iter, NULL);
1783 if (! ltag) {
1784 gint i;
1785 gchar *tmp_text;
1786 tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
1787 #ifdef NONO
1788 g_string_append (context->text_str, tmp_text);
1789 #endif
1790 for (i = 0; tmp_text[i]; i++) {
1791 if (tmp_text[i] != '\n') {
1792 if (tags_needs_reopened) {
1793 /*
1794 * (re)open tags which are still there
1795 */
1796 for (list = new_tag_list; list; list = list->next) {
1797 if (! g_list_find (opened_tags, list->data)) {
1798 opened_tags = g_list_prepend (opened_tags, list->data);
1799 g_string_append (context->text_str,
1800 serialize_tag ((GtkTextTag*) opened_tags->data,
1801 TRUE, editor));
1802 }
1803 }
1804 tags_needs_reopened = FALSE;
1805 }
1806
1807 g_string_append_c (context->text_str, tmp_text[i]);
1808 continue;
1809 }
1810 /* close all tags and re-open them after the newline */
1811 for (dlist = opened_tags; dlist; dlist = dlist->next) {
1812 g_string_append (context->text_str,
1813 serialize_tag (GTK_TEXT_TAG (dlist->data),
1814 FALSE, editor));
1815 }
1816 g_string_append_c (context->text_str, '\n');
1817 /* re-open tags */
1818 for (dlist = g_list_last (opened_tags); dlist; dlist = dlist->prev) {
1819 g_string_append (context->text_str,
1820 serialize_tag (GTK_TEXT_TAG (dlist->data),
1821 TRUE, editor));
1822 }
1823 }
1824 g_free (tmp_text);
1825 }
1826
1827 if (tags_needs_reopened) {
1828 /*
1829 * (re)open tags which are still there
1830 */
1831 for (list = new_tag_list; list; list = list->next) {
1832 if (! g_list_find (opened_tags, list->data)) {
1833 opened_tags = g_list_prepend (opened_tags, list->data);
1834 g_string_append (context->text_str,
1835 serialize_tag ((GtkTextTag*) opened_tags->data,
1836 TRUE, editor));
1837 }
1838 }
1839 tags_needs_reopened = FALSE;
1840 }
1841
1842
1843 g_slist_free (new_tag_list);
1844 }
1845 while (!gtk_text_iter_equal (&iter, &context->end));
1846
1847 /* Close any open tags */
1848 for (; opened_tags;
1849 opened_tags = g_list_delete_link (opened_tags, opened_tags)) {
1850 g_string_append (context->text_str,
1851 serialize_tag ((GtkTextTag*) opened_tags->data,
1852 FALSE, editor));
1853 }
1854
1855 /*g_string_append (context->text_str, "###");*/
1856 }
1857
1858 /*
1859 * serialize_as_txt2tag:
1860 */
1861 static guint8 *
serialize_as_txt2tag(G_GNUC_UNUSED GtkTextBuffer * register_buffer,GtkTextBuffer * content_buffer,const GtkTextIter * start,const GtkTextIter * end,gsize * length,GdauiRtEditor * editor)1862 serialize_as_txt2tag (G_GNUC_UNUSED GtkTextBuffer *register_buffer,
1863 GtkTextBuffer *content_buffer,
1864 const GtkTextIter *start,
1865 const GtkTextIter *end,
1866 gsize *length,
1867 GdauiRtEditor *editor)
1868 {
1869 SerializationContext context;
1870 GString *text;
1871
1872 context.tags = g_hash_table_new (NULL, NULL);
1873 context.text_str = g_string_new (NULL);
1874 context.start = *start;
1875 context.end = *end;
1876 context.tag_id = 0;
1877 context.tag_id_tags = g_hash_table_new (NULL, NULL);
1878
1879 serialize_text (content_buffer, &context, editor);
1880
1881 text = g_string_new (NULL);
1882
1883 g_string_append_len (text, context.text_str->str, context.text_str->len);
1884
1885 g_hash_table_destroy (context.tags);
1886 g_string_free (context.text_str, TRUE);
1887 g_hash_table_destroy (context.tag_id_tags);
1888
1889 *length = text->len;
1890
1891 return (guint8 *) g_string_free (text, FALSE);
1892 }
1893
1894
1895 /**
1896 * gdaui_rt_editor_set_contents:
1897 * @editor: a #GdauiRtEditor
1898 * @markup: the text to set in @editor, using the markup syntax (must be valid UTF-8)
1899 * @length: length of text in bytes.
1900 *
1901 * Set @editor's contents. If @length is -1, @markup must be nul-terminated
1902 *
1903 * Since: 4.2.2
1904 */
1905 void
gdaui_rt_editor_set_contents(GdauiRtEditor * editor,const gchar * markup,gint length)1906 gdaui_rt_editor_set_contents (GdauiRtEditor *editor, const gchar *markup, gint length)
1907 {
1908 g_return_if_fail (GDAUI_IS_RT_EDITOR (editor));
1909
1910 editor->priv->contents_setting = TRUE;
1911 gtk_text_buffer_set_text (editor->priv->textbuffer, markup, length);
1912 editor->priv->contents_setting = FALSE;
1913 }
1914
1915 /**
1916 * gdaui_rt_editor_set_editable:
1917 * @editor: a #GdauiRtEditor
1918 * @editable: whether it's editable
1919 *
1920 * Set @editor's editability
1921 *
1922 * Since: 4.2.2
1923 */
1924 void
gdaui_rt_editor_set_editable(GdauiRtEditor * editor,gboolean editable)1925 gdaui_rt_editor_set_editable (GdauiRtEditor *editor, gboolean editable)
1926 {
1927 g_return_if_fail (GDAUI_IS_RT_EDITOR (editor));
1928 gtk_text_view_set_editable (editor->priv->textview, editable);
1929 gtk_text_view_set_cursor_visible (editor->priv->textview, editable);
1930 show_hide_toolbar (editor);
1931 }
1932
1933 /*
1934 * _gdaui_rt_editor_set_show_markup
1935 * @editor: a #GdauiRtEditor
1936 * @show_markup: whether @editor shows markup of applies it
1937 *
1938 * If @show_markup is %FALSE, then @editor displays text with tags applied (bold text, ...); and if it's
1939 * %TRUE then it shows markup text instead
1940 *
1941 * Since: 4.2.2
1942 */
1943 static void
_gdaui_rt_editor_set_show_markup(GdauiRtEditor * editor,gboolean show_markup)1944 _gdaui_rt_editor_set_show_markup (GdauiRtEditor *editor, gboolean show_markup)
1945 {
1946 gchar *data;
1947 gint cursor_pos;
1948 GtkTextIter iter;
1949
1950 g_return_if_fail (GDAUI_IS_RT_EDITOR (editor));
1951 if (editor->priv->show_markup == show_markup)
1952 return;
1953
1954 g_object_get (editor->priv->textbuffer, "cursor-position", &cursor_pos, NULL);
1955 data = real_gdaui_rt_editor_get_contents (editor);
1956 editor->priv->show_markup = show_markup;
1957 gdaui_rt_editor_set_contents (editor, data, -1);
1958 g_free (data);
1959
1960 gtk_text_buffer_get_iter_at_offset (editor->priv->textbuffer, &iter, cursor_pos);
1961 gtk_text_buffer_place_cursor (editor->priv->textbuffer, &iter);
1962
1963 show_hide_toolbar (editor);
1964 }
1965
1966 static void
show_hide_toolbar(GdauiRtEditor * editor)1967 show_hide_toolbar (GdauiRtEditor *editor)
1968 {
1969 gboolean enable_markup = TRUE;
1970 GtkAction *action;
1971 gboolean doshow = FALSE;
1972
1973 if (gtk_text_view_get_editable (editor->priv->textview) &&
1974 gtk_widget_has_focus (GTK_WIDGET (editor->priv->textview)))
1975 doshow = TRUE;
1976
1977 if (doshow) {
1978 gtk_widget_show (editor->priv->toolbar);
1979 if (editor->priv->sw && (editor->priv->vadj_value != 0.)) {
1980 GtkAdjustment *adj;
1981 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (editor->priv->sw));
1982 while (gtk_events_pending ())
1983 gtk_main_iteration ();
1984 gtk_adjustment_set_value (adj, editor->priv->vadj_value);
1985 }
1986 }
1987 else {
1988 if (editor->priv->sw) {
1989 GtkAdjustment *adj;
1990 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (editor->priv->sw));
1991 editor->priv->vadj_value = gtk_adjustment_get_value (adj);
1992 }
1993 gtk_widget_hide (editor->priv->toolbar);
1994 }
1995
1996 if (editor->priv->show_markup ||
1997 ! gtk_text_view_get_editable (editor->priv->textview))
1998 enable_markup = FALSE;
1999
2000 action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionBold");
2001 gtk_action_set_sensitive (action, enable_markup);
2002 action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionItalic");
2003 gtk_action_set_sensitive (action, enable_markup);
2004 action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionUnderline");
2005 gtk_action_set_sensitive (action, enable_markup);
2006 action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionStrike");
2007 gtk_action_set_sensitive (action, enable_markup);
2008 action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionAddImage");
2009 gtk_action_set_sensitive (action, enable_markup);
2010 action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionReset");
2011 gtk_action_set_sensitive (action, enable_markup);
2012 }
2013