From: John Darrington Date: Fri, 16 Jun 2023 15:14:41 +0000 (+0200) Subject: Re-implemented the search dialog for the syntax editor X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e1a2cf577fac791c79cd531e688a48efab9b7c0c;p=pspp Re-implemented the search dialog for the syntax editor This new dialog offers a lot more features, such as case insensitivity, backward searching etc. This change also removes the option to build against gtksourceview-3 - gtksourceview-4 is now required. --- diff --git a/NEWS b/NEWS index 8bcb526ffa..72e49029cf 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,10 @@ See the end for copying conditions. Please send PSPP bug reports to bug-gnu-pspp@gnu.org. +Changes since 2.0.0-pre1: + + * Improved the search options in the syntax editor. + Changes from 1.6.2 to 2.0.0-pre1: * The CTABLES command is now implemented. diff --git a/configure.ac b/configure.ac index 2af7cd4214..649217255a 100644 --- a/configure.ac +++ b/configure.ac @@ -130,9 +130,8 @@ if test "$with_gui" != "no"; then PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= 3.22.0], [], [PSPP_REQUIRED_PREREQ([gtk+ 3.0 version 3.22.0 or later (or use --without-gui)])]) - PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-4 >= 4.0], [], - [PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-3.0 >= 3.4.2], [], - [PSPP_REQUIRED_PREREQ([gtksourceview 4.x or gtksourceview 3.x version 3.4.2 or later (or use --without-gui)])])]) + PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-4 >= 4.8], [], + [PSPP_REQUIRED_PREREQ([gtksourceview 4.8 or later (or use --without-gui)])]) PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.44], [], [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.44 or later (or use --without-gui)])]) diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 18885bb168..e01090777c 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -329,6 +329,8 @@ src_ui_gui_libwidgets_essential_la_SOURCES = \ src/ui/gui/psppire-dialog-action-weight.h \ src/ui/gui/psppire-dialog.c \ src/ui/gui/psppire-dialog.h \ + src/ui/gui/psppire-search-dialog.c \ + src/ui/gui/psppire-search-dialog.h \ src/ui/gui/psppire-dict.c \ src/ui/gui/psppire-dict.h \ src/ui/gui/psppire-dictview.c \ diff --git a/src/ui/gui/psppire-buttonbox.c b/src/ui/gui/psppire-buttonbox.c index a015189947..f47852211a 100644 --- a/src/ui/gui/psppire-buttonbox.c +++ b/src/ui/gui/psppire-buttonbox.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2007, 2010, 2011, 2012 Free Software Foundation + Copyright (C) 2007, 2010, 2011, 2012, 2023 Free Software Foundation 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 @@ -133,11 +133,13 @@ psppire_button_box_class_init (PsppireButtonBoxClass *class) "Buttons", "The mask that decides what buttons appear in the button box", PSPPIRE_TYPE_BUTTON_MASK, - PSPPIRE_BUTTON_OK_MASK | - PSPPIRE_BUTTON_CANCEL_MASK | - PSPPIRE_BUTTON_RESET_MASK | - PSPPIRE_BUTTON_HELP_MASK | - PSPPIRE_BUTTON_PASTE_MASK, + PSPPIRE_BUTTON_OK_MASK + | PSPPIRE_BUTTON_CANCEL_MASK + | PSPPIRE_BUTTON_CLOSE_MASK + | PSPPIRE_BUTTON_RESET_MASK + | PSPPIRE_BUTTON_HELP_MASK + | PSPPIRE_BUTTON_PASTE_MASK + | PSPPIRE_BUTTON_FIND_MASK, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_BUTTONS, @@ -219,7 +221,6 @@ goto_button_clicked (GtkWidget *w, gpointer data) close_and_respond (w, PSPPIRE_RESPONSE_GOTO); } - static void refresh_clicked (GtkWidget *w, gpointer data) { @@ -330,6 +331,10 @@ psppire_button_box_init (PsppireButtonBox *bb) psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_PASTE]); g_object_set (bb->button[PSPPIRE_BUTTON_PASTE], "no-show-all", TRUE, NULL); + bb->button[PSPPIRE_BUTTON_FIND] = gtk_button_new_with_label (_("Find")); + psppire_box_pack_start_defaults (GTK_BOX (bb), bb->button[PSPPIRE_BUTTON_FIND]); + g_object_set (bb->button[PSPPIRE_BUTTON_FIND], "no-show-all", TRUE, NULL); + bb->button[PSPPIRE_BUTTON_CANCEL] = gtk_button_new_with_label (_("Cancel")); g_signal_connect (bb->button[PSPPIRE_BUTTON_CANCEL], "clicked", G_CALLBACK (close_dialog), NULL); @@ -394,6 +399,7 @@ psppire_button_flags_get_type (void) { PSPPIRE_BUTTON_HELP_MASK, "PSPPIRE_BUTTON_HELP_MASK", "Invoke context sensitive help" }, { PSPPIRE_BUTTON_RESET_MASK, "PSPPIRE_BUTTON_RESET_MASK", "Restore dialog to its default settings" }, { PSPPIRE_BUTTON_PASTE_MASK, "PSPPIRE_BUTTON_PASTE_MASK", "Accept dialog and paste syntax" }, + { PSPPIRE_BUTTON_FIND_MASK, "PSPPIRE_BUTTON_FIND_MASK", "Find something" }, { 0, NULL, NULL } }; diff --git a/src/ui/gui/psppire-buttonbox.h b/src/ui/gui/psppire-buttonbox.h index 3cc25e9450..5d20470cfe 100644 --- a/src/ui/gui/psppire-buttonbox.h +++ b/src/ui/gui/psppire-buttonbox.h @@ -46,6 +46,7 @@ enum PSPPIRE_BUTTON_HELP, PSPPIRE_BUTTON_RESET, PSPPIRE_BUTTON_PASTE, + PSPPIRE_BUTTON_FIND, n_PsppireButtonBoxButtons }; @@ -58,7 +59,8 @@ typedef enum PSPPIRE_BUTTON_CLOSE_MASK = (1 << PSPPIRE_BUTTON_CLOSE), PSPPIRE_BUTTON_HELP_MASK = (1 << PSPPIRE_BUTTON_HELP), PSPPIRE_BUTTON_RESET_MASK = (1 << PSPPIRE_BUTTON_RESET), - PSPPIRE_BUTTON_PASTE_MASK = (1 << PSPPIRE_BUTTON_PASTE) + PSPPIRE_BUTTON_PASTE_MASK = (1 << PSPPIRE_BUTTON_PASTE), + PSPPIRE_BUTTON_FIND_MASK = (1 << PSPPIRE_BUTTON_FIND) } PsppireButtonMask; struct _PsppireButtonBox @@ -84,4 +86,3 @@ GtkWidget* psppire_button_box_new (void); G_END_DECLS #endif /* __PSPPIRE_BUTTON_BOX_H__ */ - diff --git a/src/ui/gui/psppire-dialog.h b/src/ui/gui/psppire-dialog.h index 012a3e8944..2c353b3d99 100644 --- a/src/ui/gui/psppire-dialog.h +++ b/src/ui/gui/psppire-dialog.h @@ -55,7 +55,6 @@ struct _PsppireDialog gpointer acceptable_data; gboolean slidable; gchar *help_page; - }; struct _PsppireDialogClass diff --git a/src/ui/gui/psppire-search-dialog.c b/src/ui/gui/psppire-search-dialog.c new file mode 100644 index 0000000000..8a42ab578c --- /dev/null +++ b/src/ui/gui/psppire-search-dialog.c @@ -0,0 +1,194 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2023 Free Software Foundation + + 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 . */ + + +#include + +#include +#include "psppire-search-dialog.h" +#include "psppire-buttonbox.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +static void psppire_search_dialog_class_init (PsppireSearchDialogClass *); +static void psppire_search_dialog_init (PsppireSearchDialog *); + +enum {FIND, + n_SIGNALS}; + +static guint signals [n_SIGNALS]; + + +static GObjectClass *parent_class = NULL; + +static void +psppire_search_dialog_finalize (GObject *object) +{ + // PsppireSearchDialog *dialog = PSPPIRE_SEARCH_DIALOG (object); + + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +G_DEFINE_TYPE (PsppireSearchDialog, psppire_search_dialog, PSPPIRE_TYPE_DIALOG); + + +static void +psppire_search_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + // PsppireSearchDialog *dialog = PSPPIRE_SEARCH_DIALOG (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + +static void +psppire_search_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + +{ + // PsppireSearchDialog *dialog = PSPPIRE_SEARCH_DIALOG (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + +static void +psppire_search_dialog_class_init (PsppireSearchDialogClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = psppire_search_dialog_finalize; + + object_class->set_property = psppire_search_dialog_set_property; + object_class->get_property = psppire_search_dialog_get_property; + + signals [FIND] = + g_signal_new ("find", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + parent_class = g_type_class_peek_parent (class); +} + +static void +search_forward (PsppireSearchDialog *dialog) +{ + g_signal_emit (dialog, signals [FIND], 0, FALSE); +} + +static void +search_backward (PsppireSearchDialog *dialog) +{ + g_signal_emit (dialog, signals [FIND], 0, TRUE); +} + +static void +on_find (PsppireSearchDialog *sd) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sd->backward))) + search_backward (sd); + else + search_forward (sd); +} + +static void +psppire_search_dialog_init (PsppireSearchDialog *dialog) +{ + GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + + GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + GtkWidget *label = gtk_label_new ( _("Search Text:")); + dialog->entry = gtk_search_entry_new (); + + g_signal_connect_swapped (dialog->entry, "next-match", G_CALLBACK (search_forward), + dialog); + g_signal_connect_swapped (dialog->entry, "previous-match", G_CALLBACK (search_backward), + dialog); + + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 5); + gtk_box_pack_start (GTK_BOX (hbox), dialog->entry, TRUE, TRUE, 5); + + gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, TRUE, 5); + + GtkWidget *bbo = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); + dialog->ignore_case = gtk_check_button_new_with_label ( _("Ignore case")); + gtk_box_pack_start (GTK_BOX (bbo), dialog->ignore_case, FALSE, TRUE, 5); + + dialog->wrap = gtk_check_button_new_with_label ( _("Wrap around")); + gtk_box_pack_start (GTK_BOX (bbo), dialog->wrap, FALSE, TRUE, 5); + + dialog->whole = gtk_check_button_new_with_label ( _("Match whole words only")); + gtk_box_pack_start (GTK_BOX (bbo), dialog->whole, FALSE, TRUE, 5); + + dialog->forward = gtk_radio_button_new_with_label (NULL, _("Search forward")); + gtk_box_pack_start (GTK_BOX (bbo), dialog->forward, FALSE, TRUE, 5); + + dialog->backward = gtk_radio_button_new_with_label (NULL, _("Search backward")); + gtk_box_pack_start (GTK_BOX (bbo), dialog->backward, FALSE, TRUE, 5); + + gtk_radio_button_join_group (GTK_RADIO_BUTTON (dialog->backward), + GTK_RADIO_BUTTON (dialog->forward)); + + gtk_box_pack_start (GTK_BOX (box), bbo, FALSE, TRUE, 5); + + GtkWidget *bb = psppire_button_box_new (); + g_object_set (bb, + "buttons", PSPPIRE_BUTTON_FIND_MASK | PSPPIRE_BUTTON_CLOSE_MASK, + "layout-style", GTK_BUTTONBOX_SPREAD, + NULL); + + gtk_box_pack_start (GTK_BOX (box), bb, FALSE, TRUE, 5); + + g_signal_connect_swapped (PSPPIRE_BUTTON_BOX (bb)->button[PSPPIRE_BUTTON_FIND], + "clicked", G_CALLBACK (on_find), dialog); + + gtk_widget_show_all (box); + gtk_container_add (GTK_CONTAINER (dialog), box); +} + +GtkWidget* +psppire_search_dialog_new (void) +{ + PsppireSearchDialog *dialog + = g_object_new (psppire_search_dialog_get_type (), + "title", _("PSPPIRE Search Syntax"), + NULL); + + return GTK_WIDGET (dialog) ; +} diff --git a/src/ui/gui/psppire-search-dialog.h b/src/ui/gui/psppire-search-dialog.h new file mode 100644 index 0000000000..2ca2cd0ce5 --- /dev/null +++ b/src/ui/gui/psppire-search-dialog.h @@ -0,0 +1,61 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2023 Free Software Foundation + + 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 . */ + +#ifndef __PSPPIRE_SEARCH_DIALOG_H__ +#define __PSPPIRE_SEARCH_DIALOG_H__ + +#include +#include +#include +#include "psppire-dialog.h" + + +G_BEGIN_DECLS + +#define PSPPIRE_TYPE_SEARCH_DIALOG (psppire_search_dialog_get_type ()) +#define PSPPIRE_SEARCH_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_TYPE_SEARCH_DIALOG, PsppireSearchDialog)) +#define PSPPIRE_SEARCH_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PSPPIRE_TYPE_SEARCH_DIALOG, PsppireSearchDialogClass)) +#define PSPPIRE_IS_SEARCH_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_SEARCH_DIALOG)) +#define PSPPIRE_IS_SEARCH_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_SEARCH_DIALOG)) + + +typedef struct _PsppireSearchDialog PsppireSearchDialog; +typedef struct _PsppireSearchDialogClass PsppireSearchDialogClass; + +struct _PsppireSearchDialog +{ + PsppireDialog parent; + + GtkWidget *entry; + GtkWidget *bbo; + GtkWidget *ignore_case; + GtkWidget *wrap; + GtkWidget *whole; + GtkWidget *forward; + GtkWidget *backward; +}; + +struct _PsppireSearchDialogClass +{ + PsppireDialogClass parent_class; +}; + +GType psppire_search_dialog_get_type (void); +GtkWidget* psppire_search_dialog_new (void); + +G_END_DECLS + +#endif /* __PSPPIRE_SEARCH_DIALOG_H__ */ diff --git a/src/ui/gui/psppire-syntax-window.c b/src/ui/gui/psppire-syntax-window.c index 4f244a8206..302940d931 100644 --- a/src/ui/gui/psppire-syntax-window.c +++ b/src/ui/gui/psppire-syntax-window.c @@ -36,6 +36,7 @@ #include "ui/gui/psppire-lex-reader.h" #include "ui/gui/psppire-syntax-window.h" #include "ui/gui/psppire.h" +#include "ui/gui/psppire-search-dialog.h" #include "ui/gui/windows-menu.h" #include "gl/localcharset.h" @@ -124,8 +125,6 @@ psppire_syntax_window_dispose (GObject *obj) if (sw->dispose_has_run) return; - g_object_unref (sw->search_text_buffer); - g_free (sw->encoding); sw->encoding = NULL; @@ -211,7 +210,9 @@ editor_execute_syntax (const PsppireSyntaxWindow *sw, GtkTextIter start, execute_syntax (psppire_default_data_window (), reader); } + + /* Delete the currently selected text */ static void on_edit_delete (PsppireSyntaxWindow *sw) @@ -223,84 +224,65 @@ on_edit_delete (PsppireSyntaxWindow *sw) gtk_text_buffer_delete (buffer, &begin, &end); } -/* Create and run a dialog to collect the search string. - In future this might be expanded to include options, for example - backward searching, case sensitivity etc. */ -static const char * -get_search_text (PsppireSyntaxWindow *parent) -{ - const char *search_text = NULL; - GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL; - GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Text Search"), - GTK_WINDOW (parent), - flags, - _("_OK"), - GTK_RESPONSE_OK, - _("_Cancel"), - GTK_RESPONSE_CANCEL, - NULL); - - GtkWidget *content_area = - gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - - GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - GtkWidget *label = gtk_label_new (_("Text to search for:")); - GtkWidget *entry = gtk_entry_new_with_buffer (parent->search_text_buffer); - - /* Add the label, and show everything we have added. */ - gtk_container_add (GTK_CONTAINER (content_area), box); - gtk_container_add (GTK_CONTAINER (box), label); - gtk_container_add (GTK_CONTAINER (box), entry); - gtk_widget_show_all (content_area); - - int result = gtk_dialog_run (GTK_DIALOG (dialog)); - switch (result) - { - case GTK_RESPONSE_OK: - search_text = gtk_entry_get_text (GTK_ENTRY (entry)); - break; - default: - search_text = NULL; - }; - - gtk_widget_destroy (dialog); - return search_text; -} +typedef gboolean search_function (GtkSourceSearchContext *search, + const GtkTextIter *iter, + GtkTextIter *match_start, + GtkTextIter *match_end, + gboolean *has_wrapped_around); -/* What to do when the Find menuitem is called. */ -static void -on_edit_find (PsppireSyntaxWindow *sw) +/* This function is called when the user clicks the Find button */ +static void on_find (PsppireSyntaxWindow *sw, gboolean backwards, gpointer data, gpointer junk) { + PsppireSearchDialog *dialog = PSPPIRE_SEARCH_DIALOG (data); + + const char *search_text = gtk_entry_get_text (GTK_ENTRY (dialog->entry)); + if (search_text == NULL) + return; + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (sw->buffer); GtkTextIter begin; GtkTextIter loc; - const char *target = get_search_text (sw); - if (target == NULL) - return; - - /* This is a wrap-around search. So start searching one - character after the current char. */ GtkTextMark *mark = gtk_text_buffer_get_insert (buffer); + + GtkSourceSearchSettings *sss = gtk_source_search_context_get_settings (sw->search_context); + gtk_source_search_settings_set_search_text (sss, search_text); + gtk_source_search_settings_set_case_sensitive (sss, + !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->ignore_case))); + + gtk_source_search_settings_set_at_word_boundaries (sss, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->whole))); + + gtk_source_search_settings_set_wrap_around (sss, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->wrap))); + + search_function *func = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->forward)) + ? gtk_source_search_context_forward + : gtk_source_search_context_backward; + gtk_text_buffer_get_iter_at_mark (buffer, &begin, mark); gtk_text_iter_forward_char (&begin); - if (gtk_text_iter_forward_search (&begin, target, 0, - &loc, 0, 0)) + if (func (sw->search_context, &begin, &loc, NULL, NULL)) { gtk_text_buffer_place_cursor (buffer, &loc); } - else - { - /* If not found, then continue the search from the top - of the buffer. */ - gtk_text_buffer_get_start_iter (buffer, &begin); - if (gtk_text_iter_forward_search (&begin, target, 0, - &loc, 0, 0)) - { - gtk_text_buffer_place_cursor (buffer, &loc); - } - } +} + +/* What to do when the Find menuitem is called. */ +static void +on_edit_find (PsppireSyntaxWindow *sw) +{ + GtkWidget *ww = psppire_search_dialog_new (); + gtk_window_set_transient_for (GTK_WINDOW (ww), GTK_WINDOW (sw)); + + g_signal_connect_swapped (ww, "find", G_CALLBACK (on_find), sw); + + sw->search_context = gtk_source_search_context_new (sw->buffer, NULL); + + psppire_dialog_run (PSPPIRE_DIALOG (ww)); + + g_object_unref (sw->search_context); } @@ -858,7 +840,7 @@ psppire_syntax_window_init (PsppireSyntaxWindow *window) window->cliptext = NULL; window->dispose_has_run = FALSE; - window->search_text_buffer = gtk_entry_buffer_new (NULL, -1); + window->search_context = NULL; window->edit_delete = g_simple_action_new ("delete", NULL); g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (window->edit_delete)); diff --git a/src/ui/gui/psppire-syntax-window.h b/src/ui/gui/psppire-syntax-window.h index 161b374597..61b6cc4ec5 100644 --- a/src/ui/gui/psppire-syntax-window.h +++ b/src/ui/gui/psppire-syntax-window.h @@ -68,7 +68,7 @@ struct _PsppireSyntaxWindow GSimpleAction *edit_paste; GSimpleAction *edit_find; - GtkEntryBuffer *search_text_buffer; + GtkSourceSearchContext *search_context; gulong ps_handler; gulong sel_handler;