[gnome-builder/wip/slaf/spellcheck-sidebar: 11/33] spellchecker: merge dict and spell UI
- From: Sébastien Lafargue <slafargue src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/slaf/spellcheck-sidebar: 11/33] spellchecker: merge dict and spell UI
- Date: Wed, 11 Jan 2017 20:47:46 +0000 (UTC)
commit 8f9411b952dd40232b780a3971ef0a26da877a98
Author: Sébastien Lafargue <slafargue gnome org>
Date: Sun Jan 1 21:52:55 2017 +0100
spellchecker: merge dict and spell UI
libide/Makefile.am | 5 +-
libide/editor/ide-editor-dict-widget.c | 562 ------------------------------
libide/editor/ide-editor-dict-widget.h | 46 ---
libide/editor/ide-editor-dict-widget.ui | 94 -----
libide/editor/ide-editor-perspective.c | 197 ++++++++---
libide/editor/ide-editor-spell-dict.c | 330 ++++++++++++++++++
libide/editor/ide-editor-spell-dict.h | 49 +++
libide/editor/ide-editor-spell-widget.c | 245 ++++++++++++--
libide/editor/ide-editor-spell-widget.ui | 94 +++++-
libide/resources/libide.gresource.xml | 1 -
10 files changed, 827 insertions(+), 796 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 6da7907..2e607bd 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -374,9 +374,6 @@ libide_1_0_la_SOURCES = \
application/ide-application-private.h \
application/ide-application-tests.c \
application/ide-application-tests.h \
- editor/ide-editor-dict-widget.c \
- editor/ide-editor-dict-widget.h \
- editor/ide-editor-dict-widget.ui \
editor/ide-editor-frame-actions.c \
editor/ide-editor-frame-actions.h \
editor/ide-editor-frame-private.h \
@@ -395,6 +392,8 @@ libide_1_0_la_SOURCES = \
editor/ide-editor-spell-navigator.h \
editor/ide-editor-spell-utils.c \
editor/ide-editor-spell-utils.h \
+ editor/ide-editor-spell-dict.c \
+ editor/ide-editor-spell-dict.h \
editor/ide-editor-spell-widget.c \
editor/ide-editor-spell-widget.h \
editor/ide-editor-tweak-widget.c \
diff --git a/libide/editor/ide-editor-perspective.c b/libide/editor/ide-editor-perspective.c
index 0b418a3..14f9882 100644
--- a/libide/editor/ide-editor-perspective.c
+++ b/libide/editor/ide-editor-perspective.c
@@ -20,6 +20,7 @@
#include <egg-signal-group.h>
#include <glib/gi18n.h>
+#include <pnl.h>
#include "ide-context.h"
#include "ide-debug.h"
@@ -35,6 +36,8 @@
#include "workbench/ide-workbench.h"
#include "workbench/ide-workbench-header-bar.h"
+#define OVERLAY_REVEAL_DURATION 300
+
struct _IdeEditorPerspective
{
PnlDockOverlay parent_instance;
@@ -46,8 +49,7 @@ struct _IdeEditorPerspective
EggSignalGroup *buffer_manager_signals;
- GtkAdjustment *spellchecker_edge_adj;
-
+ gint right_pane_position;
guint spellchecker_opened : 1;
};
@@ -821,75 +823,178 @@ ide_editor_perspective_get_overlay_edge (IdeEditorPerspective *self,
return pnl_dock_overlay_get_edge (PNL_DOCK_OVERLAY (self), position);
}
+static GtkOrientation
+get_orientation_from_position_type (GtkPositionType position_type)
+{
+ if (position_type == GTK_POS_LEFT || position_type == GTK_POS_RIGHT)
+ return GTK_ORIENTATION_HORIZONTAL;
+ else
+ return GTK_ORIENTATION_VERTICAL;
+}
+
+/* Triggered at the start of the animation */
+static void
+overlay_child_reveal_notify_cb (IdeEditorPerspective *self,
+ GParamSpec *pspec,
+ PnlDockOverlayEdge *edge)
+{
+ IdeLayoutPane *pane;
+ gboolean reveal;
+
+ g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+ g_assert (PNL_IS_DOCK_OVERLAY_EDGE (edge));
+
+ gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (edge),
+ "reveal", &reveal,
+ NULL);
+
+ if (!reveal && self->spellchecker_opened)
+ {
+ g_signal_handlers_disconnect_by_func (self,
+ overlay_child_reveal_notify_cb,
+ edge);
+
+ pane = IDE_LAYOUT_PANE (pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (self->layout)));
+ pnl_dock_revealer_animate_to_position (PNL_DOCK_REVEALER (pane),
+ self->right_pane_position,
+ OVERLAY_REVEAL_DURATION);
+ }
+}
+
+/* Triggered at the end of the animation */
static void
-ide_editor_perspective_edge_adj_changed_cb (IdeEditorPerspective *self,
- GtkAdjustment *edge_adj)
+overlay_child_revealed_notify_cb (IdeEditorPerspective *self,
+ GParamSpec *pspec,
+ PnlDockOverlayEdge *edge)
{
- PnlDockOverlayEdge *edge;
GtkWidget *child;
- gdouble value;
+ gboolean revealed;
g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+ g_assert (PNL_IS_DOCK_OVERLAY_EDGE (edge));
+
+ gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (edge),
+ "revealed", &revealed,
+ NULL);
- value = gtk_adjustment_get_value (edge_adj);
- if (value == 1.0)
+ if (!revealed && self->spellchecker_opened)
{
- edge = ide_editor_perspective_get_overlay_edge (self, GTK_POS_RIGHT);
+ g_signal_handlers_disconnect_by_func (self,
+ overlay_child_revealed_notify_cb,
+ edge);
+
child = gtk_bin_get_child (GTK_BIN (edge));
+ g_assert (child != NULL);
gtk_container_remove (GTK_CONTAINER (edge), child);
self->spellchecker_opened = FALSE;
+ }
+ else if (revealed)
+ self->spellchecker_opened = TRUE;
+}
+
+static void
+show_spell_checker (IdeEditorPerspective *self,
+ PnlDockOverlayEdge *overlay_edge,
+ IdeLayoutPane *pane)
+{
+ GtkOrientation pane_orientation;
+ GtkPositionType pane_position_type;
+ GtkOrientation overlay_orientation;
+ GtkPositionType overlay_position_type;
+ gint overlay_size;
- g_signal_handlers_disconnect_by_func (self->spellchecker_edge_adj,
- ide_editor_perspective_edge_adj_changed_cb,
- self);
- self->spellchecker_edge_adj = NULL;
+ g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+ g_assert (gtk_bin_get_child (GTK_BIN (overlay_edge)) != NULL);
+
+ pane_position_type = pnl_dock_bin_edge_get_edge (PNL_DOCK_BIN_EDGE (pane));
+ overlay_position_type = pnl_dock_overlay_edge_get_edge (overlay_edge);
+
+ pane_orientation = get_orientation_from_position_type (pane_position_type);
+ overlay_orientation = get_orientation_from_position_type (overlay_position_type);
+
+ g_assert (pane_orientation == overlay_orientation);
+
+ if (pnl_dock_revealer_get_position_set (PNL_DOCK_REVEALER (pane)))
+ self->right_pane_position = pnl_dock_revealer_get_position (PNL_DOCK_REVEALER (pane));
+ else
+ {
+ if (overlay_orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width (GTK_WIDGET (pane), NULL, &self->right_pane_position);
+ else
+ gtk_widget_get_preferred_height (GTK_WIDGET (pane), NULL, &self->right_pane_position);
}
+
+ if (overlay_orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width (GTK_WIDGET (overlay_edge), NULL, &overlay_size);
+ else
+ gtk_widget_get_preferred_height (GTK_WIDGET (overlay_edge), NULL, &overlay_size);
+
+ g_signal_connect_object (overlay_edge,
+ "child-notify::reveal",
+ G_CALLBACK (overlay_child_reveal_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (overlay_edge,
+ "child-notify::revealed",
+ G_CALLBACK (overlay_child_revealed_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ pnl_dock_revealer_animate_to_position (PNL_DOCK_REVEALER (pane),
+ overlay_size,
+ OVERLAY_REVEAL_DURATION);
+ gtk_container_child_set (GTK_CONTAINER (self), GTK_WIDGET (overlay_edge),
+ "reveal", TRUE,
+ NULL);
+}
+
+static GtkWidget *
+create_spellchecker_widget (IdeSourceView *source_view)
+{
+ GtkWidget *spellchecker_widget;
+ GtkWidget *scroll_window;
+ GtkWidget *spell_widget;
+
+ g_assert (IDE_IS_SOURCE_VIEW (source_view));
+
+ spellchecker_widget = g_object_new (GTK_TYPE_BOX,
+ "visible", TRUE,
+ "expand", TRUE,
+ NULL);
+ scroll_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "visible", TRUE,
+ "expand", TRUE,
+ "propagate-natural-width", TRUE,
+ NULL);
+ spell_widget = ide_editor_spell_widget_new (source_view);
+ gtk_box_pack_start (GTK_BOX (spellchecker_widget), scroll_window, TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (scroll_window), spell_widget);
+ gtk_widget_show_all (spellchecker_widget);
+
+ return spellchecker_widget;
}
void
ide_editor_perspective_show_spellchecker (IdeEditorPerspective *self,
IdeSourceView *source_view)
{
- GtkWidget *box;
- GtkWidget *scroll_window;
- GtkWidget *spell_widget;
- PnlDockOverlayEdge *edge;
+ GtkWidget *spellchecker_widget;
+ PnlDockOverlayEdge *overlay_edge;
+ IdeLayoutPane *pane;
g_return_if_fail (IDE_IS_EDITOR_PERSPECTIVE (self));
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (source_view));
if (!self->spellchecker_opened)
{
self->spellchecker_opened = TRUE;
+ spellchecker_widget = create_spellchecker_widget (source_view);
- box = g_object_new (GTK_TYPE_BOX,
- "visible", TRUE,
- "expand", TRUE,
- NULL);
- scroll_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
- "visible", TRUE,
- "expand", TRUE,
- "width-request", 500,
- "propagate-natural-width", TRUE,
- NULL);
- spell_widget = ide_editor_spell_widget_new (source_view);
- gtk_box_pack_start (GTK_BOX (box), scroll_window, TRUE, TRUE, 0);
- gtk_container_add (GTK_CONTAINER (scroll_window), spell_widget);
- gtk_widget_show_all (box);
-
- pnl_overlay_add_child (PNL_DOCK_OVERLAY (self), box, "right");
- edge = ide_editor_perspective_get_overlay_edge (self, GTK_POS_RIGHT);
- self->spellchecker_edge_adj = pnl_dock_overlay_get_edge_adjustment (PNL_DOCK_OVERLAY (self),
- GTK_POS_RIGHT);
- gtk_widget_set_child_visible (GTK_WIDGET (edge), TRUE);
- //pnl_dock_overlay_edge_set_position (PNL_DOCK_OVERLAY_EDGE (edge), 100);
-
- gtk_container_child_set (GTK_CONTAINER (self), GTK_WIDGET (edge),
- "reveal", TRUE,
- NULL);
+ pnl_overlay_add_child (PNL_DOCK_OVERLAY (self), spellchecker_widget, "right");
+ overlay_edge = ide_editor_perspective_get_overlay_edge (self, GTK_POS_RIGHT);
+ gtk_widget_set_child_visible (GTK_WIDGET (overlay_edge), TRUE);
- g_signal_connect_swapped (self->spellchecker_edge_adj,
- "value-changed",
- G_CALLBACK (ide_editor_perspective_edge_adj_changed_cb),
- self);
+ pane = IDE_LAYOUT_PANE (pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (self->layout)));
+ show_spell_checker (self, overlay_edge, pane);
}
}
diff --git a/libide/editor/ide-editor-spell-dict.c b/libide/editor/ide-editor-spell-dict.c
new file mode 100644
index 0000000..880d81b
--- /dev/null
+++ b/libide/editor/ide-editor-spell-dict.c
@@ -0,0 +1,330 @@
+/* ide-editor-spell-dict.c
+ *
+ * Copyright (C) 2016 Sébastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-editor-spell-dict.h"
+#include <gspell/gspell.h>
+
+#include <ide.h>
+
+struct _IdeEditorSpellDict
+{
+ GtkBin parent_instance;
+
+ GspellChecker *checker;
+ const GspellLanguage *language;
+};
+
+G_DEFINE_TYPE (IdeEditorSpellDict, ide_editor_spell_dict, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_CHECKER,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void read_line_cb (GObject *object, GAsyncResult *result, gpointer user_data);
+
+typedef struct
+{
+ IdeEditorSpellDict *self;
+ GFile *file;
+ GDataInputStream *data_stream;
+ GPtrArray *ar;
+} TaskState;
+
+static void
+task_state_free (gpointer data)
+{
+ TaskState *state = (TaskState *)data;
+
+ g_assert (state != NULL);
+
+ g_clear_object (&state->file);
+ g_ptr_array_unref (state->ar);
+
+ g_slice_free (TaskState, state);
+}
+
+static void
+read_line_async (GTask *task)
+{
+ TaskState *state;
+
+ state = g_task_get_task_data (task);
+ g_data_input_stream_read_line_async (state->data_stream,
+ g_task_get_priority (task),
+ g_task_get_cancellable (task),
+ read_line_cb,
+ task);
+}
+
+static void
+read_line_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = (GTask *)user_data;
+ g_autoptr(GError) error = NULL;
+ TaskState *state;
+ gchar *word;
+ gsize len;
+
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ state = g_task_get_task_data (task);
+ if (NULL == (word = g_data_input_stream_read_line_finish_utf8 (state->data_stream,
+ result,
+ &len,
+ &error)))
+ {
+ if (error != NULL)
+ {
+ /* TODO: check invalid utf8 string to skip it */
+ g_task_return_error (task, g_steal_pointer (&error));
+ }
+ else
+ g_task_return_pointer (task, state->ar, (GDestroyNotify)g_ptr_array_unref);
+ }
+ else
+ {
+ if (len > 0)
+ g_ptr_array_add (state->ar, word);
+
+ read_line_async (g_steal_pointer (&task));
+ }
+}
+
+static void
+open_file_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GTask) task = (GTask *)user_data;
+ g_autoptr(GError) error = NULL;
+ GFileInputStream *stream;
+ TaskState *state;
+
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ state = g_task_get_task_data (task);
+ if (NULL == (stream = g_file_read_finish (state->file, result, &error)))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ state->data_stream = g_data_input_stream_new (G_INPUT_STREAM (stream));
+ read_line_async (g_steal_pointer (&task));
+}
+
+void
+ide_editor_spell_dict_get_words_async (IdeEditorSpellDict *self,
+ GAsyncReadyCallback callback,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *dict_filename = NULL;
+ TaskState *state;
+ gint priority;
+
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (IDE_IS_EDITOR_SPELL_DICT (self));
+ g_assert (callback != NULL);
+
+ state = g_slice_new0 (TaskState);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_editor_spell_dict_get_words_async);
+ g_task_set_task_data (task, state, (GDestroyNotify)task_state_free);
+
+ dict_filename = g_strconcat (gspell_language_get_code (self->language), ".dic", NULL);
+ path = g_build_filename (g_get_user_config_dir (), "enchant", dict_filename, NULL);
+ state->self = self;
+ state->ar = g_ptr_array_new_with_free_func (g_free);
+ state->file = g_file_new_for_path (path);
+
+ priority = g_task_get_priority (task);
+ g_file_read_async (state->file,
+ priority,
+ cancellable,
+ open_file_cb,
+ g_steal_pointer (&task));
+}
+
+GPtrArray *
+ide_editor_spell_dict_get_words_finish (IdeEditorSpellDict *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (IDE_IS_EDITOR_SPELL_DICT (self));
+ g_assert (g_task_is_valid (result, self));
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+language_notify_cb (IdeEditorSpellDict *self,
+ GParamSpec *pspec,
+ GspellChecker *checker)
+{
+ const GspellLanguage *language;
+
+ g_assert (IDE_IS_EDITOR_SPELL_DICT (self));
+ g_assert (GSPELL_IS_CHECKER (checker));
+
+ language = gspell_checker_get_language (self->checker);
+
+ if ((self->language == NULL && language != NULL) ||
+ (self->language != NULL && language == NULL) ||
+ 0 != gspell_language_compare (language, self->language))
+ self->language = language;
+}
+
+static void
+checker_weak_ref_cb (gpointer data,
+ GObject *where_the_object_was)
+{
+ IdeEditorSpellDict *self = (IdeEditorSpellDict *)data;
+
+ g_assert (IDE_IS_EDITOR_SPELL_DICT (self));
+
+ self->checker = NULL;
+ self->language = NULL;
+}
+
+GspellChecker *
+ide_editor_spell_dict_get_checker (IdeEditorSpellDict *self)
+{
+ g_return_val_if_fail (IDE_IS_EDITOR_SPELL_DICT (self), NULL);
+
+ return self->checker;
+}
+
+void
+ide_editor_spell_dict_set_checker (IdeEditorSpellDict *self,
+ GspellChecker *checker)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SPELL_DICT (self));
+
+ if (self->checker != checker)
+ {
+ if (self->checker != NULL)
+ g_object_weak_unref (G_OBJECT (self->checker), checker_weak_ref_cb, self);
+
+ if (checker == NULL)
+ {
+ checker_weak_ref_cb (self, NULL);
+ return;
+ }
+
+ self->checker = checker;
+ g_object_weak_ref (G_OBJECT (self->checker), checker_weak_ref_cb, self);
+ g_signal_connect_object (self->checker,
+ "notify::language",
+ G_CALLBACK (language_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ language_notify_cb (self, NULL, self->checker);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CHECKER]);
+ }
+}
+
+IdeEditorSpellDict *
+ide_editor_spell_dict_new (GspellChecker *checker)
+{
+ return g_object_new (IDE_TYPE_EDITOR_SPELL_DICT,
+ "checker", checker,
+ NULL);
+}
+
+static void
+ide_editor_spell_dict_finalize (GObject *object)
+{
+}
+
+static void
+ide_editor_spell_dict_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEditorSpellDict *self = IDE_EDITOR_SPELL_DICT (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHECKER:
+ g_value_set_object (value, ide_editor_spell_dict_get_checker (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_spell_dict_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEditorSpellDict *self = IDE_EDITOR_SPELL_DICT (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHECKER:
+ ide_editor_spell_dict_set_checker (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_spell_dict_class_init (IdeEditorSpellDictClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_editor_spell_dict_finalize;
+ object_class->get_property = ide_editor_spell_dict_get_property;
+ object_class->set_property = ide_editor_spell_dict_set_property;
+
+ properties [PROP_CHECKER] =
+ g_param_spec_object ("checker",
+ "Checker",
+ "Checker",
+ GSPELL_TYPE_CHECKER,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_editor_spell_dict_init (IdeEditorSpellDict *self)
+{
+}
diff --git a/libide/editor/ide-editor-spell-dict.h b/libide/editor/ide-editor-spell-dict.h
new file mode 100644
index 0000000..85a7d00
--- /dev/null
+++ b/libide/editor/ide-editor-spell-dict.h
@@ -0,0 +1,49 @@
+/* ide-editor-spell-dict.h
+ *
+ * Copyright (C) 2016 Sébastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_EDITOR_SPELL_DICT_H
+#define IDE_EDITOR_SPELL_DICT_H
+
+#include <glib-object.h>
+#include <gspell/gspell.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_SPELL_DICT (ide_editor_spell_dict_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorSpellDict, ide_editor_spell_dict, IDE, EDITOR_SPELL_DICT, GObject)
+
+IdeEditorSpellDict *ide_editor_spell_dict_new (GspellChecker *checker);
+
+GspellChecker *ide_editor_spell_dict_get_checker (IdeEditorSpellDict *self);
+void ide_editor_spell_dict_get_words_async (IdeEditorSpellDict *self,
+ GAsyncReadyCallback callback,
+ GCancellable *cancellable,
+ gpointer user_data);
+GPtrArray *ide_editor_spell_dict_get_words_finish (IdeEditorSpellDict *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_editor_spell_dict_set_checker (IdeEditorSpellDict *self,
+ GspellChecker *checker);
+void ide_editor_spell_dict_add_word (IdeEditorSpellDict *self,
+ const gchar *word);
+
+G_END_DECLS
+
+#endif /* IDE_EDITOR_SPELL_DICT_H */
+
diff --git a/libide/editor/ide-editor-spell-widget.c b/libide/editor/ide-editor-spell-widget.c
index 86cfd5a..5a50c9c 100644
--- a/libide/editor/ide-editor-spell-widget.c
+++ b/libide/editor/ide-editor-spell-widget.c
@@ -20,7 +20,7 @@
#include <glib/gi18n.h>
#include <gspell/gspell.h>
-#include "ide-editor-dict-widget.h"
+#include "ide-editor-spell-dict.h"
#include "ide-editor-spell-navigator.h"
#include "ide-editor-spell-widget.h"
@@ -40,13 +40,10 @@ struct _IdeEditorSpellWidget
IdeSourceView *view;
IdeBuffer *buffer;
GspellChecker *checker;
- IdeEditorDictWidget *dict_widget;
+ IdeEditorSpellDict *dict_widget;
+ GPtrArray *words_array;
const GspellLanguage *spellchecker_language;
- GtkLabel *misspelled_label;
- GtkLabel *change_to_label;
- GtkLabel *suggestions_label;
- GtkLabel *language_label;
GtkLabel *word_label;
GtkLabel *count_label;
GtkEntry *word_entry;
@@ -56,9 +53,12 @@ struct _IdeEditorSpellWidget
GtkButton *change_all_button;
GtkListBox *suggestions_box;
+ GtkWidget *dict_word_entry;
+ GtkWidget *dict_add_button;
+ GtkWidget *dict_words_list;
+
GtkButton *highlight_checkbutton;
GtkButton *language_chooser_button;
- GtkWidget *dict_box;
GtkWidget *placeholder;
GAction *view_spellchecking_action;
@@ -280,7 +280,9 @@ check_word_timeout_cb (IdeEditorSpellWidget *self)
word = gtk_entry_get_text (self->word_entry);
if (!ide_str_empty0 (word))
{
- /* FIXME: suggestions can give a multiple-words suggestion that failed to the checkword test, ex: auto
tools */
+ /* FIXME: suggestions can give a multiple-words suggestion
+ * that failed to the checkword test, ex: auto tools
+ */
ret = gspell_checker_check_word (self->checker, word, -1, &error);
if (error != NULL)
{
@@ -455,7 +457,7 @@ ide_editor_spell_widget__key_press_event_cb (IdeEditorSpellWidget *self,
}
static void
-ide_editor_frame_spell__widget_mapped_cb (IdeEditorSpellWidget *self)
+ide_editor_spell__widget_mapped_cb (IdeEditorSpellWidget *self)
{
GActionGroup *group = NULL;
GtkWidget *widget = GTK_WIDGET (self);
@@ -469,6 +471,7 @@ ide_editor_frame_spell__widget_mapped_cb (IdeEditorSpellWidget *self)
widget = gtk_widget_get_parent (widget);
}
+ /* FIXME: we are not a descendant of view anymore */
if (group != NULL &&
NULL != (self->view_spellchecking_action = g_action_map_lookup_action (G_ACTION_MAP (group),
"spellchecking")))
@@ -497,6 +500,178 @@ ide_editor_spell_widget__highlight_checkbutton_toggled_cb (IdeEditorSpellWidget
}
static void
+ide_editor_spell_widget__words_counted_cb (IdeEditorSpellWidget *self,
+ GParamSpec *pspec,
+ GspellNavigator *navigator)
+{
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+
+ update_count_label (self);
+}
+
+static void
+dict_close_button_clicked_cb (IdeEditorSpellWidget *self,
+ GtkButton *button)
+{
+ GtkWidget *row;
+ gchar *word;
+
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+ g_assert (GTK_IS_BUTTON (button));
+
+ if (NULL != (row = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_LIST_BOX_ROW)))
+ {
+ word = g_object_get_data (G_OBJECT (row), "word");
+ gspell_checker_remove_word_from_personal (self->checker, word, -1);
+ gtk_container_remove (GTK_CONTAINER (self->dict_words_list), row);
+ }
+}
+
+static GtkWidget *
+dict_create_word_row (IdeEditorSpellWidget *self,
+ const gchar *word)
+{
+ GtkWidget *row;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *button;
+
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+ g_assert (!ide_str_empty0 (word));
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", word,
+ "halign", GTK_ALIGN_START,
+ NULL);
+
+ button = gtk_button_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_BUTTON);
+ g_signal_connect_swapped (button,
+ "clicked",
+ G_CALLBACK (dict_close_button_clicked_cb),
+ self);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+ gtk_box_pack_end (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+ row = gtk_list_box_row_new ();
+ gtk_container_add (GTK_CONTAINER (row), box);
+ g_object_set_data_full (G_OBJECT (row), "word", g_strdup (word), g_free);
+
+ gtk_widget_show_all (row);
+
+ return row;
+}
+
+static gboolean
+check_dict_available (IdeEditorSpellWidget *self)
+{
+ return (self->checker != NULL && self->spellchecker_language != NULL);
+}
+
+static void
+ide_editor_spell_widget__add_button_clicked_cb (IdeEditorSpellWidget *self,
+ GtkButton *button)
+{
+ const gchar *word;
+ GtkWidget *item;
+
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+ g_assert (GTK_IS_BUTTON (button));
+
+ word = gtk_entry_get_text (GTK_ENTRY (self->dict_word_entry));
+ /* TODO: check if word already in dict */
+ if (check_dict_available (self) && !ide_str_empty0 (word))
+ {
+ item = dict_create_word_row (self, word);
+ gtk_list_box_insert (GTK_LIST_BOX (self->dict_words_list), item, 0);
+ gspell_checker_add_word_to_personal (self->checker, word, -1);
+
+ gtk_entry_set_text (GTK_ENTRY (self->dict_word_entry), "");
+ gtk_widget_grab_focus (self->dict_word_entry);
+ }
+}
+
+static void
+ide_editor_spell_widget__word_entry_text_notify_cb (IdeEditorSpellWidget *self,
+ GParamSpec *pspec,
+ GtkEntry *word_entry)
+{
+ const gchar *word;
+
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+ g_assert (GTK_IS_ENTRY (word_entry));
+
+ word = gtk_entry_get_text (GTK_ENTRY (self->dict_word_entry));
+ gtk_widget_set_sensitive (GTK_WIDGET (self->dict_add_button), !ide_str_empty0 (word));
+}
+
+static void
+dict_clean_listbox (IdeEditorSpellWidget *self)
+{
+ GList *children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (self->dict_words_list));
+ for (GList *l = children; l != NULL; l = g_list_next (l))
+ gtk_widget_destroy (GTK_WIDGET (l->data));
+}
+
+static void
+dict_fill_listbox (IdeEditorSpellWidget *self,
+ GPtrArray *words_array)
+{
+ gsize len;
+ const gchar *word;
+ GtkWidget *item;
+
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+
+ dict_clean_listbox (self);
+
+ len = words_array->len;
+ for (gint i = 0; i < len; ++i)
+ {
+ word = g_ptr_array_index (words_array, i);
+ item = dict_create_word_row (self, word);
+ gtk_list_box_insert (GTK_LIST_BOX (self->dict_words_list), item, -1);
+ }
+}
+
+static void
+ide_editor_spell_widget_get_dict_words_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeEditorSpellWidget *self = (IdeEditorSpellWidget *)user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (NULL == (self->words_array = ide_editor_spell_dict_get_words_finish (self->dict_widget,
+ result,
+ &error)))
+ {
+ printf ("error: %s\n", error->message);
+ return;
+ }
+
+ dict_fill_listbox (self, self->words_array);
+ g_clear_pointer (&self->words_array, g_ptr_array_unref);
+}
+
+static void
+ide_editor_spell_widget_get_dict_words_async (IdeEditorSpellWidget *self)
+{
+ g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+
+ ide_editor_spell_dict_get_words_async (self->dict_widget,
+ ide_editor_spell_widget_get_dict_words_cb,
+ NULL,
+ self);
+}
+
+static void
ide_editor_spell_widget__language_notify_cb (IdeEditorSpellWidget *self,
GParamSpec *pspec,
GtkButton *language_chooser_button)
@@ -520,17 +695,21 @@ ide_editor_spell_widget__language_notify_cb (IdeEditorSpellWidget *self,
row = gtk_list_box_get_row_at_index (self->suggestions_box, 0);
gtk_list_box_select_row (self->suggestions_box, row);
}
- }
-}
-static void
-ide_editor_spell_widget__words_counted_cb (IdeEditorSpellWidget *self,
- GParamSpec *pspec,
- GspellNavigator *navigator)
-{
- g_assert (IDE_IS_EDITOR_SPELL_WIDGET (self));
+ g_clear_pointer (&self->words_array, g_ptr_array_unref);
+ if (current_language == NULL)
+ {
+ dict_clean_listbox (self);
+ gtk_widget_set_sensitive (GTK_WIDGET (self->dict_add_button), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (self->dict_words_list), FALSE);
- update_count_label (self);
+ return;
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self->dict_add_button), TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (self->dict_words_list), TRUE);
+ ide_editor_spell_widget_get_dict_words_async (self);
+ }
}
static void
@@ -546,12 +725,14 @@ ide_editor_spell_widget_constructed (GObject *object)
spell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (GTK_TEXT_BUFFER (self->buffer));
self->checker = gspell_text_buffer_get_spell_checker (spell_buffer);
- ide_editor_dict_widget_set_checker (self->dict_widget, self->checker);
+ ide_editor_spell_dict_set_checker (self->dict_widget, self->checker);
self->spellchecker_language = gspell_checker_get_language (self->checker);
gspell_language_chooser_set_language (GSPELL_LANGUAGE_CHOOSER (self->language_chooser_button),
self->spellchecker_language);
+ ide_editor_spell_widget_get_dict_words_async (self);
+
g_signal_connect_swapped (self->navigator,
"notify::words-counted",
G_CALLBACK (ide_editor_spell_widget__words_counted_cb),
@@ -608,6 +789,16 @@ ide_editor_spell_widget_constructed (GObject *object)
self,
G_CONNECT_SWAPPED);
+ g_signal_connect_swapped (self->dict_add_button,
+ "clicked",
+ G_CALLBACK (ide_editor_spell_widget__add_button_clicked_cb),
+ self);
+
+ g_signal_connect_swapped (self->dict_word_entry,
+ "notify::text",
+ G_CALLBACK (ide_editor_spell_widget__word_entry_text_notify_cb),
+ self);
+
self->placeholder = gtk_label_new (NULL);
gtk_widget_set_visible (self->placeholder, TRUE);
gtk_list_box_set_placeholder (self->suggestions_box, self->placeholder);
@@ -618,7 +809,7 @@ ide_editor_spell_widget_constructed (GObject *object)
*/
g_signal_connect_object (self,
"map",
- G_CALLBACK (ide_editor_frame_spell__widget_mapped_cb),
+ G_CALLBACK (ide_editor_spell__widget_mapped_cb),
NULL,
G_CONNECT_AFTER);
}
@@ -716,10 +907,6 @@ ide_editor_spell_widget_class_init (IdeEditorSpellWidgetClass *klass)
g_object_class_install_properties (object_class, N_PROPS, properties);
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-editor-spell-widget.ui");
- gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, misspelled_label);
- gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, change_to_label);
- gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, suggestions_label);
- gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, language_label);
gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, word_label);
gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, count_label);
@@ -731,18 +918,16 @@ ide_editor_spell_widget_class_init (IdeEditorSpellWidgetClass *klass)
gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, highlight_checkbutton);
gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, language_chooser_button);
gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, suggestions_box);
- gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, dict_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, dict_word_entry);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, dict_add_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSpellWidget, dict_words_list);
}
static void
ide_editor_spell_widget_init (IdeEditorSpellWidget *self)
{
-
gtk_widget_init_template (GTK_WIDGET (self));
- self->dict_widget = ide_editor_dict_widget_new (NULL);
-
- gtk_widget_show (GTK_WIDGET (self->dict_widget));
- gtk_container_add (GTK_CONTAINER (self->dict_box), GTK_WIDGET (self->dict_widget));
+ self->dict_widget = ide_editor_spell_dict_new (NULL);
self->view_spellchecker_set = FALSE;
/* FIXME: do not work, Gtk+ bug */
diff --git a/libide/editor/ide-editor-spell-widget.ui b/libide/editor/ide-editor-spell-widget.ui
index 408a994..57483ac 100644
--- a/libide/editor/ide-editor-spell-widget.ui
+++ b/libide/editor/ide-editor-spell-widget.ui
@@ -7,8 +7,9 @@
<property name="visible">true</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
+ <property name="margin">6</property>
<child>
- <object class="GtkLabel" id="misspelled_label">
+ <object class="GtkLabel">
<property name="visible">true</property>
<property name="label" translatable="yes">Misspelled</property>
<property name="xalign">0</property>
@@ -71,7 +72,7 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="change_to_label">
+ <object class="GtkLabel">
<property name="visible">true</property>
<property name="label" translatable="yes">Change _to</property>
<property name="xalign">0</property>
@@ -121,7 +122,7 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="suggestions_label">
+ <object class="GtkLabel">
<property name="visible">true</property>
<property name="label" translatable="yes">_Suggestions</property>
<property name="xalign">0</property>
@@ -157,15 +158,80 @@
</packing>
</child>
<child>
- <object class="GtkBox" id="dict_box">
- <property name="orientation">horizontal</property>
+ <object class="GtkLabel">
<property name="visible">true</property>
- <property name="spacing">6</property>
+ <property name="label" translatable="yes">Add Word</property>
+ <property name="xalign">0</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">word_entry</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
- <property name="width">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="dict_word_entry">
+ <property name="visible">true</property>
+ <property name="hexpand">true</property>
+ <property name="can_focus">true</property>
+ <property name="width-chars">20</property>
+ <property name="max-width-chars">20</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="dict_add_button">
+ <property name="label" translatable="yes">Add</property>
+ <property name="visible">true</property>
+ <property name="can_focus">true</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">5</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Dictionary</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="wrap">true</property>
+ <property name="wrap-mode">word</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <property name="expand">true</property>
+ <property name="shadow_type">in</property>
+ <property name="min-content-height">110</property>
+ <property name="max-content-height">110</property>
+ <property name="propagate-natural-height">true</property>
+ <child>
+ <object class="GtkListBox" id="dict_words_list">
+ <property name="visible">true</property>
+ <property name="can_focus">true</property>
+ <property name="activate-on-single-click">false</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">6</property>
+ <property name="width">2</property>
</packing>
</child>
<child>
@@ -178,7 +244,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">6</property>
+ <property name="top_attach">7</property>
</packing>
</child>
<child>
@@ -189,12 +255,12 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">7</property>
+ <property name="top_attach">8</property>
<property name="width">3</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="language_label">
+ <object class="GtkLabel">
<property name="visible">true</property>
<property name="label" translatable="yes">_Language</property>
<property name="xalign">0</property>
@@ -204,7 +270,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">8</property>
+ <property name="top_attach">9</property>
</packing>
</child>
<child>
@@ -216,7 +282,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">8</property>
+ <property name="top_attach">9</property>
<property name="width">2</property>
</packing>
</child>
@@ -231,7 +297,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">9</property>
+ <property name="top_attach">10</property>
</packing>
</child>
<child>
@@ -243,7 +309,7 @@
</object>
<packing>
<property name="left_attach">1</property>
- <property name="top_attach">9</property>
+ <property name="top_attach">10</property>
</packing>
</child>
</object>
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index 57281d3..1f69cf1 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -53,7 +53,6 @@
<gresource prefix="/org/gnome/builder/ui">
<file compressed="true" alias="ide-editor-frame.ui">../editor/ide-editor-frame.ui</file>
<file compressed="true"
alias="ide-editor-layout-stack-controls.ui">../editor/ide-editor-layout-stack-controls.ui</file>
- <file compressed="true" alias="ide-editor-dict-widget.ui">../editor/ide-editor-dict-widget.ui</file>
<file compressed="true" alias="ide-editor-perspective.ui">../editor/ide-editor-perspective.ui</file>
<file compressed="true" alias="ide-editor-spell-widget.ui">../editor/ide-editor-spell-widget.ui</file>
<file compressed="true" alias="ide-editor-tweak-widget.ui">../editor/ide-editor-tweak-widget.ui</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]