Use a more reliable way of setting the initial state of PsppireDialogAction objects.
[pspp] / src / ui / gui / psppire-dialog-action.c
index e3ee25be75fc7f53c021314573e29a6ec42ed537..9eeed98d8dd608fe8b101159017cd5a1ea269ad6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2012  Free Software Foundation
+   Copyright (C) 2012, 2016  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
 #include "executor.h"
 #include "helper.h"
 #include "psppire-data-window.h"
+#include "builder-wrapper.h"
 
 static void psppire_dialog_action_init            (PsppireDialogAction      *act);
 static void psppire_dialog_action_class_init      (PsppireDialogActionClass *class);
 
-G_DEFINE_ABSTRACT_TYPE (PsppireDialogAction, psppire_dialog_action, GTK_TYPE_ACTION);
+static GObjectClass * parent_class = NULL;
+
+
+static const gchar *
+__get_name (GAction *act)
+{
+  return G_OBJECT_TYPE_NAME (act);
+}
+
+static const GVariantType *
+__get_state_type (GAction *act)
+{
+  return NULL;
+}
+
+
+static GVariant *
+__get_state (GAction *act)
+{
+  return NULL;
+}
+
+
+static const GVariantType *
+__get_parameter_type (GAction *act)
+{
+  return PSPPIRE_DIALOG_ACTION (act)->parameter_type;
+}
+
+static gboolean
+__get_enabled (GAction *act)
+{
+  return TRUE;
+}
+
+static void psppire_dialog_action_activate (PsppireDialogAction *act, GVariant *parameter);
+
+void
+psppire_dialog_action_activate_null (PsppireDialogAction *act)
+{
+  psppire_dialog_action_activate (act, NULL);
+}
+
+
+static void
+__activate (GAction *action, GVariant *parameter)
+{
+  psppire_dialog_action_activate (PSPPIRE_DIALOG_ACTION (action), parameter);
+}
+
+
+static void
+action_model_init (GActionInterface *iface)
+{
+  iface->get_name = __get_name;
+  iface->get_state_type = __get_state_type;
+  iface->get_state = __get_state;
+  iface->get_parameter_type = __get_parameter_type;
+  iface->get_enabled = __get_enabled;
+  iface->activate = __activate;
+}
+
+
+GType
+psppire_dialog_action_get_type (void)
+{
+  static GType de_type = 0;
+
+  if (!de_type)
+    {
+      static const GTypeInfo de_info =
+      {
+       sizeof (PsppireDialogActionClass),
+       NULL, /* base_init */
+        NULL, /* base_finalize */
+       (GClassInitFunc) psppire_dialog_action_class_init,
+        NULL, /* class_finalize */
+       NULL, /* class_data */
+        sizeof (PsppireDialogAction),
+       0,
+       (GInstanceInitFunc) psppire_dialog_action_init,
+      };
+
+
+      static const GInterfaceInfo ga_info = {
+       (GInterfaceInitFunc) action_model_init,
+       NULL,
+       NULL
+      };
+
+
+      de_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDialogAction",
+                                       &de_info, G_TYPE_FLAG_ABSTRACT);
+
+      g_type_add_interface_static (de_type, G_TYPE_ACTION, &ga_info);
+    }
+
+  return de_type;
+}
+
 
 /* Properties */
 enum
 {
   PROP_0,
-  PROP_MANAGER,
   PROP_TOPLEVEL,
+  PROP_NAME,
+  PROP_ENABLED,
+  PROP_STATE,
+  PROP_STATE_TYPE,
+  PROP_PARAMETER_TYPE
 };
 
 static void
