From c1f362b8cee3c577ba5b0b1d9ef1711806bb1def Mon Sep 17 00:00:00 2001 From: John Darrington Date: Tue, 20 Jun 2023 18:42:24 +0200 Subject: [PATCH] Added a new dialog to generate CTABLES This dialog provides just a small subset of what ought to be available. In particular, nested tables cannot be specified and only the default summary functions work. Other caveats are also present, but it generally provides a start and proof of concept. --- src/ui/gui/automake.mk | 3 + src/ui/gui/ctables.ui | 149 ++++++ src/ui/gui/data-editor.ui | 4 + src/ui/gui/dummy.c | 3 + src/ui/gui/psppire-data-window.c | 6 +- src/ui/gui/psppire-dialog-action-ctables.c | 585 +++++++++++++++++++++ src/ui/gui/psppire-dialog-action-ctables.h | 85 +++ src/ui/gui/widgets.c | 2 + 8 files changed, 835 insertions(+), 2 deletions(-) create mode 100644 src/ui/gui/ctables.ui create mode 100644 src/ui/gui/psppire-dialog-action-ctables.c create mode 100644 src/ui/gui/psppire-dialog-action-ctables.h diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index e01090777c..6b8c4bf81d 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -27,6 +27,7 @@ UI_FILES = \ src/ui/gui/comments.ui \ src/ui/gui/crosstabs.ui \ src/ui/gui/chi-square.ui \ + src/ui/gui/ctables.ui \ src/ui/gui/descriptives.ui \ src/ui/gui/entry-dialog.ui \ src/ui/gui/examine.ui \ @@ -317,6 +318,8 @@ src_ui_gui_libwidgets_essential_la_SOURCES = \ src/ui/gui/psppire-dialog-action-sort.h \ src/ui/gui/psppire-dialog-action-split.c \ src/ui/gui/psppire-dialog-action-split.h \ + src/ui/gui/psppire-dialog-action-ctables.c \ + src/ui/gui/psppire-dialog-action-ctables.h \ src/ui/gui/psppire-dialog-action-tt1s.c \ src/ui/gui/psppire-dialog-action-tt1s.h \ src/ui/gui/psppire-dialog-action-two-sample.c \ diff --git a/src/ui/gui/ctables.ui b/src/ui/gui/ctables.ui new file mode 100644 index 0000000000..14dc60256d --- /dev/null +++ b/src/ui/gui/ctables.ui @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + False + Custiom Tables + True + CTABLES + + + True + False + vertical + + + True + True + 200 + True + True + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + never + etched-in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + False + single + + + + + + + + False + True + + + + + + True + False + True + True + True + + + 30 + 30 + True + False + 0 + + + 0 + 0 + + + + + True + False + True + + + 1 + 0 + + + + + True + False + True + + + 0 + 1 + + + + + fred + True + False + True + + + 1 + 1 + + + + + True + True + + + + + True + True + 0 + + + + + True + False + 5 + PSPPIRE_BUTTON_OK_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK | PSPPIRE_BUTTON_RESET_MASK | PSPPIRE_BUTTON_PASTE_MASK + PSPPIRE_BUTTON_OK_MASK + + + False + True + end + 2 + + + + + + diff --git a/src/ui/gui/data-editor.ui b/src/ui/gui/data-editor.ui index 214af79738..b7f2456af9 100644 --- a/src/ui/gui/data-editor.ui +++ b/src/ui/gui/data-editor.ui @@ -275,6 +275,10 @@ win.PsppireDialogActionCrosstabs + + _Tables... + win.PsppireDialogActionCtables + Compare _Means diff --git a/src/ui/gui/dummy.c b/src/ui/gui/dummy.c index 50e0d6529a..93090636b6 100644 --- a/src/ui/gui/dummy.c +++ b/src/ui/gui/dummy.c @@ -35,6 +35,7 @@ #include "t-test-options.h" #include "src/language/commands/chart-category.h" #include "src/language/commands/aggregate.h" +#include "libpspp/llx.h" const GEnumValue align[1]; const GEnumValue measure[1]; @@ -75,3 +76,5 @@ psppire_data_store_string_to_value (GtkTreeModel *model, gint col, gint row, assert (0); return FALSE; } + +const struct llx_manager llx_malloc_mgr = {NULL, NULL, NULL}; diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c index ecb9f121af..d581f57ebb 100644 --- a/src/ui/gui/psppire-data-window.c +++ b/src/ui/gui/psppire-data-window.c @@ -1,6 +1,6 @@ /* PSPPIRE - a graphical user interface for PSPP. Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, - 2016, 2017 Free Software Foundation + 2016, 2017, 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 @@ -87,6 +87,7 @@ #include "psppire-dialog-action-select.h" #include "psppire-dialog-action-sort.h" #include "psppire-dialog-action-split.h" +#include "psppire-dialog-action-ctables.h" #include "psppire-dialog-action-tt1s.h" #include "psppire-dialog-action-two-sample.h" #include "psppire-dialog-action-univariate.h" @@ -1049,7 +1050,7 @@ on_cut (PsppireDataWindow *dw) sel.start_y = sel.end_y; sel.end_y = tmp; } - + GtkClipboard *clip = gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)), GDK_SELECTION_CLIPBOARD); @@ -1538,6 +1539,7 @@ psppire_data_window_finish_init (PsppireDataWindow *de, connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BINOMIAL, de); connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RUNS, de); connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_1SKS, de); + connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CTABLES, de); connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TWO_SAMPLE, de); connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_K_RELATED, de); connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_K_INDEPENDENT, de); diff --git a/src/ui/gui/psppire-dialog-action-ctables.c b/src/ui/gui/psppire-dialog-action-ctables.c new file mode 100644 index 0000000000..f59c2b11a6 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-ctables.c @@ -0,0 +1,585 @@ +/* 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 "psppire-dialog-action-ctables.h" +#include "psppire-value-entry.h" + +#include "dialog-common.h" +#include +#include "psppire-var-view.h" + +#include "psppire-dialog.h" +#include "builder-wrapper.h" + +#include "psppire-dict.h" +#include "libpspp/str.h" +#include "libpspp/llx.h" + +#include "psppire-dictview.h" + +#include "output/cairo-fsm.h" +#include "output/output-item.h" +#include "output/pivot-table.h" +#include "data/value-labels.h" + +#include +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +static struct xr_fsm_style *get_xr_fsm_style (GtkWidget *w); +static void psppire_dialog_action_ctables_class_init + (PsppireDialogActionCtablesClass *class); + +G_DEFINE_TYPE (PsppireDialogActionCtables, psppire_dialog_action_ctables, + PSPPIRE_TYPE_DIALOG_ACTION); + + +/* Create the basis of a table. This table contasins just two dimensions + and nothing else. */ +static struct pivot_table *make_table (void) +{ + struct pivot_table *table = pivot_table_create ("$ctables-dialog-template"); + table->show_title = false; + table->show_caption = false; + + pivot_dimension_create (table, PIVOT_AXIS_ROW, "row"); + pivot_dimension_create (table, PIVOT_AXIS_COLUMN, "column"); + + return table; +} + +/* Create a new text leaf in CAT with the string TEXT iff there isn't already + such a leaf */ +static int +category_create_leaf_once (struct pivot_category *cat, const char *text) +{ + for (int s = 0; s < cat->n_subs; ++s) + { + if (cat->subs[s]->name->type == PIVOT_VALUE_TEXT) + { + if (0 == strcmp (cat->subs[s]->name->text.id, text)) + return -1; + } + else + return -1; + } + + return pivot_category_create_leaf (cat, pivot_value_new_text (text)); +} + + +/* Add a new pivot category to PARENT. + + CHILDREN must be NULL or a list of pivot_values. CHILD_NAME is the name of + the new category. + + If CHILDREN is NULL or a empty, then the new category will be a leaf with + the name CHILD_NAME. Otherwise the new category will be a group and the + contents of CHILDREN will be the leaves of that group. + */ +static void +add_child_category (struct pivot_category *parent, const char *child_name, + struct llx_list *children) +{ + if (children && llx_is_empty (children)) + { + pivot_category_create_leaf (parent, pivot_value_new_text (child_name)); + return; + } + + for (struct llx *llx = llx_head (children); llx != llx_null (children); + llx = llx_next (llx)) + { + struct pivot_value *foo = llx_data (llx); + struct pivot_category *pc = pivot_category_create_group (parent, child_name); + pivot_category_create_leaf (pc, foo); + } +} + + +/* + Supplement TABLE with a category to hold cells which could contain summary + data for VAR. PRIMARY_AXIS is the TABLE's axis which will contain the + heading for the variable itself. The perpendicular axis will contain the + headings of the summary functions. + + DICT is the dictionary which contains VAR and all previously added variables. + + Returns TRUE if successfull. False otherwise. + */ +static gboolean +augment_template_table (struct pivot_table *table, + enum pivot_axis_type primary_axis, + const struct variable *var, const struct dictionary *dict) +{ + g_return_val_if_fail (table, FALSE); + struct pivot_dimension *axis0 ; + struct pivot_dimension *axis1 ; + + g_assert (primary_axis == PIVOT_AXIS_ROW || primary_axis == PIVOT_AXIS_COLUMN); + + if (primary_axis == PIVOT_AXIS_ROW) + { + axis0 = table->dimensions[0]; + axis1 = table->dimensions[1]; + } + else + { + axis0 = table->dimensions[1]; + axis1 = table->dimensions[0]; + } + + const enum measure m = var_get_measure (var); + struct pivot_value *pv_var = pivot_value_new_variable (var); + + /* Displaying the variable label in the template tends to make it too verbose + and hard to read. So we remove the label here. */ + free (pv_var->variable.var_label); + pv_var->variable.var_label = NULL; + + if (m == MEASURE_NOMINAL || m == MEASURE_ORDINAL) + { + /* If this axis already contains headings for summary functions, + these need to be transferred to a sub category below the one + that we are adding. So make a list of them here. */ + struct llx_list summary_categories; + llx_init (&summary_categories); + for (int s = 0; s < axis0->root->n_subs; ++s) + { + if (axis0->root->subs[s]->name->type == PIVOT_VALUE_TEXT) + { + struct pivot_value *subtext + = pivot_value_clone (axis0->root->subs[s]->name); + llx_push_tail (&summary_categories, subtext, &llx_malloc_mgr); + } + } + struct pivot_category *cat = + pivot_category_create_group__ (axis0->root, pv_var); + + /* The value labels (if any) form the categories */ + if (var_has_value_labels (var)) + { + const struct val_labs *labels = var_get_value_labels (var); + size_t count = val_labs_count (labels); + + const struct val_lab **array = val_labs_sorted (labels); + for (int i = 0; i < count; ++i) + { + add_child_category (cat, array[i]->label, &summary_categories); + } + free (array); + } + else + { + add_child_category (cat, N_("Category 0"), &summary_categories); + add_child_category (cat, N_("Category 1"), &summary_categories); + } + category_create_leaf_once (axis1->root, N_("Count")); + + llx_destroy (&summary_categories, NULL, NULL, &llx_malloc_mgr); + } + else + { + /* When adding a scalar variable we must check that the other axis + doesn't also contain scalar variables. This is not allowed. */ + for (int s = 0; s < axis1->root->n_subs; ++s) + { + const struct pivot_value *name = axis1->root->subs[s]->name; + if (name->type == PIVOT_VALUE_VARIABLE) + { + const struct variable *v + = dict_lookup_var (dict, name->variable.var_name); + g_return_val_if_fail (v, FALSE); + enum measure meas = var_get_measure (v); + if (meas != MEASURE_NOMINAL && meas != MEASURE_ORDINAL) + { + return FALSE; + } + } + } + pivot_category_create_leaf (axis0->root, pv_var); + category_create_leaf_once (axis1->root, N_("Mean")); + } + + return TRUE; +} + +static gboolean +dialog_state_valid (PsppireDialogAction *pda) +{ + PsppireDialogActionCtables *act = PSPPIRE_DIALOG_ACTION_CTABLES (pda); + + if (!act->table) + return FALSE; + + if (act->table->n_dimensions < 2) + return FALSE; + + for (int d = 0; d < act->table->n_dimensions; ++d) + { + if (act->table->dimensions[d]->root->n_subs <= 0) + return FALSE; + } + + return TRUE; +} + +static void +refresh (PsppireDialogAction *pda) +{ + PsppireDialogActionCtables *act = PSPPIRE_DIALOG_ACTION_CTABLES (pda); + + output_item_unref (act->graphic); + act->graphic = NULL; + + act->table = make_table (); + act->table = pivot_table_ref (act->table); + + gtk_widget_queue_draw (act->canvas); + act->dragged_variable = NULL; +} + +static gboolean +pad_draw (GtkWidget *widget, cairo_t *cr, gpointer data) +{ + GdkRGBA color; + GtkStyleContext *context = gtk_widget_get_style_context (widget); + + guint width = gtk_widget_get_allocated_width (widget); + guint height = gtk_widget_get_allocated_height (widget); + + gtk_render_background (context, cr, 0, 0, width, height); + + cairo_rectangle (cr, 2, 2, width - 5, height - 5); + + gtk_style_context_get_color (context, + GTK_STATE_FLAG_DROP_ACTIVE, + &color); + + gdk_cairo_set_source_rgba (cr, &color); + + const double dashes[] = {10.0, 2.0}; + cairo_set_dash (cr, dashes, 2, 0.0); + cairo_stroke (cr); + + cairo_rectangle (cr, 3, 3, width - 7, height - 7); + color.red *= 0.5; + color.green *= 0.5; + color.blue *= 0.5; + color.alpha *= 0.25; + gdk_cairo_set_source_rgba (cr, &color); + cairo_fill (cr); + + return FALSE; +} + +static void +drag_begin (PsppireDictView *widget, + GdkDragContext *context, + PsppireDialogActionCtables *act) +{ + act->dragged_variable = + psppire_dict_view_get_selected_variable (widget); + + if (!act->dragged_variable) + { + gtk_drag_cancel (context); + return; + } + + /* Set the icon to be displayed during dragging operation */ + enum measure m = var_get_measure (act->dragged_variable); + struct fmt_spec fmt = var_get_print_format (act->dragged_variable); + const char *stock_id = get_var_measurement_stock_id (fmt.type, m); + + gtk_drag_set_icon_name (context, stock_id, 0, 0); +} + +static void +drag_end (PsppireDictView *widget, + GdkDragContext *context, + PsppireDialogActionCtables *act) +{ + act->dragged_variable = NULL; +} + +static gboolean +drag_failed (GtkWidget *widget, + GdkDragContext *context, + GtkDragResult result, + PsppireDialogActionCtables *act) +{ + act->dragged_variable = NULL; + return FALSE; +} + +static gboolean +drag_drop_pad (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint time, + PsppireDialogAction *pda) +{ + PsppireDialogActionCtables *act = PSPPIRE_DIALOG_ACTION_CTABLES (pda); + + enum pivot_axis_type axis + = (act->rows_pad == widget) ? PIVOT_AXIS_ROW : PIVOT_AXIS_COLUMN; + + PsppireDict *dict = PSPPIRE_DICT_VIEW (pda->source)->dict; + + gboolean ok + = augment_template_table (act->table, axis, act->dragged_variable, + dict->dict); + gtk_drag_finish (context, ok, FALSE, time); + + if (!ok) + return TRUE; + + act->table = pivot_table_ref (act->table); + + output_item_unref (act->graphic); + act->graphic = table_item_create (pivot_table_unshare (act->table)); + + gtk_widget_queue_draw (act->canvas); + + return TRUE; +} + +static gchar f1[]="ctables-dialog"; + +static const GtkTargetEntry te[1] = { + {f1, GTK_TARGET_SAME_APP, 2}, + }; + +static gboolean +canvas_draw (GtkWidget *widget, cairo_t *cr, PsppireDialogActionCtables *act) +{ + GdkRectangle clip; + if (!gdk_cairo_get_clip_rectangle (cr, &clip)) + return TRUE; + struct xr_fsm_style *style = NULL; + struct xr_fsm *fsm = NULL; + + + GdkRGBA color; + GtkStyleContext *context = gtk_widget_get_style_context (widget); + + guint width = gtk_widget_get_allocated_width (widget); + guint height = gtk_widget_get_allocated_height (widget); + + gtk_render_background (context, cr, 0, 0, width, height); + + if (act->graphic) + { + style = get_xr_fsm_style (widget); + fsm = xr_fsm_create_for_scrolling (act->graphic, style, cr); + xr_fsm_draw_region (fsm, cr, clip.x, clip.y, clip.width, clip.height); + } + + gtk_style_context_get_color (context, + gtk_style_context_get_state (context), + &color); + gdk_cairo_set_source_rgba (cr, &color); + + cairo_fill (cr); + + if (fsm) + xr_fsm_destroy (fsm); + + if (style) + xr_fsm_style_unref (style); + + return FALSE; +} + +static struct xr_fsm_style * +get_xr_fsm_style (GtkWidget *w) +{ + GtkStyleContext *context = gtk_widget_get_style_context (w); + GtkStateFlags state = gtk_widget_get_state_flags (w); + + int xr_width = 500 * 1000; + + PangoFontDescription *pf; + gtk_style_context_get (context, state, "font", &pf, NULL); + + struct xr_fsm_style *style = xmalloc (sizeof *style); + *style = (struct xr_fsm_style) { + .ref_cnt = 1, + .size = { [TABLE_HORZ] = xr_width, [TABLE_VERT] = INT_MAX }, + .min_break = { [TABLE_HORZ] = xr_width / 2, [TABLE_VERT] = 0 }, + .font = pf, + .use_system_colors = true, + .object_spacing = XR_POINT * 12, + .font_resolution = 96.0, + }; + + return style; +} + +static GtkBuilder * +psppire_dialog_action_ctables_activate (PsppireDialogAction *pda, GVariant *param) +{ + PsppireDialogActionCtables *act = PSPPIRE_DIALOG_ACTION_CTABLES (pda); + + GtkBuilder *xml = builder_new ("ctables.ui"); + act->cols_pad = get_widget_assert (xml, "columns-pad"); + act->rows_pad = get_widget_assert (xml, "rows-pad"); + act->canvas = get_widget_assert (xml, "template-canvas"); + g_signal_connect (act->rows_pad, "draw", G_CALLBACK (pad_draw), NULL); + g_signal_connect (act->cols_pad, "draw", G_CALLBACK (pad_draw), NULL); + + g_signal_connect (act->canvas, "draw", G_CALLBACK (canvas_draw), pda); + + gtk_drag_dest_set (act->rows_pad, GTK_DEST_DEFAULT_ALL, NULL, 0, + GDK_ACTION_LINK); + gtk_drag_dest_set (act->cols_pad, GTK_DEST_DEFAULT_ALL, NULL, 0, + GDK_ACTION_LINK); + + GtkTargetList *tl = gtk_target_list_new (te, 2); + + gtk_drag_dest_add_text_targets (act->rows_pad); + gtk_drag_dest_add_text_targets (act->cols_pad); + + gtk_drag_dest_set_target_list (act->rows_pad, tl); + gtk_drag_dest_set_target_list (act->cols_pad, tl); + + g_signal_connect (act->rows_pad, "drag-drop", G_CALLBACK (drag_drop_pad), pda); + g_signal_connect (act->cols_pad, "drag-drop", G_CALLBACK (drag_drop_pad), pda); + + pda->dialog = get_widget_assert (xml, "tables-dialog"); + pda->source = get_widget_assert (xml, "dict-view"); + + gtk_drag_source_set (pda->source, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_LINK); + gtk_drag_source_set_target_list (pda->source, tl); + + g_signal_connect (pda->source, "drag-begin", G_CALLBACK (drag_begin), pda); + g_signal_connect (pda->source, "drag-end", G_CALLBACK (drag_end), pda); + g_signal_connect (pda->source, "drag-failed", G_CALLBACK (drag_failed), pda); + + psppire_dialog_action_set_refresh (pda, refresh); + + psppire_dialog_action_set_valid_predicate (pda, + (ContentsAreValid) dialog_state_valid); + return xml; +} + +/* + Return an array of integers which contain the axes of the table. + The array is arranged in the order ROW, COLUMN, LAYER. + The elements of the array are the indices of the table->dimensions member, + which contain that integer. If there is no such member then the element will + be -1. + + In other words, it is the inverse of x: f(x) -> table->dimensions[x], but + adjusted to the order ROW, COLUMN, LAYER + + The caller must free this when no longer needed. + */ +static size_t *get_dimensions_permutation (const struct pivot_table *table) +{ + size_t *perm = xcalloc (PIVOT_N_AXES, sizeof *perm); + for (size_t s = 0; s < PIVOT_N_AXES; ++s) + perm[s] = -1; + + for (size_t s = 0; s < table->n_dimensions; ++s) + { + switch (table->dimensions[s]->axis_type) + { + case PIVOT_AXIS_ROW: + perm[0] = s; + break; + case PIVOT_AXIS_COLUMN: + perm[1] = s; + break; + case PIVOT_AXIS_LAYER: + perm[2] = s; + break; + default: + g_assert_not_reached (); + } + } + + return perm; +} + +static char * +generate_syntax (const PsppireDialogAction *pda) +{ + PsppireDialogActionCtables *act = PSPPIRE_DIALOG_ACTION_CTABLES (pda); + const struct pivot_table *table = act->table; + + GString *string = g_string_new ("CTABLES"); + + g_string_append (string, " /TABLE"); + + size_t *perm = get_dimensions_permutation (table); + + for (size_t idx = 0; idx < PIVOT_N_AXES; ++idx) + { + if (perm[idx] == -1) + continue; + + const struct pivot_dimension *dim = table->dimensions[perm[idx]]; + + bool first_variable = true; + for (int s = 0; s < dim->root->n_subs; ++s) + { + const struct pivot_category *sub = dim->root->subs[s]; + + if (sub->name->type == PIVOT_VALUE_VARIABLE) + { + if (idx > 0 && first_variable) + { + g_string_append (string, " BY"); + } + + g_string_append (string, " "); + if (!first_variable) + g_string_append (string, "+ "); + g_string_append (string, sub->name->variable.var_name); + first_variable = false; + } + } + } + + free (perm); + + g_string_append (string, ".\n"); + + return g_string_free_and_steal (string); +} + +static void +psppire_dialog_action_ctables_class_init (PsppireDialogActionCtablesClass *class) +{ + PSPPIRE_DIALOG_ACTION_CLASS (class)->initial_activate + = psppire_dialog_action_ctables_activate; + + PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax; +} + +static void +psppire_dialog_action_ctables_init (PsppireDialogActionCtables *act) +{ + act->graphic = NULL; + act->table = NULL; + act->dragged_variable = NULL; +} diff --git a/src/ui/gui/psppire-dialog-action-ctables.h b/src/ui/gui/psppire-dialog-action-ctables.h new file mode 100644 index 0000000000..74fff28ee0 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-ctables.h @@ -0,0 +1,85 @@ +/* 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-dialog-action.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_CTABLES_H__ +#define __PSPPIRE_DIALOG_ACTION_CTABLES_H__ + +G_BEGIN_DECLS + + +#define PSPPIRE_TYPE_DIALOG_ACTION_CTABLES (psppire_dialog_action_ctables_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_CTABLES(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_CTABLES, PsppireDialogActionCtables)) + +#define PSPPIRE_DIALOG_ACTION_CTABLES_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_CTABLES, \ + PsppireDialogActionCtablesClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_CTABLES(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_CTABLES)) + +#define PSPPIRE_IS_DIALOG_ACTION_CTABLES_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_CTABLES)) + + +#define PSPPIRE_DIALOG_ACTION_CTABLES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_CTABLES, \ + PsppireDialogActionCtablesClass)) + +typedef struct _PsppireDialogActionCtables PsppireDialogActionCtables; +typedef struct _PsppireDialogActionCtablesClass PsppireDialogActionCtablesClass; + +struct pivot_table; +struct output_item; +struct variable; + +struct _PsppireDialogActionCtables +{ + PsppireDialogAction parent; + + /*< private >*/ + GtkWidget *cols_pad; + GtkWidget *rows_pad; + GtkWidget *canvas; + struct pivot_table *table; + struct output_item *graphic; + const struct variable *dragged_variable; + + gboolean dispose_has_run ; +}; + + +struct _PsppireDialogActionCtablesClass +{ + PsppireDialogActionClass parent_class; +}; + + +GType psppire_dialog_action_ctables_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_CTABLES_H__ */ diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c index 25aff745c4..c0d3832deb 100644 --- a/src/ui/gui/widgets.c +++ b/src/ui/gui/widgets.c @@ -71,6 +71,7 @@ along with this program. If not, see . #include "psppire-dialog-action-select.h" #include "psppire-dialog-action-sort.h" #include "psppire-dialog-action-split.h" +#include "psppire-dialog-action-ctables.h" #include "psppire-dialog-action-tt1s.h" #include "psppire-dialog-action-two-sample.h" #include "psppire-dialog-action-univariate.h" @@ -128,6 +129,7 @@ static const get_type_func dialog_action_types[]= psppire_dialog_action_select_get_type, psppire_dialog_action_sort_get_type, psppire_dialog_action_split_get_type, + psppire_dialog_action_ctables_get_type, psppire_dialog_action_tt1s_get_type, psppire_dialog_action_two_sample_get_type, psppire_dialog_action_weight_get_type, -- 2.30.2