* Improved the search options in the syntax editor.
+ * Journaling is now enabled by default in interactive use. In PSPPIRE,
+ journaling can be configured using Edit|Options.
+
Changes from 1.6.2 to 2.0.0-pre1:
* The CTABLES command is now implemented.
@cindex width
@cindex tnumbers
+These subcommands affect journaling, also called logging. When
+journaling is enabled, @pspp{} writes the commands that it executes,
+plus any errors or other diagostics that it outputs, to a text file,
+called the @dfn{journal} file.
-Logging subcommands affect logging of commands executed to external
-files. These subcommands are
+@pspp{} enables journaling by default when it runs interactively in a
+terminal or in the PSPPIRE GUI. In the GUI, use @clicksequence{Edit
+@click{} Options@dots{}} to view or override the default location or
+to disable journaling. From syntax, use @code{SHOW JOURNAL} to see
+the journal's location and whether it is enabled.
@table @asis
@item JOURNAL
@itemx LOG
-These subcommands, which are synonyms, control the journal. The
-default is @subcmd{ON}, which causes commands entered interactively to be
-written to the journal file. Commands included from syntax files that
-are included interactively and error messages printed by @pspp{} are also
-written to the journal file, prefixed by @samp{>}. @subcmd{OFF} disables use
-of the journal.
-
-The journal is named @file{pspp.jnl} by default. A different name may
-be specified.
+Specify @subcmd{ON} to enable the journal and @subcmd{OFF} to disable
+it. Specify a file name to set the name of the journal file.
@end table
System file subcommands affect the default format of system files
#include "gl/xmalloca.h"
#include "gettext.h"
+#include "xvasprintf.h"
#define _(msgid) gettext (msgid)
\f
return path;
}
+const char *
+default_log_path (void)
+{
+ return default_output_path ();
+}
+
#else
/* ... whereas the rest of the world just likes it to be
return current_dir;
}
+const char *
+default_log_path (void)
+{
+ static char *log_path = NULL;
+
+ if (!log_path)
+ {
+ char *tmp = NULL;
+ const char *state_home = getenv ("XDG_STATE_HOME");
+ if (!state_home)
+ {
+ const char *home = getenv ("HOME");
+ state_home = tmp = xasprintf ("%s/.local/state", home ? home : "");
+ }
+
+ log_path = xasprintf ("%s/pspp/", state_home);
+
+ struct stat s;
+ if (!stat (state_home, &s) && stat (log_path, &s) && errno == ENOENT)
+ mkdir (log_path, 0700);
+
+ free (tmp);
+ }
+
+ return log_path;
+}
+
#endif
int fn_close (const struct file_handle *fn, FILE *file);
const char * default_output_path (void);
+const char * default_log_path (void);
#if defined _WIN32 || defined __WIN32__
#define WIN32_LEAN_AND_MEAN /* avoid including junk */
#include "data/dataset.h"
#include "data/dictionary.h"
#include "data/format.h"
+#include "data/identifier.h"
#include "data/settings.h"
#include "data/value.h"
#include "data/variable.h"
static bool
parse_JOURNAL (struct lexer *lexer)
{
- int b = parse_bool (lexer);
- if (b == true)
- journal_enable ();
- else if (b == false)
- journal_disable ();
- else if (lex_is_string (lexer) || lex_token (lexer) == T_ID)
+ do
{
- char *filename = utf8_to_filename (lex_tokcstr (lexer));
- journal_set_file_name (filename);
- free (filename);
+ int b = parse_bool (lexer);
+ if (b == true)
+ journal_enable ();
+ else if (b == false)
+ journal_disable ();
+ else if (lex_is_string (lexer) || lex_token (lexer) == T_ID)
+ {
+ char *filename = utf8_to_filename (lex_tokcstr (lexer));
+ journal_set_file_name (filename);
+ free (filename);
- lex_get (lexer);
- }
- else
- {
- lex_error (lexer, _("Syntax error expecting ON or OFF or a file name."));
- return false;
+ lex_get (lexer);
+ }
+ else
+ {
+ lex_error (lexer, _("Syntax error expecting ON or OFF or a file name."));
+ return false;
+ }
}
+ while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD);
return true;
}
add_row (table, N_("Locale Directory"), relocate2 (locale_dir, &allocated));
free (allocated);
+ add_row (table, N_("Journal File"), journal_get_file_name ());
+
add_row (table, N_("Compiler Version"),
#ifdef __VERSION__
__VERSION__
char *output_driver_substitute_heading_vars (const char *, int page_number);
+struct output_driver *output_driver_find (const struct output_driver_class *);
+
/* One kind of output driver.
Output driver implementations must not call msg() to report errors. This
{
return driver->name;
}
+
+struct output_driver *
+output_driver_find (const struct output_driver_class *class)
+{
+ struct output_engine *e = engine_stack_top ();
+
+ struct llx *llx;
+ llx_for_each (llx, &e->drivers)
+ {
+ struct output_driver *d = llx_data (llx);
+ if (d->class == class)
+ return d;
+ }
+ return NULL;
+}
\f
static struct output_engine *
output_driver_get_engine (const struct output_driver *driver)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
#include "data/file-name.h"
#include "libpspp/cast.h"
{
struct output_driver driver;
FILE *file;
-
- /* Name of journal file. */
char *file_name;
- bool destroyed;
- };
+ bool newly_opened;
+};
static const struct output_driver_class journal_class;
-/* Journal driver, if journaling is enabled. */
-static struct journal_driver journal;
-
+/* This persists even if the driver is destroyed and recreated. */
+static char *journal_file_name;
static struct journal_driver *
journal_driver_cast (struct output_driver *driver)
return UP_CAST (driver, struct journal_driver, driver);
}
-static void
-journal_close (void)
-{
- if (journal.file != NULL)
- {
- if (fwriteerror (journal.file))
- msg_error (errno, _("error writing output file `%s'"),
- journal.file_name);
-
- }
- journal.file = NULL;
-}
-
static void
journal_destroy (struct output_driver *driver)
{
struct journal_driver *j = journal_driver_cast (driver);
- if (!j->destroyed)
- journal_close ();
-
- j->destroyed = true;
+ if (fwriteerror (j->file))
+ msg_error(errno, _("error writing output file `%s'"), j->file_name);
+ free (j->file_name);
+ free (j);
}
static void
-journal_output (struct journal_driver *j, char *s)
+journal_output (struct journal_driver *j, char *s, const char *prefix)
{
if (j->file)
{
- fprintf (j->file, "%s\n", s);
+ if (j->newly_opened)
+ {
+ j->newly_opened = false;
+
+ /* Unless this file is empty, start off with a blank line. */
+ struct stat s;
+ if (!fstat (fileno (j->file), &s) && s.st_size != 0)
+ putc ('\n', j->file);
+
+ /* Write the date and time. */
+ char buf[64];
+ time_t t = time (NULL);
+ struct tm *tm = localtime (&t);
+ strftime (buf, sizeof buf, "%Y-%m-%d %H:%M:%S", tm);
+ fprintf (j->file, "* New session at %s.\n", buf);
+ }
+
+ const char *p = s;
+ do
+ {
+ size_t len = strcspn (p, "\n");
+ fputs (prefix, j->file);
+ fwrite (p, len, 1, j->file);
+ putc ('\n', j->file);
+ p += len;
+ if (*p == '\n')
+ p++;
+ }
+ while (*p);
/* Flush the journal in case the syntax we're about to write
causes a crash. Having the syntax already written to disk
switch (item->type)
{
case OUTPUT_ITEM_MESSAGE:
- journal_output (j, msg_to_string (item->message));
+ journal_output (j, msg_to_string (item->message), "> ");
break;
case OUTPUT_ITEM_TEXT:
if (item->text.subtype == TEXT_ITEM_SYNTAX)
- journal_output (j, text_item_get_plain_text (item));
+ journal_output (j, text_item_get_plain_text (item), "");
break;
case OUTPUT_ITEM_GROUP:
.destroy = journal_destroy,
.submit = journal_submit,
};
-\f
-/* Enables journaling. */
-void
-journal_init (void)
+static struct journal_driver *
+get_journal_driver (void)
{
- journal = (struct journal_driver) {
- .driver = {
- .class = &journal_class,
- .name = xstrdup ("journal"),
- .device_type = SETTINGS_DEVICE_UNFILTERED,
- }
- };
-
- output_driver_register (&journal.driver);
- journal_enable ();
+ struct output_driver *d = output_driver_find (&journal_class);
+ return d ? journal_driver_cast (d) : NULL;
}
/* Disables journaling. */
void
journal_disable (void)
{
- journal_close ();
+ struct journal_driver *j = get_journal_driver ();
+ if (j)
+ output_driver_destroy (&j->driver);
}
-
/* Enable journaling. */
void
journal_enable (void)
{
- if (journal.file == NULL)
+ if (get_journal_driver ())
+ return;
+
+ const char *file_name = journal_get_file_name ();
+ FILE *file = fopen (file_name, "a");
+ if (file == NULL)
{
- journal.file = fopen (journal_get_file_name (), "a");
- if (journal.file == NULL)
- {
- msg_error (errno, _("error opening output file `%s'"),
- journal_get_file_name ());
- journal_close ();
- }
+ msg_error (errno, _("error opening output file `%s'"), file_name);
+ return;
}
+
+ struct journal_driver *j = xmalloc (sizeof *j);
+ *j = (struct journal_driver) {
+ .driver = {
+ .class = &journal_class,
+ .name = xstrdup ("journal"),
+ .device_type = SETTINGS_DEVICE_UNFILTERED,
+ },
+ .file = file,
+ .file_name = xstrdup (file_name),
+ .newly_opened = true,
+ };
+ output_driver_register (&j->driver);
}
bool
journal_is_enabled (void)
{
- return journal.file != NULL ;
+ return get_journal_driver () != NULL;
}
/* Sets the name of the journal file to FILE_NAME. */
void
journal_set_file_name (const char *file_name)
{
- journal_close ();
- free (journal.file_name);
- journal.file_name = xstrdup (file_name);
+ if (!strcmp (file_name, journal_get_file_name ()))
+ return;
+
+ bool enabled = journal_is_enabled ();
+ if (enabled)
+ journal_disable ();
+
+ free (journal_file_name);
+ journal_file_name = xstrdup (file_name);
+
+ if (enabled)
+ journal_enable ();
}
/* Returns the name of the journal file. The caller must not modify or free
const char *
journal_get_file_name (void)
{
- if (journal.file_name == NULL)
- {
- const char *output_path = default_output_path ();
- journal.file_name = xasprintf ("%s%s", output_path, "pspp.jnl");
- }
- return journal.file_name;
+ if (!journal_file_name)
+ journal_file_name = xstrdup (journal_get_default_file_name ());
+ return journal_file_name;
+}
+
+/* Returns the name of the default journal file. The caller must not modify or
+ free the returned string. */
+const char *
+journal_get_default_file_name (void)
+{
+ static char *default_file_name;
+
+ if (!default_file_name)
+ default_file_name = xasprintf ("%s%s", default_log_path (), "pspp.jnl");
+
+ return default_file_name;
}
#include <stdbool.h>
-void journal_init (void);
void journal_enable (void);
void journal_disable (void);
bool journal_is_enabled (void);
void journal_set_file_name (const char *);
const char *journal_get_file_name (void);
+const char *journal_get_default_file_name (void);
#endif /* output/journal.h */
#include "pre-initialisation.h"
+#include "ui/gui/options-dialog.h"
#include "ui/gui/psppire.h"
#include <gtk/gtk.h>
static void
user_tip (GApplication *app)
{
- PsppireConf *conf = psppire_conf_new ();
-
gboolean show_tip = TRUE;
- psppire_conf_get_boolean (conf, "startup", "show-user-tips", &show_tip);
+ psppire_conf_get_boolean ("startup", "show-user-tips", &show_tip);
if (!show_tip)
return;
}
show_tip = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
- psppire_conf_set_boolean (conf,
- "startup", "show-user-tips",
- show_tip);
-
- g_object_unref (conf);
+ psppire_conf_set_boolean ("startup", "show-user-tips", show_tip);
+ psppire_conf_save ();
gtk_widget_destroy (d);
}
}
g_object_set (G_OBJECT (app), "register-session", TRUE, NULL);
+
return g_application_run (G_APPLICATION (app), argc, argv);
}
#include "options-dialog.h"
+#include "output/journal.h"
#include "ui/gui/helper.h"
#include "ui/gui/psppire-conf.h"
#include "ui/gui/builder-wrapper.h"
+#include "ui/gui/psppire-data-store.h"
#include "ui/gui/psppire-data-window.h"
#include "ui/gui/psppire-dialog.h"
GtkBuilder *xml;
GtkWidget *show_labels;
GtkWidget *show_names;
- PsppireConf *conf;
GtkWidget *sort_names;
GtkWidget *sort_labels;
GtkWidget *raise;
GtkWidget *show_tips;
+
+ GtkWidget *journal_disable;
+ GtkWidget *journal_default;
+ GtkWidget *journal_custom;
+ GtkWidget *journal_custom_location;
};
GType
return etype;
}
+GType
+pspp_options_journal_location_get_type (void)
+{
+ static GType etype = 0;
+ if (G_UNLIKELY(etype == 0)) {
+ static const GEnumValue values[] =
+ {
+ { PSPP_OPTIONS_JOURNAL_LOCATION_DISABLED, "PSPP_OPTIONS_JOURNAL_LOCATION_DISABLED", "disabled" },
+ { PSPP_OPTIONS_JOURNAL_LOCATION_DEFAULT, "PSPP_OPTIONS_JOURNAL_LOCATION_DEFAULT", "default" },
+ { PSPP_OPTIONS_JOURNAL_LOCATION_CUSTOM, "PSPP_OPTIONS_JOURNAL_LOCATION_CUSTOM", "custom" },
+ { 0, NULL, NULL }
+ };
+ etype = g_enum_register_static (g_intern_static_string ("PsppOptionsJournalLocation"), values);
+ }
+ return etype;
+}
+
/*
Pops up the Options dialog box
*/
fd.alert = get_widget_assert (fd.xml, "checkbutton-alert");
fd.raise = get_widget_assert (fd.xml, "checkbutton-raise");
- gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
+ fd.journal_disable = get_widget_assert (fd.xml, "journal-disable");
+ fd.journal_default = get_widget_assert (fd.xml, "journal-default");
+ fd.journal_custom = get_widget_assert (fd.xml, "journal-custom");
+ fd.journal_custom_location = get_widget_assert (fd.xml, "journal-custom-location");
- fd.conf = psppire_conf_new ();
+ GtkLabel *default_journal_location = GTK_LABEL (get_widget_assert (fd.xml, "default_journal_location"));
+ gtk_label_set_text (default_journal_location, journal_get_default_file_name ());
- if (psppire_conf_get_boolean (fd.conf,
- "VariableLists", "display-labels", &disp_labels))
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
+
+ if (psppire_conf_get_boolean ("VariableLists", "display-labels", &disp_labels))
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.show_labels),
disp_labels);
!disp_labels);
}
- if (psppire_conf_get_boolean (fd.conf,
- "startup", "show-user-tips", &show_tips))
+ if (psppire_conf_get_boolean ("startup", "show-user-tips", &show_tips))
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.show_tips),
show_tips);
}
+ int location = -1;
+ psppire_conf_get_enum ("Journal", "location",
+ PSPP_TYPE_OPTIONS_JOURNAL_LOCATION, &location);
+ switch (location)
+ {
+ case PSPP_OPTIONS_JOURNAL_LOCATION_DISABLED:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.journal_disable), true);
+ break;
+
+ case PSPP_OPTIONS_JOURNAL_LOCATION_DEFAULT:
+ default:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.journal_default), true);
+ break;
+
+ case PSPP_OPTIONS_JOURNAL_LOCATION_CUSTOM:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.journal_custom), true);
+ break;
+ }
+
+ char *custom_location;
+ if (psppire_conf_get_string ("Journal", "custom-location", &custom_location))
+ {
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fd.journal_custom_location), custom_location);
+ g_free (custom_location);
+ }
int what = -1;
- psppire_conf_get_enum (fd.conf, "VariableLists", "sort-order",
+ psppire_conf_get_enum ("VariableLists", "sort-order",
PSPP_TYPE_OPTIONS_VAR_ORDER, &what);
switch (what)
{
gboolean status;
- if (psppire_conf_get_boolean (fd.conf, "OutputWindowAction", "maximize",
- &status))
+ if (psppire_conf_get_boolean ("OutputWindowAction", "maximize", &status))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.maximize), status);
}
{
gboolean status = true;
- psppire_conf_get_boolean (fd.conf, "OutputWindowAction", "alert", &status);
+ psppire_conf_get_boolean ("OutputWindowAction", "alert", &status);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.alert), status);
}
{
gboolean status;
- if (psppire_conf_get_boolean (fd.conf, "OutputWindowAction", "raise",
- &status))
+ if (psppire_conf_get_boolean ("OutputWindowAction", "raise", &status))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd.raise), status);
}
PsppOptionsVarOrder sort_order = -1;
gboolean sl = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd.show_labels));
- psppire_conf_set_boolean (fd.conf,
- "VariableLists", "display-labels", sl);
+ psppire_conf_set_boolean ("VariableLists", "display-labels", sl);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd.sort_labels)))
{
sort_order = PSPP_OPTIONS_VAR_ORDER_UNSORTED;
}
- psppire_conf_set_enum (fd.conf,
- "VariableLists", "sort-order",
+ psppire_conf_set_enum ("VariableLists", "sort-order",
PSPP_TYPE_OPTIONS_VAR_ORDER,
sort_order);
- psppire_conf_set_boolean (fd.conf, "OutputWindowAction", "maximize",
+ psppire_conf_set_boolean ("OutputWindowAction", "maximize",
gtk_toggle_button_get_active
(GTK_TOGGLE_BUTTON (fd.maximize)));
- psppire_conf_set_boolean (fd.conf, "OutputWindowAction", "raise",
+ psppire_conf_set_boolean ("OutputWindowAction", "raise",
gtk_toggle_button_get_active
(GTK_TOGGLE_BUTTON (fd.raise)));
- psppire_conf_set_boolean (fd.conf, "OutputWindowAction", "alert",
+ psppire_conf_set_boolean ("OutputWindowAction", "alert",
gtk_toggle_button_get_active
(GTK_TOGGLE_BUTTON (fd.alert)));
- psppire_conf_set_boolean (fd.conf, "startup", "show-user-tips",
+ psppire_conf_set_boolean ("startup", "show-user-tips",
gtk_toggle_button_get_active
(GTK_TOGGLE_BUTTON (fd.show_tips)));
+
+ PsppOptionsJournalLocation journal_location;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd.journal_disable)))
+ journal_location = PSPP_OPTIONS_JOURNAL_LOCATION_DISABLED;
+ else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd.journal_custom)))
+ journal_location = PSPP_OPTIONS_JOURNAL_LOCATION_CUSTOM;
+ else
+ journal_location = PSPP_OPTIONS_JOURNAL_LOCATION_DEFAULT;
+ psppire_conf_set_enum ("Journal", "location",
+ PSPP_TYPE_OPTIONS_JOURNAL_LOCATION,
+ journal_location);
+ gchar *custom_location = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fd.journal_custom_location));
+ if (custom_location)
+ {
+ psppire_conf_set_string ("Journal", "custom-location", custom_location);
+ g_free (custom_location);
+ }
+ psppire_conf_save ();
+
+ options_init ();
}
g_object_unref (fd.xml);
}
+
+void
+options_init (void)
+{
+ char *custom_location;
+ if (!psppire_conf_get_string ("Journal", "custom-location",
+ &custom_location))
+ custom_location = g_strdup (journal_get_default_file_name ());
+
+ int location = -1;
+ psppire_conf_get_enum ("Journal", "location",
+ PSPP_TYPE_OPTIONS_JOURNAL_LOCATION, &location);
+ switch (location) {
+ case PSPP_OPTIONS_JOURNAL_LOCATION_DISABLED:
+ journal_disable ();
+ break;
+
+ case PSPP_OPTIONS_JOURNAL_LOCATION_DEFAULT:
+ default:
+ journal_set_file_name (journal_get_default_file_name ());
+ journal_enable ();
+ break;
+
+ case PSPP_OPTIONS_JOURNAL_LOCATION_CUSTOM:
+ journal_set_file_name (custom_location);
+ journal_enable ();
+ break;
+ }
+ g_free (custom_location);
+}
GType pspp_options_var_order_get_type (void) G_GNUC_CONST;
#define PSPP_TYPE_OPTIONS_VAR_ORDER (pspp_options_var_order_get_type ())
+typedef enum
+ {
+ PSPP_OPTIONS_JOURNAL_LOCATION_DISABLED,
+ PSPP_OPTIONS_JOURNAL_LOCATION_DEFAULT,
+ PSPP_OPTIONS_JOURNAL_LOCATION_CUSTOM,
+ } PsppOptionsJournalLocation;
+
+GType pspp_options_journal_location_get_type (void) G_GNUC_CONST;
+#define PSPP_TYPE_OPTIONS_JOURNAL_LOCATION (pspp_options_journal_location_get_type ())
/* Pops up the Options dialog box */
void options_dialog (PsppireDataWindow *);
+void options_init (void);
+
#endif
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.38.2 -->
+<!-- Generated with glade 3.40.0 -->
<!-- PSPP - a program for statistical analysis. -->
<!-- Copyright (C) 2017, 2021 Free Software Foundation, Inc. -->
<!-- This program is free software: you can redistribute it and/or modify -->
<interface>
<requires lib="gtk+" version="3.22"/>
<requires lib="psppire" version="2053.63976"/>
+ <object class="GtkFileFilter" id="journal-file-filter">
+ <patterns>
+ <pattern>*.jnl</pattern>
+ </patterns>
+ </object>
<object class="PsppireDialog" id="options-dialog">
<property name="can-focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="margin-left">5</property>
<property name="margin-bottom">5</property>
<property name="spacing">5</property>
<child>
- <object class="GtkFrame" id="variable-lists-frame">
+ <object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="label-xalign">0</property>
- <property name="shadow-type">in</property>
+ <property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="orientation">vertical</property>
<child>
- <object class="GtkButtonBox">
+ <object class="GtkFrame" id="variable-lists-frame">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">5</property>
- <property name="layout-style">start</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">in</property>
<child>
- <object class="GtkRadioButton" id="radiobutton-labels">
- <property name="label" translatable="yes">Display _Labels</property>
+ <object class="GtkBox">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="draw-indicator">True</property>
- <property name="group">radiobutton-names</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <!-- n-columns=1 n-rows=6 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkRadioButton" id="radiobutton-labels">
+ <property name="label" translatable="yes">Display _Labels</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="radiobutton-names">
+ <property name="label" translatable="yes">Display _Names</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">radiobutton-labels</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="radiobutton-sort-by-label">
+ <property name="label" translatable="yes">Sort by L_abel</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="radiobutton-sort-by-name">
+ <property name="label" translatable="yes">Sort by Na_me</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">radiobutton-sort-by-label</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="radiobutton-unsorted">
+ <property name="label" translatable="yes">Do not S_ort</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">radiobutton-sort-by-label</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
</object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
</child>
- <child>
- <object class="GtkRadioButton" id="radiobutton-names">
- <property name="label" translatable="yes">Display _Names</property>
+ <child type="label">
+ <object class="GtkLabel">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Variable Lists</property>
</object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
</child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkSeparator">
+ <object class="GtkFrame">
<property name="visible">True</property>
<property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkButtonBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">start</property>
+ <property name="orientation">vertical</property>
+ <property name="layout-style">start</property>
+ <child>
+ <object class="GtkCheckButton" id="checkbutton-maximize">
+ <property name="label" translatable="yes">Ma_ximize</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkbutton-raise">
+ <property name="label" translatable="yes">_Raise</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkbutton-alert">
+ <property name="label" translatable="yes">Aler_t</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Output Window Action</property>
+ </object>
+ </child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="radiobutton-sort-by-label">
- <property name="label" translatable="yes">Sort by L_abel</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="draw-indicator">True</property>
- <property name="group">radiobutton-sort-by-name</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioButton" id="radiobutton-sort-by-name">
- <property name="label" translatable="yes">Sort by Na_me</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
- </object>
- <packing>
- <property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">4</property>
+ <property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkRadioButton" id="radiobutton-unsorted">
- <property name="label" translatable="yes">Do not S_ort</property>
+ <object class="GtkFrame">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="active">True</property>
- <property name="draw-indicator">True</property>
- <property name="group">radiobutton-sort-by-name</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <child>
+ <object class="GtkCheckButton" id="checkbutton-show-tips">
+ <property name="label" translatable="yes">Show Tips</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Startup Options</property>
+ </object>
+ </child>
</object>
<packing>
- <property name="expand">True</property>
+ <property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">5</property>
+ <property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">False</property>
+ <property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">1</property>
</packing>
</child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
</child>
- <child type="label">
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Variable Lists</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="PsppireButtonBox" id="options-buttonbox">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border-width">5</property>
- <property name="orientation">vertical</property>
- <property name="buttons">PSPPIRE_BUTTON_OK_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="pack-type">end</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="orientation">vertical</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="label-xalign">0</property>
<property name="shadow-type">in</property>
<child>
- <object class="GtkButtonBox">
+ <!-- n-columns=1 n-rows=3 -->
+ <object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="orientation">vertical</property>
- <property name="layout-style">spread</property>
<child>
- <object class="GtkCheckButton" id="checkbutton-maximize">
- <property name="label" translatable="yes">Ma_ximize</property>
+ <object class="GtkRadioButton" id="journal-disable">
+ <property name="label" translatable="yes">None</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
- <property name="use-underline">True</property>
+ <property name="hexpand">True</property>
+ <property name="active">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="checkbutton-raise">
- <property name="label" translatable="yes">_Raise</property>
+ <object class="GtkBox">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="draw-indicator">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkRadioButton" id="journal-default">
+ <property name="label" translatable="yes">Default</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">journal-disable</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="default_journal_location">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="ellipsize">end</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="checkbutton-alert">
- <property name="label" translatable="yes">Aler_t</property>
+ <object class="GtkBox">
<property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="use-underline">True</property>
- <property name="draw-indicator">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkRadioButton" id="journal-custom">
+ <property name="label" translatable="yes">Custom</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">journal-disable</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="journal-custom-location">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="do-overwrite-confirmation">True</property>
+ <property name="filter">journal-file-filter</property>
+ <property name="title" translatable="yes"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
</packing>
</child>
</object>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
- <property name="label" translatable="yes">Output Window Action</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label-xalign">0</property>
- <child>
- <object class="GtkAlignment">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="left-padding">12</property>
- <child>
- <object class="GtkCheckButton" id="checkbutton-show-tips">
- <property name="label" translatable="yes">Show Tips</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">False</property>
- <property name="draw-indicator">True</property>
- </object>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Startup Options</property>
+ <property name="label" translatable="yes">Journal File</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="PsppireButtonBox" id="options-buttonbox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border-width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="buttons">PSPPIRE_BUTTON_OK_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack-type">end</property>
+ <property name="position">1</property>
</packing>
</child>
</object>
static GObjectClass *parent_class = NULL;
-static void
-conf_read (PsppireConf *conf)
-{
- g_key_file_load_from_file (conf->keyfile,
- conf->filename,
- G_KEY_FILE_KEEP_COMMENTS,
- NULL);
-}
+static PsppireConf *psppire_conf_get (void);
-static gboolean
-flush_conf (PsppireConf *conf)
+void
+psppire_conf_save (void)
{
+ PsppireConf *conf = psppire_conf_get ();
+ if (!conf->dirty)
+ return;
+ conf->dirty = FALSE;
+
gsize length = 0;
- gchar *kf = g_key_file_to_data (conf->keyfile, &length, NULL);
- GError *err = NULL;
+ gchar *new_contents = g_key_file_to_data (conf->keyfile, &length, NULL);
- if (! g_file_set_contents (conf->filename, kf, length, &err))
+ GError *err = NULL;
+ if (g_strcmp0 (new_contents, conf->contents)
+ && ! g_file_set_contents (conf->filename, new_contents, length, &err))
{
g_warning ("Cannot open %s for writing: %s", conf->filename, err->message);
g_error_free (err);
}
- g_free (kf);
- conf->idle = 0;
- return FALSE;
+ g_free (conf->contents);
+ conf->contents = new_contents;
}
static void
-conf_write (PsppireConf *conf)
+conf_dirty (PsppireConf *conf)
{
- if (conf->idle == 0)
- conf->idle = g_idle_add_full (G_PRIORITY_LOW,
- (GSourceFunc) flush_conf, conf, NULL);
+ conf->dirty = TRUE;
}
-
static void
psppire_conf_dispose (GObject *object)
{
+ G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
conf->filename = g_strdup_printf ("%s/%s", dirname, "psppirerc");
conf->keyfile = g_key_file_new ();
+ g_key_file_load_from_file (conf->keyfile,
+ conf->filename,
+ G_KEY_FILE_KEEP_COMMENTS,
+ NULL);
- conf->idle = 0;
+ conf->dirty = FALSE;
}
-
-PsppireConf *
-psppire_conf_new (void)
+/* Gets the singleton PsppireConf object. The caller should not unref the
+ object. The caller should call psppire_conf_save() if it makes changes. */
+static PsppireConf *
+psppire_conf_get (void)
{
- return g_object_new (psppire_conf_get_type (), NULL);
+ return (the_instance
+ ? the_instance
+ : g_object_new (psppire_conf_get_type (), NULL));
}
-
-
gboolean
-psppire_conf_get_int (PsppireConf *conf, const gchar *base,
- const gchar *name, gint *value)
+psppire_conf_get_int (const gchar *base, const gchar *name, gint *value)
{
+ PsppireConf *conf = psppire_conf_get ();
+
gboolean ok;
GError *err = NULL;
- conf_read (conf);
*value = g_key_file_get_integer (conf->keyfile,
base,
name, &err);
}
gboolean
-psppire_conf_get_boolean (PsppireConf *conf, const gchar *base,
- const gchar *name, gboolean *value)
+psppire_conf_get_boolean (const gchar *base, const gchar *name, gboolean *value)
{
+ PsppireConf *conf = psppire_conf_get ();
+
gboolean ok;
gboolean b;
GError *err = NULL;
- conf_read (conf);
b = g_key_file_get_boolean (conf->keyfile,
base,
name, &err);
gboolean
-psppire_conf_get_string (PsppireConf *conf, const gchar *base,
- const gchar *name, gchar **value)
+psppire_conf_get_string (const gchar *base, const gchar *name, gchar **value)
{
+ PsppireConf *conf = psppire_conf_get ();
gboolean ok;
gchar *b;
GError *err = NULL;
- conf_read (conf);
b = g_key_file_get_string (conf->keyfile,
base,
name, &err);
gboolean
-psppire_conf_get_variant (PsppireConf *conf, const gchar *base,
- const gchar *name, GVariant **v)
+psppire_conf_get_variant (const gchar *base, const gchar *name, GVariant **v)
{
+ PsppireConf *conf = psppire_conf_get ();
gboolean ok;
gchar *b;
GError *err = NULL;
- conf_read (conf);
b = g_key_file_get_string (conf->keyfile,
base,
name, &err);
}
gboolean
-psppire_conf_get_enum (PsppireConf *conf, const gchar *base,
- const gchar *name,
- GType t,
- int *v)
+psppire_conf_get_enum (const gchar *base, const gchar *name, GType t, int *v)
{
+ PsppireConf *conf = psppire_conf_get ();
gboolean ok;
gchar *b;
GError *err = NULL;
- conf_read (conf);
b = g_key_file_get_string (conf->keyfile,
base,
name, &err);
}
void
-psppire_conf_set_int (PsppireConf *conf,
- const gchar *base, const gchar *name,
+psppire_conf_set_int (const gchar *base, const gchar *name,
gint value)
{
+ PsppireConf *conf = psppire_conf_get ();
g_key_file_set_integer (conf->keyfile, base, name, value);
- conf_write (conf);
+ conf_dirty (conf);
}
void
-psppire_conf_set_boolean (PsppireConf *conf,
- const gchar *base, const gchar *name,
+psppire_conf_set_boolean (const gchar *base, const gchar *name,
gboolean value)
{
+ PsppireConf *conf = psppire_conf_get ();
g_key_file_set_boolean (conf->keyfile, base, name, value);
- conf_write (conf);
+ conf_dirty (conf);
}
void
-psppire_conf_set_string (PsppireConf *conf,
- const gchar *base, const gchar *name,
+psppire_conf_set_string (const gchar *base, const gchar *name,
const gchar *value)
{
+ PsppireConf *conf = psppire_conf_get ();
g_key_file_set_string (conf->keyfile, base, name, value);
- conf_write (conf);
+ conf_dirty (conf);
}
void
-psppire_conf_set_variant (PsppireConf *conf,
- const gchar *base, const gchar *name,
- GVariant *value)
+psppire_conf_set_variant (const gchar *base, const gchar *name, GVariant *value)
{
+ PsppireConf *conf = psppire_conf_get ();
gchar *v = g_variant_print (value, FALSE);
g_key_file_set_string (conf->keyfile, base, name, v);
- conf_write (conf);
+ conf_dirty (conf);
g_free (v);
}
void
-psppire_conf_set_enum (PsppireConf *conf,
- const gchar *base, const gchar *name,
+psppire_conf_set_enum (const gchar *base, const gchar *name,
GType enum_type,
int value)
{
+ PsppireConf *conf = psppire_conf_get ();
GEnumClass *ec = g_type_class_ref (enum_type);
GEnumValue *ev = g_enum_get_value (ec, value);
g_type_class_unref (ec);
- conf_write (conf);
+ conf_dirty (conf);
}
/*
- A convenience function to set the geometry of a
+ A convenience function to get the geometry of a
window from from a saved config
*/
void
-psppire_conf_set_window_geometry (PsppireConf *conf,
- const gchar *base,
- GtkWindow *window)
+psppire_conf_get_window_geometry (const gchar *base, GtkWindow *window)
{
gint height, width;
gint x, y;
gboolean maximize;
- if (psppire_conf_get_int (conf, base, "height", &height)
+ if (psppire_conf_get_int (base, "height", &height)
&&
- psppire_conf_get_int (conf, base, "width", &width))
+ psppire_conf_get_int (base, "width", &width))
{
gtk_window_set_default_size (window, width, height);
}
- if (psppire_conf_get_int (conf, base, "x", &x)
+ if (psppire_conf_get_int (base, "x", &x)
&&
- psppire_conf_get_int (conf, base, "y", &y))
+ psppire_conf_get_int (base, "y", &y))
{
gtk_window_move (window, x, y);
}
- if (psppire_conf_get_boolean (conf, base, "maximize", &maximize))
+ if (psppire_conf_get_boolean (base, "maximize", &maximize))
{
if (maximize)
gtk_window_maximize (window);
"configure-event" and "window-state-event" signal handlers
*/
void
-psppire_conf_save_window_geometry (PsppireConf *conf,
- const gchar *base,
- GtkWindow *gtk_window)
+psppire_conf_set_window_geometry (const gchar *base, GtkWindow *gtk_window)
{
gboolean maximized;
GdkWindow *w;
return;
maximized = (gdk_window_get_state (w) & GDK_WINDOW_STATE_MAXIMIZED) != 0;
- psppire_conf_set_boolean (conf, base, "maximize", maximized);
+ psppire_conf_set_boolean (base, "maximize", maximized);
if (!maximized)
{
gdk_window_get_position (w, &x, &y);
- psppire_conf_set_int (conf, base, "height", height);
- psppire_conf_set_int (conf, base, "width", width);
- psppire_conf_set_int (conf, base, "x", x);
- psppire_conf_set_int (conf, base, "y", y);
+ psppire_conf_set_int (base, "height", height);
+ psppire_conf_set_int (base, "width", width);
+ psppire_conf_set_int (base, "x", x);
+ psppire_conf_set_int (base, "y", y);
}
}
GKeyFile *keyfile;
gchar *filename;
- guint idle;
+ gchar *contents;
+ gboolean dirty;
};
GType psppire_conf_get_type (void) G_GNUC_CONST;
-PsppireConf * psppire_conf_new (void);
+void psppire_conf_save (void);
-gboolean psppire_conf_get_int (PsppireConf *,
- const gchar *, const gchar *, int *);
+gboolean psppire_conf_get_int (const gchar *, const gchar *, int *);
-gboolean psppire_conf_get_string (PsppireConf *,
- const gchar *, const gchar *, gchar **);
+gboolean psppire_conf_get_string (const gchar *, const gchar *, gchar **);
-gboolean psppire_conf_get_boolean (PsppireConf *,
- const gchar *, const gchar *, gboolean *);
+gboolean psppire_conf_get_boolean (const gchar *, const gchar *, gboolean *);
-gboolean psppire_conf_get_variant (PsppireConf *,
- const gchar *, const gchar *, GVariant **);
+gboolean psppire_conf_get_variant (const gchar *, const gchar *, GVariant **);
-gboolean psppire_conf_get_enum (PsppireConf *conf, const gchar *base,
- const gchar *name,
- GType t,
- int *v);
+gboolean psppire_conf_get_enum (const gchar *base, const gchar *name,
+ GType t, int *v);
-void psppire_conf_set_int (PsppireConf *conf,
- const gchar *base, const gchar *name,
+void psppire_conf_set_int (const gchar *base, const gchar *name,
gint value);
-void psppire_conf_set_boolean (PsppireConf *conf,
- const gchar *base, const gchar *name,
+void psppire_conf_set_boolean (const gchar *base, const gchar *name,
gboolean value);
-void psppire_conf_set_string (PsppireConf *conf,
- const gchar *base, const gchar *name,
+void psppire_conf_set_string (const gchar *base, const gchar *name,
const gchar *value);
-void psppire_conf_set_variant (PsppireConf *conf,
- const gchar *base, const gchar *name,
+void psppire_conf_set_variant (const gchar *base, const gchar *name,
GVariant *value);
-void psppire_conf_set_enum (PsppireConf *conf,
- const gchar *base, const gchar *name,
+void psppire_conf_set_enum (const gchar *base, const gchar *name,
GType enum_type,
int value);
-void psppire_conf_set_window_geometry (PsppireConf *conf,
- const gchar *base,
- GtkWindow *window);
-
-void psppire_conf_save_window_geometry (PsppireConf *,
- const gchar *,
- GtkWindow *);
+void psppire_conf_get_window_geometry (const gchar *base, GtkWindow *window);
+void psppire_conf_set_window_geometry (const gchar *, GtkWindow *);
G_END_DECLS
g_object_set (de, "can-focus", FALSE, NULL);
- if (psppire_conf_get_string (psppire_conf_new (),
- "Data Editor", "font",
- &fontname))
+ if (psppire_conf_get_string ("Data Editor", "font", &fontname))
{
de->font = pango_font_description_from_string (fontname);
g_free (fontname);
de->font = pango_font_description_copy (font_desc);
font_name = pango_font_description_to_string (de->font);
- psppire_conf_set_string (psppire_conf_new (),
- "Data Editor", "font",
- font_name);
+ psppire_conf_set_string ("Data Editor", "font", font_name);
+ psppire_conf_save ();
+
g_free (font_name);
}
gpointer user_data)
{
int what = -1;
- psppire_conf_get_enum (psppire_conf_new (), "VariableLists", "sort-order",
+ psppire_conf_get_enum ("VariableLists", "sort-order",
PSPP_TYPE_OPTIONS_VAR_ORDER, &what);
switch (what)
if (gtk_check_menu_item_get_inconsistent (GTK_CHECK_MENU_ITEM
(dv->override_button)))
{
- psppire_conf_get_boolean (psppire_conf_new (),
- "VariableLists", "display-labels", &disp_labels);
+ psppire_conf_get_boolean ("VariableLists", "display-labels", &disp_labels);
}
else
{
PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
gboolean global_setting = TRUE;
- psppire_conf_get_boolean (psppire_conf_new (),
- "VariableLists", "display-labels", &global_setting);
+ psppire_conf_get_boolean ("VariableLists", "display-labels", &global_setting);
if (gtk_check_menu_item_get_inconsistent (checkbox))
gtk_check_menu_item_set_active (checkbox, !global_setting);
gtk_widget_show_all (GTK_WIDGET (pod->window));
}
- PsppireConf *conf = psppire_conf_new ();
{
gboolean status = true;
- psppire_conf_get_boolean (conf, "OutputWindowAction", "alert",
- &status);
+ psppire_conf_get_boolean ("OutputWindowAction", "alert", &status);
gtk_window_set_urgency_hint (GTK_WINDOW (pod->window), status);
}
{
gboolean status ;
- if (psppire_conf_get_boolean (conf, "OutputWindowAction", "maximize",
- &status) && status)
+ if (psppire_conf_get_boolean ("OutputWindowAction", "maximize", &status)
+ && status)
gtk_window_maximize (GTK_WINDOW (pod->window));
}
{
gboolean status ;
- if (psppire_conf_get_boolean (conf, "OutputWindowAction", "raise",
- &status) && status)
+ if (psppire_conf_get_boolean ("OutputWindowAction", "raise", &status)
+ && status)
gtk_window_present (GTK_WINDOW (pod->window));
}
}
static void
realize (GtkWidget *wb)
{
- PsppireConf *conf = psppire_conf_new ();
-
- psppire_conf_set_window_geometry (conf, get_window_id (wb), GTK_WINDOW (wb));
+ psppire_conf_get_window_geometry (get_window_id (wb), GTK_WINDOW (wb));
if (GTK_WIDGET_CLASS (psppire_window_base_parent_class)->realize)
GTK_WIDGET_CLASS (psppire_window_base_parent_class)->realize (wb) ;
{
if (gtk_widget_get_mapped (wb))
{
- PsppireConf *conf = psppire_conf_new ();
-
- psppire_conf_save_window_geometry (conf, get_window_id (wb), GTK_WINDOW (wb));
+ psppire_conf_set_window_geometry (get_window_id (wb), GTK_WINDOW (wb));
+ psppire_conf_save ();
}
if (GTK_WIDGET_CLASS (psppire_window_base_parent_class)->configure_event)
#include "ui/gui/dict-display.h"
#include "ui/gui/executor.h"
+#include "ui/gui/options-dialog.h"
#include "ui/gui/psppire-data-store.h"
#include "ui/gui/psppire-data-window.h"
#include "ui/gui/psppire-dict.h"
}
break;
case 9:
- journal_init ();
+ options_init ();
break;
case 10:
textdomain (PACKAGE);
"conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
"warranty.\" for details.\n", stdout);
puts (announced_version);
- journal_init ();
+ journal_enable ();
}
static struct terminal_reader *
tests/output/ascii.at \
tests/output/charts.at \
tests/output/html.at \
+ tests/output/journal.at \
tests/output/output.at \
tests/output/paper-size.at \
tests/output/pivot-table.at \
--- /dev/null
+AT_BANNER([journal])
+
+AT_SETUP([enable and disable journal])
+AT_DATA([journal.sps], [dnl
+set journal='pspp.jnl' on.
+data list notable /x y 1-2.
+begin data.
+12
+end data.
+set journal=off.
+
+print.
+
+execute.
+set journal=on.
+])
+
+AT_CHECK([pspp journal.sps])
+AT_CHECK([sed 's/ at.*/./' pspp.jnl], [0], [dnl
+* New session.
+set journal='pspp.jnl' on.
+data list notable /x y 1-2.
+begin data.
+12
+end data.
+
+* New session.
+set journal=on.
+])
+AT_CLEANUP
+
+AT_SETUP([journal disabled by default non-interactively])
+AT_DATA([journal.sps], [dnl
+data list notable /x y 1-2.
+])
+AT_CHECK([XDG_STATE_HOME=$PWD pspp journal.sps])
+AT_CHECK([test ! -e pspp/pspp.jnl])
+AT_CLEANUP
+
+AT_SETUP([journal enabled by default interactively])
+AT_SKIP_IF([test "$SQUISH_PTY" = no])
+AT_CHECK([echo 'data list notable /x y 1-2.
+finish.' | XDG_STATE_HOME=$PWD $SQUISH_PTY pspp], [0], [ignore])
+AT_CHECK([sed 's/New session at .*/New session./' pspp/pspp.jnl], [0], [dnl
+* New session.
+data list notable /x y 1-2.
+finish.
+])
+AT_CLEANUP
-dnl PSPP - a program for statistical analysis. -*- autotest -*-
+nl PSPP - a program for statistical analysis. -*- autotest -*-
dnl Copyright (C) 2017 Free Software Foundation, Inc.
dnl
dnl This program is free software: you can redistribute it and/or modify
PATH=$wrapper_dir:$PATH
fi
])
+
+m4_divert_text([PREPARE_TESTS], [dnl
+dnl ptys are pretty system-dependent and it's hard to test them
+dnl everywhere. For example, on Mac OS the SHOW N and FINISH command
+dnl text doesn't appear in the output. So we'll just skip them
+dnl other than on the OS we know best.
+AS_CASE([$host],
+ [*-linux*],
+ [dnl Make sure that squish-pty works.
+ SQUISH_PTY="$PYTHON3 $abs_top_srcdir/tests/ui/terminal/squish-pty.py"
+ if $SQUISH_PTY true </dev/null >/dev/null 2>/dev/null; then
+ :
+ else
+ SQUISH_PTY=no
+ fi],
+ [SQUISH_PTY=no])
+])
dnl next command was supplied. This checks for regression against
dnl that bug.
AT_SETUP([interactive output appears immediately])
-dnl ptys are pretty system-dependent and it's hard to test them
-dnl everywhere. For example, on Mac OS the SHOW N and FINISH command
-dnl text doesn't appear in the output. So we'll just skip them
-dnl other than on the OS we know best.
-AT_CHECK([case $host in #(
- *-linux*) ;; #(
- *) exit 77
-esac])
-dnl We have to use squish-pty to make PSPP think that we're running
-dnl interactively. First make sure that squish-pty works at all.
-SQUISH_PTY="$PYTHON3 $top_srcdir/tests/ui/terminal/squish-pty.py"
-AT_CHECK([$SQUISH_PTY true </dev/null >/dev/null 2>/dev/null || exit 77])
-dnl Then do the real test. The crucial thing to notice here is
-dnl that the SHOW output must appear before the prompt for FINISH.
+AT_SKIP_IF([test "$SQUISH_PTY" = no])
+dnl The crucial thing to notice below is that the SHOW output
+dnl must appear before the prompt for FINISH.
AT_CHECK([echo 'SHOW N.
FINISH.' | $SQUISH_PTY pspp], [0], [stdout])
AT_CHECK([sed -n 's/\r$//