@@ -46,11 +150,10 @@ psppire_dialog_action_set_property (GObject         *object,
 
   switch (prop_id)
     {
-    case PROP_MANAGER:
+    case PROP_TOPLEVEL:
       {
-
        GObject *p = g_value_get_object (value);
-       act->uim = GTK_UI_MANAGER (p);
+       act->toplevel = GTK_WIDGET (p);
       }
       break;
     default:
@@ -70,10 +173,7 @@ psppire_dialog_action_get_property (GObject    *object,
 
   switch (prop_id)
     {
-    case PROP_MANAGER:
-      g_value_take_object (value, dialog_action->uim);
-      break;
-  case PROP_TOPLEVEL:
+    case PROP_TOPLEVEL:
       g_value_take_object (value, dialog_action->toplevel);
       break;
     default:
@@ -83,31 +183,94 @@ psppire_dialog_action_get_property (GObject    *object,
 }
 
 
+
 static void
-psppire_dialog_action_activate (PsppireDialogAction *act)
+on_destroy_dataset (GObject *w)
+{
+  GHashTable *t = g_object_get_data (w, "thing-table");
+  GSList *dl = g_object_get_data (w, "widget-list");
+
+  g_slist_free_full (dl, (GDestroyNotify) gtk_widget_destroy);
+  g_hash_table_unref (t);
+}
+
+/* Each toplevel widget - that is the data window, which generally has a 1-1 association
+   with a dataset - has an associated GHashTable.
+
+   This GHashTable is keyed by the address of a PsppireDialogAction, and its values
+   are user determined pointers (typically a GtkBuilder*).
+
+   This is useful for storing the state of dialogs so they can persist between invocations.
+*/
+GHashTable *
+psppire_dialog_action_get_hash_table (PsppireDialogAction *act)
+{
+  GHashTable *t = g_object_get_data (G_OBJECT (act->toplevel), "thing-table");
+  if (t == NULL)
+    {
+      t = g_hash_table_new_full (g_direct_hash, g_direct_equal, 0, g_object_unref);
+      g_object_set_data (G_OBJECT (act->toplevel), "thing-table", t);
+      g_object_set_data (G_OBJECT (act->toplevel), "widget-list", NULL);
+      g_signal_connect (act->toplevel, "destroy", G_CALLBACK (on_destroy_dataset), NULL);
+    }
+
+  return t;
+}
+
+GtkBuilder *
+psppire_dialog_action_get_xml (PsppireDialogAction *da)
+{
+  GHashTable *thing = psppire_dialog_action_get_hash_table (da);
+  GtkBuilder *xml = g_hash_table_lookup (thing, da);
+  return xml;
+}
+
+
+static void
+psppire_dialog_action_activate (PsppireDialogAction *act, GVariant *parameter)
 {
   gint response;
 
-  PsppireVarStore *vs;
   PsppireDialogActionClass *class = PSPPIRE_DIALOG_ACTION_GET_CLASS (act);
 
-  GSList *sl = gtk_ui_manager_get_toplevels (act->uim, GTK_UI_MANAGER_MENUBAR | GTK_UI_MANAGER_TOOLBAR);
-  g_return_if_fail (sl);
+  act->dict = PSPPIRE_DATA_WINDOW(act->toplevel)->dict;
 
-  act->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (sl->data));
-  g_slist_free (sl);
+  GSList *wl = g_object_get_data (G_OBJECT (act->toplevel), "widget-list");
+  wl = g_slist_prepend (wl, act->dialog);
+  g_object_set_data (G_OBJECT (act->toplevel), "widget-list", wl);
 
-  vs = PSPPIRE_DATA_WINDOW(act->toplevel)->var_store;
-  
-  g_object_get (vs, "dictionary", &act->dict, NULL);
-  
-  g_object_set (act->source, "model", act->dict, NULL);
+  if (class->initial_activate)
+    {
+      GHashTable *thing = psppire_dialog_action_get_hash_table (act);
+      GtkBuilder *xml = g_hash_table_lookup (thing, act);
+      if (xml == NULL)
+       {
+         xml = class->initial_activate (act, parameter);
+         g_hash_table_insert (thing, act, xml);
+       }
+    }
+
+  if (class->activate)
+    {
+      GHashTable *thing = psppire_dialog_action_get_hash_table (act);
+      GtkBuilder *xml = g_hash_table_lookup (thing, act);
+      if (xml != NULL)
+       class->activate (act, parameter);
+    }
   
-  gtk_window_set_transient_for (GTK_WINDOW (act->dialog), GTK_WINDOW (act->toplevel));
+  gtk_window_set_transient_for (GTK_WINDOW (act->dialog),
+                               GTK_WINDOW (act->toplevel));
 
+  if (act->source)
+    {
+      g_object_set (act->source, "model", act->dict, NULL);
+      gtk_widget_grab_focus (act->source);
+    }
 
-  if (GTK_ACTION_CLASS (psppire_dialog_action_parent_class)->activate)
-    GTK_ACTION_CLASS (psppire_dialog_action_parent_class)->activate ( GTK_ACTION (act));
+  if (!act->activated)
+    psppire_dialog_reload (PSPPIRE_DIALOG (act->dialog));
+
+  act->activated = TRUE;
 
   response = psppire_dialog_run (PSPPIRE_DIALOG (act->dialog));
 
@@ -133,34 +296,29 @@ psppire_dialog_action_class_init (PsppireDialogActionClass *class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (class);
 
-  GParamSpec *manager_spec =
-    g_param_spec_object ("manager",
-                        "Manager",
-                        "The GtkUIManager which created this object",
-                        GTK_TYPE_UI_MANAGER,
-                        G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
+  parent_class = g_type_class_peek_parent (class);
 
   GParamSpec *toplevel_spec =
     g_param_spec_object ("top-level",
                         "Top Level",
                         "The top level widget to which this dialog action belongs",
                         GTK_TYPE_WINDOW,
-                        G_PARAM_READABLE);
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
 
   object_class->set_property = psppire_dialog_action_set_property;
   object_class->get_property = psppire_dialog_action_get_property;
 
   class->generate_syntax = NULL;
 
-  class->activate = psppire_dialog_action_activate;
-
-  g_object_class_install_property (object_class,
-                                   PROP_MANAGER,
-                                   manager_spec);
-
   g_object_class_install_property (object_class,
                                    PROP_TOPLEVEL,
                                    toplevel_spec);
+
+  g_object_class_override_property (object_class, PROP_NAME, "name");
+  g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
+  g_object_class_override_property (object_class, PROP_STATE, "state");
+  g_object_class_override_property (object_class, PROP_STATE_TYPE, "state-type");
+  g_object_class_override_property (object_class, PROP_PARAMETER_TYPE, "parameter-type");
 }
 
 
@@ -169,20 +327,21 @@ psppire_dialog_action_init (PsppireDialogAction *act)
 {
   act->toplevel = NULL;
   act->dict = NULL;
+  act->activated = FALSE;
+  act->parameter_type = NULL;
 }
 
-
 void
-psppire_dialog_action_set_valid_predicate (PsppireDialogAction *act, 
+psppire_dialog_action_set_valid_predicate (PsppireDialogAction *act,
                                           ContentsAreValid dialog_state_valid)
 {
-  psppire_dialog_set_valid_predicate (act->dialog, dialog_state_valid, act);
+  psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (act->dialog),
+                                      dialog_state_valid, act);
 }
 
 void
-psppire_dialog_action_set_refresh (PsppireDialogAction *pda, 
+psppire_dialog_action_set_refresh (PsppireDialogAction *pda,
                                   PsppireDialogActionRefresh refresh)
 {
   g_signal_connect_swapped (pda->dialog, "refresh", G_CALLBACK (refresh),  pda);
 }
-