1 /* 2 * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com> 3 * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org> 4 * Copyright (C) 2010 David King <davidk@openismus.com> 5 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the 19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23 #include <string.h> 24 #include <gdk/gdkkeysyms.h> 25 #include <glib/gi18n-lib.h> 26 #include <libgda/libgda.h> 27 #include <libgda-ui.h> 28 #include <libgda/gda-blob-op.h> 29 #include "internal/utility.h" 30 #include "marshallers/gdaui-marshal.h" 31 #include "data-entries/gdaui-data-cell-renderer-combo.h" 32 #include "data-entries/gdaui-data-cell-renderer-info.h" 33 #include <libgda/binreloc/gda-binreloc.h> 34 #include <gtk/gtk.h> 35 #include <libgda/gda-debug-macros.h> 36 37 static void gdaui_raw_grid_class_init (GdauiRawGridClass *klass); 38 static void gdaui_raw_grid_init (GdauiRawGrid *wid); 39 static void gdaui_raw_grid_dispose (GObject *object); 40 41 static void gdaui_raw_grid_set_property (GObject *object, 42 guint param_id, 43 const GValue *value, 44 GParamSpec *pspec); 45 static void gdaui_raw_grid_get_property (GObject *object, 46 guint param_id, 47 GValue *value, 48 GParamSpec *pspec); 49 50 static void create_columns_data (GdauiRawGrid *grid); 51 52 static void proxy_sample_changed_cb (GdaDataProxy *proxy, gint sample_start, gint sample_end, GdauiRawGrid *grid); 53 static void proxy_row_updated_cb (GdaDataProxy *proxy, gint proxy_row, GdauiRawGrid *grid); 54 static void proxy_reset_pre_cb (GdaDataProxy *proxy, GdauiRawGrid *grid); 55 static void proxy_reset_cb (GdaDataProxy *proxy, GdauiRawGrid *grid); 56 static void paramlist_public_data_changed_cb (GdauiSet *paramlist, GdauiRawGrid *grid); 57 static void paramlist_param_attr_changed_cb (GdaSet *paramlist, GdaHolder *param, 58 const gchar *att_name, const GValue *att_value, GdauiRawGrid *grid); 59 static GError *iter_validate_set_cb (GdaDataModelIter *iter, GdauiRawGrid *grid); 60 static void iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdauiRawGrid *grid); 61 62 static void remove_all_columns (GdauiRawGrid *grid); 63 static void reset_columns_default (GdauiRawGrid *grid); 64 static void reset_columns_in_xml_layout (GdauiRawGrid *grid, xmlNodePtr grid_node); 65 66 67 /* GdauiDataProxy interface */ 68 static void gdaui_raw_grid_widget_init (GdauiDataProxyIface *iface); 69 static GdaDataProxy *gdaui_raw_grid_get_proxy (GdauiDataProxy *iface); 70 static void gdaui_raw_grid_set_column_editable (GdauiDataProxy *iface, gint column, gboolean editable); 71 static void gdaui_raw_grid_show_column_actions (GdauiDataProxy *iface, gint column, gboolean show_actions); 72 static GtkActionGroup *gdaui_raw_grid_get_actions_group (GdauiDataProxy *iface); 73 static gboolean gdaui_raw_grid_widget_set_write_mode (GdauiDataProxy *iface, GdauiDataProxyWriteMode mode); 74 static GdauiDataProxyWriteMode gdaui_raw_grid_widget_get_write_mode (GdauiDataProxy *iface); 75 76 /* GdauiDataSelector interface */ 77 static void gdaui_raw_grid_selector_init (GdauiDataSelectorIface *iface); 78 static GdaDataModel *gdaui_raw_grid_selector_get_model (GdauiDataSelector *iface); 79 static void gdaui_raw_grid_selector_set_model (GdauiDataSelector *iface, GdaDataModel *model); 80 static GArray *gdaui_raw_grid_selector_get_selected_rows (GdauiDataSelector *iface); 81 static GdaDataModelIter *gdaui_raw_grid_selector_get_data_set (GdauiDataSelector *iface); 82 static gboolean gdaui_raw_grid_selector_select_row (GdauiDataSelector *iface, gint row); 83 static void gdaui_raw_grid_selector_unselect_row (GdauiDataSelector *iface, gint row); 84 static void gdaui_raw_grid_selector_set_column_visible (GdauiDataSelector *iface, gint column, gboolean visible); 85 86 typedef struct { 87 GtkCellRenderer *data_cell; 88 GtkCellRenderer *info_cell; 89 GtkTreeViewColumn *column; /* no ref held */ 90 91 gboolean prog_hidden; /* status as requested by the programmer */ 92 gboolean hidden; /* real status of the column */ 93 gchar *title; 94 95 GdaHolder *single_param; 96 GdauiSetGroup *group; 97 98 gboolean info_shown; 99 gboolean data_locked; /* TRUE if no modification allowed on that column */ 100 gchar *tooltip_text; 101 } ColumnData; 102 103 #define COLUMN_DATA(x) ((ColumnData *)(x)) 104 105 static void destroy_column_data (GdauiRawGrid *grid); 106 static ColumnData *get_column_data_for_group (GdauiRawGrid *grid, GdauiSetGroup *group); 107 static ColumnData *get_column_data_for_holder (GdauiRawGrid *grid, GdaHolder *holder); 108 static ColumnData *get_column_data_for_id (GdauiRawGrid *grid, const gchar *id); 109 110 typedef struct { 111 GdauiRawGridFormatFunc func; 112 gpointer data; 113 GDestroyNotify dnotify; 114 } FormattingFuncData; 115 static FormattingFuncData *formatting_func_new (GdauiRawGridFormatFunc func, 116 gpointer data, GDestroyNotify dnotify); 117 static void formatting_func_destroy (FormattingFuncData *fd); 118 119 struct _GdauiRawGridPriv 120 { 121 GdaDataModel *data_model; /* data model provided by set_model() */ 122 GdaDataModelIter *iter; /* iterator for @store, used for its structure */ 123 GdauiSet *iter_info; 124 gint iter_row; /* @iter's last row in case of proxy reset */ 125 gboolean reset_soft; /* tells if proxy rest was "soft" */ 126 GdauiDataStore *store; /* GtkTreeModel interface, using @proxy */ 127 GdaDataProxy *proxy; /* proxy data model, proxying @data_model */ 128 129 GSList *columns_data; /* list of ColumnData */ 130 GHashTable *columns_hash; /* key = a GtkCellRenderer, value = a #ColumnData (no ref held) */ 131 132 GSList *reordered_indexes; /* Indexes of the reordered columns. */ 133 134 gboolean default_show_info_cell; 135 gboolean default_show_global_actions; 136 137 GtkActionGroup *actions_group; 138 139 gint export_type; /* used by the export dialog */ 140 GdauiDataProxyWriteMode write_mode; 141 142 GtkWidget *filter; 143 GtkWidget *filter_window; 144 145 /* store the position of the mouse for popup menu on button press event */ 146 gint bin_x; 147 gint bin_y; 148 149 GSList *formatting_funcs; /* list of #FormattingFuncData structures */ 150 }; 151 152 /* get a pointer to the parents to be able to call their destructor */ 153 static GObjectClass *parent_class = NULL; 154 155 /* signals */ 156 enum { 157 DOUBLE_CLICKED, 158 POPULATE_POPUP, 159 LAST_SIGNAL 160 }; 161 162 static gint gdaui_raw_grid_signals[LAST_SIGNAL] = { 0, 0 }; 163 164 /* properties */ 165 enum { 166 PROP_0, 167 PROP_MODEL, 168 PROP_XML_LAYOUT, 169 PROP_INFO_CELL_VISIBLE, 170 PROP_GLOBAL_ACTIONS_VISIBLE 171 }; 172 173 /* 174 * Real initialization 175 */ 176 177 static gboolean tree_view_event_cb (GtkWidget *treeview, GdkEvent *event, GdauiRawGrid *grid); 178 static gint tree_view_popup_button_pressed_cb (GtkWidget *widget, GdkEventButton *event, 179 GdauiRawGrid *grid); 180 181 static void tree_view_selection_changed_cb (GtkTreeSelection *selection, GdauiRawGrid *grid); 182 static void tree_view_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, 183 GtkTreeViewColumn *column, GdauiRawGrid *grid); 184 185 static void action_new_cb (GtkAction *action, GdauiRawGrid *grid); 186 static void action_delete_cb (GtkToggleAction *action, GdauiRawGrid *grid); 187 static void action_commit_cb (GtkAction *action, GdauiRawGrid *grid); 188 static void action_reset_cb (GtkAction *action, GdauiRawGrid *grid); 189 static void action_first_chunck_cb (GtkAction *action, GdauiRawGrid *grid); 190 static void action_prev_chunck_cb (GtkAction *action, GdauiRawGrid *grid); 191 static void action_next_chunck_cb (GtkAction *action, GdauiRawGrid *grid); 192 static void action_last_chunck_cb (GtkAction *action, GdauiRawGrid *grid); 193 static void action_filter_cb (GtkAction *action, GdauiRawGrid *grid); 194 195 static GtkToggleActionEntry ui_actions_t[] = { 196 { "ActionDelete", GTK_STOCK_REMOVE, "_Delete", NULL, N_("Delete the current record"), G_CALLBACK (action_delete_cb), FALSE}, 197 }; 198 199 static GtkActionEntry ui_actions[] = { 200 { "ActionNew", GTK_STOCK_ADD, "_New", NULL, N_("Create a new record"), G_CALLBACK (action_new_cb)}, 201 { "ActionCommit", GTK_STOCK_SAVE, "_Commit", NULL, N_("Commit the modifications"), G_CALLBACK (action_commit_cb)}, 202 { "ActionReset", GTK_STOCK_CLEAR, "_Clear", NULL, N_("Clear all the modifications"), G_CALLBACK (action_reset_cb)}, 203 { "ActionFirstChunck", GTK_STOCK_GOTO_FIRST, "_First chunck", NULL, N_("Go to first chunck"), G_CALLBACK (action_first_chunck_cb)}, 204 { "ActionLastChunck", GTK_STOCK_GOTO_LAST, "_Last chunck", NULL, N_("Go to last chunck"), G_CALLBACK (action_last_chunck_cb)}, 205 { "ActionPrevChunck", GTK_STOCK_GO_BACK, "_Previous chunck", NULL, N_("Go to previous chunck"), G_CALLBACK (action_prev_chunck_cb)}, 206 { "ActionNextChunck", GTK_STOCK_GO_FORWARD, "Ne_xt chunck", NULL, N_("Go to next chunck"), G_CALLBACK (action_next_chunck_cb)}, 207 { "ActionFilter", GTK_STOCK_FIND, "Filter", NULL, N_("Filter records"), G_CALLBACK (action_filter_cb)} 208 }; 209 210 GType 211 gdaui_raw_grid_get_type (void) 212 { 213 static GType type = 0; 214 215 if (G_UNLIKELY (type == 0)) { 216 static const GTypeInfo info = { 217 sizeof (GdauiRawGridClass), 218 (GBaseInitFunc) NULL, 219 (GBaseFinalizeFunc) NULL, 220 (GClassInitFunc) gdaui_raw_grid_class_init, 221 NULL, 222 NULL, 223 sizeof (GdauiRawGrid), 224 0, 225 (GInstanceInitFunc) gdaui_raw_grid_init, 226 0 227 }; 228 229 static const GInterfaceInfo proxy_info = { 230 (GInterfaceInitFunc) gdaui_raw_grid_widget_init, 231 NULL, 232 NULL 233 }; 234 235 static const GInterfaceInfo selector_info = { 236 (GInterfaceInitFunc) gdaui_raw_grid_selector_init, 237 NULL, 238 NULL 239 }; 240 241 type = g_type_register_static (GTK_TYPE_TREE_VIEW, "GdauiRawGrid", &info, 0); 242 g_type_add_interface_static (type, GDAUI_TYPE_DATA_PROXY, &proxy_info); 243 g_type_add_interface_static (type, GDAUI_TYPE_DATA_SELECTOR, &selector_info); 244 } 245 246 return type; 247 } 248 249 static void 250 gdaui_raw_grid_widget_init (GdauiDataProxyIface *iface) 251 { 252 iface->get_proxy = gdaui_raw_grid_get_proxy; 253 iface->set_column_editable = gdaui_raw_grid_set_column_editable; 254 iface->show_column_actions = gdaui_raw_grid_show_column_actions; 255 iface->get_actions_group = gdaui_raw_grid_get_actions_group; 256 iface->set_write_mode = gdaui_raw_grid_widget_set_write_mode; 257 iface->get_write_mode = gdaui_raw_grid_widget_get_write_mode; 258 } 259 260 static void 261 gdaui_raw_grid_selector_init (GdauiDataSelectorIface *iface) 262 { 263 iface->get_model = gdaui_raw_grid_selector_get_model; 264 iface->set_model = gdaui_raw_grid_selector_set_model; 265 iface->get_selected_rows = gdaui_raw_grid_selector_get_selected_rows; 266 iface->get_data_set = gdaui_raw_grid_selector_get_data_set; 267 iface->select_row = gdaui_raw_grid_selector_select_row; 268 iface->unselect_row = gdaui_raw_grid_selector_unselect_row; 269 iface->set_column_visible = gdaui_raw_grid_selector_set_column_visible; 270 } 271 272 static void 273 gdaui_raw_grid_class_init (GdauiRawGridClass *klass) 274 { 275 GObjectClass *object_class = G_OBJECT_CLASS (klass); 276 parent_class = g_type_class_peek_parent (klass); 277 278 /** 279 * GdauiRawGrid::double-clicked: 280 * @grid: GdauiRawGrid 281 * @row: the row that was double clicked 282 * 283 * Emitted when the user double clicks on a row 284 */ 285 gdaui_raw_grid_signals[DOUBLE_CLICKED] = 286 g_signal_new ("double-clicked", 287 G_TYPE_FROM_CLASS (object_class), 288 G_SIGNAL_RUN_FIRST, 289 G_STRUCT_OFFSET (GdauiRawGridClass, double_clicked), 290 NULL, NULL, 291 _gdaui_marshal_VOID__INT, G_TYPE_NONE, 292 1, G_TYPE_INT); 293 294 /** 295 * GdauiRawGrid::populate-popup: 296 * @grid: GdauiRawGrid 297 * @menu: a #GtkMenu to modify 298 * 299 * Connect this signal and modify the popup menu. 300 */ 301 gdaui_raw_grid_signals[POPULATE_POPUP] = 302 g_signal_new ("populate-popup", 303 G_TYPE_FROM_CLASS (object_class), 304 G_SIGNAL_RUN_FIRST, 305 G_STRUCT_OFFSET (GdauiRawGridClass, populate_popup), 306 NULL, NULL, 307 _gdaui_marshal_VOID__OBJECT, G_TYPE_NONE, 308 1, GTK_TYPE_MENU); 309 310 object_class->dispose = gdaui_raw_grid_dispose; 311 312 /* Properties */ 313 object_class->set_property = gdaui_raw_grid_set_property; 314 object_class->get_property = gdaui_raw_grid_get_property; 315 g_object_class_install_property (object_class, PROP_MODEL, 316 g_param_spec_object ("model", _("Data to display"), NULL, GDA_TYPE_DATA_MODEL, 317 G_PARAM_READABLE | G_PARAM_WRITABLE)); 318 319 g_object_class_install_property (object_class, PROP_XML_LAYOUT, 320 g_param_spec_pointer ("xml-layout", 321 _("Pointer to an XML layout specification (as an xmlNodePtr to a <gdaui_grid> node)"), NULL, 322 G_PARAM_WRITABLE)); 323 g_object_class_install_property (object_class, PROP_INFO_CELL_VISIBLE, 324 g_param_spec_boolean ("info-cell-visible", NULL, _("Info cell visible"), FALSE, 325 G_PARAM_READABLE | G_PARAM_WRITABLE)); 326 327 g_object_class_install_property (object_class, PROP_GLOBAL_ACTIONS_VISIBLE, 328 g_param_spec_boolean ("global-actions-visible", NULL, _("Global Actions visible"), FALSE, 329 G_PARAM_READABLE | G_PARAM_WRITABLE)); 330 } 331 332 static gboolean 333 gdaui_raw_grid_query_tooltip (GtkWidget *widget, 334 gint x, 335 gint y, 336 gboolean keyboard_tip, 337 GtkTooltip *tooltip, 338 G_GNUC_UNUSED gpointer data) 339 { 340 GtkTreeView *tree_view = GTK_TREE_VIEW (widget); 341 342 if (!gtk_tree_view_get_tooltip_context (tree_view, &x, &y, 343 keyboard_tip, 344 NULL, NULL, NULL)) 345 return FALSE; 346 347 gint position = 0; 348 gint col_x = 0; 349 GList *list, *columns = gtk_tree_view_get_columns (tree_view); 350 for (list = columns; list; list = list->next) { 351 GtkTreeViewColumn *column = list->data; 352 if (x >= col_x && x < (col_x + gtk_tree_view_column_get_width (column))) 353 break; 354 else 355 col_x += gtk_tree_view_column_get_width (column); 356 ++position; 357 } 358 if (columns) 359 g_list_free (columns); 360 if (!list) 361 return FALSE; 362 363 GdauiRawGrid *grid = GDAUI_RAW_GRID (tree_view); 364 ColumnData *cdata = COLUMN_DATA (g_slist_nth (grid->priv->columns_data, position)->data); 365 g_return_val_if_fail (cdata, FALSE); 366 367 if (! cdata->tooltip_text) 368 return FALSE; 369 370 gtk_tooltip_set_markup (tooltip, cdata->tooltip_text); 371 372 return TRUE; 373 } 374 375 static void 376 gdaui_raw_grid_init (GdauiRawGrid *grid) 377 { 378 GtkTreeView *tree_view; 379 GtkTreeSelection *selection; 380 381 grid->priv = g_new0 (GdauiRawGridPriv, 1); 382 grid->priv->store = NULL; 383 grid->priv->iter = NULL; 384 grid->priv->iter_row = -1; 385 grid->priv->proxy = NULL; 386 grid->priv->default_show_info_cell = FALSE; 387 grid->priv->default_show_global_actions = TRUE; 388 grid->priv->columns_data = NULL; 389 grid->priv->columns_hash = g_hash_table_new (NULL, NULL); 390 grid->priv->export_type = 1; 391 grid->priv->write_mode = GDAUI_DATA_PROXY_WRITE_ON_DEMAND; 392 393 tree_view = GTK_TREE_VIEW (grid); 394 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE); 395 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tree_view), TRUE); 396 g_signal_connect (G_OBJECT (tree_view), "event", 397 G_CALLBACK (tree_view_event_cb), grid); 398 _gdaui_setup_right_click_selection_on_treeview (tree_view); 399 g_signal_connect (G_OBJECT (tree_view), "button-press-event", 400 G_CALLBACK (tree_view_popup_button_pressed_cb), grid); 401 gtk_tree_view_set_enable_search (tree_view, FALSE); 402 403 /* selection and signal handling */ 404 selection = gtk_tree_view_get_selection (tree_view); 405 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); 406 g_signal_connect (G_OBJECT (selection), "changed", 407 G_CALLBACK (tree_view_selection_changed_cb), grid); 408 g_signal_connect (G_OBJECT (tree_view), "row-activated", 409 G_CALLBACK (tree_view_row_activated_cb), grid); 410 411 /* tooltip */ 412 g_object_set (G_OBJECT (grid), "has-tooltip", TRUE, NULL); 413 g_signal_connect (grid, "query-tooltip", 414 G_CALLBACK (gdaui_raw_grid_query_tooltip), NULL); 415 416 /* action group */ 417 grid->priv->actions_group = gtk_action_group_new ("Actions"); 418 gtk_action_group_set_translation_domain (grid->priv->actions_group, GETTEXT_PACKAGE); 419 gtk_action_group_add_actions (grid->priv->actions_group, ui_actions, G_N_ELEMENTS (ui_actions), grid); 420 gtk_action_group_add_toggle_actions (grid->priv->actions_group, ui_actions_t, G_N_ELEMENTS (ui_actions_t), grid); 421 422 grid->priv->filter = NULL; 423 grid->priv->filter_window = NULL; 424 425 grid->priv->formatting_funcs = NULL; 426 } 427 428 /** 429 * gdaui_raw_grid_new: 430 * @model: a #GdaDataModel 431 * 432 * Creates a new #GdauiRawGrid widget suitable to display the data in @model 433 * 434 * Returns: (transfer full): the new widget 435 * 436 * Since: 4.2 437 */ 438 GtkWidget * 439 gdaui_raw_grid_new (GdaDataModel *model) 440 { 441 GtkWidget *grid; 442 443 g_return_val_if_fail (!model || GDA_IS_DATA_MODEL (model), NULL); 444 445 grid = (GtkWidget *) g_object_new (GDAUI_TYPE_RAW_GRID, "model", model, NULL); 446 447 return grid; 448 } 449 450 static void gdaui_raw_grid_clean (GdauiRawGrid *grid); 451 static void 452 gdaui_raw_grid_dispose (GObject *object) 453 { 454 GdauiRawGrid *grid; 455 456 g_return_if_fail (GDAUI_IS_RAW_GRID (object)); 457 grid = GDAUI_RAW_GRID (object); 458 459 if (grid->priv) { 460 gdaui_raw_grid_clean (grid); 461 462 if (grid->priv->actions_group) { 463 g_object_unref (G_OBJECT (grid->priv->actions_group)); 464 grid->priv->actions_group = NULL; 465 } 466 467 if (grid->priv->filter) 468 gtk_widget_destroy (grid->priv->filter); 469 if (grid->priv->filter_window) 470 gtk_widget_destroy (grid->priv->filter_window); 471 472 if (grid->priv->formatting_funcs) { 473 g_slist_foreach (grid->priv->formatting_funcs, (GFunc) formatting_func_destroy, 474 NULL); 475 g_slist_free (grid->priv->formatting_funcs); 476 } 477 478 /* the private area itself */ 479 g_free (grid->priv); 480 grid->priv = NULL; 481 } 482 483 /* for the parent class */ 484 parent_class->dispose (object); 485 } 486 487 static void 488 gdaui_raw_grid_set_property (GObject *object, 489 guint param_id, 490 const GValue *value, 491 GParamSpec *pspec) 492 { 493 GdauiRawGrid *grid; 494 495 grid = GDAUI_RAW_GRID (object); 496 if (grid->priv) { 497 switch (param_id) { 498 case PROP_MODEL: { 499 GdaDataModel *model = (GdaDataModel*) g_value_get_object (value); 500 501 if (model) 502 g_return_if_fail (GDA_IS_DATA_MODEL (model)); 503 else 504 return; 505 506 if (grid->priv->proxy) { 507 /* data model has been changed */ 508 if (GDA_IS_DATA_PROXY (model)) { 509 /* clean all */ 510 gdaui_raw_grid_clean (grid); 511 g_assert (!grid->priv->proxy); 512 } 513 else 514 g_object_set (G_OBJECT (grid->priv->proxy), "model", model, NULL); 515 } 516 517 if (!grid->priv->proxy) { 518 /* first time setting */ 519 if (GDA_IS_DATA_PROXY (model)) 520 grid->priv->proxy = g_object_ref (G_OBJECT (model)); 521 else 522 grid->priv->proxy = GDA_DATA_PROXY (gda_data_proxy_new (model)); 523 524 g_signal_connect (grid->priv->proxy, "reset", 525 G_CALLBACK (proxy_reset_pre_cb), grid); 526 grid->priv->data_model = gda_data_proxy_get_proxied_model (grid->priv->proxy); 527 528 g_signal_connect (grid->priv->proxy, "sample-changed", 529 G_CALLBACK (proxy_sample_changed_cb), grid); 530 g_signal_connect (grid->priv->proxy, "row-updated", 531 G_CALLBACK (proxy_row_updated_cb), grid); 532 g_signal_connect (grid->priv->proxy, "reset", 533 G_CALLBACK (proxy_reset_cb), grid); 534 535 grid->priv->iter = gda_data_model_create_iter (GDA_DATA_MODEL (grid->priv->proxy)); 536 grid->priv->iter_row = -1; 537 grid->priv->iter_info = gdaui_set_new (GDA_SET (grid->priv->iter)); 538 539 g_signal_connect (grid->priv->iter_info, "public-data-changed", 540 G_CALLBACK (paramlist_public_data_changed_cb), grid); 541 g_signal_connect (grid->priv->iter, "holder-attr-changed", 542 G_CALLBACK (paramlist_param_attr_changed_cb), grid); 543 544 g_signal_connect (grid->priv->iter, "row-changed", 545 G_CALLBACK (iter_row_changed_cb), grid); 546 g_signal_connect (grid->priv->iter, "validate-set", 547 G_CALLBACK (iter_validate_set_cb), grid); 548 549 gda_data_model_iter_invalidate_contents (grid->priv->iter); 550 551 grid->priv->store = GDAUI_DATA_STORE (gdaui_data_store_new ((GdaDataModel*) grid->priv->proxy)); 552 gtk_tree_view_set_model ((GtkTreeView *) grid, 553 GTK_TREE_MODEL (grid->priv->store)); 554 555 create_columns_data (grid); 556 reset_columns_default (grid); 557 558 g_signal_emit_by_name (object, "proxy-changed", grid->priv->proxy); 559 } 560 561 break; 562 } 563 564 case PROP_XML_LAYOUT: { 565 /* node should be a "gdaui_grid" node */ 566 xmlNodePtr node = g_value_get_pointer (value); 567 if (node) { 568 g_return_if_fail (node); 569 g_return_if_fail (!strcmp ((gchar*) node->name, "gdaui_grid")); 570 571 reset_columns_in_xml_layout (grid, node); 572 } 573 else 574 reset_columns_default (grid); 575 break; 576 } 577 578 case PROP_INFO_CELL_VISIBLE: { 579 GSList *list; 580 gboolean show = g_value_get_boolean (value); 581 grid->priv->default_show_info_cell = show; 582 583 for (list = grid->priv->columns_data; list; list = list->next) { 584 COLUMN_DATA (list->data)->info_shown = show; 585 g_object_set (G_OBJECT (COLUMN_DATA (list->data)->info_cell), "visible", 586 show, NULL); 587 } 588 break; 589 } 590 591 case PROP_GLOBAL_ACTIONS_VISIBLE: 592 gtk_action_group_set_visible (grid->priv->actions_group, g_value_get_boolean (value)); 593 break; 594 595 default: 596 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 597 break; 598 } 599 } 600 } 601 602 static void 603 gdaui_raw_grid_get_property (GObject *object, 604 guint param_id, 605 GValue *value, 606 GParamSpec *pspec) 607 { 608 GdauiRawGrid *grid; 609 610 grid = GDAUI_RAW_GRID (object); 611 if (grid->priv) { 612 switch (param_id) { 613 case PROP_MODEL: 614 g_value_set_object (value, grid->priv->data_model); 615 break; 616 case PROP_INFO_CELL_VISIBLE: 617 g_value_set_boolean(value, grid->priv->default_show_info_cell); 618 break; 619 case PROP_GLOBAL_ACTIONS_VISIBLE: 620 g_value_set_boolean(value, grid->priv->default_show_global_actions); 621 break; 622 623 default: 624 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 625 break; 626 } 627 } 628 } 629 630 /* 631 * _gdaui_raw_grid_get_selection 632 * @grid: a #GdauiRawGrid widget 633 * 634 * Returns the list of the currently selected rows in a #GdauiRawGrid widget. 635 * The returned value is a list of integers, which represent each of the selected rows. 636 * 637 * If new rows have been inserted, then those new rows will have a row number equal to -1. 638 * 639 * Returns: a new list, should be freed (by calling g_list_free) when no longer needed. 640 */ 641 GList * 642 _gdaui_raw_grid_get_selection (GdauiRawGrid *grid) 643 { 644 GtkTreeSelection *selection; 645 GList *selected_rows; 646 647 g_return_val_if_fail (grid && GDAUI_IS_RAW_GRID (grid), NULL); 648 g_return_val_if_fail (grid->priv, NULL); 649 650 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 651 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL); 652 if (selected_rows) { 653 GList *list, *retlist = NULL; 654 GtkTreeIter iter; 655 gint row; 656 657 list = selected_rows; 658 for (list = selected_rows; list; list = list->next) { 659 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter, 660 (GtkTreePath *)(list->data))) { 661 gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), &iter, 662 GDAUI_DATA_STORE_COL_MODEL_ROW, &row, -1); 663 retlist = g_list_prepend (retlist, GINT_TO_POINTER (row)); 664 } 665 } 666 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL); 667 g_list_free (selected_rows); 668 return g_list_reverse (retlist); 669 } 670 else 671 return NULL; 672 } 673 674 675 676 /* 677 * creates a new string where underscores '_' are replaced by double underscores '__' 678 * WARNING: the old string is free'ed so it is possible to do "str=double_underscores(str);" 679 */ 680 static gchar * 681 add_double_underscores (gchar *str) 682 { 683 gchar **arr; 684 gchar *ret; 685 686 arr = g_strsplit (str, "_", 0); 687 ret = g_strjoinv ("__", arr); 688 g_strfreev (arr); 689 g_free (str); 690 691 return ret; 692 } 693 694 /* 695 * Creates a NEW string 696 */ 697 static gchar * 698 remove_double_underscores (gchar *str) 699 { 700 gchar *ptr1, *ptr2, *ret; 701 702 ret = g_new (gchar, strlen (str) + 1); 703 for (ptr1 = str, ptr2 = ret; *ptr1; ptr1++, ptr2++) { 704 *ptr2 = *ptr1; 705 if ((ptr1[0] == '_') && (ptr1[1] == '_')) 706 ptr1++; 707 } 708 *ptr2 = 0; 709 710 return ret; 711 } 712 713 static void cell_value_set_attributes (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, 714 GtkTreeModel *tree_model, GtkTreeIter *iter, GdauiRawGrid *grid); 715 static void cell_info_set_attributes (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, 716 GtkTreeModel *tree_model, GtkTreeIter *iter, GdauiRawGrid *grid); 717 718 static void data_cell_value_changed (GtkCellRenderer *renderer, const gchar *path, 719 const GValue *new_value, GdauiRawGrid *grid); 720 static void data_cell_values_changed (GtkCellRenderer *renderer, const gchar *path, 721 GSList *new_values, GSList *all_new_values, GdauiRawGrid *grid); 722 static void data_cell_status_changed (GtkCellRenderer *renderer, const gchar *path, 723 GdaValueAttribute requested_action, GdauiRawGrid *grid); 724 static void treeview_column_clicked_cb (GtkTreeViewColumn *tree_column, GdauiRawGrid *grid); 725 726 /* 727 * Creates the GtkTreeView's columns, from the grid->priv->store GtkTreeModel 728 */ 729 static void 730 create_columns_data (GdauiRawGrid *grid) 731 { 732 gint i; 733 GSList *list; 734 735 /* Creation of the columns in the treeview, to fit the parameters in grid->priv->iter param list */ 736 for (i = 0, list = grid->priv->iter_info->groups_list; 737 list; 738 i++, list = list->next) { 739 GdaHolder *param; 740 GdauiSetGroup *group; 741 GtkCellRenderer *renderer; 742 ColumnData *cdata; 743 GdaSetGroup *sg; 744 745 group = GDAUI_SET_GROUP (list->data); 746 sg = gdaui_set_group_get_group (group); 747 /* update the list of columns data */ 748 cdata = get_column_data_for_group (grid, group); 749 if (!cdata) { 750 cdata = g_new0 (ColumnData, 1); 751 cdata->group = group; 752 cdata->info_shown = grid->priv->default_show_info_cell; 753 cdata->data_locked = FALSE; 754 cdata->tooltip_text = NULL; 755 grid->priv->columns_data = g_slist_append (grid->priv->columns_data, cdata); 756 } 757 758 /* create renderers */ 759 if (gdaui_set_group_get_source (group)) { 760 /* parameters depending on a GdaDataModel */ 761 gchar *title; 762 /* 763 GSList *nodes; 764 765 for (nodes = group->group->nodes; nodes; nodes = nodes->next) { 766 if (gda_holder_get_not_null (GDA_HOLDER (GDA_SET_NODE (nodes->data)->holder))) { 767 nullok = FALSE; 768 break; 769 } 770 } 771 */ 772 773 /* determine title */ 774 if (gda_set_group_get_n_nodes (sg) == 1) 775 title = (gchar *) g_object_get_data (G_OBJECT (gda_set_node_get_holder (gda_set_group_get_node (sg))), 776 "name"); 777 else 778 title = (gchar *) g_object_get_data (G_OBJECT (gda_set_source_get_data_model (gda_set_group_get_source (sg))), 779 "name"); 780 781 if (title) 782 title = add_double_underscores (g_strdup (title)); 783 else 784 /* FIXME: find a better label */ 785 title = g_strdup (_("Value")); 786 787 /* FIXME: if nullok is FALSE, then set the column title in bold */ 788 cdata->tooltip_text = g_strdup (_("Can't be NULL")); 789 renderer = gdaui_data_cell_renderer_combo_new (grid->priv->iter_info, gdaui_set_group_get_source (group)); 790 cdata->data_cell = g_object_ref_sink ((GObject*) renderer); 791 g_hash_table_insert (grid->priv->columns_hash, renderer, cdata); 792 g_free (cdata->title); 793 cdata->title = title; 794 795 g_signal_connect (G_OBJECT (renderer), "changed", 796 G_CALLBACK (data_cell_values_changed), grid); 797 } 798 else { 799 /* single direct parameter */ 800 GType g_type; 801 const gchar *plugin = NULL; 802 const GValue *plugin_val; 803 gchar *title; 804 gint model_col; 805 806 param = gda_set_node_get_holder (gda_set_group_get_node (sg)); 807 cdata->single_param = param; 808 g_type = gda_holder_get_g_type (param); 809 810 if (gda_holder_get_not_null (param)) 811 cdata->tooltip_text = g_strdup (_("Can't be NULL")); 812 813 g_object_get (G_OBJECT (param), "name", &title, NULL); 814 if (title && *title) 815 title = add_double_underscores (title); 816 else 817 title = NULL; 818 if (!title) 819 title = g_strdup (_("No title")); 820 821 plugin_val = gda_holder_get_attribute (param, GDAUI_ATTRIBUTE_PLUGIN); 822 if (plugin_val) { 823 if (G_VALUE_TYPE (plugin_val) == G_TYPE_STRING) 824 plugin = g_value_get_string (plugin_val); 825 else 826 g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"), 827 GDAUI_ATTRIBUTE_PLUGIN); 828 } 829 renderer = _gdaui_new_cell_renderer (g_type, plugin); 830 cdata->data_cell = g_object_ref_sink ((GObject*) renderer); 831 g_hash_table_insert (grid->priv->columns_hash, renderer, cdata); 832 g_free (cdata->title); 833 cdata->title = title; 834 835 model_col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, param); 836 g_object_set_data (G_OBJECT (renderer), "model_col", GINT_TO_POINTER (model_col)); 837 838 g_signal_connect (G_OBJECT (renderer), "changed", 839 G_CALLBACK (data_cell_value_changed), grid); 840 } 841 842 /* warning: when making modifications to renderer's attributes or signal connect, 843 * also make the same modifications to paramlist_param_attr_changed_cb() */ 844 845 /* settings and signals */ 846 g_object_set (G_OBJECT (renderer), "editable", !cdata->data_locked, NULL); 847 if (g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), "set-default-if-invalid")) 848 g_object_set (G_OBJECT (renderer), "set-default-if-invalid", TRUE, NULL); 849 850 /* Adding the GValue's information cell as another GtkCellRenderer */ 851 renderer = gdaui_data_cell_renderer_info_new (grid->priv->store, grid->priv->iter, group); 852 cdata->info_cell = g_object_ref_sink ((GObject*) renderer); 853 g_hash_table_insert (grid->priv->columns_hash, renderer, cdata); 854 g_signal_connect (G_OBJECT (renderer), "status-changed", 855 G_CALLBACK (data_cell_status_changed), grid); 856 g_object_set (G_OBJECT (renderer), "visible", cdata->info_shown, NULL); 857 } 858 } 859 860 /* keep track of the columns shown and hidden */ 861 static void 862 column_visibility_changed (GtkTreeViewColumn *column, G_GNUC_UNUSED GParamSpec *pspec, ColumnData *cdata) 863 { 864 cdata->hidden = !gtk_tree_view_column_get_visible (column); 865 } 866 867 /* 868 * Remove all columns from @grid, does not modify ColumnData 869 */ 870 static void 871 remove_all_columns (GdauiRawGrid *grid) 872 { 873 GList *columns, *list; 874 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid)); 875 for (list = columns; list; list = list->next) 876 gtk_tree_view_remove_column (GTK_TREE_VIEW (grid), 877 (GtkTreeViewColumn*) (list->data)); 878 if (columns) 879 g_list_free (columns); 880 } 881 882 /* 883 * Create a treeview column and places it at posision @pos, or las last position 884 * if pos < 0 885 */ 886 static void 887 create_tree_view_column (GdauiRawGrid *grid, ColumnData *cdata, gint pos) 888 { 889 GtkTreeViewColumn *column; 890 gint n; 891 n = gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (grid), pos, 892 cdata->title, 893 cdata->data_cell, 894 (GtkTreeCellDataFunc) cell_value_set_attributes, 895 grid, NULL); 896 column = gtk_tree_view_get_column (GTK_TREE_VIEW (grid), pos >= 0 ? pos : n-1); 897 cdata->column = column; 898 g_object_set_data (G_OBJECT (column), "data_renderer", cdata->data_cell); 899 g_object_set_data (G_OBJECT (column), "__gdaui_group", cdata->group); 900 901 gtk_tree_view_column_pack_end (column, cdata->info_cell, FALSE); 902 gtk_tree_view_column_set_cell_data_func (column, cdata->info_cell, 903 (GtkTreeCellDataFunc) cell_info_set_attributes, 904 grid, NULL); 905 906 g_signal_connect (column, "notify::visible", 907 G_CALLBACK (column_visibility_changed), cdata); 908 909 /* Sorting data */ 910 gtk_tree_view_column_set_clickable (column, TRUE); 911 g_signal_connect (G_OBJECT (column), "clicked", 912 G_CALLBACK (treeview_column_clicked_cb), grid); 913 } 914 915 static void 916 reset_columns_default (GdauiRawGrid *grid) 917 { 918 GSList *list; 919 920 remove_all_columns (grid); 921 for (list = grid->priv->columns_data; list; list = list->next) { 922 ColumnData *cdata = COLUMN_DATA (list->data); 923 if (cdata->prog_hidden) 924 continue; 925 926 /* create treeview column */ 927 create_tree_view_column (grid, cdata, -1); 928 } 929 } 930 931 static void 932 reset_columns_in_xml_layout (GdauiRawGrid *grid, xmlNodePtr grid_node) 933 { 934 remove_all_columns (grid); 935 936 xmlNodePtr child; 937 for (child = grid_node->children; child; child = child->next) { 938 if (child->type != XML_ELEMENT_NODE) 939 continue; 940 941 if (xmlStrEqual (child->name, BAD_CAST "gdaui_entry")) { 942 xmlChar *name; 943 name = xmlGetProp (child, BAD_CAST "name"); 944 if (!name) 945 continue; 946 947 /* find column data */ 948 ColumnData *cdata; 949 cdata = get_column_data_for_id (grid, (gchar*) name); 950 if (! cdata) { 951 g_warning ("Could not find column named '%s', ignoring", 952 (gchar*) name); 953 xmlFree (name); 954 continue; 955 } 956 xmlFree (name); 957 958 /* create treeview column */ 959 cdata->prog_hidden = FALSE; 960 cdata->hidden = FALSE; 961 create_tree_view_column (grid, cdata, -1); 962 963 /* plugin */ 964 xmlChar *plugin; 965 plugin = xmlGetProp (child, BAD_CAST "plugin"); 966 if (plugin && cdata->single_param) { 967 GValue *value; 968 value = gda_value_new_from_string ((gchar*) plugin, G_TYPE_STRING); 969 gda_holder_set_attribute_static (cdata->single_param, 970 GDAUI_ATTRIBUTE_PLUGIN, value); 971 gda_value_free (value); 972 } 973 if (plugin) 974 xmlFree (plugin); 975 976 /* column label */ 977 xmlChar *prop; 978 prop = xmlGetProp (child, BAD_CAST "label"); 979 if (prop) { 980 g_free (cdata->title); 981 cdata->title = g_strdup ((gchar*) prop); 982 xmlFree (prop); 983 gtk_tree_view_column_set_title (cdata->column, cdata->title); 984 } 985 } 986 } 987 } 988 989 static gboolean 990 remove_formatting_function (GdauiRawGrid *grid, GdauiRawGridFormatFunc func) 991 { 992 if (grid->priv->formatting_funcs) { 993 GSList *list; 994 for (list = grid->priv->formatting_funcs; list; list = list->next) { 995 FormattingFuncData *fd = (FormattingFuncData*) list->data; 996 if (fd->func == func) { 997 grid->priv->formatting_funcs = g_slist_remove (grid->priv->formatting_funcs, fd); 998 formatting_func_destroy (fd); 999 return TRUE; 1000 } 1001 } 1002 } 1003 return FALSE; 1004 } 1005 1006 /** 1007 * gdaui_raw_grid_add_formatting_function: 1008 * @grid: a #GdauiRawGrid widget 1009 * @func: a #GdauiRawGridFormatFunc function pointer 1010 * @data: (allow-none): a pointer to pass to the @func function when called 1011 * @dnotify: (allow-none): destroy notifier for @data 1012 * 1013 * This function allows you to specify that the @func function needs to be called 1014 * whenever the rendering of a cell in @grid needs to be done. It is similar in purpose 1015 * to the gtk_tree_view_column_set_cell_data_func() function. 1016 * 1017 * Since: 5.0.3 1018 */ 1019 void 1020 gdaui_raw_grid_add_formatting_function (GdauiRawGrid *grid, GdauiRawGridFormatFunc func, 1021 gpointer data, GDestroyNotify dnotify) 1022 { 1023 g_return_if_fail (GDAUI_IS_RAW_GRID (grid)); 1024 g_return_if_fail (func); 1025 1026 /* remove existing function */ 1027 remove_formatting_function (grid, func); 1028 1029 /* add new one */ 1030 FormattingFuncData *fd; 1031 fd = formatting_func_new (func, data, dnotify); 1032 grid->priv->formatting_funcs = g_slist_append (grid->priv->formatting_funcs, fd); 1033 1034 /* redraw grid to take new function into account */ 1035 TO_IMPLEMENT; 1036 } 1037 1038 /** 1039 * gdaui_raw_grid_remove_formatting_function: 1040 * @grid: a #GdauiRawGrid widget 1041 * @func: (scope notified): a #GdauiRawGridFormatFunc function pointer 1042 * 1043 * This function undoes what has been specified before by gdaui_raw_grid_add_formatting_function() 1044 * 1045 * Since: 5.0.3 1046 */ 1047 void 1048 gdaui_raw_grid_remove_formatting_function (GdauiRawGrid *grid, GdauiRawGridFormatFunc func) 1049 { 1050 g_return_if_fail (GDAUI_IS_RAW_GRID (grid)); 1051 g_return_if_fail (func); 1052 1053 remove_formatting_function (grid, func); 1054 1055 /* redraw grid to take new function into account */ 1056 TO_IMPLEMENT; 1057 } 1058 1059 static FormattingFuncData * 1060 formatting_func_new (GdauiRawGridFormatFunc func, gpointer data, GDestroyNotify dnotify) 1061 { 1062 FormattingFuncData *fd; 1063 fd = g_new0 (FormattingFuncData, 1); 1064 fd->func = func; 1065 fd->data = data; 1066 fd->dnotify = dnotify; 1067 return fd; 1068 } 1069 1070 static void 1071 formatting_func_destroy (FormattingFuncData *fd) 1072 { 1073 if (fd->dnotify) 1074 fd->dnotify (fd->data); 1075 g_free (fd); 1076 } 1077 1078 /* 1079 * Set the attributes for each cell renderer which is not the information cell renderer, 1080 * called by each cell renderer before actually displaying anything. 1081 */ 1082 static void 1083 cell_value_set_attributes (G_GNUC_UNUSED GtkTreeViewColumn *tree_column, 1084 GtkCellRenderer *cell, 1085 G_GNUC_UNUSED GtkTreeModel *tree_model, 1086 GtkTreeIter *iter, GdauiRawGrid *grid) 1087 { 1088 GdauiSetGroup *group; 1089 GdaSetGroup *sg; 1090 guint attributes; 1091 gboolean to_be_deleted = FALSE; 1092 ColumnData *cdata; 1093 1094 cdata = g_hash_table_lookup (grid->priv->columns_hash, cell); 1095 if (!cdata) { 1096 g_warning ("Internal error: missing column data"); 1097 return; 1098 } 1099 group = cdata->group; 1100 sg = gdaui_set_group_get_group (group); 1101 1102 if (gda_set_group_get_source (sg)) { 1103 /* parameters depending on a GdaDataModel */ 1104 GList *values = NULL; 1105 1106 /* NOTE: 1107 * For performances reasons we want to provide, if possible, all the values required by the combo cell 1108 * renderer to draw whatever it wants, without further making it search for the values it wants in 1109 * source->data_model. 1110 * 1111 * GdaSetSource *source; 1112 * source = group->group->nodes_source; 1113 * 1114 * For this purpose, we try to get a complete list of values making one row of the node->data_for_params 1115 * data model, so that the combo cell renderer has all the values it wants. 1116 * 1117 * NOTE2: 1118 * This optimization is required anyway when source->data_model can be changed depending on 1119 * external events and we can't know when it has changed. 1120 */ 1121 attributes = _gdaui_utility_proxy_compute_attributes_for_group (group, 1122 grid->priv->store, 1123 grid->priv->iter, 1124 iter, &to_be_deleted); 1125 values = _gdaui_utility_proxy_compute_values_for_group (group, grid->priv->store, 1126 grid->priv->iter, iter, 1127 TRUE); 1128 if (values) { 1129 1130 g_object_set (G_OBJECT (cell), 1131 "values-display", values, 1132 "value-attributes", attributes, 1133 "editable", 1134 !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1135 "show-expander", 1136 !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1137 "cell-background", GDAUI_COLOR_NORMAL_MODIF, 1138 "cell_background-set", 1139 ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted, 1140 "to-be-deleted", to_be_deleted, 1141 "visible", !(attributes & GDA_VALUE_ATTR_UNUSED), 1142 NULL); 1143 } 1144 else { 1145 values = _gdaui_utility_proxy_compute_values_for_group (group, grid->priv->store, 1146 grid->priv->iter, iter, FALSE); 1147 g_object_set (G_OBJECT (cell), 1148 "values-display", values, 1149 "value-attributes", attributes, 1150 "editable", 1151 !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1152 "show-expander", 1153 !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1154 "cell-background", GDAUI_COLOR_NORMAL_MODIF, 1155 "cell_background-set", 1156 ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted, 1157 "to-be-deleted", to_be_deleted, 1158 "visible", !(attributes & GDA_VALUE_ATTR_UNUSED), 1159 NULL); 1160 } 1161 1162 if (values) 1163 g_list_free (values); 1164 } 1165 else { 1166 /* single direct parameter */ 1167 gint col; 1168 gint offset; 1169 GValue *value; 1170 1171 offset = gda_data_model_get_n_columns (gda_data_proxy_get_proxied_model (grid->priv->proxy)); 1172 1173 g_assert (gda_set_group_get_n_nodes (sg) == 1); 1174 col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 1175 gda_set_node_get_holder (gda_set_group_get_node (sg))); 1176 gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), iter, 1177 GDAUI_DATA_STORE_COL_TO_DELETE, &to_be_deleted, 1178 col, &value, 1179 offset + col, &attributes, -1); 1180 g_object_set (G_OBJECT (cell), 1181 "value-attributes", attributes, 1182 "value", value, 1183 "editable", 1184 !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1185 "cell-background", GDAUI_COLOR_NORMAL_MODIF, 1186 "cell_background-set", 1187 ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted, 1188 "to-be-deleted", to_be_deleted, 1189 "visible", !(attributes & GDA_VALUE_ATTR_UNUSED), 1190 NULL); 1191 } 1192 1193 if (grid->priv->formatting_funcs) { 1194 gint row, col_index; 1195 GSList *list; 1196 1197 col_index = g_slist_index (grid->priv->columns_data, cdata); 1198 gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), iter, 1199 GDAUI_DATA_STORE_COL_MODEL_ROW, &row, -1); 1200 for (list = grid->priv->formatting_funcs; list; list = list->next) { 1201 FormattingFuncData *fd = (FormattingFuncData*) list->data; 1202 fd->func (cell, cdata->column, col_index, (GdaDataModel*) grid->priv->proxy, row, fd->data); 1203 } 1204 } 1205 } 1206 1207 /* 1208 * Set the attributes for each information cell renderer, 1209 * called by each cell renderer before actually displaying anything. 1210 */ 1211 static void 1212 cell_info_set_attributes (GtkTreeViewColumn *tree_column, 1213 GtkCellRenderer *cell, 1214 G_GNUC_UNUSED GtkTreeModel *tree_model, 1215 GtkTreeIter *iter, GdauiRawGrid *grid) 1216 { 1217 GdauiSetGroup *group; 1218 GdaSetGroup *sg; 1219 guint attributes; 1220 gboolean to_be_deleted = FALSE; 1221 ColumnData *cdata; 1222 1223 cdata = g_hash_table_lookup (grid->priv->columns_hash, cell); 1224 if (!cdata) { 1225 g_warning ("Missing column data"); 1226 return; 1227 } 1228 group = cdata->group; 1229 sg = gdaui_set_group_get_group (group); 1230 1231 if (gda_set_group_get_source (sg)) { 1232 /* parameters depending on a GdaDataModel */ 1233 attributes = _gdaui_utility_proxy_compute_attributes_for_group (group, grid->priv->store, 1234 grid->priv->iter, 1235 iter, &to_be_deleted); 1236 g_object_set (G_OBJECT (cell), 1237 "editable", !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1238 "value-attributes", attributes, 1239 "cell-background", GDAUI_COLOR_NORMAL_MODIF, 1240 "cell_background-set", ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted, 1241 "to-be-deleted", to_be_deleted, 1242 "visible", cdata->info_shown && !(attributes & GDA_VALUE_ATTR_UNUSED), 1243 NULL); 1244 } 1245 else { 1246 /* single direct parameter */ 1247 gint col; 1248 gint offset; 1249 gint row; 1250 1251 offset = gda_data_model_get_n_columns (gda_data_proxy_get_proxied_model (grid->priv->proxy)); 1252 1253 g_assert (gda_set_group_get_n_nodes (sg) == 1); 1254 col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 1255 gda_set_node_get_holder (gda_set_group_get_node (sg))); 1256 gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), iter, 1257 GDAUI_DATA_STORE_COL_TO_DELETE, &to_be_deleted, 1258 GDAUI_DATA_STORE_COL_MODEL_ROW, &row, 1259 offset + col, &attributes, -1); 1260 g_object_set (G_OBJECT (cell), 1261 "editable", !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF), 1262 "value-attributes", attributes, 1263 "cell-background", GDAUI_COLOR_NORMAL_MODIF, 1264 "cell_background-set", ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted, 1265 "to-be-deleted", to_be_deleted, 1266 "visible", cdata->info_shown && !(attributes & GDA_VALUE_ATTR_UNUSED), 1267 NULL); 1268 } 1269 } 1270 1271 static gboolean set_iter_from_path (GdauiRawGrid *grid, const gchar *path, GtkTreeIter *iter); 1272 1273 /* 1274 * Callback when a value displayed as a GtkCellRenderer has been changed, for single parameter cell renderers 1275 * 1276 * Apply the new_value to the GTkTreeModel (grid->priv->store) 1277 */ 1278 static void 1279 data_cell_value_changed (GtkCellRenderer *renderer, const gchar *path, const GValue *new_value, GdauiRawGrid *grid) 1280 { 1281 GtkTreeIter iter; 1282 GdaSetGroup *sg; 1283 ColumnData *cdata; 1284 1285 cdata = g_hash_table_lookup (grid->priv->columns_hash, renderer); 1286 g_assert (cdata); 1287 sg = gdaui_set_group_get_group (cdata->group); 1288 g_assert (gda_set_group_get_n_nodes (sg) == 1); 1289 1290 if (set_iter_from_path (grid, path, &iter)) { 1291 gint col; 1292 col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 1293 gda_set_node_get_holder (gda_set_group_get_node (sg))); 1294 gdaui_data_store_set_value (grid->priv->store, &iter, col, new_value); 1295 } 1296 } 1297 1298 1299 /* 1300 * Callback when a value displayed as a GdauiDataCellRendererCombo has been changed. 1301 * 1302 * Apply the new_value to the GTkTreeModel (grid->priv->store) 1303 */ 1304 static void 1305 data_cell_values_changed (GtkCellRenderer *renderer, const gchar *path, 1306 GSList *new_values, G_GNUC_UNUSED GSList *all_new_values, GdauiRawGrid *grid) 1307 { 1308 GtkTreeIter iter; 1309 GdauiSetGroup *group; 1310 GdaSetGroup *sg; 1311 ColumnData *cdata; 1312 1313 cdata = g_hash_table_lookup (grid->priv->columns_hash, renderer); 1314 g_assert (cdata); 1315 group = cdata->group; 1316 sg = gdaui_set_group_get_group (group); 1317 g_assert (gda_set_group_get_source (sg)); 1318 1319 if (new_values) 1320 g_return_if_fail (gda_set_group_get_n_nodes (sg) == (gint) g_slist_length (new_values)); 1321 else 1322 /* the reason for not having any value is that the GdauiDataCellRendererCombo had no selected item */ 1323 return; 1324 1325 if (set_iter_from_path (grid, path, &iter)) { 1326 GSList *list, *params; 1327 gint col; 1328 1329 /* update the GdauiDataStore */ 1330 for (params = gda_set_group_get_nodes (sg), list = new_values; 1331 list; 1332 params = params->next, list = list->next) { 1333 col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 1334 gda_set_node_get_holder (GDA_SET_NODE (params->data))); 1335 gdaui_data_store_set_value (grid->priv->store, &iter, col, (GValue *)(list->data)); 1336 } 1337 1338 #ifdef PROXY_STORE_EXTRA_VALUES 1339 /* call gda_data_proxy_set_model_row_value() */ 1340 gint proxy_row; 1341 proxy_row = gdaui_data_store_get_row_from_iter (grid->priv->store, &iter); 1342 1343 gint i; 1344 for (i = 0; i < gdaui_set_source_get_shown_n_cols (gdaui_set_group_get_source (group)); i++) { 1345 GValue *value; 1346 1347 col = group->nodes_source->shown_cols_index[i]; 1348 value = (GValue *) g_slist_nth_data (all_new_values, col); 1349 gda_data_proxy_set_model_row_value (grid->priv->proxy, 1350 gda_set_source_get_data_model (gda_set_group_get_source (sg)), 1351 proxy_row, col, value); 1352 } 1353 #endif 1354 } 1355 } 1356 1357 static gboolean 1358 set_iter_from_path (GdauiRawGrid *grid, const gchar *path, GtkTreeIter *iter) 1359 { 1360 GtkTreePath *treepath; 1361 1362 g_assert (path); 1363 1364 treepath = gtk_tree_path_new_from_string (path); 1365 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), iter, treepath)) { 1366 gtk_tree_path_free (treepath); 1367 g_warning ("Can't get iter for path %s", path); 1368 return FALSE; 1369 } 1370 gtk_tree_path_free (treepath); 1371 1372 return TRUE; 1373 } 1374 1375 static void 1376 treeview_column_clicked_cb (GtkTreeViewColumn *tree_column, GdauiRawGrid *grid) 1377 { 1378 GdauiSetGroup *group; 1379 GdaSetGroup *sg; 1380 GSList *nodes; 1381 1382 group = g_object_get_data (G_OBJECT (tree_column), "__gdaui_group"); 1383 g_assert (group); 1384 sg = gdaui_set_group_get_group (group); 1385 1386 for (nodes = gda_set_group_get_nodes (sg); nodes; nodes = nodes->next) { 1387 GdaHolder *param = gda_set_node_get_holder (GDA_SET_NODE (nodes->data)); 1388 gint pos; 1389 g_assert (param); 1390 1391 pos = g_slist_index (GDA_SET (grid->priv->iter)->holders, param); 1392 if (pos >= 0) { 1393 gda_data_proxy_set_ordering_column (grid->priv->proxy, pos, NULL); 1394 break; 1395 } 1396 } 1397 } 1398 1399 /* 1400 * Actions 1401 */ 1402 static void 1403 data_cell_status_changed (GtkCellRenderer *renderer, const gchar *path, GdaValueAttribute requested_action, 1404 GdauiRawGrid *grid) 1405 { 1406 GtkTreePath *treepath; 1407 GtkTreeIter iter; 1408 GdauiSetGroup *group; 1409 GdaSetGroup *sg; 1410 GtkTreeModel *tree_model; 1411 gint col; 1412 gint offset; 1413 GValue *attribute; 1414 ColumnData *cdata; 1415 1416 cdata = g_hash_table_lookup (grid->priv->columns_hash, renderer); 1417 g_assert (cdata); 1418 group = cdata->group; 1419 sg = gdaui_set_group_get_group (group); 1420 1421 offset = gda_data_model_get_n_columns (gda_data_proxy_get_proxied_model (grid->priv->proxy)); 1422 1423 tree_model = GTK_TREE_MODEL (grid->priv->store); 1424 1425 treepath = gtk_tree_path_new_from_string (path); 1426 if (! gtk_tree_model_get_iter (tree_model, &iter, treepath)) { 1427 gtk_tree_path_free (treepath); 1428 g_warning ("Can't get iter for path %s", path); 1429 return; 1430 } 1431 gtk_tree_path_free (treepath); 1432 1433 g_value_set_uint (attribute = gda_value_new (G_TYPE_UINT), requested_action); 1434 if (gda_set_group_get_source (sg)) { 1435 /* parameters depending on a GdaDataModel */ 1436 GSList *list; 1437 1438 for (list = gda_set_group_get_nodes (sg); list; list = list->next) { 1439 col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 1440 gda_set_node_get_holder (GDA_SET_NODE (list->data))); 1441 gdaui_data_store_set_value (grid->priv->store, &iter, offset + col, attribute); 1442 } 1443 1444 #ifdef PROXY_STORE_EXTRA_VALUES 1445 gint proxy_row; 1446 proxy_row = gdaui_data_store_get_row_from_iter (grid->priv->store, &iter); 1447 1448 /* call gda_data_proxy_set_model_row_value() */ 1449 gint i; 1450 GdauiSetSource gs; 1451 gs = gdaui_set_group_get_source (group); 1452 for (i = 0; i < gdaui_set_source_get_shown_n_cols (gs); i++) { 1453 col = (gdaui_set_source_get_shown_columns (gs))[i]; 1454 1455 if (requested_action & GDA_VALUE_ATTR_IS_NULL) 1456 gda_data_proxy_set_model_row_value (grid->priv->proxy, 1457 group->nodes_source->data_model, 1458 proxy_row, col, NULL); 1459 else { 1460 if (requested_action & GDA_VALUE_ATTR_IS_UNCHANGED) 1461 gda_data_proxy_clear_model_row_value (grid->priv->proxy, 1462 group->nodes_source->data_model, 1463 proxy_row, col); 1464 else { 1465 if (requested_action & GDA_VALUE_ATTR_IS_DEFAULT) { 1466 TO_IMPLEMENT; 1467 } 1468 else 1469 TO_IMPLEMENT; 1470 } 1471 } 1472 } 1473 #endif 1474 } 1475 else { 1476 /* single direct parameter */ 1477 gint col; 1478 1479 g_assert (gda_set_group_get_n_nodes (sg) == 1); 1480 col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 1481 gda_set_node_get_holder (gda_set_group_get_node (sg))); 1482 gdaui_data_store_set_value (grid->priv->store, &iter, offset + col, attribute); 1483 } 1484 gda_value_free (attribute); 1485 } 1486 1487 static void 1488 action_new_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1489 { 1490 GtkTreeIter iter; 1491 GtkTreePath *path; 1492 1493 if (gdaui_data_store_append (grid->priv->store, &iter)) { 1494 path = gtk_tree_model_get_path (GTK_TREE_MODEL (grid->priv->store), &iter); 1495 gtk_tree_view_set_cursor (GTK_TREE_VIEW (grid), path, NULL, FALSE); 1496 gtk_tree_path_free (path); 1497 } 1498 } 1499 1500 static void 1501 action_delete_cb (GtkToggleAction *action, GdauiRawGrid *grid) 1502 { 1503 if (gtk_toggle_action_get_active (action)) { 1504 GtkTreeIter iter; 1505 GtkTreeSelection *select; 1506 GtkTreeModel *model; 1507 GList *sel_rows; 1508 GdaDataProxy *proxy; 1509 1510 select = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 1511 sel_rows = gtk_tree_selection_get_selected_rows (select, &model); 1512 proxy = gdaui_data_store_get_proxy (GDAUI_DATA_STORE (model)); 1513 1514 /* rem: get the list of selected rows after each row deletion because the data model might have changed and 1515 * row numbers might also have changed */ 1516 while (sel_rows) { 1517 gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (sel_rows->data)); 1518 if (!gda_data_proxy_row_is_deleted (proxy, 1519 gdaui_data_store_get_row_from_iter (GDAUI_DATA_STORE (model), 1520 &iter))) { 1521 gdaui_data_store_delete (grid->priv->store, &iter); 1522 g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL); 1523 g_list_free (sel_rows); 1524 sel_rows = gtk_tree_selection_get_selected_rows (select, &model); 1525 } 1526 else 1527 sel_rows = sel_rows->next; 1528 } 1529 } 1530 else { 1531 GtkTreeIter iter; 1532 GtkTreeSelection *select; 1533 GtkTreeModel *model; 1534 GList *sel_rows, *cur_row; 1535 1536 select = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 1537 sel_rows = gtk_tree_selection_get_selected_rows (select, &model); 1538 cur_row = sel_rows; 1539 while (cur_row) { 1540 gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (cur_row->data)); 1541 gdaui_data_store_undelete (grid->priv->store, &iter); 1542 cur_row = g_list_next (cur_row); 1543 } 1544 g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL); 1545 g_list_free (sel_rows); 1546 } 1547 } 1548 1549 static void 1550 action_commit_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1551 { 1552 gint row; 1553 GError *error = NULL; 1554 gboolean allok = TRUE; 1555 gint mod1, mod2; 1556 1557 mod1 = gda_data_proxy_get_n_modified_rows (grid->priv->proxy); 1558 row = gda_data_model_iter_get_row (grid->priv->iter); 1559 if (grid->priv->write_mode >= GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) { 1560 gint newrow; 1561 1562 allok = gda_data_proxy_apply_row_changes (grid->priv->proxy, row, &error); 1563 if (allok) { 1564 newrow = gda_data_model_iter_get_row (grid->priv->iter); 1565 if (row != newrow) /* => current row has changed because the 1566 proxy had to emit a "row_removed" when 1567 actually succeeded the commit 1568 => we need to come back to that row 1569 */ 1570 gda_data_model_iter_move_to_row (grid->priv->iter, row); 1571 } 1572 } 1573 else 1574 allok = gda_data_proxy_apply_all_changes (grid->priv->proxy, &error); 1575 1576 mod2 = gda_data_proxy_get_n_modified_rows (grid->priv->proxy); 1577 if (!allok) { 1578 if (mod1 != mod2) 1579 /* the data model has changed while doing the writing */ 1580 _gdaui_utility_display_error ((GdauiDataProxy *) grid, FALSE, error); 1581 else 1582 _gdaui_utility_display_error ((GdauiDataProxy *) grid, TRUE, error); 1583 g_error_free (error); 1584 } 1585 } 1586 1587 static void 1588 action_reset_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1589 { 1590 gda_data_proxy_cancel_all_changes (grid->priv->proxy); 1591 gda_data_model_send_hint (GDA_DATA_MODEL (grid->priv->proxy), GDA_DATA_MODEL_HINT_REFRESH, NULL); 1592 } 1593 1594 static void 1595 action_first_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1596 { 1597 gda_data_proxy_set_sample_start (grid->priv->proxy, 0); 1598 } 1599 1600 static void 1601 action_prev_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1602 { 1603 gint sample_size, sample_start; 1604 1605 sample_size = gda_data_proxy_get_sample_size (grid->priv->proxy); 1606 if (sample_size > 0) { 1607 sample_start = gda_data_proxy_get_sample_start (grid->priv->proxy); 1608 sample_start -= sample_size; 1609 gda_data_proxy_set_sample_start (grid->priv->proxy, sample_start); 1610 } 1611 } 1612 1613 static void 1614 action_next_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1615 { 1616 gint sample_size, sample_start; 1617 1618 sample_size = gda_data_proxy_get_sample_size (grid->priv->proxy); 1619 if (sample_size > 0) { 1620 sample_start = gda_data_proxy_get_sample_start (grid->priv->proxy); 1621 sample_start += sample_size; 1622 gda_data_proxy_set_sample_start (grid->priv->proxy, sample_start); 1623 } 1624 } 1625 1626 static void 1627 action_last_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1628 { 1629 gda_data_proxy_set_sample_start (grid->priv->proxy, G_MAXINT); 1630 } 1631 1632 static void 1633 hide_filter_window (GdauiRawGrid *grid) 1634 { 1635 gtk_widget_hide (grid->priv->filter_window); 1636 gtk_grab_remove (grid->priv->filter_window); 1637 } 1638 1639 static gboolean 1640 filter_event (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEventAny *event, GdauiRawGrid *grid) 1641 { 1642 hide_filter_window (grid); 1643 return TRUE; 1644 } 1645 1646 static gboolean 1647 key_press_filter_event (G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event, GdauiRawGrid *grid) 1648 { 1649 if (event->keyval == GDK_KEY_Escape || 1650 event->keyval == GDK_KEY_Tab || 1651 event->keyval == GDK_KEY_KP_Tab || 1652 event->keyval == GDK_KEY_ISO_Left_Tab) { 1653 hide_filter_window (grid); 1654 return TRUE; 1655 } 1656 return FALSE; 1657 } 1658 1659 static void 1660 filter_position_func (GtkWidget *widget, 1661 GtkWidget *search_dialog, 1662 G_GNUC_UNUSED gpointer user_data) 1663 { 1664 gint x, y; 1665 gint tree_x, tree_y; 1666 gint tree_width, tree_height; 1667 GdkWindow *window; 1668 GdkScreen *screen; 1669 GtkRequisition requisition; 1670 gint monitor_num; 1671 GdkRectangle monitor; 1672 1673 window = gtk_widget_get_window (widget); 1674 screen = gdk_window_get_screen (window); 1675 1676 monitor_num = gdk_screen_get_monitor_at_window (screen, window); 1677 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); 1678 1679 gtk_widget_realize (search_dialog); 1680 1681 gdk_window_get_origin (window, &tree_x, &tree_y); 1682 tree_width = gdk_window_get_width (window); 1683 tree_height = gdk_window_get_height (window); 1684 gtk_widget_get_preferred_size (search_dialog, NULL, &requisition); 1685 1686 if (tree_x + tree_width > gdk_screen_get_width (screen)) 1687 x = gdk_screen_get_width (screen) - requisition.width; 1688 else if (tree_x + tree_width - requisition.width < 0) 1689 x = 0; 1690 else 1691 x = tree_x + tree_width - requisition.width; 1692 1693 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen)) 1694 y = gdk_screen_get_height (screen) - requisition.height; 1695 else if (tree_y + tree_height < 0) /* isn't really possible ... */ 1696 y = 0; 1697 else 1698 y = tree_y + tree_height; 1699 1700 gtk_window_move (GTK_WINDOW (search_dialog), x, y); 1701 } 1702 1703 static gboolean 1704 popup_grab_on_window (GtkWidget *widget, guint32 activate_time) 1705 { 1706 GdkDeviceManager *manager; 1707 GdkDevice *pointer; 1708 GdkWindow *window; 1709 window = gtk_widget_get_window (widget); 1710 manager = gdk_display_get_device_manager (gtk_widget_get_display (widget)); 1711 pointer = gdk_device_manager_get_client_pointer (manager); 1712 if (gdk_device_grab (pointer, window, GDK_OWNERSHIP_WINDOW, TRUE, 1713 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, 1714 NULL, activate_time) == GDK_GRAB_SUCCESS) { 1715 GdkDevice *keyb; 1716 keyb = gdk_device_get_associated_device (pointer); 1717 if (gdk_device_grab (keyb, window, GDK_OWNERSHIP_WINDOW, TRUE, 1718 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, NULL, activate_time) == 0) 1719 return TRUE; 1720 else { 1721 gdk_device_ungrab (pointer, activate_time); 1722 return FALSE; 1723 } 1724 } 1725 return FALSE; 1726 } 1727 1728 static void 1729 action_filter_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid) 1730 { 1731 GtkWidget *toplevel; 1732 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (grid)); 1733 1734 if (!grid->priv->filter_window) { 1735 /* create filter window */ 1736 GtkWidget *frame, *vbox; 1737 1738 grid->priv->filter_window = gtk_window_new (GTK_WINDOW_POPUP); 1739 1740 gtk_widget_set_events (grid->priv->filter_window, 1741 gtk_widget_get_events (grid->priv->filter_window) | GDK_KEY_PRESS_MASK); 1742 1743 if (gtk_widget_is_toplevel (toplevel) && gtk_window_get_group (GTK_WINDOW (toplevel))) 1744 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), 1745 GTK_WINDOW (grid->priv->filter_window)); 1746 1747 g_signal_connect (grid->priv->filter_window, "delete-event", 1748 G_CALLBACK (filter_event), grid); 1749 g_signal_connect (grid->priv->filter_window, "button-press-event", 1750 G_CALLBACK (filter_event), grid); 1751 g_signal_connect (grid->priv->filter_window, "key-press-event", 1752 G_CALLBACK (key_press_filter_event), grid); 1753 frame = gtk_frame_new (NULL); 1754 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); 1755 gtk_widget_show (frame); 1756 gtk_container_add (GTK_CONTAINER (grid->priv->filter_window), frame); 1757 1758 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); 1759 gtk_widget_show (vbox); 1760 gtk_container_add (GTK_CONTAINER (frame), vbox); 1761 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); 1762 1763 /* add real filter widget */ 1764 if (! grid->priv->filter) { 1765 grid->priv->filter = gdaui_data_filter_new (GDAUI_DATA_PROXY (grid)); 1766 gtk_widget_show (grid->priv->filter); 1767 } 1768 gtk_container_add (GTK_CONTAINER (vbox), grid->priv->filter); 1769 } 1770 else if (gtk_widget_is_toplevel (toplevel)) { 1771 if (gtk_window_get_group ((GtkWindow*) toplevel)) 1772 gtk_window_group_add_window (gtk_window_get_group ((GtkWindow*) toplevel), 1773 GTK_WINDOW (grid->priv->filter_window)); 1774 else if (gtk_window_get_group (GTK_WINDOW (grid->priv->filter_window))) 1775 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (grid->priv->filter_window)), 1776 GTK_WINDOW (grid->priv->filter_window)); 1777 } 1778 1779 /* move the filter window to a correct location */ 1780 /* FIXME: let the user specify the position function like GtkTreeView -> search_position_func() */ 1781 gtk_widget_show (grid->priv->filter_window); 1782 gtk_grab_add (grid->priv->filter_window); 1783 filter_position_func (GTK_WIDGET (grid), grid->priv->filter_window, NULL); 1784 popup_grab_on_window (grid->priv->filter_window, 1785 gtk_get_current_event_time ()); 1786 } 1787 1788 /* 1789 * Catch any event in the GtkTreeView widget 1790 */ 1791 static gboolean 1792 tree_view_event_cb (GtkWidget *treeview, GdkEvent *event, GdauiRawGrid *grid) 1793 { 1794 gboolean done = FALSE; 1795 1796 if (event->type == GDK_KEY_PRESS) { 1797 GdkEventKey *ekey = (GdkEventKey *) event; 1798 guint modifiers = gtk_accelerator_get_default_mod_mask (); 1799 1800 /* Tab to move one column left or right */ 1801 if (ekey->keyval == GDK_KEY_Tab) { 1802 GtkTreeViewColumn *column; 1803 GtkTreePath *path; 1804 1805 /* FIXME: if a column is currently edited, then make sure the editing of that cell is not canceled */ 1806 gtk_tree_view_get_cursor (GTK_TREE_VIEW (treeview), &path, &column); 1807 if (column && path) { 1808 GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (treeview)); 1809 GList *col; 1810 GtkCellRenderer *renderer; 1811 1812 /* change column */ 1813 col = g_list_find (columns, column); 1814 g_return_val_if_fail (col, FALSE); 1815 1816 if (((ekey->state & modifiers) == GDK_SHIFT_MASK) || ((ekey->state & modifiers) == GDK_CONTROL_MASK)) 1817 col = g_list_previous (col); /* going to previous column */ 1818 else 1819 col = g_list_next (col); /* going to next column */ 1820 1821 if (col) { 1822 renderer = g_object_get_data (G_OBJECT (col->data), "data_renderer"); 1823 gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (treeview), path, 1824 GTK_TREE_VIEW_COLUMN (col->data), 1825 renderer, FALSE); 1826 gtk_widget_grab_focus (treeview); 1827 done = TRUE; 1828 } 1829 g_list_free (columns); 1830 } 1831 if (path) 1832 gtk_tree_path_free (path); 1833 } 1834 1835 /* DELETE to delete the selected row */ 1836 if (ekey->keyval == GDK_KEY_Delete) { 1837 GtkTreeIter iter; 1838 GtkTreeSelection *selection; 1839 GtkTreeModel *model; 1840 GList *sel_rows, *cur_row; 1841 1842 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 1843 sel_rows = gtk_tree_selection_get_selected_rows (selection, &model); 1844 cur_row = sel_rows; 1845 while (cur_row) { 1846 gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (cur_row->data)); 1847 if (((ekey->state & modifiers) == GDK_SHIFT_MASK) || 1848 ((ekey->state & modifiers) == GDK_CONTROL_MASK)) 1849 gdaui_data_store_undelete (grid->priv->store, &iter); 1850 else 1851 gdaui_data_store_delete (grid->priv->store, &iter); 1852 cur_row = g_list_next (cur_row); 1853 } 1854 g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL); 1855 g_list_free (sel_rows); 1856 1857 done = TRUE; 1858 } 1859 } 1860 1861 return done; 1862 } 1863 1864 static void menu_select_all_cb (GtkWidget *widget, GdauiRawGrid *grid); 1865 static void menu_unselect_all_cb (GtkWidget *widget, GdauiRawGrid *grid); 1866 static void menu_show_columns_cb (GtkWidget *widget, GdauiRawGrid *grid); 1867 static void menu_save_as_cb (GtkWidget *widget, GdauiRawGrid *grid); 1868 static void menu_set_filter_cb (GtkWidget *widget, GdauiRawGrid *grid); 1869 static void menu_unset_filter_cb (GtkWidget *widget, GdauiRawGrid *grid); 1870 static void menu_copy_row_cb (GtkWidget *widget, GdauiRawGrid *grid); 1871 static GtkWidget *new_menu_item (const gchar *label, 1872 gboolean pixmap, 1873 GCallback cb_func, 1874 gpointer user_data); 1875 static GtkWidget *new_check_menu_item (const gchar *label, 1876 gboolean active, 1877 GCallback cb_func, 1878 gpointer user_data); 1879 1880 static void 1881 hidden_column_mitem_toggled_cb (GtkCheckMenuItem *check, G_GNUC_UNUSED GdauiRawGrid *grid) 1882 { 1883 ColumnData *cdata; 1884 gboolean act; 1885 cdata = g_object_get_data (G_OBJECT (check), "c"); 1886 g_assert (cdata); 1887 act = gtk_check_menu_item_get_active (check); 1888 gtk_tree_view_column_set_visible (cdata->column, act); 1889 } 1890 1891 static gboolean 1892 tree_view_popup_button_pressed_cb (G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event, GdauiRawGrid *grid) 1893 { 1894 GtkWidget *menu, *submenu; 1895 GtkTreeView *tree_view; 1896 GtkTreeSelection *selection; 1897 GtkSelectionMode sel_mode; 1898 GSList *list; 1899 GtkWidget *mitem; 1900 1901 if (event->button != 3) 1902 return FALSE; 1903 1904 tree_view = GTK_TREE_VIEW (grid); 1905 if (event->window != gtk_tree_view_get_bin_window (tree_view)) 1906 return FALSE; 1907 1908 selection = gtk_tree_view_get_selection (tree_view); 1909 sel_mode = gtk_tree_selection_get_mode (selection); 1910 1911 grid->priv->bin_x = (gint) event->x; 1912 grid->priv->bin_y = (gint) event->y; 1913 1914 /* create the menu */ 1915 menu = gtk_menu_new (); 1916 mitem = gtk_menu_item_new_with_label (_("Shown columns")); 1917 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem); 1918 gtk_widget_show (mitem); 1919 1920 submenu = gtk_menu_new (); 1921 gtk_widget_show (submenu); 1922 gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), submenu); 1923 for (list = grid->priv->columns_data; list; list = list->next) { 1924 ColumnData *cdata = (ColumnData*) list->data; 1925 gchar *tmp; 1926 1927 if (cdata->prog_hidden) 1928 continue; 1929 tmp = remove_double_underscores (cdata->title); 1930 mitem = gtk_check_menu_item_new_with_label (tmp); 1931 g_free (tmp); 1932 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mitem), !cdata->hidden); 1933 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem); 1934 gtk_widget_show (mitem); 1935 1936 g_object_set_data (G_OBJECT (mitem), "c", cdata); 1937 g_signal_connect (mitem, "toggled", 1938 G_CALLBACK (hidden_column_mitem_toggled_cb), grid); 1939 } 1940 1941 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 1942 new_menu_item (GTK_STOCK_COPY, TRUE, 1943 G_CALLBACK (menu_copy_row_cb), grid)); 1944 1945 1946 if (sel_mode == GTK_SELECTION_MULTIPLE) 1947 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 1948 new_menu_item (_("Select _All"), FALSE, 1949 G_CALLBACK (menu_select_all_cb), grid)); 1950 1951 if ((sel_mode == GTK_SELECTION_SINGLE) || (sel_mode == GTK_SELECTION_MULTIPLE)) 1952 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 1953 new_menu_item (_("_Clear Selection"), FALSE, 1954 G_CALLBACK (menu_unselect_all_cb), grid)); 1955 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 1956 new_check_menu_item (_("Show Column _Titles"), 1957 gtk_tree_view_get_headers_visible (tree_view), 1958 G_CALLBACK (menu_show_columns_cb), grid)); 1959 1960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 1961 new_menu_item (_("_Set filter"), FALSE, 1962 G_CALLBACK (menu_set_filter_cb), grid)); 1963 1964 mitem = new_menu_item (_("_Unset filter"), FALSE, G_CALLBACK (menu_unset_filter_cb), grid); 1965 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem); 1966 if (!gda_data_proxy_get_filter_expr (grid->priv->proxy)) 1967 gtk_widget_set_sensitive (mitem, FALSE); 1968 1969 if (sel_mode != GTK_SELECTION_NONE) { 1970 gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new ()); 1971 gtk_menu_shell_append (GTK_MENU_SHELL (menu), new_menu_item (GTK_STOCK_SAVE_AS, TRUE, 1972 G_CALLBACK (menu_save_as_cb), 1973 grid)); 1974 } 1975 1976 /* allow listeners to add their custom menu items */ 1977 g_signal_emit (G_OBJECT (grid), gdaui_raw_grid_signals [POPULATE_POPUP], 0, GTK_MENU (menu)); 1978 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); 1979 gtk_widget_show_all (menu); 1980 1981 return TRUE; 1982 } 1983 1984 static GtkWidget * 1985 new_menu_item (const gchar *label, 1986 gboolean pixmap, 1987 GCallback cb_func, 1988 gpointer user_data) 1989 { 1990 GtkWidget *item; 1991 1992 if (pixmap) 1993 item = gtk_image_menu_item_new_from_stock (label, NULL); 1994 else 1995 item = gtk_menu_item_new_with_mnemonic (label); 1996 1997 g_signal_connect (G_OBJECT (item), "activate", 1998 G_CALLBACK (cb_func), user_data); 1999 2000 return item; 2001 } 2002 2003 static GtkWidget * 2004 new_check_menu_item (const gchar *label, 2005 gboolean active, 2006 GCallback cb_func, 2007 gpointer user_data) 2008 { 2009 GtkWidget *item; 2010 2011 item = gtk_check_menu_item_new_with_mnemonic (label); 2012 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), active); 2013 2014 g_signal_connect (G_OBJECT (item), "toggled", 2015 G_CALLBACK (cb_func), user_data); 2016 2017 return item; 2018 } 2019 2020 static void 2021 menu_select_all_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid) 2022 { 2023 GtkTreeSelection *selection; 2024 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 2025 2026 gtk_tree_selection_select_all (selection); 2027 } 2028 2029 static void 2030 menu_unselect_all_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid) 2031 { 2032 GtkTreeSelection *selection; 2033 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 2034 2035 gtk_tree_selection_unselect_all (selection); 2036 } 2037 2038 static void 2039 menu_copy_row_cb (GtkWidget *widget, GdauiRawGrid *grid) 2040 { 2041 GtkTreeModel *model; 2042 GtkTreeIter iter; 2043 GtkTreePath *path; 2044 GtkClipboard *cp; 2045 GtkTreeViewColumn *column; 2046 GList *columns; 2047 cp = gtk_clipboard_get (gdk_atom_intern_static_string ("CLIPBOARD")); 2048 if (!cp) 2049 return; 2050 2051 if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (grid), 2052 grid->priv->bin_x, grid->priv->bin_y, &path, 2053 &column, NULL, NULL)) 2054 return; 2055 2056 model = GTK_TREE_MODEL (grid->priv->store); 2057 if (! gtk_tree_model_get_iter (model, &iter, path)) { 2058 gtk_tree_path_free (path); 2059 return; 2060 } 2061 gtk_tree_path_free (path); 2062 2063 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid)); 2064 const GValue *cvalue; 2065 gboolean cpset = TRUE; 2066 gtk_tree_model_get (model, &iter, g_list_index (columns, column), &cvalue, -1); 2067 g_list_free (columns); 2068 if (G_VALUE_TYPE (cvalue) == GDA_TYPE_NULL) 2069 gtk_clipboard_set_text (cp, "", -1); 2070 else if ((G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY) || 2071 (G_VALUE_TYPE (cvalue) == GDA_TYPE_BLOB)) { 2072 const GdaBinary *bin; 2073 2074 cpset = FALSE; 2075 if (G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY) 2076 bin = gda_value_get_binary (cvalue); 2077 else { 2078 GdaBlob *blob; 2079 blob = (GdaBlob *) gda_value_get_blob ((GValue *) cvalue); 2080 g_assert (blob); 2081 bin = (GdaBinary *) blob; 2082 if (blob->op && 2083 (bin->binary_length != gda_blob_op_get_length (blob->op))) 2084 gda_blob_op_read_all (blob->op, blob); 2085 } 2086 if (bin) { 2087 GdkPixbufLoader *loader; 2088 GdkPixbuf *pixbuf = NULL; 2089 loader = gdk_pixbuf_loader_new (); 2090 if (gdk_pixbuf_loader_write (loader, bin->data, bin->binary_length, NULL)) { 2091 if (gdk_pixbuf_loader_close (loader, NULL)) { 2092 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); 2093 g_object_ref (pixbuf); 2094 } 2095 else 2096 gdk_pixbuf_loader_close (loader, NULL); 2097 } 2098 else 2099 gdk_pixbuf_loader_close (loader, NULL); 2100 g_object_unref (loader); 2101 2102 if (pixbuf) { 2103 gtk_clipboard_set_image (cp, pixbuf); 2104 g_object_unref (pixbuf); 2105 cpset = TRUE; 2106 } 2107 } 2108 } 2109 else 2110 cpset = FALSE; 2111 2112 if (!cpset) { 2113 gchar *str; 2114 GdaDataHandler *dh; 2115 dh = gda_data_handler_get_default (G_VALUE_TYPE (cvalue)); 2116 if (dh) 2117 str = gda_data_handler_get_str_from_value (dh, cvalue); 2118 else 2119 str = gda_value_stringify (cvalue); 2120 gtk_clipboard_set_text (cp, str, -1); 2121 g_free (str); 2122 } 2123 } 2124 2125 static void 2126 menu_show_columns_cb (GtkWidget *widget, GdauiRawGrid *grid) 2127 { 2128 GtkCheckMenuItem *item; 2129 2130 item = (GtkCheckMenuItem *) widget; 2131 2132 g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item)); 2133 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (grid), 2134 gtk_check_menu_item_get_active (item)); 2135 } 2136 2137 static void export_type_changed_cb (GtkComboBox *types, GtkWidget *dialog); 2138 static void save_as_response_cb (GtkDialog *dialog, gint response_id, GdauiRawGrid *grid); 2139 static void 2140 menu_save_as_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid) 2141 { 2142 GtkWidget *dialog; 2143 GtkWidget *label; 2144 GtkWidget *filename; 2145 GtkWidget *types, *scope; 2146 GtkWidget *hbox, *grid1, *check, *dbox; 2147 char *str; 2148 GtkTreeSelection *sel; 2149 gint selrows; 2150 2151 /* create dialog box */ 2152 dialog = gtk_dialog_new_with_buttons (_("Saving Data"), 2153 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (grid))), 0, 2154 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 2155 GTK_STOCK_SAVE, GTK_RESPONSE_OK, 2156 NULL); 2157 gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); 2158 gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); 2159 2160 str = g_strdup_printf ("<big><b>%s:</b></big>\n%s", _("Saving data to a file"), 2161 _("The data will be exported to the selected file.")); 2162 label = gtk_label_new (""); 2163 gtk_label_set_markup (GTK_LABEL (label), str); 2164 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2165 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); 2166 g_free (str); 2167 2168 dbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); 2169 gtk_box_pack_start (GTK_BOX (dbox), label, FALSE, TRUE, 2); 2170 2171 str = g_strdup_printf ("<b>%s:</b>", _("File name")); 2172 label = gtk_label_new (""); 2173 gtk_label_set_markup (GTK_LABEL (label), str); 2174 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2175 g_free (str); 2176 gtk_box_pack_start (GTK_BOX (dbox), label, FALSE, TRUE, 2); 2177 2178 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */ 2179 gtk_box_pack_start (GTK_BOX (dbox), hbox, TRUE, TRUE, 5); 2180 gtk_widget_show (hbox); 2181 label = gtk_label_new (" "); 2182 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); 2183 gtk_widget_show (label); 2184 2185 filename = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_SAVE); 2186 g_object_set_data (G_OBJECT (dialog), "filename", filename); 2187 gtk_box_pack_start (GTK_BOX (hbox), filename, TRUE, TRUE, 0); 2188 gtk_widget_show (filename); 2189 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filename), 2190 gdaui_get_default_path ()); 2191 2192 str = g_strdup_printf ("<b>%s:</b>", _("Details")); 2193 label = gtk_label_new (""); 2194 gtk_label_set_markup (GTK_LABEL (label), str); 2195 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2196 g_free (str); 2197 gtk_box_pack_start (GTK_BOX (dbox), label, FALSE, TRUE, 2); 2198 2199 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */ 2200 gtk_box_pack_start (GTK_BOX (dbox), hbox, FALSE, FALSE, 5); 2201 gtk_widget_show (hbox); 2202 label = gtk_label_new (" "); 2203 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); 2204 gtk_widget_show (label); 2205 2206 grid1 = gtk_grid_new (); 2207 gtk_grid_set_row_spacing (GTK_GRID (grid1), 5); 2208 gtk_grid_set_column_spacing (GTK_GRID (grid1), 5); 2209 gtk_box_pack_start (GTK_BOX (hbox), grid1, TRUE, TRUE, 0); 2210 gtk_widget_show (grid1); 2211 2212 /* file type */ 2213 label = gtk_label_new (_("File type:")); 2214 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2215 gtk_grid_attach (GTK_GRID (grid1), label, 0, 0, 1, 1); 2216 gtk_widget_show (label); 2217 2218 types = gtk_combo_box_text_new (); 2219 gtk_grid_attach (GTK_GRID (grid1), types, 1, 0, 1, 1); 2220 gtk_widget_show (types); 2221 g_object_set_data (G_OBJECT (dialog), "types", types); 2222 2223 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (types), _("Tab-delimited")); 2224 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (types), _("Comma-delimited")); 2225 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (types), _("XML")); 2226 gtk_combo_box_set_active (GTK_COMBO_BOX (types), grid->priv->export_type); 2227 2228 g_signal_connect (types, "changed", 2229 G_CALLBACK (export_type_changed_cb), dialog); 2230 2231 /* data scope */ 2232 label = gtk_label_new (_("Data to save:")); 2233 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2234 gtk_grid_attach (GTK_GRID (grid1), label, 0, 1, 1, 1); 2235 gtk_widget_show (label); 2236 2237 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 2238 selrows = gtk_tree_selection_count_selected_rows (sel); 2239 if (selrows <= 0) 2240 gtk_widget_set_sensitive (label, FALSE); 2241 2242 scope = gtk_combo_box_text_new (); 2243 gtk_grid_attach (GTK_GRID (grid1), scope, 1, 1, 1, 1); 2244 gtk_widget_show (scope); 2245 g_object_set_data (G_OBJECT (dialog), "scope", scope); 2246 2247 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope), _("All data (without any local modification)")); 2248 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope), _("Only displayed data")); 2249 if (selrows > 0) 2250 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope), _("Only selected data")); 2251 gtk_combo_box_set_active (GTK_COMBO_BOX (scope), 0); 2252 2253 /* other options */ 2254 GtkWidget *exp; 2255 exp = gtk_expander_new (_("Other options")); 2256 gtk_grid_attach (GTK_GRID (grid1), exp, 0, 2, 2, 1); 2257 2258 GtkWidget *grid2; 2259 grid2 = gtk_grid_new (); 2260 gtk_container_add (GTK_CONTAINER (exp), grid2); 2261 2262 label = gtk_label_new (_("Empty string when NULL?")); 2263 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2264 gtk_grid_attach (GTK_GRID (grid2), label, 0, 0, 1, 1); 2265 gtk_widget_set_tooltip_text (label, _("Export NULL values as an empty \"\" string")); 2266 2267 check = gtk_check_button_new (); 2268 gtk_grid_attach (GTK_GRID (grid2), check, 1, 0, 1, 1); 2269 g_object_set_data (G_OBJECT (dialog), "null_as_empty", check); 2270 gtk_widget_set_tooltip_text (check, _("Export NULL values as an empty \"\" string")); 2271 2272 label = gtk_label_new (_("Invalid data as NULL?")); 2273 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2274 gtk_grid_attach (GTK_GRID (grid2), label, 2, 0, 1, 1); 2275 gtk_widget_set_tooltip_text (label, _("Don't export invalid data,\nbut export a NULL value instead")); 2276 2277 check = gtk_check_button_new (); 2278 gtk_grid_attach (GTK_GRID (grid2), check, 3, 0, 1, 1); 2279 g_object_set_data (G_OBJECT (dialog), "invalid_as_null", check); 2280 gtk_widget_set_tooltip_text (check, _("Don't export invalid data,\nbut export a NULL value instead")); 2281 2282 label = gtk_label_new (_("Field names on first row?")); 2283 gtk_misc_set_alignment (GTK_MISC (label), 0., -1); 2284 gtk_grid_attach (GTK_GRID (grid2), label, 0, 1, 1, 1); 2285 gtk_widget_set_tooltip_text (label, _("Add a row at beginning with columns names")); 2286 2287 check = gtk_check_button_new (); 2288 gtk_grid_attach (GTK_GRID (grid2), check, 1, 1, 1, 1); 2289 g_object_set_data (G_OBJECT (dialog), "first_row", check); 2290 gtk_widget_set_tooltip_text (check, _("Add a row at beginning with columns names")); 2291 2292 export_type_changed_cb (GTK_COMBO_BOX (types), dialog); 2293 2294 /* run the dialog */ 2295 g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (save_as_response_cb), grid); 2296 gtk_widget_show_all (dialog); 2297 } 2298 2299 static void 2300 export_type_changed_cb (GtkComboBox *types, GtkWidget *dialog) 2301 { 2302 gboolean is_cvs = TRUE; 2303 GtkWidget *wid; 2304 if (gtk_combo_box_get_active (types) == 2) /* XML */ 2305 is_cvs = FALSE; 2306 wid = g_object_get_data (G_OBJECT (dialog), "first_row"); 2307 gtk_widget_set_sensitive (wid, is_cvs); 2308 wid = g_object_get_data (G_OBJECT (dialog), "invalid_as_null"); 2309 gtk_widget_set_sensitive (wid, is_cvs); 2310 wid = g_object_get_data (G_OBJECT (dialog), "null_as_empty"); 2311 gtk_widget_set_sensitive (wid, is_cvs); 2312 } 2313 2314 static gboolean confirm_file_overwrite (GtkWindow *parent, const gchar *path); 2315 2316 static void 2317 save_as_response_cb (GtkDialog *dialog, gint response_id, GdauiRawGrid *grid) 2318 { 2319 if (response_id == GTK_RESPONSE_OK) { 2320 GtkWidget *types; 2321 gint export_type; 2322 GtkWidget *filename; 2323 gboolean selection_only = FALSE; 2324 gboolean null_as_empty = FALSE; 2325 gboolean invalid_as_null = FALSE; 2326 gboolean first_row = FALSE; 2327 gchar *body; 2328 gchar *path; 2329 GList *columns, *list; 2330 gint *cols, nb_cols; 2331 gint *rows = NULL, nb_rows = 0; 2332 GdaHolder *param; 2333 GdaSet *paramlist; 2334 GdaDataModel *model_to_use = (GdaDataModel*) grid->priv->proxy; 2335 GtkWidget *scope; 2336 gint scope_v; 2337 2338 model_to_use = model_to_use; 2339 scope = g_object_get_data (G_OBJECT (dialog), "scope"); 2340 scope_v = gtk_combo_box_get_active (GTK_COMBO_BOX (scope)); 2341 if (scope_v == 0) 2342 model_to_use = grid->priv->data_model; 2343 else if (scope_v == 2) 2344 selection_only = TRUE; 2345 2346 types = g_object_get_data (G_OBJECT (dialog), "types"); 2347 filename = g_object_get_data (G_OBJECT (dialog), "filename"); 2348 gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (filename))); 2349 null_as_empty = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 2350 (g_object_get_data (G_OBJECT (dialog), "null_as_empty"))); 2351 invalid_as_null = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 2352 (g_object_get_data (G_OBJECT (dialog), "invalid_as_null"))); 2353 first_row = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 2354 (g_object_get_data (G_OBJECT (dialog), "first_row"))); 2355 2356 /* output columns computation */ 2357 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid)); 2358 cols = g_new (gint, gda_data_model_get_n_columns (GDA_DATA_MODEL (model_to_use))); 2359 nb_cols = 0; 2360 for (list = columns; list; list = list->next) { 2361 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) { 2362 GdauiSetGroup *group; 2363 GSList *params; 2364 2365 group = g_object_get_data (G_OBJECT (list->data), "__gdaui_group"); 2366 for (params = gda_set_group_get_nodes (gdaui_set_group_get_group (group)) 2367 ; params; nb_cols ++, params = params->next) 2368 cols [nb_cols] = g_slist_index (((GdaSet *)grid->priv->iter)->holders, 2369 gda_set_node_get_holder (GDA_SET_NODE (params->data))); 2370 } 2371 } 2372 g_list_free (columns); 2373 2374 /* output rows computation */ 2375 if (selection_only) { 2376 GtkTreeSelection *selection; 2377 GList *sel_rows, *list; 2378 gint pos; 2379 2380 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 2381 sel_rows = gtk_tree_selection_get_selected_rows (selection, NULL); 2382 2383 nb_rows = g_list_length (sel_rows); 2384 rows = g_new0 (gint, nb_rows); 2385 2386 for (pos = 0, list = sel_rows; list; list = list->next, pos++) { 2387 gint *ind = gtk_tree_path_get_indices ((GtkTreePath*) list->data); 2388 rows [pos] = *ind; 2389 gtk_tree_path_free ((GtkTreePath*) list->data); 2390 } 2391 g_list_free (sel_rows); 2392 } 2393 2394 /* Actual ouput computations */ 2395 export_type = gtk_combo_box_get_active (GTK_COMBO_BOX (types)); 2396 grid->priv->export_type = export_type; 2397 paramlist = gda_set_new (NULL); 2398 2399 if (null_as_empty) { 2400 param = gda_holder_new_boolean ("NULL_AS_EMPTY", TRUE); 2401 gda_set_add_holder (paramlist, param); 2402 g_object_unref (param); 2403 } 2404 if (invalid_as_null) { 2405 param = gda_holder_new_boolean ("INVALID_AS_NULL", TRUE); 2406 gda_set_add_holder (paramlist, param); 2407 g_object_unref (param); 2408 } 2409 if (first_row) { 2410 param = gda_holder_new_boolean ("FIELDS_NAME", TRUE); 2411 gda_set_add_holder (paramlist, param); 2412 g_object_unref (param); 2413 } 2414 2415 switch (export_type) { 2416 case 0: 2417 param = gda_holder_new_string ("SEPARATOR", "\t"); 2418 gda_set_add_holder (paramlist, param); 2419 g_object_unref (param); 2420 body = gda_data_model_export_to_string (GDA_DATA_MODEL (model_to_use), 2421 GDA_DATA_MODEL_IO_TEXT_SEPARATED, 2422 cols, nb_cols, rows, nb_rows, paramlist); 2423 break; 2424 case 1: 2425 param = gda_holder_new_string ("SEPARATOR", ","); 2426 gda_set_add_holder (paramlist, param); 2427 g_object_unref (param); 2428 body = gda_data_model_export_to_string (GDA_DATA_MODEL (model_to_use), 2429 GDA_DATA_MODEL_IO_TEXT_SEPARATED, 2430 cols, nb_cols, rows, nb_rows, paramlist); 2431 break; 2432 case 2: 2433 param = NULL; 2434 body = (gchar *) g_object_get_data (G_OBJECT (model_to_use), "name"); 2435 if (body) 2436 param = gda_holder_new_string ("NAME", body); 2437 if (param) { 2438 gda_set_add_holder (paramlist, param); 2439 g_object_unref (param); 2440 } 2441 body = gda_data_model_export_to_string (GDA_DATA_MODEL (model_to_use), 2442 GDA_DATA_MODEL_IO_DATA_ARRAY_XML, 2443 cols, nb_cols, rows, nb_rows, paramlist); 2444 break; 2445 default: 2446 g_assert_not_reached (); 2447 break; 2448 } 2449 g_object_unref (paramlist); 2450 g_free (cols); 2451 if (rows) 2452 g_free (rows); 2453 2454 /* saving */ 2455 if (body) { 2456 path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filename)); 2457 if (path) { 2458 if (g_file_test (path, G_FILE_TEST_EXISTS)) { 2459 if (! confirm_file_overwrite (GTK_WINDOW (dialog), path)) { 2460 g_free (body); 2461 g_free (path); 2462 return; 2463 } 2464 } 2465 2466 if (! g_file_set_contents (path, body, strlen (body), NULL)) { 2467 _gdaui_utility_show_error (NULL, _("Could not save file %s"), path); 2468 g_free (body); 2469 g_free (path); 2470 return; 2471 } 2472 g_free (path); 2473 } 2474 else { 2475 _gdaui_utility_show_error (NULL, _("You must specify a file name")); 2476 g_free (body); 2477 return; 2478 } 2479 g_free (body); 2480 } else 2481 _gdaui_utility_show_error (NULL,_("Got empty file while converting the data")); 2482 } 2483 2484 gtk_widget_destroy (GTK_WIDGET (dialog)); 2485 } 2486 2487 static gboolean 2488 confirm_file_overwrite (GtkWindow *parent, const gchar *path) 2489 { 2490 GtkWidget *dialog, *button; 2491 gboolean yes; 2492 gchar *msg; 2493 2494 msg = g_strdup_printf (_("File '%s' already exists.\n" 2495 "Do you want to overwrite it?"), path); 2496 2497 /* build the dialog */ 2498 dialog = gtk_message_dialog_new_with_markup (parent, 2499 GTK_DIALOG_DESTROY_WITH_PARENT | 2500 GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, 2501 GTK_BUTTONS_YES_NO, 2502 "<span weight=\"bold\">%s</span>\n%s\n", 2503 msg, 2504 _("If you choose yes, the contents will be lost.")); 2505 g_free (msg); 2506 2507 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); 2508 gtk_widget_set_can_default (button, TRUE); 2509 gtk_dialog_set_default_response (GTK_DIALOG (dialog), 2510 GTK_RESPONSE_NO); 2511 2512 /* run the dialog */ 2513 gtk_widget_show_all (dialog); 2514 yes = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES; 2515 2516 /* free memory */ 2517 gtk_widget_destroy (dialog); 2518 2519 return yes; 2520 } 2521 2522 static void 2523 menu_set_filter_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid) 2524 { 2525 action_filter_cb (NULL, grid); 2526 } 2527 2528 static void 2529 menu_unset_filter_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid) 2530 { 2531 gda_data_proxy_set_filter_expr (grid->priv->proxy, NULL, NULL); 2532 } 2533 2534 2535 static void 2536 tree_view_row_activated_cb (G_GNUC_UNUSED GtkTreeView *tree_view, GtkTreePath *path, 2537 G_GNUC_UNUSED GtkTreeViewColumn *column, GdauiRawGrid *grid) 2538 { 2539 gint *indices; 2540 2541 indices = gtk_tree_path_get_indices (path); 2542 #ifdef debug_signal 2543 g_print (">> 'DOUBLE_CLICKED' from %s %p\n", G_OBJECT_TYPE_NAME (grid), grid); 2544 #endif 2545 g_signal_emit (G_OBJECT (grid), gdaui_raw_grid_signals[DOUBLE_CLICKED], 0, *indices); 2546 #ifdef debug_signal 2547 g_print ("<< 'DOUBLE_CLICKED' from %s %p\n", G_OBJECT_TYPE_NAME (grid), grid); 2548 #endif 2549 } 2550 2551 /* 2552 * Synchronize the values of the parameters in priv->iter with the currently selected row 2553 */ 2554 static void 2555 tree_view_selection_changed_cb (GtkTreeSelection *selection, GdauiRawGrid *grid) 2556 { 2557 GtkTreeIter iter; 2558 GtkTreeModel *model; 2559 gint has_selection = 0; 2560 2561 /* block the GdaDataModelIter' "changed" signal */ 2562 g_signal_handlers_block_by_func (grid->priv->iter, 2563 G_CALLBACK (iter_row_changed_cb), grid); 2564 2565 if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE) { 2566 has_selection = gtk_tree_selection_count_selected_rows (selection); 2567 if (has_selection == 1) { 2568 GList *sel_rows; 2569 2570 sel_rows = gtk_tree_selection_get_selected_rows (selection, &model); 2571 has_selection = gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (sel_rows->data)) ? 1 : 0; 2572 g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL); 2573 g_list_free (sel_rows); 2574 } 2575 } 2576 else 2577 has_selection = gtk_tree_selection_get_selected (selection, &model, &iter) ? 1 : 0; 2578 2579 if (has_selection == 1) { 2580 if (!gda_data_model_iter_move_to_row (grid->priv->iter, 2581 gdaui_data_store_get_row_from_iter (grid->priv->store, 2582 &iter))) { 2583 /* selection changing is refused, return to the current selected row */ 2584 GtkTreePath *path; 2585 path = gtk_tree_path_new_from_indices (gda_data_model_iter_get_row (grid->priv->iter), -1); 2586 g_signal_handlers_block_by_func (G_OBJECT (selection), 2587 G_CALLBACK (tree_view_selection_changed_cb), grid); 2588 2589 gtk_tree_selection_unselect_all (selection); 2590 gtk_tree_selection_select_path (selection, path); 2591 g_signal_handlers_unblock_by_func (G_OBJECT (selection), 2592 G_CALLBACK (tree_view_selection_changed_cb), grid); 2593 2594 gtk_tree_path_free (path); 2595 } 2596 } 2597 else { 2598 /* render all the parameters invalid, and make the iter point to row -1 */ 2599 gda_data_model_iter_invalidate_contents (grid->priv->iter); 2600 gda_data_model_iter_move_to_row (grid->priv->iter, -1); 2601 } 2602 2603 g_signal_emit_by_name (G_OBJECT (grid), "selection-changed"); 2604 2605 /* unblock the GdaDataModelIter' "changed" signal */ 2606 g_signal_handlers_unblock_by_func (grid->priv->iter, 2607 G_CALLBACK (iter_row_changed_cb), grid); 2608 } 2609 2610 static void 2611 destroy_column_data (GdauiRawGrid *grid) 2612 { 2613 if (grid->priv->columns_data) { 2614 GSList *list; 2615 for (list = grid->priv->columns_data; list; list = list->next) { 2616 ColumnData *cdata = COLUMN_DATA (list->data); 2617 g_object_unref (cdata->data_cell); 2618 g_object_unref (cdata->info_cell); 2619 g_free (cdata->tooltip_text); 2620 g_free (cdata->title); 2621 g_free (cdata); 2622 } 2623 g_slist_free (grid->priv->columns_data); 2624 grid->priv->columns_data = NULL; 2625 g_hash_table_remove_all (grid->priv->columns_hash); 2626 } 2627 } 2628 2629 static ColumnData * 2630 get_column_data_for_group (GdauiRawGrid *grid, GdauiSetGroup *group) 2631 { 2632 GSList *list; 2633 for (list = grid->priv->columns_data; list; list = list->next) { 2634 if (COLUMN_DATA (list->data)->group == group) 2635 return COLUMN_DATA (list->data); 2636 } 2637 2638 return NULL; 2639 } 2640 2641 static ColumnData * 2642 get_column_data_for_holder (GdauiRawGrid *grid, GdaHolder *holder) 2643 { 2644 GSList *list; 2645 for (list = grid->priv->columns_data; list; list = list->next) { 2646 if (COLUMN_DATA (list->data)->single_param == holder) 2647 return COLUMN_DATA (list->data); 2648 } 2649 2650 return NULL; 2651 } 2652 2653 static ColumnData * 2654 get_column_data_for_id (GdauiRawGrid *grid, const gchar *id) 2655 { 2656 GSList *list; 2657 for (list = grid->priv->columns_data; list; list = list->next) { 2658 ColumnData *cdata = COLUMN_DATA (list->data); 2659 if (cdata->title && !strcmp (cdata->title, id)) 2660 return cdata; 2661 if (cdata->single_param) { 2662 const gchar *hid; 2663 hid = gda_holder_get_id (cdata->single_param); 2664 if (hid && !strcmp (hid, id)) 2665 return cdata; 2666 } 2667 } 2668 2669 return NULL; 2670 } 2671 2672 /** 2673 * gdaui_raw_grid_set_sample_size: 2674 * @grid: a #GdauiRawGrid 2675 * @sample_size: the size of the sample displayed in @grid 2676 * 2677 * Sets the size of each chunk of data to display: the maximum number of rows which 2678 * can be displayed at a time. See gdaui_grid_set_sample_size() and gda_data_proxy_set_sample_size() 2679 * 2680 * Since: 4.2 2681 */ 2682 void 2683 gdaui_raw_grid_set_sample_size (GdauiRawGrid *grid, gint sample_size) 2684 { 2685 g_return_if_fail (grid && GDAUI_IS_RAW_GRID (grid)); 2686 g_return_if_fail (grid->priv); 2687 2688 gda_data_proxy_set_sample_size (grid->priv->proxy, sample_size); 2689 } 2690 2691 /** 2692 * gdaui_raw_grid_set_sample_start: 2693 * @grid: a #GdauiRawGrid 2694 * @sample_start: 2695 * 2696 * 2697 * Since: 4.2 2698 */ 2699 void 2700 gdaui_raw_grid_set_sample_start (GdauiRawGrid *grid, gint sample_start) 2701 { 2702 g_return_if_fail (grid && GDAUI_IS_RAW_GRID (grid)); 2703 g_return_if_fail (grid->priv); 2704 2705 gda_data_proxy_set_sample_start (grid->priv->proxy, sample_start); 2706 } 2707 2708 /** 2709 * gdaui_raw_grid_set_layout_from_file: 2710 * @grid: a #GdauiRawGrid 2711 * @file_name: XML file name to use 2712 * @grid_name: the name of the grid to use, in @file_name 2713 * 2714 * Sets a grid's columns layout according an XML description contained in @file_name, for the grid identified 2715 * by the @grid_name name (as an XML layout file can contain the descriptions of several forms and grids). 2716 * 2717 * Since: 4.2 2718 */ 2719 void 2720 gdaui_raw_grid_set_layout_from_file (GdauiRawGrid *grid, const gchar *file_name, const gchar *grid_name) 2721 { 2722 g_return_if_fail (GDAUI_IS_RAW_GRID (grid)); 2723 g_return_if_fail (file_name); 2724 g_return_if_fail (grid_name); 2725 2726 xmlDocPtr doc; 2727 doc = xmlParseFile (file_name); 2728 if (doc == NULL) { 2729 g_warning (_("'%s' document not parsed successfully"), file_name); 2730 return; 2731 } 2732 2733 xmlDtdPtr dtd = NULL; 2734 2735 gchar *file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "dtd", "gdaui-layout.dtd", NULL); 2736 if (g_file_test (file, G_FILE_TEST_EXISTS)) 2737 dtd = xmlParseDTD (NULL, BAD_CAST file); 2738 if (dtd == NULL) { 2739 g_warning (_("'%s' DTD not parsed successfully. " 2740 "XML data layout validation will not be " 2741 "performed (some errors may occur)"), file); 2742 } 2743 g_free (file); 2744 2745 /* Get the root element node */ 2746 xmlNodePtr root_node = NULL; 2747 root_node = xmlDocGetRootElement (doc); 2748 2749 /* Must have root element, a name and the name must be "gdaui_layouts" */ 2750 if (!root_node || !root_node->name || 2751 ! xmlStrEqual (root_node->name, BAD_CAST "gdaui_layouts")) { 2752 xmlFreeDoc (doc); 2753 return; 2754 } 2755 2756 xmlNodePtr node; 2757 for (node = root_node->children; node; node = node->next) { 2758 if ((node->type == XML_ELEMENT_NODE) && 2759 xmlStrEqual (node->name, BAD_CAST "gdaui_grid")) { 2760 xmlChar *str; 2761 str = xmlGetProp (node, BAD_CAST "name"); 2762 if (str) { 2763 if (!strcmp ((gchar*) str, grid_name)) { 2764 g_object_set (G_OBJECT (grid), "xml-layout", node, NULL); 2765 xmlFree (str); 2766 break; 2767 } 2768 xmlFree (str); 2769 } 2770 } 2771 } 2772 2773 /* Free the document */ 2774 xmlFreeDoc (doc); 2775 } 2776 2777 /* 2778 * GdauiDataProxy interface 2779 */ 2780 2781 static GdaDataProxy * 2782 gdaui_raw_grid_get_proxy (GdauiDataProxy *iface) 2783 { 2784 GdauiRawGrid *grid; 2785 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL); 2786 grid = GDAUI_RAW_GRID (iface); 2787 g_return_val_if_fail (grid->priv, NULL); 2788 2789 return grid->priv->proxy; 2790 } 2791 2792 static void 2793 gdaui_raw_grid_set_column_editable (GdauiDataProxy *iface, gint column, gboolean editable) 2794 { 2795 GdauiRawGrid *grid; 2796 GdaHolder *param; 2797 ColumnData *cdata; 2798 GdauiSetGroup *group; 2799 2800 g_return_if_fail (GDAUI_IS_RAW_GRID (iface)); 2801 grid = GDAUI_RAW_GRID (iface); 2802 g_return_if_fail (grid->priv); 2803 2804 if (grid->priv->data_model) { 2805 editable = editable && !gda_data_proxy_is_read_only (grid->priv->proxy); 2806 2807 param = gda_data_model_iter_get_holder_for_field (grid->priv->iter, column); 2808 g_return_if_fail (param); 2809 2810 group = _gdaui_set_get_group (grid->priv->iter_info, param); 2811 g_return_if_fail (group); 2812 2813 cdata = get_column_data_for_group (grid, group); 2814 g_return_if_fail (cdata); 2815 2816 if (editable && !gda_data_proxy_is_read_only (grid->priv->proxy)) 2817 cdata->data_locked = FALSE; 2818 else 2819 cdata->data_locked = TRUE; 2820 } 2821 } 2822 2823 static void 2824 gdaui_raw_grid_show_column_actions (GdauiDataProxy *iface, gint column, gboolean show_actions) 2825 { 2826 GdauiRawGrid *grid; 2827 2828 g_return_if_fail (GDAUI_IS_RAW_GRID (iface)); 2829 grid = GDAUI_RAW_GRID (iface); 2830 g_return_if_fail (grid->priv); 2831 2832 if (column >= 0) { 2833 GdaHolder *param; 2834 GdauiSetGroup *group; 2835 ColumnData *cdata; 2836 2837 /* setting applies only to the @column column */ 2838 param = gda_data_model_iter_get_holder_for_field (grid->priv->iter, column); 2839 g_return_if_fail (param); 2840 2841 group = _gdaui_set_get_group (grid->priv->iter_info, param); 2842 g_return_if_fail (group); 2843 2844 cdata = get_column_data_for_group (grid, group); 2845 g_return_if_fail (cdata); 2846 2847 if (show_actions != cdata->info_shown) { 2848 cdata->info_shown = show_actions; 2849 g_object_set (G_OBJECT (cdata->info_cell), "visible", cdata->info_shown, NULL); 2850 } 2851 } 2852 else { 2853 /* setting applies to all columns */ 2854 GSList *list; 2855 for (list = grid->priv->columns_data; list; list = list->next) { 2856 ColumnData *cdata = (ColumnData*)(list->data); 2857 if (show_actions != cdata->info_shown) { 2858 cdata->info_shown = show_actions; 2859 g_object_set (G_OBJECT (cdata->info_cell), "visible", cdata->info_shown, NULL); 2860 } 2861 } 2862 grid->priv->default_show_info_cell = show_actions; 2863 } 2864 } 2865 2866 static GtkActionGroup * 2867 gdaui_raw_grid_get_actions_group (GdauiDataProxy *iface) 2868 { 2869 GdauiRawGrid *grid; 2870 2871 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL); 2872 grid = GDAUI_RAW_GRID (iface); 2873 g_return_val_if_fail (grid->priv, NULL); 2874 2875 return grid->priv->actions_group; 2876 } 2877 2878 static void 2879 paramlist_public_data_changed_cb (G_GNUC_UNUSED GdauiSet *info, GdauiRawGrid *grid) 2880 { 2881 /* info cells */ 2882 destroy_column_data (grid); 2883 create_columns_data (grid); 2884 reset_columns_default (grid); 2885 } 2886 2887 static void 2888 paramlist_param_attr_changed_cb (G_GNUC_UNUSED GdaSet *paramlist, GdaHolder *param, 2889 const gchar *att_name, const GValue *att_value, GdauiRawGrid *grid) 2890 { 2891 if (!strcmp (att_name, GDAUI_ATTRIBUTE_PLUGIN)) { 2892 ColumnData *cdata; 2893 const gchar *plugin = NULL; 2894 cdata = get_column_data_for_holder (grid, param); 2895 if (!cdata) 2896 return; 2897 2898 if (att_value) { 2899 if (G_VALUE_TYPE (att_value) == G_TYPE_STRING) 2900 plugin = g_value_get_string (att_value); 2901 else { 2902 g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"), 2903 GDAUI_ATTRIBUTE_PLUGIN); 2904 return; 2905 } 2906 } 2907 2908 /* get rid of previous renderer */ 2909 g_signal_handlers_disconnect_by_func (G_OBJECT (cdata->data_cell), 2910 G_CALLBACK (data_cell_value_changed), grid); 2911 g_hash_table_remove (grid->priv->columns_hash, cdata->data_cell); 2912 g_object_unref (cdata->data_cell); 2913 2914 /* create new renderer */ 2915 GtkCellRenderer *renderer; 2916 gint model_col; 2917 renderer = _gdaui_new_cell_renderer (gda_holder_get_g_type (param), plugin); 2918 cdata->data_cell = g_object_ref_sink ((GObject*) renderer); 2919 g_hash_table_insert (grid->priv->columns_hash, renderer, cdata); 2920 2921 model_col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, param); 2922 g_object_set_data (G_OBJECT (renderer), "model_col", GINT_TO_POINTER (model_col)); 2923 2924 g_object_set (G_OBJECT (renderer), "editable", !cdata->data_locked, NULL); 2925 if (g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), "set-default-if-invalid")) 2926 g_object_set (G_OBJECT (renderer), "set-default-if-invalid", TRUE, NULL); 2927 2928 g_signal_connect (G_OBJECT (renderer), "changed", 2929 G_CALLBACK (data_cell_value_changed), grid); 2930 2931 /* replace column */ 2932 GtkTreeViewColumn *column; 2933 gint pos; 2934 pos = -1; 2935 column = cdata->column; 2936 if (column) { 2937 GList *cols; 2938 cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid)); 2939 pos = g_list_index (cols, column); 2940 g_list_free (cols); 2941 gtk_tree_view_remove_column (GTK_TREE_VIEW (grid), column); 2942 } 2943 create_tree_view_column (grid, cdata, pos); 2944 } 2945 else if (!strcmp (att_name, GDA_ATTRIBUTE_NAME)) { 2946 ColumnData *cdata; 2947 cdata = get_column_data_for_holder (grid, param); 2948 if (!cdata) 2949 return; 2950 if (att_value) { 2951 if (G_VALUE_TYPE (att_value) == G_TYPE_STRING) { 2952 g_free (cdata->title); 2953 cdata->title = g_value_dup_string (att_value); 2954 gtk_tree_view_column_set_title (cdata->column, cdata->title); 2955 } 2956 else 2957 g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"), 2958 GDA_ATTRIBUTE_NAME); 2959 } 2960 } 2961 } 2962 2963 static GError * 2964 iter_validate_set_cb (GdaDataModelIter *iter, GdauiRawGrid *grid) 2965 { 2966 GError *error = NULL; 2967 gint row = gda_data_model_iter_get_row (iter); 2968 2969 if (row < 0) 2970 return NULL; 2971 2972 if ((grid->priv->write_mode >= GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) && 2973 /* write back the current row */ 2974 gda_data_proxy_row_has_changed (grid->priv->proxy, row) && 2975 !gda_data_proxy_apply_row_changes (grid->priv->proxy, row, &error)) { 2976 if (_gdaui_utility_display_error_with_keep_or_discard_choice ((GdauiDataProxy *) grid, 2977 error)) { 2978 gda_data_proxy_cancel_row_changes (grid->priv->proxy, row, -1); 2979 if (error) { 2980 g_error_free (error); 2981 error = NULL; 2982 } 2983 } 2984 } 2985 2986 return error; 2987 } 2988 2989 static void 2990 iter_row_changed_cb (G_GNUC_UNUSED GdaDataModelIter *iter, gint row, GdauiRawGrid *grid) 2991 { 2992 GtkTreeSelection *selection; 2993 GtkTreePath *path; 2994 2995 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 2996 2997 if (row >= 0) { 2998 GtkSelectionMode mode; 2999 GtkTreeIter treeiter; 3000 3001 mode = gtk_tree_selection_get_mode (selection); 3002 if (mode != GTK_SELECTION_SINGLE) 3003 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); 3004 path = gtk_tree_path_new_from_indices (row, -1); 3005 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &treeiter, path)) { 3006 gtk_tree_selection_select_path (selection, path); 3007 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (grid), path, NULL, 3008 FALSE, 0., 0.); 3009 } 3010 gtk_tree_path_free (path); 3011 if (mode != GTK_SELECTION_SINGLE) 3012 gtk_tree_selection_set_mode (selection, mode); 3013 } 3014 else 3015 gtk_tree_selection_unselect_all (selection); 3016 } 3017 3018 static void 3019 proxy_sample_changed_cb (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED gint sample_start, 3020 G_GNUC_UNUSED gint sample_end, GdauiRawGrid *grid) 3021 { 3022 /* bring back the vertical scrollbar to the top */ 3023 gtk_adjustment_set_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (grid)), 0.); 3024 } 3025 3026 static void 3027 proxy_row_updated_cb (GdaDataProxy *proxy, gint proxy_row, GdauiRawGrid *grid) 3028 { 3029 if (grid->priv->write_mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_ACTIVATED) { 3030 gint row; 3031 3032 /* REM: this may lead to problems in the proxy because it is not re-entrant, solution: add a very short timeout */ 3033 row = gda_data_model_iter_get_row (grid->priv->iter); 3034 if ((row >= 0) && (row == proxy_row)) { 3035 /* write back the current row */ 3036 if (gda_data_proxy_row_has_changed (grid->priv->proxy, row)) { 3037 GError *error = NULL; 3038 3039 /* If the write fails, the proxy sets the fields back to was they were. We do not want 3040 * te get notified about this because we would the try again to write to the database, 3041 * which would fail again, and so on. */ 3042 g_signal_handlers_block_by_func(G_OBJECT(proxy), G_CALLBACK(proxy_row_updated_cb), grid); 3043 3044 if (!gda_data_proxy_apply_row_changes (grid->priv->proxy, row, &error)) { 3045 gboolean discard; 3046 discard = _gdaui_utility_display_error_with_keep_or_discard_choice ((GdauiDataProxy *) grid, 3047 error); 3048 if (discard) 3049 gda_data_proxy_cancel_row_changes (grid->priv->proxy, row, -1); 3050 g_error_free (error); 3051 } 3052 3053 g_signal_handlers_unblock_by_func(G_OBJECT(proxy), G_CALLBACK(proxy_row_updated_cb), grid); 3054 } 3055 } 3056 } 3057 } 3058 3059 static gboolean 3060 model_reset_was_soft (GdauiRawGrid *grid, GdaDataModel *new_model) 3061 { 3062 GdaDataModelIter *iter; 3063 gboolean retval = FALSE; 3064 3065 if (!new_model) 3066 return FALSE; 3067 else if (new_model == (GdaDataModel*) grid->priv->proxy) 3068 return TRUE; 3069 else if (!grid->priv->iter) 3070 return FALSE; 3071 3072 iter = gda_data_model_create_iter (new_model); 3073 retval = ! _gdaui_utility_iter_differ (grid->priv->iter, iter); 3074 g_object_unref (iter); 3075 return retval; 3076 } 3077 3078 static void 3079 proxy_reset_pre_cb (GdaDataProxy *proxy, GdauiRawGrid *grid) 3080 { 3081 grid->priv->iter_row = gda_data_model_iter_get_row (grid->priv->iter); 3082 grid->priv->reset_soft = model_reset_was_soft (grid, gda_data_proxy_get_proxied_model (grid->priv->proxy)); 3083 } 3084 3085 static void 3086 proxy_reset_cb (GdaDataProxy *proxy, GdauiRawGrid *grid) 3087 { 3088 GdkRectangle vis = {0, 0, 0, 0}; 3089 if (gtk_widget_get_realized ((GtkWidget*) grid)) 3090 gtk_tree_view_get_visible_rect ((GtkTreeView*) grid, &vis); 3091 3092 if (grid->priv->iter_row >= 0) 3093 gda_data_model_iter_move_to_row (grid->priv->iter, grid->priv->iter_row); 3094 else 3095 gda_data_model_iter_invalidate_contents (grid->priv->iter); 3096 grid->priv->iter_row = -1; 3097 grid->priv->data_model = gda_data_proxy_get_proxied_model (grid->priv->proxy); 3098 3099 if (! grid->priv->reset_soft) 3100 g_signal_emit_by_name (grid, "proxy-changed", grid->priv->proxy); 3101 3102 if (gtk_widget_get_realized ((GtkWidget*) grid)) 3103 gtk_tree_view_scroll_to_point ((GtkTreeView*) grid, vis.x, vis.y); 3104 } 3105 3106 static void 3107 gdaui_raw_grid_clean (GdauiRawGrid *grid) 3108 { 3109 /* column data */ 3110 destroy_column_data (grid); 3111 3112 /* store */ 3113 if (grid->priv->store) { 3114 g_object_unref (grid->priv->store); 3115 grid->priv->store = NULL; 3116 } 3117 3118 /* private data set */ 3119 if (grid->priv->iter) { 3120 g_signal_handlers_disconnect_by_func (grid->priv->iter_info, 3121 G_CALLBACK (paramlist_public_data_changed_cb), grid); 3122 g_signal_handlers_disconnect_by_func (grid->priv->iter, 3123 G_CALLBACK (paramlist_param_attr_changed_cb), grid); 3124 g_signal_handlers_disconnect_by_func (grid->priv->iter, 3125 G_CALLBACK (iter_row_changed_cb), grid); 3126 g_signal_handlers_disconnect_by_func (grid->priv->iter, 3127 G_CALLBACK (iter_validate_set_cb), grid); 3128 g_object_unref (grid->priv->iter); 3129 g_object_unref (grid->priv->iter_info); 3130 grid->priv->iter = NULL; 3131 grid->priv->iter_info = NULL; 3132 } 3133 3134 /* proxy */ 3135 if (grid->priv->proxy) { 3136 g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_sample_changed_cb), grid); 3137 g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_row_updated_cb), grid); 3138 g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_reset_pre_cb), grid); 3139 g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_reset_cb), grid); 3140 g_object_unref (grid->priv->proxy); 3141 grid->priv->proxy = NULL; 3142 } 3143 } 3144 3145 static gboolean 3146 gdaui_raw_grid_widget_set_write_mode (GdauiDataProxy *iface, GdauiDataProxyWriteMode mode) 3147 { 3148 GdauiRawGrid *grid; 3149 3150 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), FALSE); 3151 grid = GDAUI_RAW_GRID (iface); 3152 g_return_val_if_fail (grid->priv, FALSE); 3153 3154 grid->priv->write_mode = mode; 3155 if (mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_CHANGE) { 3156 grid->priv->write_mode = GDAUI_DATA_PROXY_WRITE_ON_VALUE_ACTIVATED; 3157 return FALSE; 3158 } 3159 3160 if (mode == GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) { 3161 GtkTreeSelection *selection; 3162 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 3163 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); 3164 } 3165 return TRUE; 3166 } 3167 3168 static GdauiDataProxyWriteMode 3169 gdaui_raw_grid_widget_get_write_mode (GdauiDataProxy *iface) 3170 { 3171 GdauiRawGrid *grid; 3172 3173 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), GDAUI_DATA_PROXY_WRITE_ON_DEMAND); 3174 grid = GDAUI_RAW_GRID (iface); 3175 g_return_val_if_fail (grid->priv, GDAUI_DATA_PROXY_WRITE_ON_DEMAND); 3176 3177 return grid->priv->write_mode; 3178 } 3179 3180 /* GdauiDataSelector interface */ 3181 static GdaDataModel * 3182 gdaui_raw_grid_selector_get_model (GdauiDataSelector *iface) 3183 { 3184 GdauiRawGrid *grid; 3185 3186 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL); 3187 grid = GDAUI_RAW_GRID (iface); 3188 3189 return GDA_DATA_MODEL (grid->priv->proxy); 3190 } 3191 3192 static void 3193 gdaui_raw_grid_selector_set_model (GdauiDataSelector *iface, GdaDataModel *model) 3194 { 3195 GdauiRawGrid *grid; 3196 3197 g_return_if_fail (GDAUI_IS_RAW_GRID (iface)); 3198 grid = GDAUI_RAW_GRID (iface); 3199 3200 g_object_set (grid, "model", model, NULL); 3201 } 3202 3203 static GArray * 3204 gdaui_raw_grid_selector_get_selected_rows (GdauiDataSelector *iface) 3205 { 3206 GtkTreeSelection *selection; 3207 GList *selected_rows; 3208 GdauiRawGrid *grid; 3209 3210 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL); 3211 grid = GDAUI_RAW_GRID (iface); 3212 3213 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 3214 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL); 3215 if (selected_rows) { 3216 GList *list; 3217 GArray *selarray = NULL; 3218 GtkTreeIter iter; 3219 gint row; 3220 3221 for (list = selected_rows; list; list = list->next) { 3222 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter, 3223 (GtkTreePath *)(list->data))) { 3224 gint *ind; 3225 ind = gtk_tree_path_get_indices ((GtkTreePath *)(list->data)); 3226 g_assert (ind); 3227 row = *ind; 3228 if (!selarray) 3229 selarray = g_array_new (FALSE, FALSE, sizeof (gint)); 3230 g_array_append_val (selarray, row); 3231 } 3232 } 3233 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL); 3234 g_list_free (selected_rows); 3235 return selarray; 3236 } 3237 else 3238 return NULL; 3239 } 3240 3241 static GdaDataModelIter * 3242 gdaui_raw_grid_selector_get_data_set (GdauiDataSelector *iface) 3243 { 3244 GdauiRawGrid *grid; 3245 3246 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL); 3247 grid = GDAUI_RAW_GRID (iface); 3248 3249 return grid->priv->iter; 3250 } 3251 3252 static gboolean 3253 gdaui_raw_grid_selector_select_row (GdauiDataSelector *iface, gint row) 3254 { 3255 GdauiRawGrid *grid; 3256 GtkTreeSelection *selection; 3257 GtkTreePath *path; 3258 gboolean retval = TRUE; 3259 GtkTreeIter iter; 3260 3261 g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), FALSE); 3262 grid = GDAUI_RAW_GRID (iface); 3263 3264 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 3265 path = gtk_tree_path_new_from_indices (row, -1); 3266 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter, path)) 3267 gtk_tree_selection_select_path (selection, path); 3268 else 3269 retval = FALSE; 3270 gtk_tree_path_free (path); 3271 3272 return retval; 3273 } 3274 3275 static void 3276 gdaui_raw_grid_selector_unselect_row (GdauiDataSelector *iface, gint row) 3277 { 3278 GdauiRawGrid *grid; 3279 GtkTreeSelection *selection; 3280 GtkTreePath *path; 3281 GtkTreeIter iter; 3282 3283 g_return_if_fail (GDAUI_IS_RAW_GRID (iface)); 3284 grid = GDAUI_RAW_GRID (iface); 3285 3286 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid)); 3287 path = gtk_tree_path_new_from_indices (row, -1); 3288 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter, path)) 3289 gtk_tree_selection_unselect_path (selection, path); 3290 gtk_tree_path_free (path); 3291 } 3292 3293 static void 3294 gdaui_raw_grid_selector_set_column_visible (GdauiDataSelector *iface, gint column, gboolean visible) 3295 { 3296 GdauiRawGrid *grid; 3297 gint pos = -1; 3298 GtkTreeViewColumn *viewcol; 3299 GdaSetGroup *group; 3300 GdaHolder *param; 3301 3302 g_return_if_fail (GDAUI_IS_RAW_GRID (iface)); 3303 grid = GDAUI_RAW_GRID (iface); 3304 g_return_if_fail (grid->priv); 3305 3306 param = gda_data_model_iter_get_holder_for_field (grid->priv->iter, column); 3307 g_return_if_fail (param); 3308 3309 group = gda_set_get_group ((GdaSet *)grid->priv->iter, param); 3310 pos = g_slist_index (((GdaSet *)grid->priv->iter)->groups_list, group); 3311 g_assert (pos >= 0); 3312 3313 viewcol = gtk_tree_view_get_column (GTK_TREE_VIEW (grid), pos); 3314 3315 /* Sets the column's visibility */ 3316 gtk_tree_view_column_set_visible (viewcol, visible); 3317 ColumnData *cdata; 3318 cdata = g_slist_nth_data (grid->priv->columns_data, column); 3319 g_assert (cdata); 3320 cdata->prog_hidden = !visible; 3321 } 3322