/* * mooeditaction.c * * Copyright (C) 2004-2007 by Yevgen Muntyan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation. * * See COPYING file that comes with this distribution. */ #define MOOEDIT_COMPILATION #include "mooedit/mooeditaction.h" #include "mooedit/mooeditaction-factory.h" #include "mooedit/mooedit-private.h" #include "mooedit/mooeditfiltersettings.h" #include #include typedef enum { FILTER_SENSITIVE, FILTER_VISIBLE } FilterType; #define N_FILTERS 2 struct _MooEditActionPrivate { MooEdit *doc; MooEditFilter *file_filter; GRegex *filters[N_FILTERS]; }; typedef struct { GRegex *regex; guint use_count; } RegexRef; typedef struct { GHashTable *hash; } FilterStore; static FilterStore filter_store; G_DEFINE_TYPE (MooEditAction, moo_edit_action, MOO_TYPE_ACTION) enum { PROP_0, PROP_DOC, PROP_FILE_FILTER, PROP_FILTER_SENSITIVE, PROP_FILTER_VISIBLE }; static GRegex *get_filter_regex (const char *pattern); static void unuse_filter_regex (GRegex *regex); static gboolean moo_edit_action_check_visible (MooEditAction *action); static gboolean moo_edit_action_check_sensitive (MooEditAction *action); static void moo_edit_action_check_state (MooEditAction *action); static void moo_edit_action_finalize (GObject *object) { guint i; MooEditAction *action = MOO_EDIT_ACTION (object); for (i = 0; i < N_FILTERS; ++i) if (action->priv->filters[i]) unuse_filter_regex (action->priv->filters[i]); _moo_edit_filter_free (action->priv->file_filter); G_OBJECT_CLASS(moo_edit_action_parent_class)->finalize (object); } static const char * moo_edit_action_get_filter (MooEditAction *action, FilterType type) { g_assert (type < N_FILTERS); return action->priv->filters[type] ? g_regex_get_pattern (action->priv->filters[type]) : NULL; } static void moo_edit_action_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MooEditAction *action = MOO_EDIT_ACTION (object); switch (prop_id) { case PROP_DOC: g_value_set_object (value, action->priv->doc); break; case PROP_FILTER_VISIBLE: g_value_set_string (value, moo_edit_action_get_filter (action, FILTER_VISIBLE)); break; case PROP_FILTER_SENSITIVE: g_value_set_string (value, moo_edit_action_get_filter (action, FILTER_SENSITIVE)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void moo_edit_action_set_file_filter (MooEditAction *action, const char *string) { _moo_edit_filter_free (action->priv->file_filter); if (string && string[0]) action->priv->file_filter = _moo_edit_filter_new (string); else action->priv->file_filter = NULL; g_object_notify (G_OBJECT (action), "file-filter"); } MooEdit * moo_edit_action_get_doc (MooEditAction *action) { g_return_val_if_fail (MOO_IS_EDIT_ACTION (action), NULL); return action->priv->doc; } static void moo_edit_action_set_filter (MooEditAction *action, const char *filter, FilterType type) { GRegex *tmp; g_assert (type < N_FILTERS); tmp = action->priv->filters[type]; action->priv->filters[type] = filter ? get_filter_regex (filter) : NULL; unuse_filter_regex (tmp); } static void moo_edit_action_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MooEditAction *action = MOO_EDIT_ACTION (object); switch (prop_id) { case PROP_DOC: action->priv->doc = g_value_get_object (value); g_object_notify (object, "doc"); break; case PROP_FILE_FILTER: moo_edit_action_set_file_filter (action, g_value_get_string (value)); break; case PROP_FILTER_SENSITIVE: moo_edit_action_set_filter (action, g_value_get_string (value), FILTER_SENSITIVE); break; case PROP_FILTER_VISIBLE: moo_edit_action_set_filter (action, g_value_get_string (value), FILTER_VISIBLE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void moo_edit_action_init (MooEditAction *action) { action->priv = G_TYPE_INSTANCE_GET_PRIVATE (action, MOO_TYPE_EDIT_ACTION, MooEditActionPrivate); } static const char * get_current_line (MooEdit *doc) { char *line; line = g_object_get_data (G_OBJECT (doc), "moo-edit-current-line"); if (!line) { GtkTextIter line_start, line_end; GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (doc)); gtk_text_buffer_get_iter_at_mark (buffer, &line_start, gtk_text_buffer_get_insert (buffer)); line_end = line_start; gtk_text_iter_set_line_offset (&line_start, 0); if (!gtk_text_iter_ends_line (&line_end)) gtk_text_iter_forward_to_line_end (&line_end); line = gtk_text_buffer_get_slice (buffer, &line_start, &line_end, TRUE); g_object_set_data_full (G_OBJECT (doc), "moo-edit-current-line", line, g_free); } return line; } static gboolean moo_edit_action_check_visible_real (MooEditAction *action) { gboolean visible = TRUE; GRegex *filter = action->priv->filters[FILTER_VISIBLE]; if (!action->priv->doc) return gtk_action_get_visible (GTK_ACTION (action)); if (!action->priv->file_filter && !filter) return gtk_action_get_visible (GTK_ACTION (action)); if (visible && action->priv->file_filter) if (!_moo_edit_filter_match (action->priv->file_filter, action->priv->doc)) visible = FALSE; if (visible && filter) { const char *line = get_current_line (action->priv->doc); if (!g_regex_match (filter, line, 0, NULL)) visible = FALSE; } return visible; } static gboolean moo_edit_action_check_sensitive_real (MooEditAction *action) { const char *line; GRegex *filter = action->priv->filters[FILTER_SENSITIVE]; if (!action->priv->doc || !filter) return gtk_action_get_sensitive (GTK_ACTION (action)); line = get_current_line (action->priv->doc); return g_regex_match (filter, line, 0, NULL); } static void moo_edit_action_check_state_real (MooEditAction *action) { gboolean visible, sensitive; visible = moo_edit_action_check_visible (action); sensitive = moo_edit_action_check_sensitive (action); g_object_set (action, "sensitive", sensitive, "visible", visible, NULL); } static void moo_edit_action_class_init (MooEditActionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = moo_edit_action_finalize; gobject_class->set_property = moo_edit_action_set_property; gobject_class->get_property = moo_edit_action_get_property; klass->check_state = moo_edit_action_check_state_real; klass->check_visible = moo_edit_action_check_visible_real; klass->check_sensitive = moo_edit_action_check_sensitive_real; g_type_class_add_private (klass, sizeof (MooEditActionPrivate)); g_object_class_install_property (gobject_class, PROP_DOC, g_param_spec_object ("doc", "doc", "doc", MOO_TYPE_EDIT, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILE_FILTER, g_param_spec_string ("file-filter", "file-filter", "file-filter", NULL, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_FILTER_SENSITIVE, g_param_spec_string ("filter-sensitive", "filter-sensitive", "filter-sensitive", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILTER_VISIBLE, g_param_spec_string ("filter-visible", "filter-visible", "filter-visible", NULL, G_PARAM_READWRITE)); } static gboolean moo_edit_action_check_visible (MooEditAction *action) { g_return_val_if_fail (MOO_IS_EDIT_ACTION (action), TRUE); if (MOO_EDIT_ACTION_GET_CLASS (action)->check_visible) return MOO_EDIT_ACTION_GET_CLASS (action)->check_visible (action); else return gtk_action_get_visible (GTK_ACTION (action)); } static gboolean moo_edit_action_check_sensitive (MooEditAction *action) { g_return_val_if_fail (MOO_IS_EDIT_ACTION (action), TRUE); if (MOO_EDIT_ACTION_GET_CLASS (action)->check_sensitive) return MOO_EDIT_ACTION_GET_CLASS (action)->check_sensitive (action); else return gtk_action_get_sensitive (GTK_ACTION (action)); } static void moo_edit_action_check_state (MooEditAction *action) { g_return_if_fail (MOO_IS_EDIT_ACTION (action)); g_return_if_fail (MOO_EDIT_ACTION_GET_CLASS (action)->check_state != NULL); MOO_EDIT_ACTION_GET_CLASS (action)->check_state (action); } void _moo_edit_check_actions (MooEdit *edit) { GtkActionGroup *group = moo_edit_get_actions (edit); GList *actions = gtk_action_group_list_actions (group); while (actions) { if (MOO_IS_EDIT_ACTION (actions->data)) moo_edit_action_check_state (actions->data); actions = g_list_delete_link (actions, actions); } } static void regex_ref_free (RegexRef *ref) { if (ref) { g_regex_unref (ref->regex); g_free (ref); } } static void init_filter_store (void) { if (!filter_store.hash) filter_store.hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) regex_ref_free); } static GRegex * get_filter_regex (const char *pattern) { RegexRef *ref; g_return_val_if_fail (pattern != NULL, NULL); init_filter_store (); ref = g_hash_table_lookup (filter_store.hash, pattern); if (!ref) { GRegex *regex; GError *error = NULL; regex = g_regex_new (pattern, G_REGEX_OPTIMIZE, 0, &error); if (!regex) { g_warning ("%s: %s", G_STRLOC, error->message); g_error_free (error); return NULL; } ref = g_new0 (RegexRef, 1); ref->regex = regex; g_hash_table_insert (filter_store.hash, g_strdup (pattern), ref); } ref->use_count++; return ref->regex; } static void unuse_filter_regex (GRegex *regex) { RegexRef *ref; const char *pattern; g_return_if_fail (regex != NULL); init_filter_store (); pattern = g_regex_get_pattern (regex); ref = g_hash_table_lookup (filter_store.hash, pattern); g_return_if_fail (ref != NULL); if (!--ref->use_count) g_hash_table_remove (filter_store.hash, pattern); }