[libgda] New GdauiRtEditor
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] New GdauiRtEditor
- Date: Sat, 27 Nov 2010 14:15:52 +0000 (UTC)
commit faa52e080ccb7623ba6e300d60c49a9f6bf10ee8
Author: Vivien Malerba <malerba gnome-db org>
Date: Sat Nov 27 12:13:39 2010 +0100
New GdauiRtEditor
doc/C/Makefile.am | 2 +-
doc/C/libgda-4.0-docs.sgml | 2 +
doc/C/libgda-ui-sections.txt | 16 +
doc/C/libgda-ui.types | 1 +
doc/C/tmpl/gdaui-rt-editor.sgml | 103 ++
doc/C/vi-rte.png | Bin 0 -> 13283 bytes
doc/C/visual_index.xml | 3 +
libgda-ui/Makefile.am | 3 +
libgda-ui/bullet.h | 31 +
libgda-ui/bullet.png | Bin 0 -> 239 bytes
libgda-ui/bulleth.h | 31 +
libgda-ui/bulleth.png | Bin 0 -> 247 bytes
libgda-ui/data-entries/plugins/Makefile.am | 2 +
libgda-ui/data-entries/plugins/gdaui-entry-rt.c | 304 ++++
libgda-ui/data-entries/plugins/gdaui-entry-rt.h | 59 +
libgda-ui/data-entries/plugins/libmain.c | 25 +-
libgda-ui/gdaui-rt-editor.c | 1904 +++++++++++++++++++++++
libgda-ui/gdaui-rt-editor.h | 70 +
libgda-ui/libgda-ui.h | 3 +-
libgda-ui/libgda-ui.symbols | 5 +
testing/.gitignore | 1 +
testing/Makefile.am | 11 +-
testing/gdaui-test-rt-editor.c | 178 +++
23 files changed, 2750 insertions(+), 4 deletions(-)
---
diff --git a/doc/C/Makefile.am b/doc/C/Makefile.am
index 7d43fab..779d873 100644
--- a/doc/C/Makefile.am
+++ b/doc/C/Makefile.am
@@ -67,7 +67,7 @@ HTML_IMAGES = DataModels.png \
SqlIdentifiers.png thread-wrapper.png \
vi-basic-form.png vi-combo.png vi-data-entry.png \
vi-login.png vi-cloud.png vi-provider-selector.png vi-raw-grid.png \
- vi-info.png vi-filter.png vi-server-op.png
+ vi-info.png vi-filter.png vi-server-op.png vi-rte.png
# Extra options to supply to gtkdoc-fixref
FIXXREF_OPTIONS=
diff --git a/doc/C/libgda-4.0-docs.sgml b/doc/C/libgda-4.0-docs.sgml
index 820f439..7f4499c 100644
--- a/doc/C/libgda-4.0-docs.sgml
+++ b/doc/C/libgda-4.0-docs.sgml
@@ -152,6 +152,7 @@
<!ENTITY libgdaui-GdauiTreeStore SYSTEM "xml/gdaui-tree-store.xml">
<!ENTITY libgdaui-GdauiCloud SYSTEM "xml/gdaui-cloud.xml">
<!ENTITY libgdaui-GdauiDataSelector SYSTEM "xml/gdaui-data-selector.xml">
+<!ENTITY libgdaui-GdauiRtEditor SYSTEM "xml/gdaui-rt-editor.xml">
]>
<book id="index">
@@ -1277,6 +1278,7 @@ g_object_unref (store);
&libgdaui-GdauiRawGrid;
&libgdaui-GdauiCombo;
&libgdaui-GdauiCloud;
+ &libgdaui-GdauiRtEditor;
&libgdaui-gdaui-plugins;
diff --git a/doc/C/libgda-ui-sections.txt b/doc/C/libgda-ui-sections.txt
index d0d83b9..2fa2538 100644
--- a/doc/C/libgda-ui-sections.txt
+++ b/doc/C/libgda-ui-sections.txt
@@ -326,3 +326,19 @@ GDAUI_IS_DATA_SELECTOR
GDAUI_TYPE_DATA_SELECTOR
gdaui_data_selector_get_type
</SECTION>
+
+<SECTION>
+<FILE>gdaui-rt-editor</FILE>
+<TITLE>GdauiRtEditor</TITLE>
+GdauiRtEditor
+gdaui_rt_editor_new
+gdaui_rt_editor_get_contents
+gdaui_rt_editor_set_contents
+gdaui_rt_editor_set_editable
+<SUBSECTION Standard>
+GDAUI_RT_EDITOR
+GDAUI_RT_EDITOR_CLASS
+GDAUI_IS_RT_EDITOR
+GDAUI_TYPE_RT_EDITOR
+gdaui_rt_editor_get_type
+</SECTION>
diff --git a/doc/C/libgda-ui.types b/doc/C/libgda-ui.types
index e2bec78..8031ee0 100644
--- a/doc/C/libgda-ui.types
+++ b/doc/C/libgda-ui.types
@@ -16,3 +16,4 @@ gdaui_raw_grid_get_type
gdaui_server_operation_get_type
gdaui_tree_store_get_type
gdaui_cloud_get_type
+gdaui_rt_editor_get_type
diff --git a/doc/C/tmpl/gdaui-rt-editor.sgml b/doc/C/tmpl/gdaui-rt-editor.sgml
new file mode 100644
index 0000000..185505e
--- /dev/null
+++ b/doc/C/tmpl/gdaui-rt-editor.sgml
@@ -0,0 +1,103 @@
+<!-- ##### SECTION Title ##### -->
+GdauiRtEditor
+
+<!-- ##### SECTION Short_Description ##### -->
+Rich text editor which uses a subset of the <ulink url="http://www.txt2tags.org/markup.html">txt2tags</ulink> markup.
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+ The text entered in the editor can be formatted using bold, underline, title, ... attributes
+ and then extracted using a subset of the <ulink url="http://www.txt2tags.org/markup.html">txt2tags</ulink>
+ markup. Use this widget to edit textual fields where some markup is desirable to organize the text.
+</para>
+<para>
+ For example the real text used to obtain the formatting in the figure is:
+ <programlisting>
+blah //italic// blah.
+and ** BOLD!//both italic and bold// Bold!**
+Nice Picture: [[[R2RrUAA...y8vLy8tYQwAA]]] Yes
+- List item --One--
+- List item **Two**
+ - sub1
+ - sub2</programlisting>
+ where the picture's serialized data has been truncated here for readability
+ (between the [[[ and ]]] markers). Pictures are usually inserted using the incorporated
+ tollbar and not y hand (even though it's possible).
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT GdauiRtEditor ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SIGNAL GdauiRtEditor::changed ##### -->
+<para>
+
+</para>
+
+ gdauirteditor: the object which received the signal.
+
+<!-- ##### ARG GdauiRtEditor:buffer ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG GdauiRtEditor:no-background ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG GdauiRtEditor:show-markup ##### -->
+<para>
+
+</para>
+
+<!-- ##### FUNCTION gdaui_rt_editor_new ##### -->
+<para>
+
+</para>
+
+ void:
+ Returns:
+
+
+<!-- ##### FUNCTION gdaui_rt_editor_get_contents ##### -->
+<para>
+
+</para>
+
+ editor:
+ Returns:
+
+
+<!-- ##### FUNCTION gdaui_rt_editor_set_contents ##### -->
+<para>
+
+</para>
+
+ editor:
+ markup:
+ length:
+
+
+<!-- ##### FUNCTION gdaui_rt_editor_set_editable ##### -->
+<para>
+
+</para>
+
+ editor:
+ editable:
+
+
diff --git a/doc/C/vi-rte.png b/doc/C/vi-rte.png
new file mode 100644
index 0000000..0efc663
Binary files /dev/null and b/doc/C/vi-rte.png differ
diff --git a/doc/C/visual_index.xml b/doc/C/visual_index.xml
index ee6e903..42f8035 100644
--- a/doc/C/visual_index.xml
+++ b/doc/C/visual_index.xml
@@ -29,4 +29,7 @@
<link linkend="GdauiServerOperation">
<inlinegraphic fileref="vi-server-op.png" format="PNG"></inlinegraphic>
</link>
+ <link linkend="GdauiRtEditor">
+ <inlinegraphic fileref="vi-rte.png" format="PNG"></inlinegraphic>
+ </link>
</para>
diff --git a/libgda-ui/Makefile.am b/libgda-ui/Makefile.am
index 6830feb..fbc46d4 100644
--- a/libgda-ui/Makefile.am
+++ b/libgda-ui/Makefile.am
@@ -35,6 +35,7 @@ ui_headers = \
gdaui-provider-selector.h \
gdaui-raw-form.h \
gdaui-raw-grid.h \
+ gdaui-rt-editor.h \
gdaui-server-operation.h \
gdaui-tree-store.h \
gdaui-plugin.h
@@ -56,6 +57,8 @@ ui_sources = \
gdaui-provider-selector.c \
gdaui-raw-form.c \
gdaui-raw-grid.c \
+ bullet.h \
+ gdaui-rt-editor.c \
gdaui-server-operation.c \
gdaui-set.h \
gdaui-set.c \
diff --git a/libgda-ui/bullet.h b/libgda-ui/bullet.h
new file mode 100644
index 0000000..92cd1a3
--- /dev/null
+++ b/libgda-ui/bullet.h
@@ -0,0 +1,31 @@
+/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (bullet_pixdata)
+#endif
+#ifdef __GNUC__
+static const guint8 bullet_pixdata[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 bullet_pixdata[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (136) */
+ "\0\0\0\240"
+ /* pixdata_type (0x2010002) */
+ "\2\1\0\2"
+ /* rowstride (24) */
+ "\0\0\0\30"
+ /* width (6) */
+ "\0\0\0\6"
+ /* height (9) */
+ "\0\0\0\11"
+ /* pixel_data: */
+ "\2\0\0\0\20\0\0\0 \202\0\0\0o\4\0\0\0@\0\0\0\20\0\0\0 \0\0\0\257\202"
+ "\0\0\0\357\4\0\0\0\257\0\0\0 \0\0\0o\0\0\0\357\202\0\0\0\377\1\0\0\0"
+ "\357\202\0\0\0o\1\0\0\0\357\202\0\0\0\377\4\0\0\0\357\0\0\0o\0\0\0 \0"
+ "\0\0\257\202\0\0\0\357\4\0\0\0\257\0\0\0 \0\0\0\20\0\0\0@\202\0\0\0o"
+ "\2\0\0\0 \0\0\0\20\222\0\0\0\0"};
+
+
diff --git a/libgda-ui/bullet.png b/libgda-ui/bullet.png
new file mode 100644
index 0000000..0ccbf5b
Binary files /dev/null and b/libgda-ui/bullet.png differ
diff --git a/libgda-ui/bulleth.h b/libgda-ui/bulleth.h
new file mode 100644
index 0000000..7178c14
--- /dev/null
+++ b/libgda-ui/bulleth.h
@@ -0,0 +1,31 @@
+/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (bulleth_pixdata)
+#endif
+#ifdef __GNUC__
+static const guint8 bulleth_pixdata[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 bulleth_pixdata[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (136) */
+ "\0\0\0\240"
+ /* pixdata_type (0x2010002) */
+ "\2\1\0\2"
+ /* rowstride (24) */
+ "\0\0\0\30"
+ /* width (6) */
+ "\0\0\0\6"
+ /* height (9) */
+ "\0\0\0\11"
+ /* pixel_data: */
+ "\2\0\0\0\20\0\0\0 \202\0\0\0o\4\0\0\0@\0\0\0\20\0\0\0 \0\0\0\231\202"
+ "\0\0\0\225\4\0\0\0\231\0\0\0 \0\0\0o\0\0\0\225\202\0\0\0\40\1\0\0\0\225"
+ "\202\0\0\0o\1\0\0\0\225\202\0\0\0\40\4\0\0\0\225\0\0\0o\0\0\0 \0\0\0"
+ "\231\202\0\0\0\225\4\0\0\0\231\0\0\0 \0\0\0\20\0\0\0@\202\0\0\0o\2\0"
+ "\0\0 \0\0\0\20\222\0\0\0\0"};
+
+
diff --git a/libgda-ui/bulleth.png b/libgda-ui/bulleth.png
new file mode 100644
index 0000000..b38d119
Binary files /dev/null and b/libgda-ui/bulleth.png differ
diff --git a/libgda-ui/data-entries/plugins/Makefile.am b/libgda-ui/data-entries/plugins/Makefile.am
index 2c4c113..344553c 100644
--- a/libgda-ui/data-entries/plugins/Makefile.am
+++ b/libgda-ui/data-entries/plugins/Makefile.am
@@ -30,6 +30,7 @@ plugins_headers = \
gdaui-entry-cidr.h \
gdaui-entry-text.h \
gdaui-entry-pict.h \
+ gdaui-entry-rt.h \
$(gcrypt_headers)
libgda_ui_plugins_la_SOURCES = \
@@ -45,6 +46,7 @@ libgda_ui_plugins_la_SOURCES = \
gdaui-entry-cidr.c \
gdaui-entry-text.c \
gdaui-entry-pict.c \
+ gdaui-entry-rt.c \
$(gcrypt_sources)
libgda_ui_plugins_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED)
diff --git a/libgda-ui/data-entries/plugins/gdaui-entry-rt.c b/libgda-ui/data-entries/plugins/gdaui-entry-rt.c
new file mode 100644
index 0000000..8a248b6
--- /dev/null
+++ b/libgda-ui/data-entries/plugins/gdaui-entry-rt.c
@@ -0,0 +1,304 @@
+/* gdaui-entry-rt.c
+ *
+ * Copyright (C) 2010 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <string.h>
+#include "gdaui-entry-rt.h"
+#include <libgda-ui/gdaui-rt-editor.h>
+#include <libgda/gda-data-handler.h>
+#include <libgda/gda-blob-op.h>
+
+/*
+ * Main static functions
+ */
+static void gdaui_entry_rt_class_init (GdauiEntryRtClass * class);
+static void gdaui_entry_rt_init (GdauiEntryRt * srv);
+static void gdaui_entry_rt_dispose (GObject * object);
+static void gdaui_entry_rt_finalize (GObject * object);
+
+/* virtual functions */
+static GtkWidget *create_entry (GdauiEntryWrapper *mgwrap);
+static void real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value);
+static GValue *real_get_value (GdauiEntryWrapper *mgwrap);
+static void connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb);
+static gboolean can_expand (GdauiEntryWrapper *mgwrap, gboolean horiz);
+static void set_editable (GdauiEntryWrapper *mgwrap, gboolean editable);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+
+/* private structure */
+struct _GdauiEntryRtPrivate
+{
+ GtkWidget *view;
+};
+
+
+GType
+gdaui_entry_rt_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (GdauiEntryRtClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gdaui_entry_rt_class_init,
+ NULL,
+ NULL,
+ sizeof (GdauiEntryRt),
+ 0,
+ (GInstanceInitFunc) gdaui_entry_rt_init,
+ 0
+ };
+
+ type = g_type_register_static (GDAUI_TYPE_ENTRY_WRAPPER, "GdauiEntryRt", &info, 0);
+ }
+ return type;
+}
+
+static void
+gdaui_entry_rt_class_init (GdauiEntryRtClass * class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = gdaui_entry_rt_dispose;
+ object_class->finalize = gdaui_entry_rt_finalize;
+
+ GDAUI_ENTRY_WRAPPER_CLASS (class)->create_entry = create_entry;
+ GDAUI_ENTRY_WRAPPER_CLASS (class)->real_set_value = real_set_value;
+ GDAUI_ENTRY_WRAPPER_CLASS (class)->real_get_value = real_get_value;
+ GDAUI_ENTRY_WRAPPER_CLASS (class)->connect_signals = connect_signals;
+ GDAUI_ENTRY_WRAPPER_CLASS (class)->can_expand = can_expand;
+ GDAUI_ENTRY_WRAPPER_CLASS (class)->set_editable = set_editable;
+}
+
+static void
+gdaui_entry_rt_init (GdauiEntryRt * gdaui_entry_rt)
+{
+ gdaui_entry_rt->priv = g_new0 (GdauiEntryRtPrivate, 1);
+ gdaui_entry_rt->priv->view = NULL;
+}
+
+/**
+ * gdaui_entry_rt_new
+ * @dh: the data handler to be used by the new widget
+ * @type: the requested data type (compatible with @dh)
+ * @options: the options
+ *
+ * Creates a new widget which is mainly a GtkEntry
+ *
+ * Returns: the new widget
+ */
+GtkWidget *
+gdaui_entry_rt_new (GdaDataHandler *dh, GType type, const gchar *options)
+{
+ GObject *obj;
+
+ g_return_val_if_fail (dh && GDA_IS_DATA_HANDLER (dh), NULL);
+ g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
+ g_return_val_if_fail (gda_data_handler_accepts_g_type (dh, type), NULL);
+
+ obj = g_object_new (GDAUI_TYPE_ENTRY_RT, "handler", dh, NULL);
+ gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (obj), type);
+
+ return GTK_WIDGET (obj);
+}
+
+
+static void
+gdaui_entry_rt_dispose (GObject * object)
+{
+ GdauiEntryRt *gdaui_entry_rt;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDAUI_IS_ENTRY_RT (object));
+
+ gdaui_entry_rt = GDAUI_ENTRY_RT (object);
+ if (gdaui_entry_rt->priv) {
+ }
+
+ /* parent class */
+ parent_class->dispose (object);
+}
+
+static void
+gdaui_entry_rt_finalize (GObject * object)
+{
+ GdauiEntryRt *gdaui_entry_rt;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDAUI_IS_ENTRY_RT (object));
+
+ gdaui_entry_rt = GDAUI_ENTRY_RT (object);
+ if (gdaui_entry_rt->priv) {
+ g_free (gdaui_entry_rt->priv);
+ gdaui_entry_rt->priv = NULL;
+ }
+
+ /* parent class */
+ parent_class->finalize (object);
+}
+
+static GtkWidget *
+create_entry (GdauiEntryWrapper *mgwrap)
+{
+ GdauiEntryRt *mgtxt;
+
+ g_return_val_if_fail (mgwrap && GDAUI_IS_ENTRY_RT (mgwrap), NULL);
+ mgtxt = GDAUI_ENTRY_RT (mgwrap);
+ g_return_val_if_fail (mgtxt->priv, NULL);
+
+ mgtxt->priv->view = gdaui_rt_editor_new ();
+
+ return mgtxt->priv->view;
+}
+
+static void
+real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
+{
+ GdauiEntryRt *mgtxt;
+
+ g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_RT (mgwrap));
+ mgtxt = GDAUI_ENTRY_RT (mgwrap);
+ g_return_if_fail (mgtxt->priv);
+
+ gdaui_rt_editor_set_contents (GDAUI_RT_EDITOR (mgtxt->priv->view), "", -1);
+ if (value) {
+ if (! gda_value_is_null ((GValue *) value)) {
+ GdaDataHandler *dh;
+ gchar *str;
+ gboolean done = FALSE;
+
+ if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
+ const GdaBlob *blob;
+ GdaBinary *bin;
+ blob = gda_value_get_blob (value);
+ bin = (GdaBinary *) blob;
+ if (blob->op &&
+ (bin->binary_length != gda_blob_op_get_length (blob->op)))
+ gda_blob_op_read_all (blob->op, (GdaBlob*) blob);
+ if (g_utf8_validate ((gchar*) bin->data, bin->binary_length, NULL)) {
+ gdaui_rt_editor_set_contents (GDAUI_RT_EDITOR (mgtxt->priv->view),
+ (gchar*) bin->data,
+ bin->binary_length);
+ done = TRUE;
+ }
+ }
+ else if (G_VALUE_TYPE (value) == GDA_TYPE_BINARY) {
+ const GdaBinary *bin;
+ bin = gda_value_get_binary (value);
+ if (g_utf8_validate ((gchar*) bin->data, bin->binary_length, NULL)) {
+ gdaui_rt_editor_set_contents (GDAUI_RT_EDITOR (mgtxt->priv->view),
+ (gchar*) bin->data,
+ bin->binary_length);
+ done = TRUE;
+ }
+ }
+
+ if (!done) {
+ dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
+ str = gda_data_handler_get_str_from_value (dh, value);
+ if (str) {
+ gdaui_rt_editor_set_contents (GDAUI_RT_EDITOR (mgtxt->priv->view),
+ str, -1);
+ g_free (str);
+ }
+ }
+ }
+ }
+}
+
+static GValue *
+real_get_value (GdauiEntryWrapper *mgwrap)
+{
+ GValue *value;
+ GdauiEntryRt *mgtxt;
+ GdaDataHandler *dh;
+ gchar *str;
+
+ g_return_val_if_fail (mgwrap && GDAUI_IS_ENTRY_RT (mgwrap), NULL);
+ mgtxt = GDAUI_ENTRY_RT (mgwrap);
+ g_return_val_if_fail (mgtxt->priv, NULL);
+
+ dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
+ str = gdaui_rt_editor_get_contents (GDAUI_RT_EDITOR (mgtxt->priv->view));
+ value = gda_data_handler_get_value_from_str (dh, str,
+ gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgwrap)));
+ g_free (str);
+ if (!value) {
+ /* in case the gda_data_handler_get_value_from_sql() returned an error because
+ the contents of the GtkEntry cannot be interpreted as a GValue */
+ value = gda_value_new_null ();
+ }
+
+ return value;
+}
+
+typedef void (*Callback2) (gpointer, gpointer);
+static gboolean
+focus_out_cb (GtkWidget *widget, GdkEventFocus *event, GdauiEntryRt *mgtxt)
+{
+ GCallback activate_cb;
+ activate_cb = g_object_get_data (G_OBJECT (widget), "_activate_cb");
+ g_assert (activate_cb);
+ ((Callback2)activate_cb) (widget, mgtxt);
+
+ return gtk_widget_event (GTK_WIDGET (mgtxt), (GdkEvent*) event);
+}
+
+static void
+connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb)
+{
+ GdauiEntryRt *mgtxt;
+
+ g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_RT (mgwrap));
+ mgtxt = GDAUI_ENTRY_RT (mgwrap);
+ g_return_if_fail (mgtxt->priv);
+
+ g_object_set_data (G_OBJECT (mgtxt->priv->view), "_activate_cb", activate_cb);
+ g_signal_connect (G_OBJECT (GDAUI_RT_EDITOR (mgtxt->priv->view)), "changed",
+ modify_cb, mgwrap);
+ g_signal_connect (G_OBJECT (mgtxt->priv->view), "focus-out-event",
+ G_CALLBACK (focus_out_cb), mgtxt);
+ /* FIXME: how does the user "activates" the GtkRtView widget ? */
+}
+
+static gboolean
+can_expand (G_GNUC_UNUSED GdauiEntryWrapper *mgwrap, gboolean horiz)
+{
+ if (horiz)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+set_editable (GdauiEntryWrapper *mgwrap, gboolean editable)
+{
+ GdauiEntryRt *mgtxt;
+
+ g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_RT (mgwrap));
+ mgtxt = GDAUI_ENTRY_RT (mgwrap);
+
+ gdaui_rt_editor_set_editable (GDAUI_RT_EDITOR (mgtxt->priv->view), editable);
+}
diff --git a/libgda-ui/data-entries/plugins/gdaui-entry-rt.h b/libgda-ui/data-entries/plugins/gdaui-entry-rt.h
new file mode 100644
index 0000000..afef532
--- /dev/null
+++ b/libgda-ui/data-entries/plugins/gdaui-entry-rt.h
@@ -0,0 +1,59 @@
+/* gdaui-entry-rt.h
+ *
+ * Copyright (C) 2010 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+
+#ifndef __GDAUI_ENTRY_RT_H_
+#define __GDAUI_ENTRY_RT_H_
+
+#include <libgda-ui/data-entries/gdaui-entry-wrapper.h>
+
+G_BEGIN_DECLS
+
+#define GDAUI_TYPE_ENTRY_RT (gdaui_entry_rt_get_type())
+#define GDAUI_ENTRY_RT(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, gdaui_entry_rt_get_type(), GdauiEntryRt)
+#define GDAUI_ENTRY_RT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, gdaui_entry_rt_get_type (), GdauiEntryRtClass)
+#define GDAUI_IS_ENTRY_RT(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, gdaui_entry_rt_get_type ())
+
+
+typedef struct _GdauiEntryRt GdauiEntryRt;
+typedef struct _GdauiEntryRtClass GdauiEntryRtClass;
+typedef struct _GdauiEntryRtPrivate GdauiEntryRtPrivate;
+
+
+/* struct for the object's data */
+struct _GdauiEntryRt
+{
+ GdauiEntryWrapper object;
+ GdauiEntryRtPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _GdauiEntryRtClass
+{
+ GdauiEntryWrapperClass parent_class;
+};
+
+GType gdaui_entry_rt_get_type (void) G_GNUC_CONST;
+GtkWidget *gdaui_entry_rt_new (GdaDataHandler *dh, GType type, const gchar *options);
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgda-ui/data-entries/plugins/libmain.c b/libgda-ui/data-entries/plugins/libmain.c
index d622c0a..ea3b7af 100644
--- a/libgda-ui/data-entries/plugins/libmain.c
+++ b/libgda-ui/data-entries/plugins/libmain.c
@@ -1,5 +1,5 @@
/* libmain.c
- * Copyright (C) 2006 - 2009 The GNOME Foundation
+ * Copyright (C) 2006 - 2010 The GNOME Foundation
*
* AUTHORS:
* Vivien Malerba <malerba gdaui org>
@@ -30,6 +30,7 @@
#include "gdaui-entry-cidr.h"
#include "gdaui-entry-text.h"
#include "gdaui-entry-pict.h"
+#include "gdaui-entry-rt.h"
#include "gdaui-data-cell-renderer-pict.h"
#ifdef HAVE_LIBGCRYPT
@@ -52,6 +53,7 @@
static GdauiDataEntry *plugin_entry_filesel_create_func (GdaDataHandler *handler, GType type, const gchar *options);
static GdauiDataEntry *plugin_entry_cidr_create_func (GdaDataHandler *handler, GType type, const gchar *options);
static GdauiDataEntry *plugin_entry_text_create_func (GdaDataHandler *handler, GType type, const gchar *options);
+static GdauiDataEntry *plugin_entry_rt_create_func (GdaDataHandler *handler, GType type, const gchar *options);
static GdauiDataEntry *plugin_entry_pict_create_func (GdaDataHandler *handler, GType type, const gchar *options);
static GtkCellRenderer *plugin_cell_renderer_pict_create_func (GdaDataHandler *handler, GType type, const gchar *options);
@@ -213,6 +215,21 @@ plugin_init (GError **error)
g_free (file);
#endif
+ /* TEXT */
+ plugin = g_new0 (GdauiPlugin, 1);
+ plugin->plugin_name = "rtext";
+ plugin->plugin_descr = "Rich text editor entry";
+ plugin->plugin_file = NULL; /* always leave NULL */
+ plugin->nb_g_types = 3;
+ plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
+ plugin->valid_g_types [0] = G_TYPE_STRING;
+ plugin->valid_g_types [1] = GDA_TYPE_BLOB;
+ plugin->valid_g_types [2] = GDA_TYPE_BINARY;
+ plugin->options_xml_spec = NULL;
+ plugin->entry_create_func = plugin_entry_rt_create_func;
+ plugin->cell_create_func = NULL;
+ retlist = g_slist_append (retlist, plugin);
+
/* Picture - binary */
plugin = g_new0 (GdauiPlugin, 1);
plugin->plugin_name = "picture";
@@ -284,6 +301,12 @@ plugin_entry_text_create_func (GdaDataHandler *handler, GType type, const gchar
}
static GdauiDataEntry *
+plugin_entry_rt_create_func (GdaDataHandler *handler, GType type, const gchar *options)
+{
+ return (GdauiDataEntry *) gdaui_entry_rt_new (handler, type, options);
+}
+
+static GdauiDataEntry *
plugin_entry_pict_create_func (GdaDataHandler *handler, GType type, const gchar *options)
{
return (GdauiDataEntry *) gdaui_entry_pict_new (handler, type, options);
diff --git a/libgda-ui/gdaui-rt-editor.c b/libgda-ui/gdaui-rt-editor.c
new file mode 100644
index 0000000..d88450b
--- /dev/null
+++ b/libgda-ui/gdaui-rt-editor.c
@@ -0,0 +1,1904 @@
+/* gdaui-rt-editor.c
+ *
+ * Copyright (C) 2010 Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <string.h>
+#include "gdaui-rt-editor.h"
+#include <glib/gi18n-lib.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#include "bullet.h"
+#include "bulleth.h"
+
+static void gdaui_rt_editor_class_init (GdauiRtEditorClass *klass);
+static void gdaui_rt_editor_init (GdauiRtEditor *wid);
+static void gdaui_rt_editor_dispose (GObject *object);
+
+static void gdaui_rt_editor_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gdaui_rt_editor_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gdaui_rt_editor_show_all (GtkWidget *widget);
+
+static void _gdaui_rt_editor_set_show_markup (GdauiRtEditor *editor, gboolean show_markup);
+
+/* tag types */
+enum {
+ TEXT_TAG_ITALIC,
+ TEXT_TAG_BOLD,
+ TEXT_TAG_TT,
+ TEXT_TAG_VERBATIM,
+ TEXT_TAG_UNDERLINE,
+ TEXT_TAG_STRIKE,
+ TEXT_TAG_TITLE1,
+ TEXT_TAG_TITLE2,
+ TEXT_TAG_LIST1,
+ TEXT_TAG_LIST2,
+ TEXT_TAG_BULLET,
+
+ TEXT_TAG_LAST
+};
+
+typedef struct {
+ GtkTextTag *tag;
+ gchar *action_name;
+} TagData;
+
+struct _GdauiRtEditorPriv
+{
+ GtkTextView *textview;
+ GtkTextBuffer *textbuffer;
+ GtkWidget *toolbar;
+ GtkActionGroup *actions_group;
+ GtkUIManager *uimanager;
+ TagData tags[TEXT_TAG_LAST];
+ gboolean selection_changing;
+ gboolean show_markup;
+ gchar *saved_for_help;
+ gboolean enable_changed_signal;
+ gboolean no_background;
+ gint insert_offset;
+};
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+static gchar *help_str=N_("\"\"\"= Title level 1 =\n"
+ "== Title level 2 ==\n"
+ "\"\"\"= Title level 1 =\n"
+ "== Title level 2 ==\n\n"
+ "\"\"\""
+ "For beautifiers we have **bold**\n"
+ "and //italic//.\n"
+ "There is also __underline__, --strike--\n"
+ "and ``monospaced``.\n"
+ "\"\"\"\n"
+ "For beautifiers we have **bold**\n"
+ "and //italic//.\n"
+ "There is also __underline__, --strike--\n"
+ "and ``monospaced``.\n\n"
+ "\"\"\""
+ "- This is a list of items\n"
+ "- Just use hyphens\n"
+ " - And starting space for indenting\n"
+ "\"\"\"- This is a list of items\n"
+ "- Just use hyphens\n"
+ " - And starting space for indenting\n"
+ "\nRaw areas are enclosed inside three doublequotes and no markup is interpreted");
+
+/* signals */
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static gint gdaui_rt_editor_signals[LAST_SIGNAL] = { 0 };
+
+/* properties */
+enum {
+ PROP_0,
+ PROP_NO_BACKGROUND,
+ PROP_SHOW_MARKUP,
+ PROP_TEXTBUFFER
+};
+
+/* global pixbufs */
+static GdkPixbuf *bullet_pix = NULL;
+static GdkPixbuf *bulleth_pix = NULL;
+
+static gint spaces_since_start_of_line (GtkTextIter *iter);
+static gchar *real_gdaui_rt_editor_get_contents (GdauiRtEditor *editor);
+
+static void mark_set_cb (GtkTextBuffer *textbuffer, GtkTextIter *location,
+ GtkTextMark *mark, GdauiRtEditor *rte);
+static void delete_range_cb (GtkTextBuffer *textbuffer, GtkTextIter *start, GtkTextIter *end, GdauiRtEditor *rte);
+static void insert_text_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, GdauiRtEditor *rte);
+static void insert_text_after_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, GdauiRtEditor *rte);
+static void text_buffer_changed_cb (GtkTextBuffer *textbuffer, GdauiRtEditor *rte);
+static void populate_popup_cb (GtkTextView *entry, GtkMenu *menu, GdauiRtEditor *rte);
+
+static void show_hide_toolbar (GdauiRtEditor *editor);
+
+static gchar *add_newlines_to_base64 (gchar *base64);
+static gchar *remove_newlines_from_base64 (gchar *base64);
+
+static void italic_cb (GtkToggleAction *action, GdauiRtEditor *rte);
+static void strike_cb (GtkToggleAction *action, GdauiRtEditor *rte);
+static void underline_cb (GtkToggleAction *action, GdauiRtEditor *rte);
+static void bold_cb (GtkToggleAction *action, GdauiRtEditor *rte);
+static void reset_all_cb (GtkAction *action, GdauiRtEditor *rte);
+static void add_image_cb (GtkAction *action, GdauiRtEditor *rte);
+static void help_cb (GtkToggleAction *action, GdauiRtEditor *rte);
+
+static const GtkToggleActionEntry ui_toggle_actions [] =
+{
+ { "ActionBold", GTK_STOCK_BOLD, N_("_Bold"), NULL, N_("Bold text"), G_CALLBACK (bold_cb), FALSE},
+ { "ActionItalic", GTK_STOCK_ITALIC, N_("_Italic"), NULL, N_("Italic text"), G_CALLBACK (italic_cb), FALSE},
+ { "ActionUnderline", GTK_STOCK_UNDERLINE, N_("_Underline"), NULL, N_("Underline text"), G_CALLBACK (underline_cb), FALSE},
+ { "ActionStrike", GTK_STOCK_STRIKETHROUGH, N_("_Strike through"), NULL, N_("Strike through text"), G_CALLBACK (strike_cb), FALSE},
+ { "ActionHelp", GTK_STOCK_HELP, N_("_Syntax help"), NULL, N_("Show syntax help"), G_CALLBACK (help_cb), FALSE}
+};
+
+/* STOCK_SELECT_COLOR */
+static const GtkActionEntry ui_actions[] = {
+ { "ActionAddImage", "insert-image", N_("_Add image"), NULL, N_("Insert image"), G_CALLBACK (add_image_cb)},
+ { "ActionReset", GTK_STOCK_CLEAR, N_("_Reset"), NULL, N_("Reset to normal text"), G_CALLBACK (reset_all_cb)},
+};
+
+static const gchar *ui_actions_info =
+ "<ui>"
+ " <toolbar name='ToolBar'>"
+ " <toolitem action='ActionBold'/>"
+ " <toolitem action='ActionItalic'/>"
+ " <toolitem action='ActionUnderline'/>"
+ " <toolitem action='ActionStrike'/>"
+ " <toolitem action='ActionAddImage'/>"
+ " <toolitem action='ActionReset'/>"
+ " <toolitem action='ActionHelp'/>"
+ " </toolbar>"
+ "</ui>";
+
+GType
+gdaui_rt_editor_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (GdauiRtEditorClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gdaui_rt_editor_class_init,
+ NULL,
+ NULL,
+ sizeof (GdauiRtEditor),
+ 0,
+ (GInstanceInitFunc) gdaui_rt_editor_init,
+ 0
+ };
+
+ type = g_type_register_static (GTK_TYPE_VBOX, "GdauiRtEditor", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+gdaui_rt_editor_class_init (GdauiRtEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = gdaui_rt_editor_dispose;
+
+ GTK_WIDGET_CLASS (klass)->show_all = gdaui_rt_editor_show_all;
+
+ /* signals */
+ gdaui_rt_editor_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdauiRtEditorClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* Properties */
+ object_class->set_property = gdaui_rt_editor_set_property;
+ object_class->get_property = gdaui_rt_editor_get_property;
+ /**
+ * GdauiRtEditor:no-background:
+ *
+ * If set to %TRUE, then the default text background is removed
+ * and thus the textbackground is the default widget's background.
+ *
+ * This property has to be set before the widget is realized, and is taken into account only
+ * if the widget is not editable (when it's realized).
+ **/
+ g_object_class_install_property (object_class, PROP_NO_BACKGROUND,
+ g_param_spec_boolean ("no-background",
+ _("Don't display a specific background for the text"),
+ NULL, FALSE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ /**
+ * GdauiRtEditor:show-markup:
+ *
+ * Instead of showing the formatted text, display the raw text (in the txt2tags syntax)
+ **/
+ g_object_class_install_property (object_class, PROP_SHOW_MARKUP,
+ g_param_spec_boolean ("show-markup",
+ _("Display raw markup text instead of formatted text"),
+ NULL, FALSE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ /**
+ * GdauiRtEditor:buffer:
+ *
+ * Get access to the actual #GtkTextBuffer used. Do not modify it!
+ **/
+ g_object_class_install_property (object_class, PROP_TEXTBUFFER,
+ g_param_spec_object ("buffer",
+ _("The buffer which is displayed"),
+ NULL, GTK_TYPE_TEXT_BUFFER,
+ G_PARAM_READABLE));
+}
+
+static void
+text_view_realized_cb (GtkWidget *tv, GdauiRtEditor *rte)
+{
+ GdkWindow *win;
+ GtkStyle *style;
+ if (rte->priv->no_background && ! gtk_text_view_get_editable (GTK_TEXT_VIEW (tv))) {
+ win = gtk_text_view_get_window (GTK_TEXT_VIEW (tv), GTK_TEXT_WINDOW_TEXT);
+ style = gtk_widget_get_style (tv);
+ gdk_window_set_background (win, &(style->bg [GTK_STATE_NORMAL]));
+ }
+}
+
+static void
+gdaui_rt_editor_init (GdauiRtEditor *rte)
+{
+ GtkWidget *sw, *textview, *toolbar;
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_end (GTK_BOX (rte), sw, TRUE, TRUE, 0);
+
+ textview = gtk_text_view_new ();
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (textview), GTK_WRAP_WORD);
+ gtk_container_add (GTK_CONTAINER (sw), textview);
+ g_signal_connect (textview, "realize",
+ G_CALLBACK (text_view_realized_cb), rte);
+
+ gtk_widget_show_all (sw);
+
+ rte->priv = g_new0 (GdauiRtEditorPriv, 1);
+ rte->priv->saved_for_help = NULL;
+ rte->priv->enable_changed_signal = TRUE;
+ rte->priv->no_background = FALSE;
+ rte->priv->insert_offset = -1;
+ rte->priv->textview = GTK_TEXT_VIEW (textview);
+ rte->priv->textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+ g_signal_connect (rte->priv->textbuffer, "changed",
+ G_CALLBACK (text_buffer_changed_cb), rte);
+ g_signal_connect (rte->priv->textbuffer, "mark-set",
+ G_CALLBACK (mark_set_cb), rte);
+ g_signal_connect (rte->priv->textbuffer, "insert-text",
+ G_CALLBACK (insert_text_cb), rte);
+ g_signal_connect_after (rte->priv->textbuffer, "insert-text",
+ G_CALLBACK (insert_text_after_cb), rte);
+ g_signal_connect (rte->priv->textbuffer, "delete-range",
+ G_CALLBACK (delete_range_cb), rte);
+ g_signal_connect (rte->priv->textview, "populate-popup",
+ G_CALLBACK (populate_popup_cb), rte);
+
+ /* tags. REM: leave the LIST* and BULLET tags defined 1st as they will be with less priority
+ * and it affects the result returned by gtk_text_iter_get_tags() */
+ rte->priv->show_markup = FALSE;
+ memset (rte->priv->tags, 0, sizeof (rte->priv->tags));
+
+ rte->priv->tags[TEXT_TAG_LIST1].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "left_margin", 15,
+ /*"background", "#cbbcbc",*/
+ NULL);
+ rte->priv->tags[TEXT_TAG_LIST2].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "left_margin", 30,
+ /*"background", "#dcbcbc",*/
+ NULL);
+ rte->priv->tags[TEXT_TAG_BULLET].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "indent", -10,
+ /*"background", "#cbbcbc",*/
+ NULL);
+ rte->priv->tags[TEXT_TAG_ITALIC].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "style", PANGO_STYLE_ITALIC, NULL);
+ rte->priv->tags[TEXT_TAG_ITALIC].action_name = "/ToolBar/ActionItalic";
+
+ rte->priv->tags[TEXT_TAG_BOLD].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "weight", PANGO_WEIGHT_BOLD, NULL);
+ rte->priv->tags[TEXT_TAG_BOLD].action_name = "/ToolBar/ActionBold";
+
+ rte->priv->tags[TEXT_TAG_TT].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "family", "Monospace", NULL);
+ rte->priv->tags[TEXT_TAG_VERBATIM].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "background", "#e5e2e2",
+ NULL);
+
+ rte->priv->tags[TEXT_TAG_UNDERLINE].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "underline", PANGO_UNDERLINE_SINGLE, NULL);
+ rte->priv->tags[TEXT_TAG_UNDERLINE].action_name = "/ToolBar/ActionUnderline";
+
+ rte->priv->tags[TEXT_TAG_STRIKE].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "strikethrough", TRUE, NULL);
+ rte->priv->tags[TEXT_TAG_STRIKE].action_name = "/ToolBar/ActionStrike";
+
+ rte->priv->tags[TEXT_TAG_TITLE1].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "size", 15 * PANGO_SCALE,
+ "weight", PANGO_WEIGHT_SEMIBOLD,
+ NULL);
+ rte->priv->tags[TEXT_TAG_TITLE2].tag = gtk_text_buffer_create_tag (rte->priv->textbuffer, NULL,
+ "size", 13 * PANGO_SCALE,
+ "weight", PANGO_WEIGHT_SEMIBOLD,
+ NULL);
+
+ /* action group */
+ rte->priv->actions_group = gtk_action_group_new ("Actions");
+ gtk_action_group_add_toggle_actions (rte->priv->actions_group, ui_toggle_actions,
+ G_N_ELEMENTS (ui_toggle_actions), rte);
+ gtk_action_group_add_actions (rte->priv->actions_group, ui_actions, G_N_ELEMENTS (ui_actions),
+ rte);
+
+ /* ui manager */
+ rte->priv->uimanager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (rte->priv->uimanager, rte->priv->actions_group, 0);
+ gtk_ui_manager_add_ui_from_string (rte->priv->uimanager, ui_actions_info, -1, NULL);
+
+ /* toolbar */
+ toolbar = gtk_ui_manager_get_widget (rte->priv->uimanager, "/ToolBar");
+ gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
+ rte->priv->toolbar = toolbar;
+ gtk_box_pack_end (GTK_BOX (rte), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+}
+
+/**
+ * gdaui_rt_editor_new:
+ *
+ * Creates a new #GdauiRtEditor widget
+ *
+ * Returns: (transfer full): the new widget
+ *
+ * Since: 4.2.2
+ */
+GtkWidget *
+gdaui_rt_editor_new ()
+{
+ GtkWidget *rte;
+
+ rte = (GtkWidget *) g_object_new (GDAUI_TYPE_RT_EDITOR, NULL);
+
+ return rte;
+}
+
+static void
+gdaui_rt_editor_dispose (GObject *object)
+{
+ GdauiRtEditor *rte;
+
+ g_return_if_fail (GDAUI_IS_RT_EDITOR (object));
+ rte = GDAUI_RT_EDITOR (object);
+
+ if (rte->priv) {
+ if (rte->priv->actions_group) {
+ g_object_unref (G_OBJECT (rte->priv->actions_group));
+ rte->priv->actions_group = NULL;
+ }
+
+ if (rte->priv->uimanager)
+ g_object_unref (rte->priv->uimanager);
+
+ g_free (rte->priv->saved_for_help);
+ /* NB: GtkTextTags are owned by the GtkTextBuffer's text tags table */
+
+ /* the private area itself */
+ g_free (rte->priv);
+ rte->priv = NULL;
+ }
+
+ /* for the parent class */
+ parent_class->dispose (object);
+}
+
+static void
+gdaui_rt_editor_show_all (GtkWidget *widget)
+{
+ GdauiRtEditor *editor;
+ editor = GDAUI_RT_EDITOR (widget);
+ GTK_WIDGET_CLASS (parent_class)->show_all (widget);
+ show_hide_toolbar (editor);
+}
+
+static void
+gdaui_rt_editor_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdauiRtEditor *editor;
+
+ editor = GDAUI_RT_EDITOR (object);
+ if (editor->priv) {
+ switch (param_id) {
+ case PROP_NO_BACKGROUND:
+ editor->priv->no_background = g_value_get_boolean (value);
+ break;
+ case PROP_SHOW_MARKUP:
+ _gdaui_rt_editor_set_show_markup (editor, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+static void
+gdaui_rt_editor_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdauiRtEditor *editor;
+
+ editor = GDAUI_RT_EDITOR (object);
+ if (editor->priv) {
+ switch (param_id) {
+ case PROP_NO_BACKGROUND:
+ g_value_set_boolean (value, editor->priv->no_background);
+ break;
+ case PROP_SHOW_MARKUP:
+ g_value_set_boolean (value, editor->priv->show_markup);
+ break;
+ case PROP_TEXTBUFFER:
+ g_value_set_object (value, editor->priv->textbuffer);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+/* tags management */
+static void
+apply_tag (GdauiRtEditor *rte, gboolean reverse, GtkTextTag *tag)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_if_fail (rte->priv->textbuffer);
+
+ if (rte->priv->selection_changing)
+ return;
+
+ if (gtk_text_buffer_get_selection_bounds (rte->priv->textbuffer, &start, &end)) {
+ if (tag) {
+ if (reverse)
+ gtk_text_buffer_remove_tag (rte->priv->textbuffer, tag, &start, &end);
+ else {
+ /* don't apply tag if there is a LIST tag */
+ gboolean doapply = TRUE;
+ GtkTextIter iter;
+ for (iter = start; gtk_text_iter_compare (&iter, &end) < 0; ) {
+ if (gtk_text_iter_has_tag (&iter, rte->priv->tags[TEXT_TAG_BULLET].tag) ||
+ gtk_text_iter_has_tag (&iter, rte->priv->tags[TEXT_TAG_LIST1].tag) ||
+ gtk_text_iter_has_tag (&iter, rte->priv->tags[TEXT_TAG_LIST2].tag)) {
+ doapply = FALSE;
+ break;
+ }
+ if (! gtk_text_iter_forward_char (&iter))
+ break;
+ }
+ if (doapply)
+ gtk_text_buffer_apply_tag (rte->priv->textbuffer, tag, &start, &end);
+ }
+ }
+ else {
+ GtkTextIter iter;
+ for (iter = start; gtk_text_iter_compare (&iter, &end) < 0; ) {
+
+ GSList *tags, *list;
+ tags = gtk_text_iter_get_tags (&iter);
+ if (tags) {
+ for (list = tags; list; list = list->next) {
+ GtkTextTag *tag = (GtkTextTag *) list->data;
+ if ((tag != rte->priv->tags[TEXT_TAG_BULLET].tag) &&
+ (tag != rte->priv->tags[TEXT_TAG_LIST1].tag) &&
+ (tag != rte->priv->tags[TEXT_TAG_LIST2].tag))
+ gtk_text_buffer_remove_tag (rte->priv->textbuffer,
+ tag, &start, &end);
+ }
+ g_slist_free (tags);
+ }
+ if (! gtk_text_iter_forward_char (&iter))
+ break;
+ }
+ }
+ }
+}
+
+static void
+help_cb (GtkToggleAction *action, GdauiRtEditor *rte)
+{
+ if (gtk_toggle_action_get_active (action)) {
+ rte->priv->enable_changed_signal = FALSE;
+ g_free (rte->priv->saved_for_help);
+ rte->priv->saved_for_help = gdaui_rt_editor_get_contents (rte);
+ gdaui_rt_editor_set_contents (rte, help_str, -1);
+ }
+ else {
+ gdaui_rt_editor_set_contents (rte, rte->priv->saved_for_help, -1);
+ rte->priv->enable_changed_signal = TRUE;
+ g_free (rte->priv->saved_for_help);
+ rte->priv->saved_for_help = NULL;
+ }
+}
+
+static void
+italic_cb (GtkToggleAction *action, GdauiRtEditor *rte)
+{
+ apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
+ rte->priv->tags [TEXT_TAG_ITALIC].tag);
+}
+
+static void
+bold_cb (GtkToggleAction *action, GdauiRtEditor *rte)
+{
+ apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
+ rte->priv->tags [TEXT_TAG_BOLD].tag);
+}
+
+static void
+strike_cb (GtkToggleAction *action, GdauiRtEditor *rte)
+{
+ apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
+ rte->priv->tags [TEXT_TAG_STRIKE].tag);
+}
+
+static void
+underline_cb (GtkToggleAction *action, GdauiRtEditor *rte)
+{
+ apply_tag (rte, gtk_toggle_action_get_active (action) ? FALSE : TRUE,
+ rte->priv->tags [TEXT_TAG_UNDERLINE].tag);
+}
+
+static void
+reset_all_cb (GtkAction *action, GdauiRtEditor *rte)
+{
+ apply_tag (rte, FALSE, NULL);
+}
+
+static void
+add_image_cb (GtkAction *action, GdauiRtEditor *rte)
+{
+ GtkWidget *dlg;
+ GtkFileFilter *filter;
+
+ dlg = gtk_file_chooser_dialog_new (_("Select image to load"),
+ GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) rte)),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_add_pixbuf_formats (filter);
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dlg), filter);
+
+ if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+ GError *error = NULL;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
+
+ GdkPixbuf *pixbuf;
+ pixbuf = gdk_pixbuf_new_from_file (filename, &error);
+ if (pixbuf) {
+ GtkTextIter start;
+ GtkTextIter end;
+ gboolean ret = FALSE;
+
+ ret = gtk_text_buffer_get_selection_bounds (rte->priv->textbuffer, &start, &end);
+ if (ret)
+ gtk_text_buffer_delete (rte->priv->textbuffer, &start, &end);
+
+ gtk_text_buffer_get_iter_at_mark (rte->priv->textbuffer, &start,
+ gtk_text_buffer_get_insert (rte->priv->textbuffer));
+ gtk_text_buffer_insert_pixbuf (rte->priv->textbuffer, &start, pixbuf);
+ g_object_unref (pixbuf);
+ }
+ else {
+ GtkWidget *msg;
+
+ msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) rte)),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Could not load the contents of '%s':\n %s"),
+ filename,
+ error && error->message ? error->message : _("No detail"));
+ g_clear_error (&error);
+ gtk_widget_destroy (dlg);
+ dlg = NULL;
+
+ gtk_dialog_run (GTK_DIALOG (msg));
+ gtk_widget_destroy (msg);
+ }
+ }
+
+ gtk_widget_destroy (dlg);
+}
+
+static void
+mark_set_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, GtkTextMark *mark, GdauiRtEditor *rte)
+{
+ if (mark == gtk_text_buffer_get_insert (textbuffer)) {
+ GtkAction *action;
+ gboolean act;
+ gint i;
+
+ rte->priv->selection_changing = TRUE;
+
+ for (i = 0; i < TEXT_TAG_LAST; i++) {
+ if (! rte->priv->tags[i].action_name)
+ continue;
+
+ action = gtk_ui_manager_get_action (rte->priv->uimanager,
+ rte->priv->tags[i].action_name);
+ if (gtk_text_buffer_get_has_selection (textbuffer))
+ act = FALSE;
+ else
+ act = gtk_text_iter_has_tag (location, rte->priv->tags [i].tag);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), act);
+ }
+
+ rte->priv->selection_changing = FALSE;
+ }
+}
+
+static void
+insert_text_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, GdauiRtEditor *rte)
+{
+ rte->priv->insert_offset = gtk_text_iter_get_offset (location);
+}
+
+static void
+insert_text_after_cb (GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, GdauiRtEditor *rte)
+{
+ GtkTextIter start, end, sol;
+
+ if ((rte->priv->insert_offset < 0) || rte->priv->show_markup)
+ return;
+
+ gtk_text_buffer_get_iter_at_offset (textbuffer, &start, rte->priv->insert_offset);
+ end = *location;
+ if (gtk_text_iter_backward_chars (&end, g_utf8_strlen (text, -1))) {
+ gint i;
+ for (i = 0; i < TEXT_TAG_LAST; i++) {
+ GtkAction *action;
+ if (! rte->priv->tags[i].action_name)
+ continue;
+ action = gtk_ui_manager_get_action (rte->priv->uimanager,
+ rte->priv->tags[i].action_name);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ gtk_text_buffer_apply_tag (rte->priv->textbuffer,
+ rte->priv->tags[i].tag, location, &end);
+ }
+ }
+ rte->priv->insert_offset = -1;
+
+ /* check if the start of the line has a list tag */
+ sol = end;
+ gtk_text_iter_set_line_offset (&sol, 0);
+ if (gtk_text_iter_has_tag (&sol, rte->priv->tags [TEXT_TAG_LIST1].tag))
+ gtk_text_buffer_apply_tag (rte->priv->textbuffer,
+ rte->priv->tags[TEXT_TAG_LIST1].tag, location, &end);
+ else if (gtk_text_iter_has_tag (&sol, rte->priv->tags [TEXT_TAG_LIST2].tag))
+ gtk_text_buffer_apply_tag (rte->priv->textbuffer,
+ rte->priv->tags[TEXT_TAG_LIST2].tag, location, &end);
+
+ if (*text == '\n') {
+ /* check if we are at a bullet to add another bullet to make a list */
+ if (! gtk_text_iter_ends_tag (&start, rte->priv->tags [TEXT_TAG_BULLET].tag)) {
+ gint line;
+ line = gtk_text_iter_get_line (&start);
+ for (; gtk_text_iter_backward_char (&start) &&
+ (gtk_text_iter_get_line (&start) == line); ) {
+ if (gtk_text_iter_begins_tag (&start, rte->priv->tags [TEXT_TAG_BULLET].tag)) {
+ gtk_text_iter_forward_char (&end);
+ if (gtk_text_iter_has_tag (&start, rte->priv->tags [TEXT_TAG_LIST1].tag))
+ gtk_text_buffer_insert (textbuffer, &end, "- ", -1);
+ else if (gtk_text_iter_has_tag (&start, rte->priv->tags [TEXT_TAG_LIST2].tag))
+ gtk_text_buffer_insert (textbuffer, &end, " - ", -1);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+delete_range_cb (GtkTextBuffer *textbuffer, GtkTextIter *start, GtkTextIter *end, GdauiRtEditor *rte)
+{
+ GtkTextIter prev;
+
+ /* check if there is a start or end TEXT_TAG_BULLET in the deleted range */
+ prev = *start;
+ do {
+ if (gtk_text_iter_has_tag (&prev, rte->priv->tags[TEXT_TAG_BULLET].tag)) {
+ GtkTextIter iter;
+ iter = prev;
+ if (! gtk_text_iter_begins_tag (&prev, rte->priv->tags[TEXT_TAG_BULLET].tag)) {
+ /* find start of tag */
+ for (; gtk_text_iter_backward_char (&iter); ) {
+ if (gtk_text_iter_begins_tag (&iter,
+ rte->priv->tags[TEXT_TAG_BULLET].tag)) {
+ if (gtk_text_iter_compare (&iter, start) < 0)
+ *start = iter;
+ break;
+ }
+ }
+ }
+
+ /* find end of tag */
+ for (; gtk_text_iter_forward_char (&prev); ) {
+ if (gtk_text_iter_ends_tag (&prev,
+ rte->priv->tags[TEXT_TAG_BULLET].tag)) {
+ if (gtk_text_iter_compare (&prev, end) > 0)
+ *end = prev;
+ break;
+ }
+ }
+ gtk_text_buffer_remove_tag (rte->priv->textbuffer,
+ rte->priv->tags[TEXT_TAG_BULLET].tag,
+ &iter, &prev);
+ }
+ }
+ while (gtk_text_iter_forward_char (&prev) && (gtk_text_iter_compare (&prev, end) <= 0));
+
+ /* check if TEXT_TAG_LIST1 left opened in range */
+ if (gtk_text_iter_has_tag (end, rte->priv->tags[TEXT_TAG_LIST1].tag)) {
+ GtkTextIter iter;
+ iter = *start;
+ if (gtk_text_iter_backward_char (&iter) &&
+ ! gtk_text_iter_has_tag (&iter, rte->priv->tags[TEXT_TAG_LIST1].tag)) {
+ for (iter = *end; gtk_text_iter_forward_char (&iter); ) {
+ if (gtk_text_iter_ends_tag (&iter, rte->priv->tags[TEXT_TAG_LIST1].tag))
+ break;
+ }
+ gtk_text_buffer_remove_tag (rte->priv->textbuffer,
+ rte->priv->tags[TEXT_TAG_LIST1].tag,
+ start, &iter);
+ }
+ }
+ /* check if TEXT_TAG_LIST2 left opened in range */
+ if (gtk_text_iter_has_tag (end, rte->priv->tags[TEXT_TAG_LIST2].tag)) {
+ GtkTextIter iter;
+ iter = *start;
+ if (gtk_text_iter_backward_char (&iter) &&
+ ! gtk_text_iter_has_tag (&iter, rte->priv->tags[TEXT_TAG_LIST2].tag)) {
+ for (iter = *end; gtk_text_iter_forward_char (&iter); ) {
+ if (gtk_text_iter_ends_tag (&iter, rte->priv->tags[TEXT_TAG_LIST2].tag))
+ break;
+ }
+ gtk_text_buffer_remove_tag (rte->priv->textbuffer,
+ rte->priv->tags[TEXT_TAG_LIST2].tag,
+ start, &iter);
+ }
+ }
+}
+
+static void
+show_markup_item_activate_cb (GtkCheckMenuItem *checkmenuitem, GdauiRtEditor *rte)
+{
+ gboolean show;
+ show = gtk_check_menu_item_get_active (checkmenuitem);
+ _gdaui_rt_editor_set_show_markup (rte, show);
+}
+
+static void
+populate_popup_cb (GtkTextView *entry, GtkMenu *menu, GdauiRtEditor *rte)
+{
+ GtkWidget *item;
+
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_check_menu_item_new_with_label (_("Show source markup"));
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), rte->priv->show_markup);
+ g_signal_connect (G_OBJECT (item), "toggled",
+ G_CALLBACK (show_markup_item_activate_cb), rte);
+ gtk_widget_show (item);
+}
+
+/* RTE markup analysis */
+typedef enum {
+ MARKUP_NONE, /* 0 */
+ MARKUP_BOLD,
+ MARKUP_TT,
+ MARKUP_VERBATIM,
+ MARKUP_ITALIC,
+ MARKUP_STRIKE, /* 5 */
+ MARKUP_UNDERLINE,
+
+ MARKUP_TITLE1_S,
+ MARKUP_TITLE1_E,
+ MARKUP_TITLE2_S,
+ MARKUP_TITLE2_E, /* 10 */
+
+ MARKUP_PICTURE_S,
+ MARKUP_PICTURE_E,
+
+ MARKUP_LIST_S,
+ MARKUP_LIST_E,
+
+ MARKUP_EOF
+} MarkupTag;
+
+typedef struct {
+ GtkTextMark *m_start;
+ GtkTextMark *m_end;
+ gboolean init;
+ MarkupTag markup;
+} TextTag;
+
+
+static MarkupTag get_markup_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
+ TextTag *start_tag, GdauiRtEditor *rte);
+static MarkupTag get_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
+ TextTag *start_tag, GdauiRtEditor *rte);
+static gchar get_char_at_iter (GtkTextIter *iter, gboolean move_forward_first);
+static gboolean markup_tag_match (MarkupTag tag1, gint tagline1, MarkupTag tag2, gint tagline2);
+
+static void
+apply_markup (GdauiRtEditor *rte, GtkTextBuffer *textbuffer, TextTag *current, GtkTextMark *mark_start, GtkTextMark *mark_end)
+{
+ gint ssol;
+ GtkTextIter start, end;
+
+ rte->priv->insert_offset = -1;
+
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &start, mark_start);
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &end, mark_end);
+
+
+ /* apply markup */
+ GtkTextIter astart;
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &astart, current->m_start);
+ switch (current->markup) {
+ case MARKUP_BOLD:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_BOLD].tag,
+ &astart, &end);
+ break;
+ case MARKUP_VERBATIM:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_VERBATIM].tag,
+ &astart, &end);
+ break;
+ case MARKUP_TT:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_TT].tag,
+ &astart, &end);
+ break;
+ case MARKUP_ITALIC:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_ITALIC].tag,
+ &astart, &end);
+ break;
+ case MARKUP_STRIKE:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_STRIKE].tag,
+ &astart, &end);
+ break;
+ case MARKUP_UNDERLINE:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_UNDERLINE].tag,
+ &astart, &end);
+ break;
+ case MARKUP_TITLE1_S:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_TITLE1].tag,
+ &astart, &end);
+ break;
+ case MARKUP_TITLE2_S:
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_TITLE2].tag,
+ &astart, &end);
+ break;
+ case MARKUP_LIST_S: {
+ if (! bullet_pix)
+ bullet_pix = gdk_pixbuf_new_from_inline (-1, bullet_pixdata,
+ FALSE, NULL);
+ if (! bulleth_pix)
+ bulleth_pix = gdk_pixbuf_new_from_inline (-1, bulleth_pixdata,
+ FALSE, NULL);
+ GtkTextIter ps, pe;
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_start);
+ ssol = spaces_since_start_of_line (&ps);
+ if (ssol > 0) {
+ GtkTextIter diter;
+ diter = ps;
+ if (gtk_text_iter_backward_chars (&diter, ssol))
+ gtk_text_buffer_delete (textbuffer, &diter, &ps);
+ }
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_end);
+ pe = ps;
+ if (ssol > 0)
+ gtk_text_buffer_insert_pixbuf (textbuffer, &pe, bulleth_pix);
+ else
+ gtk_text_buffer_insert_pixbuf (textbuffer, &pe, bullet_pix);
+ gtk_text_buffer_insert (textbuffer, &pe, " ", -1);
+
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_start);
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_BULLET].tag,
+ &ps, &pe);
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &astart, current->m_start);
+ if (! gtk_text_iter_ends_line (&ps))
+ for (; gtk_text_iter_forward_char (&ps); ) {
+ if (gtk_text_iter_ends_line (&ps))
+ break;
+ }
+ else
+ gtk_text_iter_forward_char (&ps);
+ if (ssol > 0)
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_LIST2].tag,
+ &astart, &ps);
+ else
+ gtk_text_buffer_apply_tag (textbuffer,
+ rte->priv->tags[TEXT_TAG_LIST1].tag,
+ &astart, &ps);
+ break;
+ }
+ case MARKUP_PICTURE_S: {
+ gchar *data;
+ GtkTextIter ps, pe;
+ gsize length;
+ GdkPixdata pixdata;
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, current->m_end);
+ pe = start;
+ data = remove_newlines_from_base64 (gtk_text_buffer_get_text (textbuffer, &ps, &pe, FALSE));
+ /*g_print ("{{{%s}}}\n", data);*/
+ g_base64_decode_inplace (data, &length);
+ if (gdk_pixdata_deserialize (&pixdata, length, (guint8*) data, NULL)) {
+ GdkPixbuf *pixbuf;
+ pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, NULL);
+ if (pixbuf) {
+ gtk_text_buffer_delete (textbuffer, &ps, &pe);
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &ps, mark_end);
+ gtk_text_buffer_insert_pixbuf (textbuffer, &ps, pixbuf);
+ g_object_unref (pixbuf);
+ }
+ }
+ g_free (data);
+ break;
+ }
+ default:
+ g_warning ("Unhandled marker (type %d)", current->markup);
+ break;
+ }
+ /* remove markup text */
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &start, mark_start);
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &end, mark_end);
+ if (! gtk_text_iter_equal (&start, &end))
+ gtk_text_buffer_delete (textbuffer, &start, &end);
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &start, current->m_start);
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &end, current->m_end);
+ if (! gtk_text_iter_equal (&start, &end))
+ gtk_text_buffer_delete (textbuffer, &start, &end);
+}
+
+static void
+text_buffer_changed_cb (GtkTextBuffer *textbuffer, GdauiRtEditor *rte)
+{
+ GSList *queue = NULL;
+ MarkupTag mt;
+ TextTag *current = NULL;
+ GtkTextIter start, end;
+ gint ssol;
+
+ if (rte->priv->show_markup) {
+ if (rte->priv->enable_changed_signal)
+ g_signal_emit (rte, gdaui_rt_editor_signals[CHANGED], 0, NULL);
+ return;
+ }
+
+ gtk_text_buffer_get_start_iter (textbuffer, &start);
+
+ g_signal_handlers_block_by_func (textbuffer,
+ G_CALLBACK (text_buffer_changed_cb), rte);
+ g_signal_handlers_block_by_func (textbuffer,
+ G_CALLBACK (delete_range_cb), rte);
+
+
+ for (mt = get_token (&start, &ssol, &end, current, rte);
+ mt != MARKUP_EOF;
+ mt = get_token (&start, &ssol, &end, current, rte)) {
+ gchar *text= gtk_text_iter_get_text (&start, &end);
+ /*g_print ("Token %d [%s] with SSOL %d\n", mt, text, ssol);*/
+ g_free (text);
+
+ if (mt == MARKUP_NONE) {
+ start = end;
+ continue;
+ }
+ if (! current) {
+ current = g_new (TextTag, 1);
+ current->markup = mt;
+ current->m_start = gtk_text_buffer_create_mark (textbuffer, NULL, &start, TRUE);
+ current->m_end = gtk_text_buffer_create_mark (textbuffer, NULL, &end, TRUE);
+
+ queue = g_slist_prepend (queue, current);
+ }
+ else {
+ GtkTextIter liter;
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &liter, current->m_start);
+ if (markup_tag_match (current->markup, gtk_text_iter_get_line (&liter),
+ mt, gtk_text_iter_get_line (&start))) {
+ /* save iters as marks */
+ GtkTextMark *mark_start, *mark_end;
+ mark_start = gtk_text_buffer_create_mark (textbuffer, NULL, &start, TRUE);
+ mark_end = gtk_text_buffer_create_mark (textbuffer, NULL, &end, TRUE);
+
+ /* apply markup */
+ apply_markup (rte, textbuffer, current, mark_start, mark_end);
+
+ /* get rid of @current */
+ gtk_text_buffer_delete_mark (textbuffer, current->m_start);
+ gtk_text_buffer_delete_mark (textbuffer, current->m_end);
+ g_free (current);
+ queue = g_slist_remove (queue, current);
+ current = NULL;
+
+ if (queue) {
+ current = (TextTag*) queue->data;
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &end, current->m_end);
+ }
+ else {
+ /* restore iter from marks */
+ gtk_text_buffer_get_iter_at_mark (textbuffer, &end, mark_end);
+ }
+
+ /* delete marks */
+ gtk_text_buffer_delete_mark (textbuffer, mark_end);
+ gtk_text_buffer_delete_mark (textbuffer, mark_start);
+ }
+ else {
+ current = g_new (TextTag, 1);
+ current->markup = mt;
+ current->m_start = gtk_text_buffer_create_mark (textbuffer, NULL, &start,
+ TRUE);
+ current->m_end = gtk_text_buffer_create_mark (textbuffer, NULL, &end, TRUE);
+
+ queue = g_slist_prepend (queue, current);
+ }
+ }
+
+ start = end;
+ }
+
+ while (queue) {
+ current = (TextTag*) queue->data;
+ gtk_text_buffer_delete_mark (textbuffer, current->m_start);
+ gtk_text_buffer_delete_mark (textbuffer, current->m_end);
+ g_free (current);
+ queue = g_slist_delete_link (queue, queue);
+ }
+
+ g_signal_handlers_unblock_by_func (textbuffer,
+ G_CALLBACK (delete_range_cb), rte);
+ g_signal_handlers_unblock_by_func (textbuffer,
+ G_CALLBACK (text_buffer_changed_cb), rte);
+
+ if (rte->priv->enable_changed_signal)
+ g_signal_emit (rte, gdaui_rt_editor_signals[CHANGED], 0, NULL);
+}
+
+/**
+ * get_token
+ *
+ * returns the token type starting from @iter, and positions @out_end to the last used position
+ * position.
+ */
+static MarkupTag
+get_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
+ TextTag *start_tag, GdauiRtEditor *rte)
+{
+ MarkupTag retval;
+ GtkTextIter inti;
+ inti = *iter;
+
+ retval = get_markup_token (&inti, out_nb_spaces_before, out_end, start_tag, rte);
+ if ((retval != MARKUP_NONE) || (retval == MARKUP_EOF))
+ return retval;
+
+ for (; gtk_text_iter_forward_char (&inti);) {
+ retval = get_markup_token (&inti, NULL, NULL, start_tag, rte);
+ if ((retval != MARKUP_NONE) || (retval == MARKUP_EOF))
+ break;
+ }
+ *out_end = inti;
+ return MARKUP_NONE;
+}
+
+/*
+ * spaces_since_start_of_line
+ * @iter: an iterator, __not modified__
+ *
+ * Computes the number of spaces since start of line in case there is no other
+ * character on the line except for spaces
+ *
+ * Returns: number of spaces, or -1 if there is not only some spaces on the line before @iter
+ */
+static gint
+spaces_since_start_of_line (GtkTextIter *iter)
+{
+ gint i = 0;
+ GtkTextIter inti = *iter;
+ gunichar u;
+ for (; !gtk_text_iter_starts_line (&inti) && gtk_text_iter_backward_char (&inti); i++) {
+ u = gtk_text_iter_get_char (&inti);
+ if (! g_unichar_isspace (u))
+ return -1;
+ }
+
+#ifdef GDA_DEBUG_NO
+ gchar *data;
+ data = gtk_text_buffer_get_slice (gtk_text_iter_get_buffer (iter), &inti, iter, TRUE);
+ g_print ("FOUND %d spaces for [%s]\n", i, data);
+ g_free (data);
+#endif
+ return i;
+}
+
+/**
+ * get_markup_token
+ * @iter: starting position
+ * @out_nb_spaces_before: a place to set the value returned by spaces_since_start_of_line() if called
+ * @out_end: place to put the last used position, or %NULL
+ * @rte: the #GdauiRtEditor
+ *
+ * Parses marking tokens, nothing else
+ *
+ * Returns: a markup token, or MARKUP_NONE or MARKUP_EOF otherwise
+ */
+static MarkupTag
+get_markup_token (GtkTextIter *iter, gint *out_nb_spaces_before, GtkTextIter *out_end,
+ TextTag *start_tag, GdauiRtEditor *rte)
+{
+ GtkTextIter inti;
+ gchar c;
+ gint ssol = -1; /* spaces since start of line */
+ MarkupTag start_markup = MARKUP_EOF;
+ gint linestart = 0;
+
+#define SET_OUT \
+ if (out_end) { \
+ gtk_text_iter_forward_char (&inti); \
+ *out_end = inti; \
+ } \
+ if (out_nb_spaces_before) \
+ *out_nb_spaces_before = ssol
+
+ if (start_tag) {
+ start_markup = start_tag->markup;
+ gtk_text_buffer_get_iter_at_mark (gtk_text_iter_get_buffer (iter), &inti, start_tag->m_start);
+ linestart = gtk_text_iter_get_line (&inti);
+ }
+
+ inti = *iter;
+ if (out_end)
+ *out_end = inti;
+
+ c = get_char_at_iter (&inti, FALSE);
+
+ /* tests involving starting markup before anything else */
+ if (start_markup == MARKUP_PICTURE_S) {
+ if (c == ']') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == ']') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == ']') {
+ SET_OUT;
+ return MARKUP_PICTURE_E;
+ }
+ }
+ }
+ if (!c)
+ return MARKUP_EOF;
+ else
+ return MARKUP_NONE;
+ }
+ else if (start_markup == MARKUP_VERBATIM) {
+ if (c == '"') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '"') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '"') {
+ SET_OUT;
+ return MARKUP_VERBATIM;
+ }
+ }
+ }
+ if (!c)
+ return MARKUP_EOF;
+ else
+ return MARKUP_NONE;
+ }
+ else if (gtk_text_iter_has_tag (&inti, rte->priv->tags[TEXT_TAG_VERBATIM].tag)) {
+ if (!c)
+ return MARKUP_EOF;
+ else
+ return MARKUP_NONE;
+ }
+
+ if (gtk_text_iter_ends_line (&inti) && (start_markup == MARKUP_LIST_S) &&
+ (linestart == gtk_text_iter_get_line (&inti)))
+ return MARKUP_LIST_E;
+
+ if (!c)
+ return MARKUP_EOF;
+
+ /* other tests */
+ ssol = spaces_since_start_of_line (&inti);
+ if (ssol >= 0) {
+ /* we are on a line with only spaces since its start */
+ if (c == '=') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == ' ') {
+ SET_OUT;
+ return MARKUP_TITLE1_S;
+ }
+ else if (c == '=') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == ' ') {
+ SET_OUT;
+ return MARKUP_TITLE2_S;
+ }
+ }
+ }
+ else if (c == '-') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == ' ') {
+ SET_OUT;
+ return MARKUP_LIST_S;
+ }
+ }
+ }
+
+ if (c == '*') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '*') {
+ SET_OUT;
+ return MARKUP_BOLD;
+ }
+ }
+ else if (c == '/') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '/') {
+ SET_OUT;
+ return MARKUP_ITALIC;
+ }
+ }
+ else if (c == '_') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '_') {
+ SET_OUT;
+ return MARKUP_UNDERLINE;
+ }
+ }
+ else if (c == '-') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '-') {
+ SET_OUT;
+ return MARKUP_STRIKE;
+ }
+ }
+ else if (c == '`') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '`') {
+ SET_OUT;
+ return MARKUP_TT;
+ }
+ }
+ else if (c == '"') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '"') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '"') {
+ SET_OUT;
+ return MARKUP_VERBATIM;
+ }
+ }
+ }
+ else if (c == ' ') {
+ gboolean line;
+ line = gtk_text_iter_starts_line (&inti);
+
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '=') {
+ if (start_markup == MARKUP_TITLE1_S) {
+ GtkTextIter it = inti;
+ gtk_text_iter_forward_char (&it);
+ if (gtk_text_iter_ends_line (&it) &&
+ (linestart == gtk_text_iter_get_line (&inti))) {
+ SET_OUT;
+ return MARKUP_TITLE1_E;
+ }
+ }
+ else {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '=') {
+ GtkTextIter it = inti;
+ gtk_text_iter_forward_char (&it);
+ if ((start_markup == MARKUP_TITLE2_S) && gtk_text_iter_ends_line (&it) &&
+ (linestart == gtk_text_iter_get_line (&inti))) {
+ SET_OUT;
+ return MARKUP_TITLE2_E;
+ }
+ }
+ }
+ }
+ }
+ else if (c == '[') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '[') {
+ c = get_char_at_iter (&inti, TRUE);
+ if (c == '[') {
+ SET_OUT;
+ return MARKUP_PICTURE_S;
+ }
+ }
+ }
+ return MARKUP_NONE;
+}
+
+/**
+ * get_char_at_iter
+ * @iter: an iter
+ * @move_forward_first: %TRUE if @iter should be moved forward first
+ *
+ * Returns: 0 for EOF, 1 for the "unknown" unicode char (usually pixbufs) or for chars represented
+ * by more than one byte
+ */
+static gchar
+get_char_at_iter (GtkTextIter *iter, gboolean move_forward_first)
+{
+ if (!move_forward_first ||
+ (move_forward_first && gtk_text_iter_forward_char (iter))) {
+ gunichar uc;
+ gchar buf1[6];
+ uc = gtk_text_iter_get_char (iter);
+ if (!uc)
+ return 0;
+ if (g_unichar_to_utf8 (uc, buf1) == 1)
+ return *buf1;
+ return 1;
+ }
+ return 0;
+}
+
+static gboolean
+markup_tag_match (MarkupTag tag1, gint tagline1, MarkupTag tag2, gint tagline2)
+{
+ gboolean retval;
+ switch (tag1) {
+ case MARKUP_BOLD:
+ case MARKUP_TT:
+ case MARKUP_VERBATIM:
+ case MARKUP_ITALIC:
+ case MARKUP_STRIKE:
+ case MARKUP_UNDERLINE:
+ retval = (tag1 == tag2) ? TRUE : FALSE;
+ break;
+ case MARKUP_TITLE1_S:
+ retval = (tag2 == MARKUP_TITLE1_E) ? TRUE : FALSE;
+ break;
+ case MARKUP_TITLE2_S:
+ retval = (tag2 == MARKUP_TITLE2_E) ? TRUE : FALSE;
+ break;
+ case MARKUP_PICTURE_S:
+ retval = (tag2 == MARKUP_PICTURE_E) ? TRUE : FALSE;
+ break;
+ case MARKUP_LIST_S:
+ retval = (tag2 == MARKUP_LIST_E) ? TRUE : FALSE;
+ break;
+ default:
+ retval = FALSE;
+ break;
+ }
+
+ if (retval) {
+ if (tag1 != MARKUP_PICTURE_S)
+ retval = (tagline1 == tagline2) ? TRUE : FALSE;
+ }
+ return retval;
+}
+
+static guint8 *serialize_as_txt2tag (GtkTextBuffer *register_buffer,
+ GtkTextBuffer *content_buffer,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ gsize *length,
+ GdauiRtEditor *editor);
+
+/**
+ * gdaui_rt_editor_get_contents
+ * @editor: a #GdauiRtEditor
+ *
+ * Get the contents of @editor, using the markup syntax
+ *
+ * Returns: (transfer full): a new string, or %NULL if there was an error
+ *
+ * Since: 4.2.2
+ */
+gchar *
+gdaui_rt_editor_get_contents (GdauiRtEditor *editor)
+{
+ g_return_val_if_fail (GDAUI_IS_RT_EDITOR (editor), NULL);
+
+ if (editor->priv->saved_for_help)
+ return g_strdup (editor->priv->saved_for_help);
+ else
+ return real_gdaui_rt_editor_get_contents (editor);
+}
+
+static gchar *
+real_gdaui_rt_editor_get_contents (GdauiRtEditor *editor)
+{
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_bounds (editor->priv->textbuffer, &start, &end);
+
+ if (editor->priv->show_markup)
+ return gtk_text_buffer_get_text (editor->priv->textbuffer, &start, &end, FALSE);
+ else {
+ GdkAtom format;
+ guint8 *data;
+ gsize length;
+ format = gtk_text_buffer_register_serialize_format (editor->priv->textbuffer, "txt/rte",
+ (GtkTextBufferSerializeFunc) serialize_as_txt2tag,
+ editor, NULL);
+ data = gtk_text_buffer_serialize (editor->priv->textbuffer, editor->priv->textbuffer, format,
+ &start, &end, &length);
+ return (gchar*) data;
+ }
+}
+
+/*
+ * Serialization as txt2tag
+ */
+typedef struct
+{
+ GString *text_str;
+ GHashTable *tags;
+ GtkTextIter start, end;
+
+ gint tag_id;
+ GHashTable *tag_id_tags;
+} SerializationContext;
+
+/*
+ * serialize_tag:
+ * @tag: a #GtkTextTag
+ * @starting: %TRUE if serialization has to be done for the opening part
+ *
+ * Returns: a static string, never %NULL
+ */
+static const gchar *
+serialize_tag (GtkTextTag *tag, gboolean starting, GdauiRtEditor *editor)
+{
+ if (tag == editor->priv->tags[TEXT_TAG_ITALIC].tag)
+ return "//";
+ else if (tag == editor->priv->tags[TEXT_TAG_BOLD].tag)
+ return "**";
+ else if (tag == editor->priv->tags[TEXT_TAG_UNDERLINE].tag)
+ return "__";
+ else if (tag == editor->priv->tags[TEXT_TAG_STRIKE].tag)
+ return "--";
+ else if (tag == editor->priv->tags[TEXT_TAG_TT].tag)
+ return "``";
+ else if (tag == editor->priv->tags[TEXT_TAG_VERBATIM].tag)
+ return "\"\"\"";
+ else if (tag == editor->priv->tags[TEXT_TAG_TITLE1].tag) {
+ if (starting)
+ return "= ";
+ else
+ return " =";
+ }
+ else if (tag == editor->priv->tags[TEXT_TAG_TITLE2].tag) {
+ if (starting)
+ return "== ";
+ else
+ return " ==";
+ }
+ else if (tag == editor->priv->tags[TEXT_TAG_BULLET].tag)
+ return "";
+ else if (tag == editor->priv->tags[TEXT_TAG_LIST1].tag) {
+ if (starting)
+ return "- ";
+ else
+ return "";
+ }
+ else if (tag == editor->priv->tags[TEXT_TAG_LIST2].tag) {
+ if (starting)
+ return " - ";
+ else
+ return "";
+ }
+ else {
+ gchar *tagname;
+ g_object_get ((GObject*) tag, "name", &tagname, NULL);
+ g_warning ("Unknown tag '%s'\n", tagname);
+ g_free (tagname);
+ return "";
+ }
+}
+
+/**
+ * steals @base64
+ */
+static gchar *
+add_newlines_to_base64 (gchar *base64)
+{
+ GString *string;
+ gint i;
+ gchar *ptr;
+ string = g_string_new ("");
+ for (i = 0, ptr = base64; *ptr; i++, ptr++) {
+ if (i && ! (i % 100))
+ g_string_append_c (string, '\n');
+ g_string_append_c (string, *ptr);
+ }
+ g_free (base64);
+ return g_string_free (string, FALSE);
+}
+
+/**
+ * steals @base64
+ */
+static gchar *
+remove_newlines_from_base64 (gchar *base64)
+{
+ GString *string;
+ gchar *ptr;
+ string = g_string_new ("");
+ for (ptr = base64; *ptr; ptr++) {
+ if (*ptr != '\n')
+ g_string_append_c (string, *ptr);
+ }
+ g_free (base64);
+ return g_string_free (string, FALSE);
+}
+
+static void
+serialize_text (GtkTextBuffer *buffer, SerializationContext *context, GdauiRtEditor *editor)
+{
+ GtkTextIter iter, old_iter;
+ GList *opened_tags = NULL; /* 1st element of the list is the last opened tag (ie. the one
+ * which should be closed 1st */
+
+ /*g_string_append (context->text_str, "###");*/
+ iter = context->start;
+ do {
+ GSList *new_tag_list, *list;
+ GList *dlist;
+ gboolean tags_needs_reopened = TRUE;
+ new_tag_list = gtk_text_iter_get_tags (&iter);
+
+ /*
+ * Close tags which need closing
+ */
+ /* Find the last element in @opened_tags which is not opened anymore */
+ for (dlist = g_list_last (opened_tags); dlist; dlist = dlist->prev) {
+ if (! g_slist_find (new_tag_list, dlist->data))
+ break;
+ }
+
+ if (dlist) {
+ /* close all the tags up to dlist->data and at the same time remove them
+ from @opened_tags */
+ for (; opened_tags != dlist;
+ opened_tags = g_list_delete_link (opened_tags, opened_tags)) {
+ g_string_append (context->text_str,
+ serialize_tag ((GtkTextTag*) opened_tags->data,
+ FALSE, editor));
+ }
+ g_string_append (context->text_str,
+ serialize_tag ((GtkTextTag*) opened_tags->data,
+ FALSE, editor));
+ opened_tags = g_list_delete_link (opened_tags, opened_tags);
+ }
+
+ /* Now try to go to either the next tag toggle, or if a pixbuf appears */
+ old_iter = iter;
+ while (1) {
+ gunichar ch = gtk_text_iter_get_char (&iter);
+
+ if (ch == 0xFFFC) {
+ GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
+
+ if (pixbuf) {
+ /* Append the text before the pixbuf */
+ gchar *tmp_text;
+ tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
+ g_string_append (context->text_str, tmp_text);
+ g_free (tmp_text);
+
+ if ((pixbuf != bullet_pix) && (pixbuf != bulleth_pix)) {
+ GdkPixdata pixdata;
+ guint8 *tmp;
+ guint len;
+ gchar *data;
+
+ gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
+ tmp = gdk_pixdata_serialize (&pixdata, &len);
+ data = add_newlines_to_base64 (g_base64_encode (tmp, len));
+ g_free (tmp);
+ g_string_append (context->text_str, "[[[");
+ g_string_append (context->text_str, data);
+ g_free (data);
+ g_string_append (context->text_str, "]]]");
+ }
+
+ /* Forward so we don't get the 0xfffc char */
+ gtk_text_iter_forward_char (&iter);
+ old_iter = iter;
+ }
+ }
+ else if (ch == 0) {
+ break;
+ }
+ else
+ gtk_text_iter_forward_char (&iter);
+
+ if (gtk_text_iter_toggles_tag (&iter, NULL)) {
+ /*g_print ("Toggle @pos %d:%d\n", gtk_text_iter_get_line (&iter),
+ gtk_text_iter_get_line_offset (&iter));*/
+ break;
+ }
+ }
+
+ /* We might have moved too far */
+ if (gtk_text_iter_compare (&iter, &context->end) > 0)
+ iter = context->end;
+
+ /* Append the text, except if there is the TEXT_TAG_BULLET tag */
+ if (! gtk_text_iter_has_tag (&old_iter, editor->priv->tags[TEXT_TAG_BULLET].tag)) {
+ gint i;
+ gchar *tmp_text;
+ tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
+#ifdef NONO
+ g_string_append (context->text_str, tmp_text);
+#endif
+ for (i = 0; tmp_text[i]; i++) {
+ if (tmp_text[i] != '\n') {
+ if (tags_needs_reopened) {
+ /*
+ * (re)open tags which are still there
+ */
+ for (list = new_tag_list; list; list = list->next) {
+ if (! g_list_find (opened_tags, list->data)) {
+ opened_tags = g_list_prepend (opened_tags, list->data);
+ g_string_append (context->text_str,
+ serialize_tag ((GtkTextTag*) opened_tags->data,
+ TRUE, editor));
+ }
+ }
+ tags_needs_reopened = FALSE;
+ }
+
+ g_string_append_c (context->text_str, tmp_text[i]);
+ continue;
+ }
+ /* close all tags and re-open them after the newline */
+ for (dlist = opened_tags; dlist; dlist = dlist->next) {
+ g_string_append (context->text_str,
+ serialize_tag (GTK_TEXT_TAG (dlist->data),
+ FALSE, editor));
+ }
+ g_string_append_c (context->text_str, '\n');
+ /* re-open tags */
+ for (dlist = g_list_last (opened_tags); dlist; dlist = dlist->prev) {
+ g_string_append (context->text_str,
+ serialize_tag (GTK_TEXT_TAG (dlist->data),
+ TRUE, editor));
+ }
+ }
+ g_free (tmp_text);
+ }
+
+ if (tags_needs_reopened) {
+ /*
+ * (re)open tags which are still there
+ */
+ for (list = new_tag_list; list; list = list->next) {
+ if (! g_list_find (opened_tags, list->data)) {
+ opened_tags = g_list_prepend (opened_tags, list->data);
+ g_string_append (context->text_str,
+ serialize_tag ((GtkTextTag*) opened_tags->data,
+ TRUE, editor));
+ }
+ }
+ tags_needs_reopened = FALSE;
+ }
+
+
+ g_slist_free (new_tag_list);
+ }
+ while (!gtk_text_iter_equal (&iter, &context->end));
+
+ /* Close any open tags */
+ for (; opened_tags;
+ opened_tags = g_list_delete_link (opened_tags, opened_tags)) {
+ g_string_append (context->text_str,
+ serialize_tag ((GtkTextTag*) opened_tags->data,
+ FALSE, editor));
+ }
+
+ /*g_string_append (context->text_str, "###");*/
+}
+
+/*
+ * serialize_as_txt2tag:
+ */
+static guint8 *
+serialize_as_txt2tag (GtkTextBuffer *register_buffer,
+ GtkTextBuffer *content_buffer,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ gsize *length,
+ GdauiRtEditor *editor)
+{
+ SerializationContext context;
+ GString *text;
+
+ context.tags = g_hash_table_new (NULL, NULL);
+ context.text_str = g_string_new (NULL);
+ context.start = *start;
+ context.end = *end;
+ context.tag_id = 0;
+ context.tag_id_tags = g_hash_table_new (NULL, NULL);
+
+ serialize_text (content_buffer, &context, editor);
+
+ text = g_string_new (NULL);
+
+ g_string_append_len (text, context.text_str->str, context.text_str->len);
+
+ g_hash_table_destroy (context.tags);
+ g_string_free (context.text_str, TRUE);
+ g_hash_table_destroy (context.tag_id_tags);
+
+ *length = text->len;
+
+ return (guint8 *) g_string_free (text, FALSE);
+}
+
+
+/**
+ * gdaui_rt_editor_set_contents
+ * @editor: a #GdauiRtEditor
+ * @markup: the text to set in @editor, using the markup syntax (must be valid UTF-8)
+ * @length: length of text in bytes.
+ *
+ * Set @editor's contents. If @length is -1, @markup must be nul-terminated
+ *
+ * Since: 4.2.2
+ */
+void
+gdaui_rt_editor_set_contents (GdauiRtEditor *editor, const gchar *markup, gint length)
+{
+ g_return_if_fail (GDAUI_IS_RT_EDITOR (editor));
+
+ gtk_text_buffer_set_text (editor->priv->textbuffer, markup, length);
+}
+
+/**
+ * gdaui_rt_editor_set_editable
+ * @editor: a #GdauiRtEditor
+ * @editable: whether it's editable
+ *
+ * Set @editor's editability
+ *
+ * Since: 4.2.2
+ */
+void
+gdaui_rt_editor_set_editable (GdauiRtEditor *editor, gboolean editable)
+{
+ g_return_if_fail (GDAUI_IS_RT_EDITOR (editor));
+ gtk_text_view_set_editable (editor->priv->textview, editable);
+ show_hide_toolbar (editor);
+}
+
+/*
+ * _gdaui_rt_editor_set_show_markup
+ * @editor: a #GdauiRtEditor
+ * @show_markup: whether @editor shows markup of applies it
+ *
+ * If @show_markup is %FALSE, then @editor displays text with tags applied (bold text, ...); and if it's
+ * %TRUE then it shows markup text instead
+ *
+ * Since: 4.2.2
+ */
+static void
+_gdaui_rt_editor_set_show_markup (GdauiRtEditor *editor, gboolean show_markup)
+{
+ gchar *data;
+ gint cursor_pos;
+ GtkTextIter iter;
+
+ g_return_if_fail (GDAUI_IS_RT_EDITOR (editor));
+ if (editor->priv->show_markup == show_markup)
+ return;
+
+ g_object_get (editor->priv->textbuffer, "cursor-position", &cursor_pos, NULL);
+ data = real_gdaui_rt_editor_get_contents (editor);
+ editor->priv->show_markup = show_markup;
+ gdaui_rt_editor_set_contents (editor, data, -1);
+ g_free (data);
+
+ gtk_text_buffer_get_iter_at_offset (editor->priv->textbuffer, &iter, cursor_pos);
+ gtk_text_buffer_place_cursor (editor->priv->textbuffer, &iter);
+
+ show_hide_toolbar (editor);
+}
+
+static void
+show_hide_toolbar (GdauiRtEditor *editor)
+{
+ gboolean enable_markup = TRUE;
+ GtkAction *action;
+
+ if (gtk_text_view_get_editable (editor->priv->textview))
+ gtk_widget_show (editor->priv->toolbar);
+ else
+ gtk_widget_hide (editor->priv->toolbar);
+
+ if (editor->priv->show_markup ||
+ ! gtk_text_view_get_editable (editor->priv->textview))
+ enable_markup = FALSE;
+
+ action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionBold");
+ gtk_action_set_sensitive (action, enable_markup);
+ action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionItalic");
+ gtk_action_set_sensitive (action, enable_markup);
+ action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionUnderline");
+ gtk_action_set_sensitive (action, enable_markup);
+ action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionStrike");
+ gtk_action_set_sensitive (action, enable_markup);
+ action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionAddImage");
+ gtk_action_set_sensitive (action, enable_markup);
+ action = gtk_ui_manager_get_action (editor->priv->uimanager, "/ToolBar/ActionReset");
+ gtk_action_set_sensitive (action, enable_markup);
+}
diff --git a/libgda-ui/gdaui-rt-editor.h b/libgda-ui/gdaui-rt-editor.h
new file mode 100644
index 0000000..89926cb
--- /dev/null
+++ b/libgda-ui/gdaui-rt-editor.h
@@ -0,0 +1,70 @@
+/* gdaui-rt-editor.h
+ *
+ * Copyright (C) 2010 Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef __GDAUI_RT_EDITOR__
+#define __GDAUI_RT_EDITOR__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDAUI_TYPE_RT_EDITOR (gdaui_rt_editor_get_type())
+#define GDAUI_RT_EDITOR(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, gdaui_rt_editor_get_type(), GdauiRtEditor)
+#define GDAUI_RT_EDITOR_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, gdaui_rt_editor_get_type (), GdauiRtEditorClass)
+#define GDAUI_IS_RT_EDITOR(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, gdaui_rt_editor_get_type ())
+
+
+typedef struct _GdauiRtEditor GdauiRtEditor;
+typedef struct _GdauiRtEditorClass GdauiRtEditorClass;
+typedef struct _GdauiRtEditorPriv GdauiRtEditorPriv;
+
+/* struct for the object's data */
+struct _GdauiRtEditor
+{
+ GtkVBox object;
+
+ GdauiRtEditorPriv *priv;
+};
+
+/* struct for the object's class */
+struct _GdauiRtEditorClass
+{
+ GtkVBoxClass parent_class;
+
+ /* signals */
+ void (* changed) (GdauiRtEditor *editor);
+};
+
+/*
+ * Generic widget's methods
+ */
+GType gdaui_rt_editor_get_type (void) G_GNUC_CONST;
+
+GtkWidget *gdaui_rt_editor_new (void);
+gchar *gdaui_rt_editor_get_contents (GdauiRtEditor *editor);
+void gdaui_rt_editor_set_contents (GdauiRtEditor *editor, const gchar *markup, gint length);
+void gdaui_rt_editor_set_editable (GdauiRtEditor *editor, gboolean editable);
+
+G_END_DECLS
+
+#endif
+
+
+
diff --git a/libgda-ui/libgda-ui.h b/libgda-ui/libgda-ui.h
index eb783d7..05cbf64 100644
--- a/libgda-ui/libgda-ui.h
+++ b/libgda-ui/libgda-ui.h
@@ -1,5 +1,5 @@
/* GDA library
- * Copyright (C) 2009 The GNOME Foundation.
+ * Copyright (C) 2009 - 2010 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -44,6 +44,7 @@
#include <libgda-ui/gdaui-tree-store.h>
#include <libgda-ui/gdaui-cloud.h>
#include <libgda-ui/gdaui-data-selector.h>
+#include <libgda-ui/gdaui-rt-editor.h>
G_BEGIN_DECLS
diff --git a/libgda-ui/libgda-ui.symbols b/libgda-ui/libgda-ui.symbols
index 38f5c3b..fd3fb72 100644
--- a/libgda-ui/libgda-ui.symbols
+++ b/libgda-ui/libgda-ui.symbols
@@ -164,6 +164,11 @@
gdaui_raw_grid_set_layout_from_file
gdaui_raw_grid_set_sample_size
gdaui_raw_grid_set_sample_start
+ gdaui_rt_editor_get_contents
+ gdaui_rt_editor_get_type
+ gdaui_rt_editor_new
+ gdaui_rt_editor_set_contents
+ gdaui_rt_editor_set_editable
gdaui_server_operation_get_type
gdaui_server_operation_new
gdaui_server_operation_new_in_dialog
diff --git a/testing/.gitignore b/testing/.gitignore
index f32ecf8..f2bb3ea 100644
--- a/testing/.gitignore
+++ b/testing/.gitignore
@@ -4,6 +4,7 @@ gda-provider-status
gdaui-test-data-entries
gdaui-test-widget-entry
gdaui-test-errors
+gdaui-test-rt-editor
virtual-test
virtual-test-2
index.html
diff --git a/testing/Makefile.am b/testing/Makefile.am
index 6f0db3b..c6271e8 100644
--- a/testing/Makefile.am
+++ b/testing/Makefile.am
@@ -7,7 +7,7 @@ AM_CPPFLAGS = \
bin_PROGRAMS = gda-test-connection-4.0
if HAVE_UI
-UI_PROGS=gdaui-test-data-entries gdaui-test-widget-entry gdaui-test-errors
+UI_PROGS=gdaui-test-data-entries gdaui-test-widget-entry gdaui-test-errors gdaui-test-rt-editor
endif
noinst_PROGRAMS = gda-test-blob gda-provider-status virtual-test virtual-test-2 $(UI_PROGS)
@@ -81,5 +81,14 @@ gdaui_test_errors_LDADD = \
$(LIBGDA_LIBS) \
$(GTK_LIBS)
+gdaui_test_rt_editor_CFLAGS = $(GTK_CFLAGS)
+gdaui_test_rt_editor_SOURCES = \
+ gdaui-test-rt-editor.c
+
+gdaui_test_rt_editor_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(top_builddir)/libgda-ui/libgda-ui-4.0.la \
+ $(LIBGDA_LIBS) \
+ $(GTK_LIBS)
EXTRA_DIST =
diff --git a/testing/gdaui-test-rt-editor.c b/testing/gdaui-test-rt-editor.c
new file mode 100644
index 0000000..b4f88a3
--- /dev/null
+++ b/testing/gdaui-test-rt-editor.c
@@ -0,0 +1,178 @@
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#include <string.h>
+#include <libgda-ui/gdaui-rt-editor.h>
+
+static gboolean
+texttags_equal (GtkTextTag *tag1, GtkTextTag *tag2)
+{
+ return TRUE;
+}
+
+static gboolean
+textbuffers_equal (GtkTextBuffer *buffer1, GtkTextBuffer *buffer2, GError **error)
+{
+ GtkTextIter iter1, iter2;
+ gunichar ch1, ch2;
+ gtk_text_buffer_get_start_iter (buffer1, &iter1);
+ gtk_text_buffer_get_start_iter (buffer2, &iter2);
+
+ do {
+ ch1 = gtk_text_iter_get_char (&iter1);
+ ch2 = gtk_text_iter_get_char (&iter2);
+ if (ch1 != ch2) {
+ g_set_error (error, 0, 0, "difference at line %d, offset %d",
+ gtk_text_iter_get_line (&iter1),
+ gtk_text_iter_get_line_offset (&iter1));
+ return FALSE;
+ }
+ if (ch1 == 0)
+ break;
+ gchar buf1[6];
+ if ((g_unichar_to_utf8 (ch1, buf1) == 1) &&
+ (*buf1 == '\n')) {
+ gtk_text_iter_forward_char (&iter1);
+ gtk_text_iter_forward_char (&iter2);
+ continue;
+ }
+
+ GSList *list1, *list2, *p1, *p2;
+ list1 = gtk_text_iter_get_tags (&iter1);
+ list2 = gtk_text_iter_get_tags (&iter2);
+ for (p1 = list1; p1; p1 = p1->next) {
+ for (p2 = list2; p2; p2 = p2->next) {
+ if (texttags_equal (GTK_TEXT_TAG (p1->data),
+ GTK_TEXT_TAG (p2->data))) {
+ list2 = g_slist_remove (list2, p2->data);
+ break;
+ }
+ }
+ if (!p2) {
+ g_set_error (error, 0, 0, "Missing text tag in textbuffer2 "
+ "at line %d, offset %d",
+ gtk_text_iter_get_line (&iter1),
+ gtk_text_iter_get_line_offset (&iter1));
+ return FALSE;
+ }
+ }
+ for (p2 = list2; p2; p2 = p2->next) {
+ for (p1 = list1; p1; p1 = p1->next) {
+ if (texttags_equal (GTK_TEXT_TAG (p1->data), GTK_TEXT_TAG (p2->data)))
+ break;
+ }
+ if (!p1) {
+ g_set_error (error, 0, 0, "Missing text tag in textbuffer1 "
+ "at line %d, offset %d",
+ gtk_text_iter_get_line (&iter1),
+ gtk_text_iter_get_line_offset (&iter1));
+ return FALSE;
+ }
+ }
+ g_slist_free (list1);
+ g_slist_free (list2);
+ gtk_text_iter_forward_char (&iter1);
+ gtk_text_iter_forward_char (&iter2);
+ } while ((ch1 != 0) && (ch2 != 0));
+
+ GtkTextIter end1, end2;
+ gtk_text_buffer_get_end_iter (buffer1, &end1);
+ gtk_text_buffer_get_end_iter (buffer2, &end2);
+ if (gtk_text_iter_compare (&iter1, &end1)) {
+ g_set_error (error, 0, 0, "textbuffer1 is shorter than textbuffer2");
+ return FALSE;
+ }
+ if (gtk_text_iter_compare (&iter2, &end2)) {
+ g_set_error (error, 0, 0, "textbuffer2 is shorter than textbuffer1");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+copy_cb (GtkButton *button, GdauiRtEditor *from)
+{
+ GdauiRtEditor *rte;
+ gchar *data;
+ rte = g_object_get_data (G_OBJECT (button), "destrte");
+ data = gdaui_rt_editor_get_contents (from);
+ gdaui_rt_editor_set_contents (rte, data, -1);
+ g_free (data);
+
+#ifdef GDA_DEBUG
+ GtkTextBuffer *b1, *b2;
+ GError *lerror = NULL;
+ g_object_get ((GObject*) from, "buffer", &b1, NULL);
+ g_object_get ((GObject*) rte, "buffer", &b2, NULL);
+ if (! textbuffers_equal (b1, b2, &lerror)) {
+ g_warning ("ERROR: %s\n", lerror->message);
+ g_clear_error (&lerror);
+ }
+ g_object_unref ((GObject*) b1);
+ g_object_unref ((GObject*) b2);
+#endif
+}
+
+
+static GtkWidget *
+create_window (void)
+{
+ GtkWidget *p_win = NULL;
+ GtkWidget *vbox = NULL;
+ GtkWidget *rte = NULL;
+ GtkWidget *button;
+
+ /* Window */
+ p_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (p_win), "GtkTextView & GtkTextTag");
+ gtk_window_set_default_size (GTK_WINDOW (p_win), 640, 480);
+ gtk_container_set_border_width (GTK_CONTAINER (p_win), 5);
+ gtk_window_set_position (GTK_WINDOW (p_win), GTK_WIN_POS_CENTER);
+ g_signal_connect (G_OBJECT (p_win), "destroy", gtk_main_quit, NULL);
+
+ /* contents */
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_add (GTK_CONTAINER (p_win), vbox);
+ rte = gdaui_rt_editor_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), rte, TRUE, TRUE, 0);
+ gdaui_rt_editor_set_contents (GDAUI_RT_EDITOR (rte), "No tags here..., ``Monospaced here``\n= Title level 1 =\n\n"
+ "blah **important**\n\n"
+ "== title level 2 ==\n\n"
+ "blah //italic// blah.\n"
+ "and ** BOLD!//both italic and bold// Bold!**\nNice Picture: [[[R2RrUAAABIwBAQABAAAAPAAAABMAAAAT6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urqAAAAy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLAAAAy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLyMfJxMnCycnFyMfJyMfJy8vLy8vLy8vLy8vLy8vLy8vLAAAAy8vLy8vLy8vLy8vLy8vLy8vLy8vLwcK8lcOSWMFRQ8I5RLo6YrBcmK+Uv8m+y8vLy8vLy8vLy8vLAAAAwcK8y8vLy8vLy8vLy8vLy8vLsMivN9AuA+gACvMACPcACvMAA+gACLUAPps2tLisy8vLy8vLy8vLAAAAnpVNycnFy8vLy8vLy8vLpcmjE9cGAvsAE/4GE/4GE/4GE/4GE/4GFPQGAsUAHJAWtbOzy8vLy8vLAAAAtagDnZx1y8vLy8vLv8m+L9cmAvsAE/4GE/4GE/4GE/4GE/4GE/4GE/4GFPQGCLUAQo88wcK8y8vLAAAAzsEHlokgwcK8y8vLi8qHAvsAE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GE9cGBpIAiKeEy8vLAAAA3dAEn5ERsLCXxMnCVtJSAvsAFf4IE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GE+kGCKUCW5JUy8vLAAAA5NgHo5UGn6BysMivONQyCPcAFf4IE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GE+kGCKUCS5FEy8vLAAAA3dAEo5UGnZx1sMivONQyCPcAE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GEeIIC5gAS5FEy8vLAAAAx7kFlok
gqqqLwcK8TsRKCvMAE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GFPQGE9cGBpIAW5JUy8vLAAAAtagDh3oRt8W2y8vLfK53B9YAE/4GE/4GE/4GE/4GE/4GE/4GE/4GE/4GEeIIEbsEBX4BfJ53y8vLAAAAk4YHlIxSy8vLy8vLt8W2GqoPA+gAE/4GE/4GE/4GE/4GE/4GFPQGE+kGFMcHBpIAI3EdqrWoy8vLAAAAhnwtvLq7y8vLy8vLy8vLiKeECKUCB9YAE+kGFPQGFPQGE+kGE9cGEbsEC5gAEnENkaeOy8vLy8vLAAAAtLisy8vLy8vLy8vLy8vLyMfJfJ53LYImCKUCCLUAEbsECLUACKUCBX4BI3EdkaeOy8vLy8vLy8vLAAAAy8vLy8vLy8vLy8vLy8vLy8vLy8vLmK+UYJRaOYUyKnsiKnsiPXw1YYdbqrWoy8vLy8vLy8vLy8vLAAAAy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLtbOzqrWoq6eltLisyMfJy8vLy8vLy8vLy8vLy8vLAAAAy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8tYQwAA]]] Yes\n"
+ "- List item --One--\n"
+ "- \n"
+ "- List item **Two**\n"
+ " - sub1\n"
+ " - sub2\n\n"
+ "multi line markup **starting here\nand ending here**\n"
+ "multi line markup **starting here**\n**and ending here**\nnot here"
+ "A Line with **formatting -- error **\n",
+ -1);
+ button = gtk_button_new_with_label ("Copy");
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 5);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (copy_cb), rte);
+
+ rte = gdaui_rt_editor_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), rte, TRUE, TRUE, 0);
+ gdaui_rt_editor_set_editable (GDAUI_RT_EDITOR (rte), FALSE);
+ g_object_set_data (G_OBJECT (button), "destrte", rte);
+
+ return p_win;
+}
+
+/*
+ * Entree du programme:
+ */
+int main (int argc, char ** argv)
+{
+ gtk_init (& argc, & argv);
+
+ /*
+ * Creation et mise en place de la fenetre:
+ */
+ gtk_widget_show_all (create_window ());
+ gtk_main ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]