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.
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)])])
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 \
/* 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
"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,
close_and_respond (w, PSPPIRE_RESPONSE_GOTO);
}
-
static void
refresh_clicked (GtkWidget *w, gpointer data)
{
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);
{ 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 }
};
PSPPIRE_BUTTON_HELP,
PSPPIRE_BUTTON_RESET,
PSPPIRE_BUTTON_PASTE,
+ PSPPIRE_BUTTON_FIND,
n_PsppireButtonBoxButtons
};
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
G_END_DECLS
#endif /* __PSPPIRE_BUTTON_BOX_H__ */
-
gpointer acceptable_data;
gboolean slidable;
gchar *help_page;
-
};
struct _PsppireDialogClass
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#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) ;
+}
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef __PSPPIRE_SEARCH_DIALOG_H__
+#define __PSPPIRE_SEARCH_DIALOG_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#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__ */
#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"
if (sw->dispose_has_run)
return;
- g_object_unref (sw->search_text_buffer);
-
g_free (sw->encoding);
sw->encoding = NULL;
execute_syntax (psppire_default_data_window (), reader);
}
+
\f
+
/* Delete the currently selected text */
static void
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);
}
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));
GSimpleAction *edit_paste;
GSimpleAction *edit_find;
- GtkEntryBuffer *search_text_buffer;
+ GtkSourceSearchContext *search_context;
gulong ps_handler;
gulong sel_handler;