1 /*
2 * Copyright (C) 2006 - 2011 Murray Cumming <murrayc@murrayc.com>
3 * Copyright (C) 2006 - 2012 Vivien Malerba <malerba@gnome-db.org>
4 * Copyright (C) 2007 Armin Burgmeier <arminb@src.gnome.org>
5 * Copyright (C) 2007 Leonardo Boshell <lb@kmc.com.co>
6 * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
7 * Copyright (C) 2010 David King <davidk@openismus.com>
8 * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
9 * Copyright (C) 2011 - 2012 Daniel Espinosa <despinosa@src.gnome.org>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
25 */
26
27 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 #include "gda-data-proxy.h"
30 #include "gda-data-model.h"
31 #include "gda-data-model-array.h"
32 #include "gda-data-model-extra.h"
33 #include "gda-data-model-iter.h"
34 #include "gda-data-select.h"
35 #include "gda-holder.h"
36 #include "gda-set.h"
37 #include "gda-marshal.h"
38 #include "gda-enums.h"
39 #include "gda-util.h"
40 #include "gda-marshal.h"
41 #include "gda-data-access-wrapper.h"
42 #include "gda-enum-types.h"
43 #include <virtual/libgda-virtual.h>
44 #include <sql-parser/gda-sql-parser.h>
45 #include <sql-parser/gda-sql-statement.h>
46 #include <sql-parser/gda-statement-struct-util.h>
47 #include <libgda/gda-custom-marshal.h>
48 #include <libgda/gda-types.h>
49 #include <gda-mutex.h>
50
51 /*
52 * Main static functions
53 */
54 static void gda_data_proxy_class_init (GdaDataProxyClass * class);
55 static void gda_data_proxy_init (GdaDataProxy *srv);
56 static void gda_data_proxy_dispose (GObject *object);
57 static void gda_data_proxy_finalize (GObject *object);
58
59 static void gda_data_proxy_set_property (GObject *object,
60 guint param_id,
61 const GValue *value,
62 GParamSpec *pspec);
63 static void gda_data_proxy_get_property (GObject *object,
64 guint param_id,
65 GValue *value,
66 GParamSpec *pspec);
67 /* GdaDataModel interface */
68 static void gda_data_proxy_data_model_init (GdaDataModelIface *iface);
69
70 static gint gda_data_proxy_get_n_rows (GdaDataModel *model);
71 static gint gda_data_proxy_get_n_columns (GdaDataModel *model);
72 static GdaColumn *gda_data_proxy_describe_column (GdaDataModel *model, gint col);
73 static const GValue *gda_data_proxy_get_value_at (GdaDataModel *model, gint col, gint row, GError **error);
74 static GdaValueAttribute gda_data_proxy_get_attributes_at (GdaDataModel *model, gint col, gint row);
75
76
77 static GdaDataModelAccessFlags gda_data_proxy_get_access_flags(GdaDataModel *model);
78
79 static gboolean gda_data_proxy_set_value_at (GdaDataModel *model, gint col, gint row,
80 const GValue *value, GError **error);
81 static gboolean gda_data_proxy_set_values (GdaDataModel *model, gint row,
82 GList *values, GError **error);
83 static gint gda_data_proxy_find_row_from_values (GdaDataModel *model, GSList *values,
84 gint *cols_index);
85
86 static gint gda_data_proxy_append_values (GdaDataModel *model, const GList *values, GError **error);
87 static gint gda_data_proxy_append_row (GdaDataModel *model, GError **error);
88 static gboolean gda_data_proxy_remove_row (GdaDataModel *model, gint row, GError **error);
89
90 static void gda_data_proxy_set_notify (GdaDataModel *model, gboolean do_notify_changes);
91 static gboolean gda_data_proxy_get_notify (GdaDataModel *model);
92 static void gda_data_proxy_send_hint (GdaDataModel *model, GdaDataModelHint hint,
93 const GValue *hint_value);
94
95 /* cache management */
96 static void clean_cached_changes (GdaDataProxy *proxy);
97 static void migrate_current_changes_to_cache (GdaDataProxy *proxy);
98 static void fetch_current_cached_changes (GdaDataProxy *proxy);
99
100 #define DEBUG_SYNC
101 #undef DEBUG_SYNC
102
103 /* get a pointer to the parents to be able to call their destructor */
104 static GObjectClass *parent_class = NULL;
105 extern GdaAttributesManager *gda_holder_attributes_manager;
106
107 static GMutex parser_mutex;
108 static GdaSqlParser *internal_parser;
109 static GMutex provider_mutex;
110 static GdaVirtualProvider *virtual_provider = NULL;
111
112 /* signals */
113 enum
114 {
115 ROW_DELETE_CHANGED,
116 SAMPLE_SIZE_CHANGED,
117 SAMPLE_CHANGED,
118 VALIDATE_ROW_CHANGES,
119 ROW_CHANGES_APPLIED,
120 FILTER_CHANGED,
121 LAST_SIGNAL
122 };
123
124 static gint gda_data_proxy_signals[LAST_SIGNAL] = { 0, 0, 0, 0, 0, 0 };
125
126
127 /* properties */
128 enum
129 {
130 PROP_0,
131 PROP_MODEL,
132 PROP_ADD_NULL_ENTRY,
133 PROP_DEFER_SYNC,
134 PROP_SAMPLE_SIZE,
135 PROP_CACHE_CHANGES
136 };
137
138 /*
139 * Structure to hold the status and all the modifications made to a single
140 * row. It is not possible to have ((model_row < 0) && !modify_values)
141 */
142 typedef struct
143 {
144 gint model_row; /* row index in the proxied GdaDataModel, -1 if new row */
145 gboolean to_be_deleted;/* TRUE if row is to be deleted */
146 GSList *modify_values; /* list of RowValue structures */
147 GValue **orig_values; /* array of the original GValues, indexed on the column numbers */
148 gint orig_values_size; /* size of @orig_values, OR (for new rows) the number of columns of the proxied data model the new row is for */
149 } RowModif;
150 #define ROW_MODIF(x) ((RowModif *)(x))
151
152 /*
153 * Structure to hold the modifications made to a single value
154 */
155 typedef struct
156 {
157 RowModif *row_modif; /* RowModif in which this structure instance appears */
158 gint model_column; /* column index in the GdaDataModel */
159 GValue *value; /* values are owned here */
160 GdaValueAttribute attributes; /* holds flags */
161 } RowValue;
162 #define ROW_VALUE(x) ((RowValue *)(x))
163
164 /*
165 * Structure to hold which part of the whole data is displayed
166 */
167 typedef struct {
168 GArray *mapping; /* for proxy's row nb i (or i+1 if
169 * there is a NULL rows first), mapping[i] points to
170 * the absolute row.
171 *
172 * If mapping is empty, then either there are no
173 * rows to display, or the number of rows is unknown
174 * (then it's not filled until row existance can be testes
175 */
176 } DisplayChunk;
177 static DisplayChunk *display_chunk_new (gint reserved_size);
178 static void display_chunk_free (DisplayChunk *chunk);
179
180 /*
181 * NOTE about the row numbers:
182 *
183 * There may be more rows in the GdaDataProxy than in the GdaDataModel if:
184 * - some rows have been added in the proxy
185 * and are not yet in the GdaDataModel (RowModif->model_row = -1 in this case); or
186 * - there is a NULL row at the beginning
187 *
188 * There are 3 kinds of row numbers:
189 * - the absolute row numbers which unify under a single numbering scheme all the possible proxy's rows
190 * which can either be new rows, or data model rows (which may or may not have been modified),
191 * absolute row numbers are strictly private to the proxy object
192 * - the row numbers as identified in the proxy (named "proxy_row"), which are the row numbers used
193 * by the GdaDataModel interface
194 * - the row numbers in the GdaDataModel being proxied (named "model_row")
195 *
196 * Absolute row numbers are assigned as:
197 * - rows 0 to N-1 included corresponding to the N rows of the proxied data model
198 * - rows N to N+M-1 included corresponding to the M added rows
199 *
200 * The rule to go from one row numbering to the other is to use the row conversion functions.
201 *
202 */
203 struct _GdaDataProxyPrivate
204 {
205 GdaMutex *mutex;
206
207 GdaDataModel *model; /* Gda model which is proxied */
208
209 GdaConnection *filter_vcnc; /* virtual connection used for filtering */
210 gchar *filter_expr; /* NULL if no filter applied */
211 GdaStatement *filter_stmt; /* NULL if no filter applied */
212 GdaDataModel *filtered_rows; /* NULL if no filter applied. Lists rows (by their number) which must be displayed */
213
214 GdaValueAttribute *columns_attrs; /* Each GValue holds a flag of GdaValueAttribute to proxy. cols. attributes */
215
216 gint model_nb_cols; /* = gda_data_model_get_n_columns (model) */
217 gint model_nb_rows; /* = gda_data_model_get_n_rows (model) */
218 gboolean notify_changes;
219
220 GSList *all_modifs; /* list of RowModif structures, for memory management */
221 GSList *new_rows; /* list of RowModif, no data allocated in this list */
222 GHashTable *modify_rows; /* key = model_row number, value = RowModif, NOT for new rows */
223
224 gboolean defer_proxied_model_insert;
225 gint catched_inserted_row;
226
227 gboolean add_null_entry; /* artificially add a NULL entry at the beginning of the tree model */
228
229 gboolean defer_sync;
230
231 /* chunking */
232 gboolean force_direct_mapping;
233 gint sample_first_row;
234 gint sample_last_row;
235 gint sample_size;
236 guint chunk_sync_idle_id;
237 DisplayChunk *chunk; /* number of proxy_rows depends directly on chunk->mapping->len */
238 DisplayChunk *chunk_to; /* NULL if nothing to do */
239 gint chunk_sep;
240 gint chunk_proxy_nb_rows;
241
242 /* for ALL the columns of proxy */
243 GdaColumn **columns;
244
245 /* modifications cache */
246 gboolean cache_changes;
247 GSList *cached_modifs;
248 GSList *cached_inserts;
249 };
250
251
252 /*
253 * Row conversion functions
254 */
255
256 /*
257 * May return -1 if:
258 * - @model_row < 0
259 * - @model_row is out of bounds (only checked if @proxy's number of rows is known)
260 */
261 static gint
model_row_to_absolute_row(GdaDataProxy * proxy,gint model_row)262 model_row_to_absolute_row (GdaDataProxy *proxy, gint model_row)
263 {
264 if (model_row < 0)
265 return -1;
266 if ((model_row >= proxy->priv->model_nb_rows) && (proxy->priv->model_nb_rows >= 0))
267 return -1;
268 else
269 return model_row;
270 }
271
272 /*
273 * May return -1 if:
274 * - @rm is NULL
275 * - @rm is a new row and @proxy's number of rows is NOT know
276 * - @rm is not a new row and @rm->model_row == -1
277 */
278 static gint
row_modif_to_absolute_row(GdaDataProxy * proxy,RowModif * rm)279 row_modif_to_absolute_row (GdaDataProxy *proxy, RowModif *rm)
280 {
281 if (rm == NULL)
282 return -1;
283 if (rm->model_row == -1) {
284 gint index;
285 if (proxy->priv->model_nb_rows == -1)
286 return -1;
287 index = g_slist_index (proxy->priv->new_rows, rm);
288 if (index < 0)
289 return -1;
290 return proxy->priv->model_nb_rows + index;
291 }
292 else
293 return rm->model_row;
294 }
295
296 /*
297 * if @rm is not %NULL, then it will contain a pointer to the RowModif structure associated to @row
298 *
299 * May return -1 if:
300 * - absolute row is not a model row
301 */
302 static gint
absolute_row_to_model_row(GdaDataProxy * proxy,gint abs_row,RowModif ** rm)303 absolute_row_to_model_row (GdaDataProxy *proxy, gint abs_row, RowModif **rm)
304 {
305 if (abs_row < 0)
306 return -1;
307 if ((abs_row < proxy->priv->model_nb_rows) || (proxy->priv->model_nb_rows < 0)) {
308 if (rm) {
309 gint tmp;
310 tmp = abs_row;
311 *rm = g_hash_table_lookup (proxy->priv->modify_rows, &tmp);
312 }
313 return abs_row;
314 }
315 else {
316 if (rm)
317 *rm = g_slist_nth_data (proxy->priv->new_rows, abs_row - proxy->priv->model_nb_rows);
318 return -1;
319 }
320 }
321
322 /*
323 * May return -1 if:
324 * - @row is not an absolute row
325 */
326 static gint
proxy_row_to_absolute_row(GdaDataProxy * proxy,gint proxy_row)327 proxy_row_to_absolute_row (GdaDataProxy *proxy, gint proxy_row)
328 {
329 if (proxy_row < 0)
330 return -1;
331 if (proxy->priv->force_direct_mapping)
332 return proxy_row;
333
334 if (proxy->priv->add_null_entry) {
335 if (proxy_row == 0)
336 return -1;
337 else
338 proxy_row--;
339 }
340 if (proxy->priv->chunk) {
341 if ((guint)proxy_row < proxy->priv->chunk->mapping->len)
342 return g_array_index (proxy->priv->chunk->mapping, gint, proxy_row);
343 else
344 return -1;
345 }
346 else {
347 if (proxy->priv->chunk_to &&
348 proxy->priv->chunk_to->mapping &&
349 (proxy_row < proxy->priv->chunk_sep) &&
350 ((guint)proxy_row < proxy->priv->chunk_to->mapping->len))
351 return g_array_index (proxy->priv->chunk_to->mapping, gint, proxy_row);
352 else
353 return proxy_row;
354 }
355 }
356
357 #define proxy_row_to_model_row(proxy, proxy_row) \
358 absolute_row_to_model_row ((proxy), proxy_row_to_absolute_row ((proxy), (proxy_row)), NULL);
359 #define row_modif_to_proxy_row(proxy, rm) \
360 absolute_row_to_proxy_row ((proxy), row_modif_to_absolute_row ((proxy), (rm)))
361
362
363 static RowModif *
proxy_row_to_row_modif(GdaDataProxy * proxy,gint proxy_row)364 proxy_row_to_row_modif (GdaDataProxy *proxy, gint proxy_row)
365 {
366 RowModif *rm = NULL;
367 absolute_row_to_model_row (proxy, proxy_row_to_absolute_row (proxy, proxy_row), &rm);
368
369 return rm;
370 }
371
372 /*
373 * May return -1 if:
374 * - @abs_row is not a proxy row
375 * - @abs_row is out of bounds (only checked if @proxy's number of rows is known)
376 */
377 static gint
absolute_row_to_proxy_row(GdaDataProxy * proxy,gint abs_row)378 absolute_row_to_proxy_row (GdaDataProxy *proxy, gint abs_row)
379 {
380 gint proxy_row = -1;
381 if (abs_row < 0)
382 return -1;
383
384 if (proxy->priv->force_direct_mapping) {
385 gint proxy_n_rows;
386 proxy_row = abs_row;
387 proxy_n_rows = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy);
388 if ((proxy_row >= proxy_n_rows) && (proxy_n_rows >= 0))
389 proxy_row = -1;
390 return proxy_row;
391 }
392
393 if (proxy->priv->chunk) {
394 gsize i;
395 for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
396 if (g_array_index (proxy->priv->chunk->mapping, gint, i) == abs_row) {
397 proxy_row = i;
398 break;
399 }
400 }
401 if ((proxy_row >= 0) && proxy->priv->add_null_entry)
402 proxy_row ++;
403 }
404 else {
405 if (proxy->priv->chunk_to && proxy->priv->chunk_to->mapping) {
406 /* search in the proxy->priv->chunk_sep first rows of proxy->priv->chunk_to */
407 gint i;
408 for (i = 0; i < MIN ((gint)proxy->priv->chunk->mapping->len, proxy->priv->chunk_sep); i++) {
409 if (g_array_index (proxy->priv->chunk_to->mapping, gint, i) == abs_row) {
410 proxy_row = i;
411 break;
412 }
413 }
414 if ((proxy_row >= 0) && proxy->priv->add_null_entry)
415 proxy_row ++;
416 }
417 if (proxy_row < 0) {
418 gint proxy_n_rows;
419 proxy_row = abs_row;
420 if (proxy->priv->add_null_entry)
421 proxy_row ++;
422 proxy_n_rows = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy);
423 if ((proxy_row >= proxy_n_rows) && (proxy_n_rows >= 0))
424 proxy_row = -1;
425 }
426 }
427
428 return proxy_row;
429 }
430
431
432 /*
433 * Free a RowModif structure
434 *
435 * Warning: only the allocated RowModif is freed, it's not removed from any list or hash table.
436 */
437 static void
row_modifs_free(RowModif * rm)438 row_modifs_free (RowModif *rm)
439 {
440 GSList *list;
441 gint i;
442
443 list = rm->modify_values;
444 while (list) {
445 if (ROW_VALUE (list->data)->value)
446 gda_value_free (ROW_VALUE (list->data)->value);
447 g_free (list->data);
448 list = g_slist_next (list);
449 }
450 g_slist_free (rm->modify_values);
451
452 if (rm->orig_values) {
453 for (i = 0; i < rm->orig_values_size; i++) {
454 if (rm->orig_values [i])
455 gda_value_free (rm->orig_values [i]);
456 }
457 g_free (rm->orig_values);
458 }
459
460 g_free (rm);
461 }
462
463 /*
464 * Allocates a new RowModif
465 *
466 * WARNING: the new RowModif is not inserted in any list or hash table.
467 */
468 static RowModif *
row_modifs_new(GdaDataProxy * proxy,gint proxy_row)469 row_modifs_new (GdaDataProxy *proxy, gint proxy_row)
470 {
471 RowModif *rm;
472
473 #ifdef GDA_DEBUG
474 rm = proxy_row_to_row_modif (proxy, proxy_row);
475 if (rm)
476 g_warning ("%s(): RowModif already exists for that proxy_row", __FUNCTION__);
477 #endif
478
479 rm = g_new0 (RowModif, 1);
480 if (proxy_row >= 0) {
481 gint i, model_row;
482
483 rm->orig_values = g_new0 (GValue *, proxy->priv->model_nb_cols);
484 rm->orig_values_size = proxy->priv->model_nb_cols;
485 model_row = proxy_row_to_model_row (proxy, proxy_row);
486
487 if (model_row >= 0) {
488 for (i=0; i<proxy->priv->model_nb_cols; i++) {
489 const GValue *oval;
490
491 oval = gda_data_model_get_value_at (proxy->priv->model, i, model_row, NULL);
492 if (oval)
493 rm->orig_values [i] = gda_value_copy ((GValue *) oval);
494 }
495 }
496 }
497
498 return rm;
499 }
500
501 /* module error */
gda_data_proxy_error_quark(void)502 GQuark gda_data_proxy_error_quark (void)
503 {
504 static GQuark quark;
505 if (!quark)
506 quark = g_quark_from_static_string ("gda_data_proxy_error");
507 return quark;
508 }
509
510 GType
gda_data_proxy_get_type(void)511 gda_data_proxy_get_type (void)
512 {
513 static GType type = 0;
514
515 if (G_UNLIKELY (type == 0)) {
516 static GMutex registering;
517 static const GTypeInfo info = {
518 sizeof (GdaDataProxyClass),
519 (GBaseInitFunc) NULL,
520 (GBaseFinalizeFunc) NULL,
521 (GClassInitFunc) gda_data_proxy_class_init,
522 NULL,
523 NULL,
524 sizeof (GdaDataProxy),
525 0,
526 (GInstanceInitFunc) gda_data_proxy_init,
527 0
528 };
529
530 static const GInterfaceInfo data_model_info = {
531 (GInterfaceInitFunc) gda_data_proxy_data_model_init,
532 NULL,
533 NULL
534 };
535
536 g_mutex_lock (®istering);
537 if (type == 0) {
538 type = g_type_register_static (G_TYPE_OBJECT, "GdaDataProxy", &info, 0);
539 g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
540 }
541 g_mutex_unlock (®istering);
542 }
543 return type;
544 }
545
546 static gboolean
validate_row_changes_accumulator(G_GNUC_UNUSED GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,G_GNUC_UNUSED gpointer data)547 validate_row_changes_accumulator (G_GNUC_UNUSED GSignalInvocationHint *ihint,
548 GValue *return_accu,
549 const GValue *handler_return,
550 G_GNUC_UNUSED gpointer data)
551 {
552 GError *error;
553
554 error = g_value_get_boxed (handler_return);
555 g_value_set_boxed (return_accu, error);
556
557 return error ? FALSE : TRUE; /* stop signal if 'thisvalue' is FALSE */
558 }
559
560 static GError *
m_validate_row_changes(G_GNUC_UNUSED GdaDataProxy * proxy,G_GNUC_UNUSED gint row,G_GNUC_UNUSED gint proxied_row)561 m_validate_row_changes (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED gint row,
562 G_GNUC_UNUSED gint proxied_row)
563 {
564 return NULL; /* defaults allows changes */
565 }
566
567 static void
gda_data_proxy_class_init(GdaDataProxyClass * klass)568 gda_data_proxy_class_init (GdaDataProxyClass *klass)
569 {
570 GObjectClass *object_class = G_OBJECT_CLASS (klass);
571
572 parent_class = g_type_class_peek_parent (klass);
573
574 /* signals */
575 /**
576 * GdaDataProxy::row-delete-changed:
577 * @proxy: the #GdaDataProxy
578 * @row: the concerned @proxy's row
579 * @to_be_deleted: tells if the @row is marked to be deleted
580 *
581 * Gets emitted whenever a row has been marked to be deleted, or has been unmarked to be deleted
582 */
583 gda_data_proxy_signals [ROW_DELETE_CHANGED] =
584 g_signal_new ("row-delete-changed",
585 G_TYPE_FROM_CLASS (object_class),
586 G_SIGNAL_RUN_FIRST,
587 G_STRUCT_OFFSET (GdaDataProxyClass, row_delete_changed),
588 NULL, NULL,
589 _gda_marshal_VOID__INT_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_BOOLEAN);
590 /**
591 * GdaDataProxy::sample-size-changed:
592 * @proxy: the #GdaDataProxy
593 * @sample_size: the new sample size
594 *
595 * Gets emitted whenever @proxy's sample size has been changed
596 */
597 gda_data_proxy_signals [SAMPLE_SIZE_CHANGED] =
598 g_signal_new ("sample-size-changed",
599 G_TYPE_FROM_CLASS (object_class),
600 G_SIGNAL_RUN_FIRST,
601 G_STRUCT_OFFSET (GdaDataProxyClass, sample_size_changed),
602 NULL, NULL,
603 g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
604 /**
605 * GdaDataProxy::sample-changed:
606 * @proxy: the #GdaDataProxy
607 * @sample_start: the first row of the sample
608 * @sample_end: the last row of the sample
609 *
610 * Gets emitted whenever @proxy's sample size has been changed. @sample_start and @sample_end are
611 * in reference to the proxied data model.
612 */
613 gda_data_proxy_signals [SAMPLE_CHANGED] =
614 g_signal_new ("sample-changed",
615 G_TYPE_FROM_CLASS (object_class),
616 G_SIGNAL_RUN_FIRST,
617 G_STRUCT_OFFSET (GdaDataProxyClass, sample_changed),
618 NULL, NULL,
619 _gda_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
620 /**
621 * GdaDataProxy::validate-row-changes:
622 * @proxy: the #GdaDataProxy
623 * @row: the proxy's row
624 * @proxied_row: the proxied data model's row
625 *
626 * Gets emitted when @proxy is about to commit a row change to the proxied data model. If any
627 * callback returns a non %NULL value, then the change commit fails with the returned #GError
628 *
629 * Returns: a new #GError if validation failed, or %NULL
630 */
631 gda_data_proxy_signals [VALIDATE_ROW_CHANGES] =
632 g_signal_new ("validate-row-changes",
633 G_TYPE_FROM_CLASS (object_class),
634 G_SIGNAL_RUN_LAST,
635 G_STRUCT_OFFSET (GdaDataProxyClass, validate_row_changes),
636 validate_row_changes_accumulator, NULL,
637 _gda_marshal_ERROR__INT_INT, G_TYPE_ERROR, 2, G_TYPE_INT, G_TYPE_INT);
638 /**
639 * GdaDataProxy::row-changes-applied:
640 * @proxy: the #GdaDataProxy
641 * @row: the proxy's row
642 * @proxied_row: the proxied data model's row
643 *
644 * Gets emitted when @proxy has committed a row change to the proxied data model.
645 */
646 gda_data_proxy_signals [ROW_CHANGES_APPLIED] =
647 g_signal_new ("row-changes-applied",
648 G_TYPE_FROM_CLASS (object_class),
649 G_SIGNAL_RUN_FIRST,
650 G_STRUCT_OFFSET (GdaDataProxyClass, row_changes_applied),
651 NULL, NULL,
652 _gda_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
653 /**
654 * GdaDataProxy::filter-changed:
655 * @proxy: the #GdaDataProxy
656 *
657 * Gets emitted when @proxy's filter has been changed
658 */
659 gda_data_proxy_signals [FILTER_CHANGED] =
660 g_signal_new ("filter-changed",
661 G_TYPE_FROM_CLASS (object_class),
662 G_SIGNAL_RUN_FIRST,
663 G_STRUCT_OFFSET (GdaDataProxyClass, filter_changed),
664 NULL, NULL,
665 _gda_marshal_VOID__VOID, G_TYPE_NONE, 0);
666
667 klass->row_delete_changed = NULL;
668 klass->sample_size_changed = NULL;
669 klass->sample_changed = NULL;
670 klass->validate_row_changes = m_validate_row_changes;
671 klass->row_changes_applied = NULL;
672 klass->filter_changed = NULL;
673
674 /* virtual functions */
675 object_class->dispose = gda_data_proxy_dispose;
676 object_class->finalize = gda_data_proxy_finalize;
677
678 /* Properties */
679 object_class->set_property = gda_data_proxy_set_property;
680 object_class->get_property = gda_data_proxy_get_property;
681
682 g_object_class_install_property (object_class, PROP_MODEL,
683 g_param_spec_object ("model", NULL, "Proxied data model",
684 GDA_TYPE_DATA_MODEL,
685 (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
686 g_object_class_install_property (object_class, PROP_ADD_NULL_ENTRY,
687 g_param_spec_boolean ("prepend-null-entry", NULL,
688 "Tells if a row composed of NULL values is inserted "
689 "as the proxy's first row", FALSE,
690 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
691 g_object_class_install_property (object_class, PROP_DEFER_SYNC,
692 g_param_spec_boolean ("defer-sync", NULL,
693 "Tells if changes to the sample of rows displayed "
694 "is done in background in several steps or if it's "
695 "done in one step.", TRUE,
696 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
697 g_object_class_install_property (object_class, PROP_SAMPLE_SIZE,
698 g_param_spec_int ("sample-size", NULL,
699 "Number of rows which the proxy will contain at any time, "
700 "like a sliding window on the proxied data model",
701 0, G_MAXINT - 1, 300,
702 (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
703
704 /**
705 * GdaDataProxy:cache-changes:
706 *
707 * Defines how changes kept in the data proxy are handled when the proxied data model
708 * is changed (using the "model" property). The default is to silently discard all the
709 * changes, but if this property is set to %TRUE, then the changes are cached.
710 *
711 * If set to %TRUE, each cached change will be re-applied to a newly set proxied data model if
712 * the change's number of columns match the proxied data model's number of columns and based on:
713 * <itemizedlist>
714 * <listitem><para>the contents of the proxied data model's modified row for updates and deletes</para></listitem>
715 * <listitem><para>the inserts are always kept</para></listitem>
716 * </itemizedlist>
717 *
718 * Since: 5.2
719 **/
720 g_object_class_install_property (object_class, PROP_CACHE_CHANGES,
721 g_param_spec_boolean ("cache-changes", NULL,
722 "set to TRUE to keep track of changes even when the proxied data model is changed", FALSE,
723 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
724
725 g_mutex_lock (&parser_mutex);
726 internal_parser = gda_sql_parser_new ();
727 g_mutex_unlock (&parser_mutex);
728 }
729
730 static void
gda_data_proxy_data_model_init(GdaDataModelIface * iface)731 gda_data_proxy_data_model_init (GdaDataModelIface *iface)
732 {
733 iface->i_get_n_rows = gda_data_proxy_get_n_rows;
734 iface->i_get_n_columns = gda_data_proxy_get_n_columns;
735 iface->i_describe_column = gda_data_proxy_describe_column;
736 iface->i_get_access_flags = gda_data_proxy_get_access_flags;
737 iface->i_get_value_at = gda_data_proxy_get_value_at;
738 iface->i_get_attributes_at = gda_data_proxy_get_attributes_at;
739
740 iface->i_create_iter = NULL;
741 iface->i_iter_at_row = NULL;
742 iface->i_iter_next = NULL;
743 iface->i_iter_prev = NULL;
744
745 iface->i_set_value_at = gda_data_proxy_set_value_at;
746 iface->i_iter_set_value = NULL;
747 iface->i_set_values = gda_data_proxy_set_values;
748 iface->i_append_values = gda_data_proxy_append_values;
749 iface->i_append_row = gda_data_proxy_append_row;
750 iface->i_remove_row = gda_data_proxy_remove_row;
751 iface->i_find_row = gda_data_proxy_find_row_from_values;
752
753 iface->i_set_notify = gda_data_proxy_set_notify;
754 iface->i_get_notify = gda_data_proxy_get_notify;
755 iface->i_send_hint = gda_data_proxy_send_hint;
756
757 iface->row_inserted = NULL;
758 iface->row_updated = NULL;
759 iface->row_removed = NULL;
760 }
761
762 /*
763 * REM: @add_null_entry, @defer_sync and @cache_changes are not defined
764 */
765 static void
do_init(GdaDataProxy * proxy)766 do_init (GdaDataProxy *proxy)
767 {
768 if (!proxy->priv->mutex)
769 proxy->priv->mutex = gda_mutex_new ();
770
771 proxy->priv->modify_rows = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
772 proxy->priv->notify_changes = TRUE;
773
774 proxy->priv->force_direct_mapping = FALSE;
775 proxy->priv->sample_size = 0;
776 proxy->priv->chunk = NULL;
777 proxy->priv->chunk_to = NULL;
778 proxy->priv->chunk_sync_idle_id = 0;
779 proxy->priv->columns = NULL;
780
781 proxy->priv->defer_proxied_model_insert = FALSE;
782 proxy->priv->catched_inserted_row = -1;
783 }
784
785 static void
gda_data_proxy_init(GdaDataProxy * proxy)786 gda_data_proxy_init (GdaDataProxy *proxy)
787 {
788 proxy->priv = g_new0 (GdaDataProxyPrivate, 1);
789
790 do_init (proxy);
791
792 proxy->priv->add_null_entry = FALSE;
793 proxy->priv->defer_sync = TRUE;
794 proxy->priv->cache_changes = FALSE;
795 }
796
797 static DisplayChunk *compute_display_chunk (GdaDataProxy *proxy);
798 static void adjust_displayed_chunk (GdaDataProxy *proxy);
799 static gboolean chunk_sync_idle (GdaDataProxy *proxy);
800 static void ensure_chunk_sync (GdaDataProxy *proxy);
801
802
803 static void proxied_model_row_inserted_cb (GdaDataModel *model, gint row, GdaDataProxy *proxy);
804 static void proxied_model_row_updated_cb (GdaDataModel *model, gint row, GdaDataProxy *proxy);
805 static void proxied_model_row_removed_cb (GdaDataModel *model, gint row, GdaDataProxy *proxy);
806 static void proxied_model_reset_cb (GdaDataModel *model, GdaDataProxy *proxy);
807 static void proxied_model_access_changed_cb (GdaDataModel *model, GdaDataProxy *proxy);
808
809
810 /**
811 * gda_data_proxy_new:
812 * @model: (transfer none): Data model to be proxied
813 *
814 * Creates a new proxy for @model. For bindings use @gda_data_proxy_new_with_data_model.
815 *
816 * Returns: (transfer full): a new #GdaDataProxy object
817 */
818 GObject *
gda_data_proxy_new(GdaDataModel * model)819 gda_data_proxy_new (GdaDataModel *model)
820 {
821 GObject *obj;
822
823 g_return_val_if_fail (model && GDA_IS_DATA_MODEL (model), NULL);
824
825 obj = g_object_new (GDA_TYPE_DATA_PROXY, "model", model, NULL);
826
827 return obj;
828 }
829
830 /**
831 * gda_data_proxy_new_with_data_model:
832 * @model: (transfer none): Data model to be proxied
833 *
834 * Creates a new proxy for @model. This is the preferred method to create
835 * #GdaDataProxy objects by bindings.
836 *
837 * Returns: (transfer full): a new #GdaDataProxy object
838 *
839 * Since: 5.2.0
840 */
841 GdaDataProxy*
gda_data_proxy_new_with_data_model(GdaDataModel * model)842 gda_data_proxy_new_with_data_model (GdaDataModel *model)
843 {
844 GObject *obj;
845
846 g_return_val_if_fail (model && GDA_IS_DATA_MODEL (model), NULL);
847
848 obj = g_object_new (GDA_TYPE_DATA_PROXY, "model", model, NULL);
849
850 return (GdaDataProxy*) obj;
851 }
852
853 static void
clean_proxy(GdaDataProxy * proxy)854 clean_proxy (GdaDataProxy *proxy)
855 {
856 if (proxy->priv->all_modifs) {
857 gda_data_proxy_cancel_all_changes (proxy);
858 g_assert (! proxy->priv->all_modifs);
859 }
860
861 if (proxy->priv->modify_rows) {
862 g_hash_table_destroy (proxy->priv->modify_rows);
863 proxy->priv->modify_rows = NULL;
864 }
865
866 if (proxy->priv->filter_vcnc) {
867 g_object_unref (proxy->priv->filter_vcnc);
868 proxy->priv->filter_vcnc = NULL;
869 }
870
871 if (proxy->priv->filter_expr) {
872 g_free (proxy->priv->filter_expr);
873 proxy->priv->filter_expr = NULL;
874 }
875
876 if (proxy->priv->filter_stmt) {
877 g_object_unref (proxy->priv->filter_stmt);
878 proxy->priv->filter_stmt = NULL;
879 }
880
881 if (proxy->priv->filtered_rows) {
882 g_object_unref (proxy->priv->filtered_rows);
883 proxy->priv->filtered_rows = NULL;
884 }
885
886 proxy->priv->force_direct_mapping = FALSE;
887 if (proxy->priv->chunk_sync_idle_id) {
888 g_idle_remove_by_data (proxy);
889 proxy->priv->chunk_sync_idle_id = 0;
890 }
891
892 if (proxy->priv->chunk) {
893 display_chunk_free (proxy->priv->chunk);
894 proxy->priv->chunk = NULL;
895 }
896 if (proxy->priv->chunk_to) {
897 display_chunk_free (proxy->priv->chunk_to);
898 proxy->priv->chunk_to = NULL;
899 }
900
901 if (proxy->priv->columns) {
902 gint i;
903 for (i = 0; i < 2 * proxy->priv->model_nb_cols; i++)
904 g_object_unref (G_OBJECT (proxy->priv->columns[i]));
905 g_free (proxy->priv->columns);
906 proxy->priv->columns = NULL;
907 }
908
909 if (proxy->priv->model) {
910 g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
911 G_CALLBACK (proxied_model_row_inserted_cb), proxy);
912 g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
913 G_CALLBACK (proxied_model_row_updated_cb), proxy);
914 g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
915 G_CALLBACK (proxied_model_row_removed_cb), proxy);
916 g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
917 G_CALLBACK (proxied_model_reset_cb), proxy);
918 g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
919 G_CALLBACK (proxied_model_access_changed_cb), proxy);
920 g_object_unref (proxy->priv->model);
921 proxy->priv->model = NULL;
922 }
923
924 if (proxy->priv->columns_attrs) {
925 g_free (proxy->priv->columns_attrs);
926 proxy->priv->columns_attrs = NULL;
927 }
928 }
929
930 static void
gda_data_proxy_dispose(GObject * object)931 gda_data_proxy_dispose (GObject *object)
932 {
933 GdaDataProxy *proxy;
934
935 g_return_if_fail (GDA_IS_DATA_PROXY (object));
936
937 proxy = GDA_DATA_PROXY (object);
938 if (proxy->priv) {
939 clean_proxy (proxy);
940 if (proxy->priv->mutex) {
941 gda_mutex_free (proxy->priv->mutex);
942 proxy->priv->mutex = NULL;
943 }
944
945 clean_cached_changes (proxy);
946 }
947
948 /* parent class */
949 parent_class->dispose (object);
950 }
951
952 static void
gda_data_proxy_finalize(GObject * object)953 gda_data_proxy_finalize (GObject *object)
954 {
955 GdaDataProxy *proxy;
956
957 g_return_if_fail (object != NULL);
958 g_return_if_fail (GDA_IS_DATA_PROXY (object));
959
960 proxy = GDA_DATA_PROXY (object);
961 if (proxy->priv) {
962 g_free (proxy->priv);
963 proxy->priv = NULL;
964 }
965
966 /* parent class */
967 parent_class->finalize (object);
968 }
969
970 static void
gda_data_proxy_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)971 gda_data_proxy_set_property (GObject *object,
972 guint param_id,
973 const GValue *value,
974 GParamSpec *pspec)
975 {
976 GdaDataProxy *proxy;
977
978 proxy = GDA_DATA_PROXY (object);
979 if (proxy->priv) {
980 gda_mutex_lock (proxy->priv->mutex);
981 switch (param_id) {
982 case PROP_MODEL: {
983 GdaDataModel *model;
984 gint col;
985 gboolean already_set = FALSE;
986
987 if (proxy->priv->model) {
988 if (proxy->priv->cache_changes)
989 migrate_current_changes_to_cache (proxy);
990
991 gboolean notify_changes;
992 notify_changes = proxy->priv->notify_changes;
993
994 proxy->priv->notify_changes = FALSE;
995 clean_proxy (proxy);
996 proxy->priv->notify_changes = notify_changes;
997
998 do_init (proxy);
999 already_set = TRUE;
1000 }
1001
1002 model = (GdaDataModel*) g_value_get_object (value);
1003 g_return_if_fail (GDA_IS_DATA_MODEL (model));
1004
1005 if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM)) {
1006 g_warning (_("GdaDataProxy can't handle non random access data models"));
1007 gda_mutex_unlock (proxy->priv->mutex);
1008 return;
1009 }
1010 proxy->priv->model = g_object_ref (model);
1011
1012 proxy->priv->model_nb_cols = gda_data_model_get_n_columns (model);
1013 proxy->priv->model_nb_rows = gda_data_model_get_n_rows (model);
1014
1015 /* column attributes */
1016 proxy->priv->columns_attrs = g_new0 (GdaValueAttribute, proxy->priv->model_nb_cols);
1017 for (col = 0; col < proxy->priv->model_nb_cols; col++) {
1018 GdaColumn *column;
1019 GdaValueAttribute flags = GDA_VALUE_ATTR_IS_UNCHANGED;
1020
1021 column = gda_data_model_describe_column (model, col);
1022 if (gda_column_get_allow_null (column))
1023 flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
1024 if (gda_column_get_default_value (column))
1025 flags |= GDA_VALUE_ATTR_CAN_BE_DEFAULT;
1026 proxy->priv->columns_attrs[col] = flags;
1027 }
1028
1029 g_signal_connect (G_OBJECT (model), "row-inserted",
1030 G_CALLBACK (proxied_model_row_inserted_cb), proxy);
1031 g_signal_connect (G_OBJECT (model), "row-updated",
1032 G_CALLBACK (proxied_model_row_updated_cb), proxy);
1033 g_signal_connect (G_OBJECT (model), "row-removed",
1034 G_CALLBACK (proxied_model_row_removed_cb), proxy);
1035 g_signal_connect (G_OBJECT (model), "reset",
1036 G_CALLBACK (proxied_model_reset_cb), proxy);
1037 g_signal_connect (G_OBJECT (model), "access-changed",
1038 G_CALLBACK (proxied_model_access_changed_cb), proxy);
1039
1040 /* initial chunk settings, no need to emit any signal as it's an initial state */
1041 proxy->priv->chunk = compute_display_chunk (proxy);
1042 if (!proxy->priv->chunk->mapping) {
1043 display_chunk_free (proxy->priv->chunk);
1044 proxy->priv->chunk = NULL;
1045 }
1046
1047 if (proxy->priv->cache_changes)
1048 fetch_current_cached_changes (proxy);
1049
1050 if (already_set)
1051 gda_data_model_reset (GDA_DATA_MODEL (proxy));
1052 break;
1053 }
1054 case PROP_ADD_NULL_ENTRY:
1055 if (proxy->priv->add_null_entry != g_value_get_boolean (value)) {
1056 proxy->priv->add_null_entry = g_value_get_boolean (value);
1057
1058 if (proxy->priv->add_null_entry)
1059 gda_data_model_row_inserted ((GdaDataModel *) proxy, 0);
1060 else
1061 gda_data_model_row_removed ((GdaDataModel *) proxy, 0);
1062 }
1063 break;
1064 case PROP_DEFER_SYNC:
1065 proxy->priv->defer_sync = g_value_get_boolean (value);
1066 if (!proxy->priv->defer_sync && proxy->priv->chunk_sync_idle_id) {
1067 g_idle_remove_by_data (proxy);
1068 proxy->priv->chunk_sync_idle_id = 0;
1069 chunk_sync_idle (proxy);
1070 }
1071 break;
1072 case PROP_SAMPLE_SIZE:
1073 proxy->priv->sample_size = g_value_get_int (value);
1074 if (proxy->priv->sample_size < 0)
1075 proxy->priv->sample_size = 0;
1076
1077 /* initial chunk settings, no need to emit any signal as it's an initial state */
1078 proxy->priv->chunk = compute_display_chunk (proxy);
1079 if (!proxy->priv->chunk->mapping) {
1080 display_chunk_free (proxy->priv->chunk);
1081 proxy->priv->chunk = NULL;
1082 }
1083 break;
1084 case PROP_CACHE_CHANGES:
1085 proxy->priv->cache_changes = g_value_get_boolean (value);
1086 if (! proxy->priv->cache_changes)
1087 clean_cached_changes (proxy);
1088 break;
1089 default:
1090 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1091 break;
1092 }
1093 gda_mutex_unlock (proxy->priv->mutex);
1094 }
1095 }
1096
1097 static void
gda_data_proxy_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)1098 gda_data_proxy_get_property (GObject *object,
1099 guint param_id,
1100 GValue *value,
1101 GParamSpec *pspec)
1102 {
1103 GdaDataProxy *proxy;
1104
1105 proxy = GDA_DATA_PROXY (object);
1106 if (proxy->priv) {
1107 gda_mutex_lock (proxy->priv->mutex);
1108 switch (param_id) {
1109 case PROP_ADD_NULL_ENTRY:
1110 g_value_set_boolean (value, proxy->priv->add_null_entry);
1111 break;
1112 case PROP_DEFER_SYNC:
1113 g_value_set_boolean (value, proxy->priv->defer_sync);
1114 break;
1115 case PROP_SAMPLE_SIZE:
1116 g_value_set_int (value, proxy->priv->sample_size);
1117 break;
1118 case PROP_CACHE_CHANGES:
1119 g_value_set_boolean (value, proxy->priv->cache_changes);
1120 break;
1121 default:
1122 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1123 break;
1124 }
1125 gda_mutex_unlock (proxy->priv->mutex);
1126 }
1127 }
1128
1129 static void
proxied_model_row_inserted_cb(G_GNUC_UNUSED GdaDataModel * model,gint row,GdaDataProxy * proxy)1130 proxied_model_row_inserted_cb (G_GNUC_UNUSED GdaDataModel *model, gint row, GdaDataProxy *proxy)
1131 {
1132 gint abs_row;
1133 gint signal_row_offset = proxy->priv->add_null_entry ? 1 : 0;
1134 abs_row = row; /* can't call model_row_to_absolute_row because @row is not *officially* part of the computations */
1135
1136 /* internal cleanups: update chunk and chunk_to arrays */
1137 if (proxy->priv->chunk) {
1138 gsize i;
1139 gint *v;
1140
1141 for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1142 v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1143 if (*v >= abs_row)
1144 *v += 1;
1145 }
1146 }
1147 if (proxy->priv->chunk_to && proxy->priv->chunk->mapping) {
1148 gsize i;
1149 gint *v;
1150
1151 for (i = 0; i < proxy->priv->chunk_to->mapping->len; i++) {
1152 v = &g_array_index (proxy->priv->chunk_to->mapping, gint, i);
1153 if (*v >= abs_row)
1154 *v -= 1;
1155 }
1156 }
1157
1158 /* update all the RowModif where model_row > row */
1159 if (proxy->priv->all_modifs) {
1160 GSList *list;
1161 for (list = proxy->priv->all_modifs; list; list = list->next) {
1162 RowModif *tmprm;
1163 tmprm = ROW_MODIF (list->data);
1164 if (tmprm->model_row > row) {
1165 gint tmp;
1166 tmp = tmprm->model_row;
1167 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1168 tmprm->model_row ++;
1169
1170 gint *ptr;
1171 ptr = g_new (gint, 1);
1172 *ptr = tmprm->model_row;
1173 g_hash_table_insert (proxy->priv->modify_rows, ptr, tmprm);
1174 }
1175 }
1176 }
1177
1178 /* Note: if there is a chunk, then the new row will *not* be part of that chunk and so
1179 * no signal will be emitted for its insertion */
1180 proxy->priv->model_nb_rows ++;
1181 if (proxy->priv->defer_proxied_model_insert)
1182 proxy->priv->catched_inserted_row = row;
1183 else if (!proxy->priv->chunk && !proxy->priv->chunk_to)
1184 gda_data_model_row_inserted ((GdaDataModel *) proxy, row + signal_row_offset);
1185 }
1186
1187 static void
proxied_model_row_updated_cb(G_GNUC_UNUSED GdaDataModel * model,gint row,GdaDataProxy * proxy)1188 proxied_model_row_updated_cb (G_GNUC_UNUSED GdaDataModel *model, gint row, GdaDataProxy *proxy)
1189 {
1190 gint proxy_row, tmp;
1191 RowModif *rm;
1192
1193 /* destroy any RowModif associated ro @row */
1194 tmp = row;
1195 rm = g_hash_table_lookup (proxy->priv->modify_rows, &tmp);
1196 if (rm) {
1197 /* FIXME: compare with the new value of the updated row and remove RowModif only if there
1198 * are no more differences. For now we only get rid of that RowModif.
1199 */
1200 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1201 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1202 row_modifs_free (rm);
1203 }
1204
1205 /* if @row is a "visible" row, then emit the updated signal on it */
1206 proxy_row = absolute_row_to_proxy_row (proxy, model_row_to_absolute_row (proxy, row));
1207 if (proxy_row >= 0)
1208 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1209 }
1210
1211 static void
proxied_model_row_removed_cb(G_GNUC_UNUSED GdaDataModel * model,gint row,GdaDataProxy * proxy)1212 proxied_model_row_removed_cb (G_GNUC_UNUSED GdaDataModel *model, gint row, GdaDataProxy *proxy)
1213 {
1214 gint proxy_row, abs_row;
1215 RowModif *rm;
1216 gint signal_row_offset = proxy->priv->add_null_entry ? 1 : 0;
1217 abs_row = model_row_to_absolute_row (proxy, row);
1218 proxy_row = absolute_row_to_proxy_row (proxy, abs_row);
1219
1220 /* internal cleanups: update chunk and chunk_to arrays */
1221 if (proxy->priv->chunk) {
1222 gsize i;
1223 gint *v, remove_index = -1;
1224
1225 for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1226 v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1227 if (*v > abs_row)
1228 *v -= 1;
1229 else if (*v == abs_row) {
1230 g_assert (remove_index == -1);
1231 remove_index = i;
1232 }
1233 }
1234 if (remove_index >= 0)
1235 g_array_remove_index (proxy->priv->chunk->mapping, remove_index);
1236 if ((proxy_row >= 0) && (proxy->priv->chunk_sep >= (proxy_row - signal_row_offset)))
1237 proxy->priv->chunk_sep--;
1238 }
1239 if (proxy->priv->chunk_to && proxy->priv->chunk->mapping) {
1240 guint i;
1241 gint *v, remove_index = -1;
1242
1243 for (i = 0; i < proxy->priv->chunk_to->mapping->len; i++) {
1244 v = &g_array_index (proxy->priv->chunk_to->mapping, gint, i);
1245 if (*v > abs_row)
1246 *v -= 1;
1247 else if (*v == abs_row) {
1248 g_assert (remove_index == -1);
1249 remove_index = i;
1250 }
1251 }
1252 if (remove_index >= 0)
1253 g_array_remove_index (proxy->priv->chunk_to->mapping, remove_index);
1254 }
1255 proxy->priv->chunk_proxy_nb_rows--;
1256 proxy->priv->model_nb_rows --;
1257
1258 /* destroy any RowModif associated ro @row */
1259 gint tmp;
1260 tmp = row;
1261 rm = g_hash_table_lookup (proxy->priv->modify_rows, &tmp);
1262 if (rm) {
1263 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1264 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1265 row_modifs_free (rm);
1266 }
1267
1268 /* update all the RowModif where model_row > row */
1269 if (proxy->priv->all_modifs) {
1270 GSList *list;
1271 for (list = proxy->priv->all_modifs; list; list = list->next) {
1272 RowModif *tmprm;
1273 tmprm = ROW_MODIF (list->data);
1274 if (tmprm->model_row > row) {
1275 tmp = tmprm->model_row;
1276 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1277 tmprm->model_row --;
1278
1279 gint *ptr;
1280 ptr = g_new (gint, 1);
1281 *ptr = tmprm->model_row;
1282 g_hash_table_insert (proxy->priv->modify_rows, ptr, tmprm);
1283 }
1284 }
1285 }
1286
1287 /* actual signal emission if row is 'visible' */
1288 if (proxy_row >= 0)
1289 gda_data_model_row_removed ((GdaDataModel *) proxy, proxy_row);
1290 }
1291
1292 /*
1293 * called when the proxied model emits a "reset" signal
1294 */
1295 static void
proxied_model_reset_cb(GdaDataModel * model,GdaDataProxy * proxy)1296 proxied_model_reset_cb (GdaDataModel *model, GdaDataProxy *proxy)
1297 {
1298 g_object_ref (G_OBJECT (model));
1299 clean_proxy (proxy);
1300 do_init (proxy);
1301 g_object_set (G_OBJECT (proxy), "model", model, NULL);
1302 g_object_unref (G_OBJECT (model));
1303
1304 if (proxy->priv->columns) {
1305 /* adjust column's types */
1306 gint i;
1307 GdaColumn *orig;
1308 for (i = 0; i < proxy->priv->model_nb_cols; i++) {
1309 orig = gda_data_model_describe_column (proxy->priv->model, i);
1310 gda_column_set_g_type (proxy->priv->columns[i], gda_column_get_g_type (orig));
1311 }
1312 for (; i < 2 * proxy->priv->model_nb_cols; i++) {
1313 orig = gda_data_model_describe_column (proxy->priv->model,
1314 i - proxy->priv->model_nb_cols);
1315 gda_column_set_g_type (proxy->priv->columns[i], gda_column_get_g_type (orig));
1316 }
1317 }
1318
1319 gda_data_model_reset (GDA_DATA_MODEL (proxy));
1320 }
1321
1322 static void
proxied_model_access_changed_cb(G_GNUC_UNUSED GdaDataModel * model,GdaDataProxy * proxy)1323 proxied_model_access_changed_cb (G_GNUC_UNUSED GdaDataModel *model, GdaDataProxy *proxy)
1324 {
1325 g_signal_emit_by_name (proxy, "access-changed");
1326 }
1327
1328 /**
1329 * gda_data_proxy_get_proxied_model:
1330 * @proxy: a #GdaDataProxy object
1331 *
1332 * Fetch the #GdaDataModel which @proxy does proxy
1333 *
1334 * Returns: (transfer none): the proxied data model
1335 */
1336 GdaDataModel *
gda_data_proxy_get_proxied_model(GdaDataProxy * proxy)1337 gda_data_proxy_get_proxied_model (GdaDataProxy *proxy)
1338 {
1339 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), NULL);
1340 g_return_val_if_fail (proxy->priv, NULL);
1341
1342 return proxy->priv->model;
1343 }
1344
1345 /**
1346 * gda_data_proxy_get_proxied_model_n_cols:
1347 * @proxy: a #GdaDataProxy object
1348 *
1349 * Get the number of columns in the proxied data model
1350 *
1351 * Returns: the number of columns, or -1 if an error occurred
1352 */
1353 gint
gda_data_proxy_get_proxied_model_n_cols(GdaDataProxy * proxy)1354 gda_data_proxy_get_proxied_model_n_cols (GdaDataProxy *proxy)
1355 {
1356 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
1357 g_return_val_if_fail (proxy->priv, -1);
1358
1359 return proxy->priv->model_nb_cols;
1360 }
1361
1362 /**
1363 * gda_data_proxy_get_proxied_model_n_rows:
1364 * @proxy: a #GdaDataProxy object
1365 *
1366 * Get the number of rows in the proxied data model
1367 *
1368 * Returns: the number of rows, or -1 if the number of rows is not known
1369 */
1370 gint
gda_data_proxy_get_proxied_model_n_rows(GdaDataProxy * proxy)1371 gda_data_proxy_get_proxied_model_n_rows (GdaDataProxy *proxy)
1372 {
1373 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
1374 g_return_val_if_fail (proxy->priv, -1);
1375
1376 return gda_data_model_get_n_rows (proxy->priv->model);
1377 }
1378
1379 /**
1380 * gda_data_proxy_is_read_only:
1381 * @proxy: a #GdaDataProxy object
1382 *
1383 * Returns: TRUE if the proxied data model is itself read-only
1384 */
1385 gboolean
gda_data_proxy_is_read_only(GdaDataProxy * proxy)1386 gda_data_proxy_is_read_only (GdaDataProxy *proxy)
1387 {
1388 GdaDataModelAccessFlags flags;
1389
1390 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), TRUE);
1391 g_return_val_if_fail (proxy->priv, TRUE);
1392
1393 flags = gda_data_model_get_access_flags (proxy->priv->model);
1394 return ! (flags & GDA_DATA_MODEL_ACCESS_WRITE);
1395 }
1396
1397
1398 static RowModif *find_or_create_row_modif (GdaDataProxy *proxy, gint proxy_row, gint col, RowValue **ret_rv);
1399
1400
1401 /*
1402 * Stores the new RowValue in @rv
1403 */
1404 static RowModif *
find_or_create_row_modif(GdaDataProxy * proxy,gint proxy_row,gint col,RowValue ** ret_rv)1405 find_or_create_row_modif (GdaDataProxy *proxy, gint proxy_row, gint col, RowValue **ret_rv)
1406 {
1407 RowModif *rm = NULL;
1408 RowValue *rv = NULL;
1409 gint model_row;
1410 g_assert (proxy_row >= 0);
1411
1412 model_row = absolute_row_to_model_row (proxy,
1413 proxy_row_to_absolute_row (proxy, proxy_row), &rm);
1414 if (!rm) {
1415 /* create a new RowModif */
1416 g_assert (model_row >= 0);
1417 rm = row_modifs_new (proxy, proxy_row);
1418 rm->model_row = model_row;
1419
1420 gint *ptr;
1421 ptr = g_new (gint, 1);
1422 *ptr = model_row;
1423 g_hash_table_insert (proxy->priv->modify_rows, ptr, rm);
1424 proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
1425 }
1426 else {
1427 /* there are already some modifications to the row, try to catch the RowValue if available */
1428 GSList *list;
1429
1430 list = rm->modify_values;
1431 while (list && !rv) {
1432 if (ROW_VALUE (list->data)->model_column == col)
1433 rv = ROW_VALUE (list->data);
1434 list = g_slist_next (list);
1435 }
1436 }
1437
1438 if (ret_rv)
1439 *ret_rv = rv;
1440 return rm;
1441 }
1442
1443
1444 /**
1445 * gda_data_proxy_get_values:
1446 * @proxy: a #GdaDataProxy object
1447 * @proxy_row: a proxy row
1448 * @cols_index: (array length=n_cols): array containing the columns for which the values are requested
1449 * @n_cols: size of @cols_index
1450 *
1451 * Retrieve a whole list of values from the @proxy data model. This function
1452 * calls gda_data_proxy_get_value()
1453 * for each column index specified in @cols_index, and generates a #GSList on the way.
1454 *
1455 * Returns: (element-type GValue) (transfer container): a new list of values (the list must be freed, not the values),
1456 * or %NULL if an error occurred
1457 */
1458 GSList *
gda_data_proxy_get_values(GdaDataProxy * proxy,gint proxy_row,gint * cols_index,gint n_cols)1459 gda_data_proxy_get_values (GdaDataProxy *proxy, gint proxy_row, gint *cols_index, gint n_cols)
1460 {
1461 GSList *retval = NULL;
1462 gint i;
1463 const GValue *value;
1464
1465 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), NULL);
1466 g_return_val_if_fail (proxy->priv, NULL);
1467 g_return_val_if_fail (proxy_row >= 0, NULL);
1468
1469 gda_mutex_lock (proxy->priv->mutex);
1470 for (i = 0; i < n_cols; i++) {
1471 value = gda_data_proxy_get_value_at ((GdaDataModel *) proxy, cols_index[i], proxy_row, NULL);
1472 if (value)
1473 retval = g_slist_prepend (retval, (GValue *) value);
1474 else {
1475 g_slist_free (retval);
1476 gda_mutex_unlock (proxy->priv->mutex);
1477 return NULL;
1478 }
1479 }
1480 gda_mutex_unlock (proxy->priv->mutex);
1481
1482 return g_slist_reverse (retval);
1483 }
1484
1485 /**
1486 * gda_data_proxy_get_value_attributes:
1487 * @proxy: a #GdaDataProxy object
1488 * @proxy_row: a proxy row
1489 * @col: a valid proxy column
1490 *
1491 * Get the attributes of the value stored at (proxy_row, col) in @proxy, which
1492 * is an ORed value of #GdaValueAttribute flags
1493 *
1494 * Returns: a #GdaValueAttribute with the value's attributes at given position
1495 */
1496 GdaValueAttribute
gda_data_proxy_get_value_attributes(GdaDataProxy * proxy,gint proxy_row,gint col)1497 gda_data_proxy_get_value_attributes (GdaDataProxy *proxy, gint proxy_row, gint col)
1498 {
1499 gint model_row;
1500 RowModif *rm;
1501 gboolean value_has_modifs = FALSE;
1502 GdaValueAttribute flags = 0;
1503 gint model_column;
1504
1505 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
1506 g_return_val_if_fail (proxy->priv, 0);
1507 g_return_val_if_fail (proxy_row >= 0, 0);
1508
1509 gda_mutex_lock (proxy->priv->mutex);
1510 model_column = col % proxy->priv->model_nb_cols;
1511 model_row = proxy_row_to_model_row (proxy, proxy_row);
1512 flags = gda_data_model_get_attributes_at (proxy->priv->model, model_column, model_row);
1513 if (model_row < 0)
1514 flags |= GDA_VALUE_ATTR_IS_NULL;
1515
1516 rm = proxy_row_to_row_modif (proxy, proxy_row);
1517 if (rm) {
1518 if (rm->modify_values) {
1519 /* there are some modifications to the row */
1520 GSList *list;
1521 RowValue *rv = NULL;
1522
1523 list = rm->modify_values;
1524 while (list && !rv) {
1525 if (ROW_VALUE (list->data)->model_column == model_column)
1526 rv = ROW_VALUE (list->data);
1527 list = g_slist_next (list);
1528 }
1529 if (rv) {
1530 value_has_modifs = TRUE;
1531 flags |= rv->attributes;
1532 if (rv->value && !gda_value_is_null (rv->value))
1533 flags &= ~GDA_VALUE_ATTR_IS_NULL;
1534 else
1535 flags |= GDA_VALUE_ATTR_IS_NULL;
1536 }
1537 }
1538 }
1539
1540 if (! value_has_modifs)
1541 flags |= GDA_VALUE_ATTR_IS_UNCHANGED;
1542
1543 /* compute the GDA_VALUE_ATTR_DATA_NON_VALID attribute */
1544 if (! (flags & GDA_VALUE_ATTR_CAN_BE_NULL)) {
1545 if ((flags & GDA_VALUE_ATTR_IS_NULL) && !(flags & GDA_VALUE_ATTR_IS_DEFAULT))
1546 flags |= GDA_VALUE_ATTR_DATA_NON_VALID;
1547 }
1548
1549 gda_mutex_unlock (proxy->priv->mutex);
1550
1551 /*g_print ("%s (%p, %d, %d) => %d\n", __FUNCTION__, proxy, col, proxy_row, flags);*/
1552 return flags;
1553 }
1554
1555 /**
1556 * gda_data_proxy_alter_value_attributes:
1557 * @proxy: a #GdaDataProxy object
1558 * @proxy_row: A proxy row number
1559 * @col: a valid column number
1560 * @alter_flags: (transfer none): flags to alter the attributes
1561 *
1562 * Alters the attributes of the value stored at (proxy_row, col) in @proxy. the @alter_flags
1563 * can only contain the GDA_VALUE_ATTR_IS_NULL, GDA_VALUE_ATTR_IS_DEFAULT and GDA_VALUE_ATTR_IS_UNCHANGED
1564 * flags (other flags are ignored).
1565 */
1566 void
gda_data_proxy_alter_value_attributes(GdaDataProxy * proxy,gint proxy_row,gint col,GdaValueAttribute alter_flags)1567 gda_data_proxy_alter_value_attributes (GdaDataProxy *proxy, gint proxy_row, gint col, GdaValueAttribute alter_flags)
1568 {
1569 gint model_col;
1570
1571 g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1572 g_return_if_fail (proxy->priv);
1573 g_return_if_fail (proxy_row >= 0);
1574
1575 gda_mutex_lock (proxy->priv->mutex);
1576
1577 model_col = col % proxy->priv->model_nb_cols;
1578 if (alter_flags & GDA_VALUE_ATTR_IS_NULL)
1579 gda_data_proxy_set_value_at ((GdaDataModel*) proxy,
1580 model_col, proxy_row, NULL, NULL);
1581 else {
1582 RowModif *rm;
1583 RowValue *rv = NULL;
1584
1585 rm = find_or_create_row_modif (proxy, proxy_row, model_col, &rv);
1586 g_assert (rm);
1587
1588 if (alter_flags & GDA_VALUE_ATTR_IS_DEFAULT) {
1589 GdaValueAttribute flags = 0;
1590 if (!rv) {
1591 /* create a new RowValue */
1592 rv = g_new0 (RowValue, 1);
1593 rv->row_modif = rm;
1594 rv->model_column = model_col;
1595 rv->attributes = proxy->priv->columns_attrs [col];
1596 flags = rv->attributes;
1597
1598 rv->value = NULL;
1599 flags &= ~GDA_VALUE_ATTR_IS_UNCHANGED;
1600 if (rm->model_row >= 0)
1601 flags |= GDA_VALUE_ATTR_HAS_VALUE_ORIG;
1602 else
1603 flags &= ~GDA_VALUE_ATTR_HAS_VALUE_ORIG;
1604
1605 rm->modify_values = g_slist_prepend (rm->modify_values, rv);
1606 }
1607 else {
1608 flags = rv->attributes;
1609 if (rv->value) {
1610 gda_value_free (rv->value);
1611 rv->value = NULL;
1612 }
1613 }
1614 flags |= GDA_VALUE_ATTR_IS_DEFAULT;
1615 rv->attributes = flags;
1616
1617 if (proxy->priv->notify_changes)
1618 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1619 }
1620 if (alter_flags & GDA_VALUE_ATTR_IS_UNCHANGED) {
1621 if (!rm->orig_values)
1622 g_warning ("Alter_Flags = GDA_VALUE_ATTR_IS_UNCHANGED, no RowValue!");
1623 else
1624 gda_data_proxy_set_value_at ((GdaDataModel*) proxy,
1625 model_col, proxy_row,
1626 rm->orig_values [model_col],
1627 NULL);
1628 }
1629 }
1630
1631 gda_mutex_unlock (proxy->priv->mutex);
1632 }
1633
1634 /**
1635 * gda_data_proxy_get_proxied_model_row:
1636 * @proxy: a #GdaDataProxy object
1637 * @proxy_row: A proxy row number
1638 *
1639 * Get the @proxy's proxied model row corresponding to @proxy_row
1640
1641 * Returns: the proxied model's row, or -1 if @proxy row which only exists @proxy
1642 */
1643 gint
gda_data_proxy_get_proxied_model_row(GdaDataProxy * proxy,gint proxy_row)1644 gda_data_proxy_get_proxied_model_row (GdaDataProxy *proxy, gint proxy_row)
1645 {
1646 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
1647 g_return_val_if_fail (proxy->priv, 0);
1648 g_return_val_if_fail (proxy_row >= 0, 0);
1649
1650 return proxy_row_to_model_row (proxy, proxy_row);
1651 }
1652
1653 /**
1654 * gda_data_proxy_delete:
1655 * @proxy: a #GdaDataProxy object
1656 * @proxy_row: A proxy row number
1657 *
1658 * Marks the row @proxy_row to be deleted
1659 */
1660 void
gda_data_proxy_delete(GdaDataProxy * proxy,gint proxy_row)1661 gda_data_proxy_delete (GdaDataProxy *proxy, gint proxy_row)
1662 {
1663 RowModif *rm = NULL;
1664 gboolean do_signal = FALSE;
1665 gint model_row, abs_row;
1666
1667 g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1668 g_return_if_fail (proxy->priv);
1669 g_return_if_fail (proxy_row >= 0);
1670
1671 gda_mutex_lock (proxy->priv->mutex);
1672
1673 /* ensure that there is no sync to be done */
1674 ensure_chunk_sync (proxy);
1675
1676 if (proxy->priv->add_null_entry && proxy_row == 0) {
1677 g_warning (_("The first row is an empty row artificially prepended and cannot be removed"));
1678 gda_mutex_unlock (proxy->priv->mutex);
1679 return;
1680 }
1681
1682 if (! (gda_data_model_get_access_flags ((GdaDataModel*) proxy) & GDA_DATA_MODEL_ACCESS_DELETE)) {
1683 gda_mutex_unlock (proxy->priv->mutex);
1684 return;
1685 }
1686
1687 abs_row = proxy_row_to_absolute_row (proxy, proxy_row);
1688 model_row = absolute_row_to_model_row (proxy, abs_row, &rm);
1689 if (rm) {
1690 if (! rm->to_be_deleted) {
1691 if (rm->model_row == -1) {
1692 /* remove the row completely because it does not exist in the data model */
1693 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1694 proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
1695 row_modifs_free (rm);
1696
1697 if (proxy->priv->chunk) {
1698 /* Update chunk */
1699 gsize i;
1700 gint *v;
1701 gint row_cmp = proxy_row - (proxy->priv->add_null_entry ? 1 : 0);
1702 for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1703 v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1704 if (*v > abs_row)
1705 *v -= 1;
1706 }
1707 g_array_remove_index (proxy->priv->chunk->mapping, row_cmp);
1708 }
1709
1710 if (proxy->priv->notify_changes)
1711 gda_data_model_row_removed ((GdaDataModel *) proxy, proxy_row);
1712 }
1713 else {
1714 rm->to_be_deleted = TRUE;
1715 do_signal = TRUE;
1716 }
1717 }
1718 }
1719 else {
1720 /* the row is an existing row in the data model, create a new RowModif */
1721 rm = row_modifs_new (proxy, proxy_row);
1722 rm->model_row = model_row;
1723
1724 gint *ptr;
1725 ptr = g_new (gint, 1);
1726 *ptr = model_row;
1727 g_hash_table_insert (proxy->priv->modify_rows, ptr, rm);
1728 proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
1729 rm->to_be_deleted = TRUE;
1730 do_signal = TRUE;
1731 }
1732
1733 if (do_signal && proxy->priv->notify_changes) {
1734 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1735 g_signal_emit (G_OBJECT (proxy),
1736 gda_data_proxy_signals[ROW_DELETE_CHANGED],
1737 0, proxy_row, TRUE);
1738 }
1739
1740 gda_mutex_unlock (proxy->priv->mutex);
1741 }
1742
1743 /**
1744 * gda_data_proxy_undelete:
1745 * @proxy: a #GdaDataProxy object
1746 * @proxy_row: A proxy row number
1747 *
1748 * Remove the "to be deleted" mark at the row @proxy_row, if it existed.
1749 */
1750 void
gda_data_proxy_undelete(GdaDataProxy * proxy,gint proxy_row)1751 gda_data_proxy_undelete (GdaDataProxy *proxy, gint proxy_row)
1752 {
1753 RowModif *rm = NULL;
1754 gboolean do_signal = FALSE;
1755 gint model_row;
1756
1757 g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1758 g_return_if_fail (proxy->priv);
1759 g_return_if_fail (proxy_row >= 0);
1760
1761 gda_mutex_lock (proxy->priv->mutex);
1762
1763 /* ensure that there is no sync to be done */
1764 ensure_chunk_sync (proxy);
1765
1766 model_row = absolute_row_to_model_row (proxy,
1767 proxy_row_to_absolute_row (proxy, proxy_row), &rm);
1768 if (rm) {
1769 rm->to_be_deleted = FALSE;
1770 if (!rm->modify_values) {
1771 /* get rid of that RowModif */
1772 do_signal= TRUE;
1773
1774 gint tmp;
1775 tmp = model_row;
1776 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1777 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1778 row_modifs_free (rm);
1779 }
1780 else
1781 do_signal= TRUE;
1782 }
1783
1784 if (do_signal && proxy->priv->notify_changes) {
1785 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1786 g_signal_emit (G_OBJECT (proxy),
1787 gda_data_proxy_signals[ROW_DELETE_CHANGED],
1788 0, proxy_row, FALSE);
1789 }
1790
1791 gda_mutex_unlock (proxy->priv->mutex);
1792 }
1793
1794 /**
1795 * gda_data_proxy_row_is_deleted:
1796 * @proxy: a #GdaDataProxy object
1797 * @proxy_row: A proxy row number
1798 *
1799 * Tells if the row number @proxy_row is marked to be deleted.
1800 *
1801 * Returns: TRUE if the row is marked to be deleted
1802 */
1803 gboolean
gda_data_proxy_row_is_deleted(GdaDataProxy * proxy,gint proxy_row)1804 gda_data_proxy_row_is_deleted (GdaDataProxy *proxy, gint proxy_row)
1805 {
1806 RowModif *rm;
1807
1808 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
1809 g_return_val_if_fail (proxy->priv, FALSE);
1810 g_return_val_if_fail (proxy_row >= 0, FALSE);
1811
1812 rm = proxy_row_to_row_modif (proxy, proxy_row);
1813 return rm && rm->to_be_deleted ? TRUE : FALSE;
1814 }
1815
1816 /**
1817 * gda_data_proxy_row_is_inserted:
1818 * @proxy: a #GdaDataProxy object
1819 * @proxy_row: A proxy row number
1820 *
1821 * Tells if the row number @proxy_row is a row which has been inserted in @proxy
1822 * (and is thus not in the proxied data model).
1823 *
1824 * Returns: TRUE if the row is an inserted row
1825 */
1826 gboolean
gda_data_proxy_row_is_inserted(GdaDataProxy * proxy,gint proxy_row)1827 gda_data_proxy_row_is_inserted (GdaDataProxy *proxy, gint proxy_row)
1828 {
1829 RowModif *rm;
1830
1831 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
1832 g_return_val_if_fail (proxy->priv, FALSE);
1833 g_return_val_if_fail (proxy_row >= 0, FALSE);
1834
1835 rm = proxy_row_to_row_modif (proxy, proxy_row);
1836 if (rm && (rm->model_row < 0))
1837 return TRUE;
1838 else
1839 return FALSE;
1840 }
1841
1842 /*
1843 * gda_data_proxy_append
1844 * @proxy: a #GdaDataProxy object
1845 *
1846 * Appends a new row to the proxy. The operation can fail if either:
1847 * <itemizedlist>
1848 * <listitem><para>The INSERT operation is not accepted by the proxied data model</para></listitem>
1849 * <listitem><para>There is an unknown number of rows in the proxy</para></listitem>
1850 * </itemizedlist>
1851 *
1852 * Returns: the proxy row number of the new row, or -1 if the row could not be appended
1853 */
1854 static gint
gda_data_proxy_append(GdaDataProxy * proxy)1855 gda_data_proxy_append (GdaDataProxy *proxy)
1856 {
1857 RowModif *rm;
1858 gint col;
1859 gint proxy_row;
1860 gint abs_row;
1861
1862 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
1863 g_return_val_if_fail (proxy->priv, -1);
1864
1865 /* ensure that there is no sync to be done */
1866 ensure_chunk_sync (proxy);
1867
1868 if (! (gda_data_model_get_access_flags ((GdaDataModel *) proxy) & GDA_DATA_MODEL_ACCESS_INSERT))
1869 return -1;
1870 if (proxy->priv->model_nb_rows == -1)
1871 return -1;
1872
1873 /* RowModif structure */
1874 rm = row_modifs_new (proxy, -1);
1875 rm->model_row = -1;
1876 rm->orig_values = NULL; /* there is no original value */
1877 rm->orig_values_size = proxy->priv->model_nb_cols;
1878
1879 proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
1880 proxy->priv->new_rows = g_slist_append (proxy->priv->new_rows, rm);
1881
1882 /* new proxy row value */
1883 abs_row = row_modif_to_absolute_row (proxy, rm);
1884 if (proxy->priv->chunk) {
1885 proxy_row = proxy->priv->chunk->mapping->len;
1886 g_array_append_val (proxy->priv->chunk->mapping, abs_row);
1887 if (proxy->priv->add_null_entry)
1888 proxy_row++;
1889 }
1890 else
1891 proxy_row = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy) - 1;
1892
1893 /* for the columns which allow a default value, set them to the default value */
1894 for (col = 0; col < proxy->priv->model_nb_cols; col ++) {
1895 GdaColumn *column;
1896 const GValue *def;
1897 RowValue *rv;
1898 GdaValueAttribute flags = 0;
1899
1900 /* create a new RowValue */
1901 rv = g_new0 (RowValue, 1);
1902 rv->row_modif = rm;
1903 rv->model_column = col;
1904 rv->attributes = GDA_VALUE_ATTR_NONE;
1905 rv->value = NULL;
1906 rm->modify_values = g_slist_prepend (rm->modify_values, rv);
1907
1908 column = gda_data_model_describe_column (proxy->priv->model, col);
1909 def = gda_column_get_default_value (column);
1910 if (def) {
1911 flags |= (GDA_VALUE_ATTR_IS_DEFAULT | GDA_VALUE_ATTR_CAN_BE_DEFAULT);
1912 if (G_VALUE_TYPE (def) == gda_column_get_g_type (column))
1913 rv->value = gda_value_copy (def);
1914 }
1915 if (gda_column_get_allow_null (column)) {
1916 GdaValueAttribute attributes;
1917
1918 attributes = gda_data_model_get_attributes_at (proxy->priv->model, col, -1);;
1919 if (attributes & GDA_VALUE_ATTR_CAN_BE_NULL)
1920 flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
1921 }
1922
1923 if (gda_column_get_auto_increment (column))
1924 flags |= (GDA_VALUE_ATTR_IS_DEFAULT | GDA_VALUE_ATTR_CAN_BE_DEFAULT);
1925
1926 rv->attributes = flags;
1927 }
1928
1929 /* signal row insertion */
1930 if (proxy->priv->notify_changes)
1931 gda_data_model_row_inserted ((GdaDataModel *) proxy, proxy_row);
1932
1933 return proxy_row;
1934 }
1935
1936 /**
1937 * gda_data_proxy_cancel_row_changes:
1938 * @proxy: a #GdaDataProxy object
1939 * @proxy_row: the row to cancel changes
1940 * @col: the column to cancel changes, or less than 0 to cancel any change on the @row row
1941 *
1942 * Resets data at the corresponding row and column. If @proxy_row corresponds to a new row, then
1943 * that new row is deleted from @proxy.
1944 */
1945 void
gda_data_proxy_cancel_row_changes(GdaDataProxy * proxy,gint proxy_row,gint col)1946 gda_data_proxy_cancel_row_changes (GdaDataProxy *proxy, gint proxy_row, gint col)
1947 {
1948 g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1949 g_return_if_fail (proxy->priv);
1950 g_return_if_fail (proxy_row >= 0);
1951
1952 gda_mutex_lock (proxy->priv->mutex);
1953
1954 /* ensure that there is no sync to be done */
1955 ensure_chunk_sync (proxy);
1956
1957 if (((col >= 0) && (col < proxy->priv->model_nb_cols)) ||
1958 (col < 0)) {
1959 RowModif *rm;
1960 gboolean signal_update = FALSE;
1961 gboolean signal_delete = FALSE;
1962
1963 rm = proxy_row_to_row_modif (proxy, proxy_row);
1964 if (rm && rm->modify_values) {
1965 /* there are some modifications to the row */
1966 GSList *list;
1967 RowValue *rv = NULL;
1968
1969 list = rm->modify_values;
1970 while (list && (!rv || (col < 0))) {
1971 if ((col < 0) || (ROW_VALUE (list->data)->model_column == col)) {
1972 rv = ROW_VALUE (list->data);
1973
1974 /* remove this RowValue from the RowList */
1975 rm->modify_values = g_slist_remove (rm->modify_values, rv);
1976 if (!rm->modify_values && !rm->to_be_deleted) {
1977 /* remove this RowList as well */
1978 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1979 if (rm->model_row < 0) {
1980 if (proxy->priv->chunk) {
1981 /* Update chunk */
1982 gsize i;
1983 gint *v, abs_row;
1984 gint row_cmp = proxy_row - (proxy->priv->add_null_entry ? 1 : 0);
1985 abs_row = proxy_row_to_absolute_row (proxy, proxy_row);
1986 for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1987 v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1988 if (*v > abs_row)
1989 *v -= 1;
1990 }
1991 g_array_remove_index (proxy->priv->chunk->mapping, row_cmp);
1992 }
1993 signal_delete = TRUE;
1994 proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
1995 }
1996 else {
1997 gint tmp;
1998 tmp = rm->model_row;
1999 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2000 }
2001 row_modifs_free (rm);
2002 rm = NULL;
2003 }
2004 else {
2005 signal_update = TRUE;
2006 }
2007 if (rm)
2008 list = rm->modify_values;
2009 else
2010 list = NULL;
2011 }
2012 else
2013 list = list->next;
2014 }
2015 }
2016
2017 if (proxy->priv->notify_changes) {
2018 if (signal_delete)
2019 gda_data_model_row_removed ((GdaDataModel *) proxy, proxy_row);
2020 else if (signal_update)
2021 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
2022 }
2023 }
2024 else
2025 g_warning ("GdaDataProxy column %d is not a modifiable data column", col);
2026
2027 gda_mutex_unlock (proxy->priv->mutex);
2028 }
2029
2030 static gboolean commit_row_modif (GdaDataProxy *proxy, RowModif *rm, gboolean adjust_display, GError **error);
2031
2032 /**
2033 * gda_data_proxy_apply_row_changes:
2034 * @proxy: a #GdaDataProxy object
2035 * @proxy_row: the row number to commit
2036 * @error: place to store the error, or %NULL
2037 *
2038 * Commits the modified data in the proxy back into the #GdaDataModel.
2039 *
2040 * Returns: TRUE if no error occurred.
2041 */
2042 gboolean
gda_data_proxy_apply_row_changes(GdaDataProxy * proxy,gint proxy_row,GError ** error)2043 gda_data_proxy_apply_row_changes (GdaDataProxy *proxy, gint proxy_row, GError **error)
2044 {
2045 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2046 g_return_val_if_fail (proxy->priv, FALSE);
2047 g_return_val_if_fail (proxy_row >= 0, FALSE);
2048
2049 return commit_row_modif (proxy, proxy_row_to_row_modif (proxy, proxy_row), TRUE, error);
2050 }
2051
2052 /*
2053 * Commits the modifications held in one single RowModif structure.
2054 *
2055 * Returns: TRUE if no error occurred
2056 */
2057 static gboolean
commit_row_modif(GdaDataProxy * proxy,RowModif * rm,gboolean adjust_display,GError ** error)2058 commit_row_modif (GdaDataProxy *proxy, RowModif *rm, gboolean adjust_display, GError **error)
2059 {
2060 gboolean err = FALSE;
2061 gint proxy_row, model_row;
2062 GError *lerror = NULL;
2063
2064 if (!rm)
2065 return TRUE;
2066
2067 gda_mutex_lock (proxy->priv->mutex);
2068
2069 model_row = rm->model_row;
2070
2071 /* ensure that there is no sync to be done */
2072 ensure_chunk_sync (proxy);
2073
2074 /*
2075 * Steps in this procedure:
2076 * -1- send the "validate-row-changes" signal, and abort if return value is FALSE
2077 * -2- apply desired modification (which _should_ trigger "row_{inserted,removed,updated}" signals from
2078 * the proxied model)
2079 * -3- if no error then destroy the RowModif which has just been applied
2080 * and refresh displayed chunks if @adjust_display is set to TRUE
2081 * -4- send the "row-changes-applied" signal
2082 */
2083 proxy_row = row_modif_to_proxy_row (proxy, rm);
2084
2085 /* validate the changes to this row */
2086 g_signal_emit (G_OBJECT (proxy),
2087 gda_data_proxy_signals[VALIDATE_ROW_CHANGES],
2088 0, proxy_row, rm->model_row, &lerror);
2089 if (lerror) {
2090 g_propagate_error (error, lerror);
2091 gda_mutex_unlock (proxy->priv->mutex);
2092 return FALSE;
2093 }
2094
2095 /* apply the changes */
2096 if (rm->to_be_deleted) {
2097 /* delete the row */
2098 g_assert (rm->model_row >= 0);
2099 if (!gda_data_model_remove_row (proxy->priv->model, rm->model_row, error))
2100 err = TRUE;
2101 }
2102 else {
2103 if (rm->model_row >= 0) {
2104 /* update the row */
2105 GList *values = NULL;
2106 gint i;
2107
2108 g_assert (rm->modify_values);
2109 g_assert (rm->orig_values);
2110 for (i=0; i < rm->orig_values_size; i++) {
2111 gboolean newvalue_found = FALSE;
2112 GValue *newvalue = NULL;
2113 GSList *list;
2114
2115 for (list = rm->modify_values; list; list = list->next) {
2116 if (ROW_VALUE (list->data)->model_column == i) {
2117 newvalue_found = TRUE;
2118 if (ROW_VALUE (list->data)->attributes &
2119 GDA_VALUE_ATTR_IS_DEFAULT)
2120 newvalue = NULL;
2121 else {
2122 if (! ROW_VALUE (list->data)->value)
2123 newvalue = gda_value_new_null ();
2124 else
2125 newvalue = gda_value_copy (ROW_VALUE (list->data)->value);
2126 }
2127 break;
2128 }
2129 }
2130 if (!newvalue_found && rm->orig_values[i])
2131 newvalue = gda_value_copy (rm->orig_values[i]);
2132 values = g_list_append (values, newvalue);
2133 }
2134
2135 err = ! gda_data_model_set_values (proxy->priv->model, rm->model_row,
2136 values, error);
2137 g_list_foreach (values, (GFunc) gda_value_free, NULL);
2138 g_list_free (values);
2139 }
2140 else {
2141 /* insert a new row */
2142 GSList *list;
2143 GList *values = NULL;
2144 gint i;
2145 GValue *newvalue;
2146 GValue **free_val;
2147 gint new_row;
2148
2149 g_assert (rm->modify_values);
2150 free_val = g_new0 (GValue *, proxy->priv->model_nb_cols);
2151 for (i = 0; i < proxy->priv->model_nb_cols; i++) {
2152 newvalue = NULL;
2153
2154 list = rm->modify_values;
2155 while (list && !newvalue) {
2156 if (ROW_VALUE (list->data)->model_column == i) {
2157 if (ROW_VALUE (list->data)->attributes &
2158 GDA_VALUE_ATTR_IS_DEFAULT)
2159 newvalue = NULL;
2160 else {
2161 if (! ROW_VALUE (list->data)->value) {
2162 newvalue = gda_value_new_null ();
2163 free_val [i] = newvalue;
2164 }
2165 else
2166 newvalue = ROW_VALUE (list->data)->value;
2167
2168 }
2169 }
2170 list = g_slist_next (list);
2171 }
2172 values = g_list_append (values, newvalue);
2173 }
2174
2175 proxy->priv->defer_proxied_model_insert = TRUE;
2176 proxy->priv->catched_inserted_row = -1;
2177 new_row = gda_data_model_append_values (proxy->priv->model, values, error);
2178 err = new_row >= 0 ? FALSE : TRUE;
2179
2180 g_list_free (values);
2181 for (i = 0; i < proxy->priv->model_nb_cols; i++)
2182 if (free_val [i])
2183 gda_value_free (free_val [i]);
2184 g_free (free_val);
2185 if (!err) {
2186 if (proxy->priv->catched_inserted_row < 0) {
2187 g_warning (_("Proxied data model reports the modifications as accepted, yet did not emit the "
2188 "corresponding \"row-inserted\", \"row-updated\" or \"row-removed\" signal. This "
2189 "is a bug of the %s's implementation (please report a bug)."),
2190 G_OBJECT_TYPE_NAME (proxy->priv->model));
2191 }
2192
2193 proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
2194 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
2195
2196 gint tmp;
2197 tmp = rm->model_row;
2198 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2199 row_modifs_free (rm);
2200 rm = NULL;
2201
2202 if (proxy_row >= 0)
2203 gda_data_model_row_updated ((GdaDataModel*) proxy, proxy_row);
2204
2205 /* signal row actually changed */
2206 g_signal_emit (G_OBJECT (proxy),
2207 gda_data_proxy_signals[ROW_CHANGES_APPLIED],
2208 0, proxy_row, -1);
2209 }
2210
2211 proxy->priv->catched_inserted_row = -1;
2212 proxy->priv->defer_proxied_model_insert = FALSE;
2213 }
2214 }
2215
2216 if (!err && rm) {
2217 /* signal row actually changed */
2218 g_signal_emit (G_OBJECT (proxy),
2219 gda_data_proxy_signals[ROW_CHANGES_APPLIED],
2220 0, proxy_row, model_row);
2221
2222 /* get rid of the committed change; if the changes have been applied correctly, @rm should
2223 * have been removed from the proxy->priv->all_modifs list because the proxied model
2224 * should habe emitted the "row_{inserted,removed,updated}" signals */
2225 if (rm && g_slist_find (proxy->priv->all_modifs, rm)) {
2226 g_warning (_("Proxied data model reports the modifications as accepted, yet did not emit the "
2227 "corresponding \"row-inserted\", \"row-updated\" or \"row-removed\" signal. This "
2228 "may be a bug of the %s's implementation (please report a bug)."),
2229 G_OBJECT_TYPE_NAME (proxy->priv->model));
2230 proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
2231 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
2232
2233 gint tmp;
2234 tmp = rm->model_row;
2235 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2236 row_modifs_free (rm);
2237 }
2238 }
2239
2240 if (adjust_display)
2241 adjust_displayed_chunk (proxy);
2242
2243 gda_mutex_unlock (proxy->priv->mutex);
2244
2245 return !err;
2246 }
2247
2248 /**
2249 * gda_data_proxy_has_changed:
2250 * @proxy: a #GdaDataProxy object
2251 *
2252 * Tells if @proxy contains any modifications not applied to the proxied data model.
2253 *
2254 * Returns: TRUE if there are some modifications in @proxy
2255 */
2256 gboolean
gda_data_proxy_has_changed(GdaDataProxy * proxy)2257 gda_data_proxy_has_changed (GdaDataProxy *proxy)
2258 {
2259 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2260 g_return_val_if_fail (proxy->priv, FALSE);
2261
2262 return proxy->priv->all_modifs ? TRUE : FALSE;
2263 }
2264
2265 /**
2266 * gda_data_proxy_row_has_changed:
2267 * @proxy: a #GdaDataProxy object
2268 * @proxy_row: A proxy row number
2269 *
2270 * Tells if the row number @proxy_row has changed
2271 *
2272 * Returns: TRUE if the row has changed
2273 */
2274 gboolean
gda_data_proxy_row_has_changed(GdaDataProxy * proxy,gint proxy_row)2275 gda_data_proxy_row_has_changed (GdaDataProxy *proxy, gint proxy_row)
2276 {
2277 RowModif *rm;
2278
2279 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2280 g_return_val_if_fail (proxy->priv, FALSE);
2281 g_return_val_if_fail (proxy_row >= 0, FALSE);
2282
2283 rm = proxy_row_to_row_modif (proxy, proxy_row);
2284 return rm && (rm->modify_values || rm->to_be_deleted) ? TRUE : FALSE;
2285 }
2286
2287 /**
2288 * gda_data_proxy_get_n_new_rows:
2289 * @proxy: a #GdaDataProxy object
2290 *
2291 * Get the number of rows which have been added to @proxy and which are not part of
2292 * the proxied data model.
2293 *
2294 * Returns: the number of new rows
2295 */
2296 gint
gda_data_proxy_get_n_new_rows(GdaDataProxy * proxy)2297 gda_data_proxy_get_n_new_rows (GdaDataProxy *proxy)
2298 {
2299 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2300 g_return_val_if_fail (proxy->priv, 0);
2301
2302 return g_slist_length (proxy->priv->new_rows);
2303 }
2304
2305 /**
2306 * gda_data_proxy_get_n_modified_rows:
2307 * @proxy: a #GdaDataProxy object
2308 *
2309 * Get the number of rows which have been modified in the proxy (the sum of rows existing in
2310 * the proxied data model which have been modified, and new rows).
2311 *
2312 * Returns: the number of modified rows
2313 */
2314 gint
gda_data_proxy_get_n_modified_rows(GdaDataProxy * proxy)2315 gda_data_proxy_get_n_modified_rows (GdaDataProxy *proxy)
2316 {
2317 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2318 g_return_val_if_fail (proxy->priv, 0);
2319
2320 return g_slist_length (proxy->priv->all_modifs);
2321 }
2322
2323 /**
2324 * gda_data_proxy_set_sample_size:
2325 * @proxy: a #GdaDataProxy object
2326 * @sample_size: the requested size of a chunk, or 0
2327 *
2328 * Sets the size of each chunk of data to display: the maximum number of rows which
2329 * can be "displayed" at a time (the maximum number of rows which @proxy pretends to have).
2330 * The default value is arbitrary 300 as it is big enough to
2331 * be able to display quite a lot of data, but small enough to avoid too much data
2332 * displayed at the same time.
2333 *
2334 * Note: the rows which have been added but not yet committed will always be displayed
2335 * regardless of the current chunk of data, and the modified rows which are not visible
2336 * when the displayed chunk of data changes are still held as modified rows.
2337 *
2338 * To remove the chunking of the data to display, simply pass @sample_size the %0 value.
2339 */
2340 void
gda_data_proxy_set_sample_size(GdaDataProxy * proxy,gint sample_size)2341 gda_data_proxy_set_sample_size (GdaDataProxy *proxy, gint sample_size)
2342 {
2343 gint new_sample_size;
2344 g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
2345 g_return_if_fail (proxy->priv);
2346
2347 gda_mutex_lock (proxy->priv->mutex);
2348
2349 /* ensure that there is no sync to be done */
2350 ensure_chunk_sync (proxy);
2351
2352 new_sample_size = sample_size <= 0 ? 0 : sample_size;
2353 if (proxy->priv->sample_size != new_sample_size) {
2354 proxy->priv->sample_size = new_sample_size;
2355 adjust_displayed_chunk (proxy);
2356 g_signal_emit (G_OBJECT (proxy),
2357 gda_data_proxy_signals[SAMPLE_SIZE_CHANGED],
2358 0, sample_size);
2359 }
2360
2361 gda_mutex_unlock (proxy->priv->mutex);
2362 }
2363
2364 /**
2365 * gda_data_proxy_get_sample_size:
2366 * @proxy: a #GdaDataProxy object
2367 *
2368 * Get the size of each chunk of data displayed at a time.
2369 *
2370 * Returns: the chunk (or sample) size, or 0 if chunking is disabled.
2371 */
2372 gint
gda_data_proxy_get_sample_size(GdaDataProxy * proxy)2373 gda_data_proxy_get_sample_size (GdaDataProxy *proxy)
2374 {
2375 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2376 g_return_val_if_fail (proxy->priv, 0);
2377
2378 return proxy->priv->sample_size;
2379 }
2380
2381 /**
2382 * gda_data_proxy_set_sample_start:
2383 * @proxy: a #GdaDataProxy object
2384 * @sample_start: the number of the first row to be displayed
2385 *
2386 * Sets the number of the first row to be available in @proxy (in reference to the proxied data model)
2387 */
2388 void
gda_data_proxy_set_sample_start(GdaDataProxy * proxy,gint sample_start)2389 gda_data_proxy_set_sample_start (GdaDataProxy *proxy, gint sample_start)
2390 {
2391 g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
2392 g_return_if_fail (proxy->priv);
2393 g_return_if_fail (sample_start >= 0);
2394
2395 gda_mutex_lock (proxy->priv->mutex);
2396
2397 /* ensure that there is no sync to be done */
2398 ensure_chunk_sync (proxy);
2399
2400 if (proxy->priv->sample_first_row != sample_start) {
2401 proxy->priv->sample_first_row = sample_start;
2402 adjust_displayed_chunk (proxy);
2403 }
2404
2405 gda_mutex_unlock (proxy->priv->mutex);
2406 }
2407
2408 /**
2409 * gda_data_proxy_get_sample_start:
2410 * @proxy: a #GdaDataProxy object
2411 *
2412 * Get the number of the first row to be available in @proxy (in reference to the proxied data model)
2413 *
2414 * Returns: the number of the first proxied model's row.
2415 */
2416 gint
gda_data_proxy_get_sample_start(GdaDataProxy * proxy)2417 gda_data_proxy_get_sample_start (GdaDataProxy *proxy)
2418 {
2419 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2420 g_return_val_if_fail (proxy->priv, 0);
2421
2422 return proxy->priv->sample_first_row;
2423 }
2424
2425 /**
2426 * gda_data_proxy_get_sample_end:
2427 * @proxy: a #GdaDataProxy object
2428 *
2429 * Get the number of the last row to be available in @proxy (in reference to the proxied data model)
2430 *
2431 * Returns: the number of the last proxied model's row.
2432 */
2433 gint
gda_data_proxy_get_sample_end(GdaDataProxy * proxy)2434 gda_data_proxy_get_sample_end (GdaDataProxy *proxy)
2435 {
2436 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2437 g_return_val_if_fail (proxy->priv, 0);
2438
2439 return proxy->priv->sample_last_row;
2440 }
2441
2442 static DisplayChunk *
display_chunk_new(gint reserved_size)2443 display_chunk_new (gint reserved_size)
2444 {
2445 DisplayChunk *chunk;
2446
2447 chunk = g_new0 (DisplayChunk, 1);
2448 chunk->mapping = g_array_sized_new (FALSE, TRUE, sizeof (gint), reserved_size);
2449
2450 return chunk;
2451 }
2452
2453 static void
display_chunk_free(DisplayChunk * chunk)2454 display_chunk_free (DisplayChunk *chunk)
2455 {
2456 if (chunk->mapping)
2457 g_array_free (chunk->mapping, TRUE);
2458 g_free (chunk);
2459 }
2460
2461 #ifdef GDA_DEBUG
2462 static void
display_chunks_dump(GdaDataProxy * proxy)2463 display_chunks_dump (GdaDataProxy *proxy)
2464 {
2465 #define DUMP
2466 #undef DUMP
2467 #ifdef DUMP
2468 gint i, total1 = 0, total2 = 0;
2469
2470 g_print ("================== CHUNK=%p, TO=%p (mapping=%p), SEP=%d\n", proxy->priv->chunk, proxy->priv->chunk_to,
2471 proxy->priv->chunk_to ? proxy->priv->chunk_to->mapping : NULL,
2472 proxy->priv->chunk_sep);
2473 if (!proxy->priv->chunk && !proxy->priv->chunk_to)
2474 g_print ("No chunks at all\n");
2475
2476 if (proxy->priv->chunk)
2477 total1 = proxy->priv->chunk->mapping->len;
2478 if (proxy->priv->chunk_to && proxy->priv->chunk_to->mapping)
2479 total2 = proxy->priv->chunk_to->mapping->len;
2480
2481 g_print ("CHUNK CHUNK_TO\n");
2482 for (i = 0; i < MAX (total1, total2); i++) {
2483 if (i < total1)
2484 g_print ("%03d", g_array_index (proxy->priv->chunk->mapping, gint, i));
2485 else
2486 g_print (" ");
2487 g_print (" ==== ");
2488 if (i < total2)
2489 g_print ("%03d", g_array_index (proxy->priv->chunk_to->mapping, gint, i));
2490 else
2491 g_print (" ");
2492 g_print ("\n");
2493 }
2494 #endif
2495 }
2496 #else
2497 static void
display_chunks_dump(G_GNUC_UNUSED GdaDataProxy * proxy)2498 display_chunks_dump (G_GNUC_UNUSED GdaDataProxy *proxy)
2499 {}
2500 #endif
2501
2502 /*
2503 * Makes sure the sync, if necessary, is finished
2504 */
2505 static void
ensure_chunk_sync(GdaDataProxy * proxy)2506 ensure_chunk_sync (GdaDataProxy *proxy)
2507 {
2508 gda_mutex_lock (proxy->priv->mutex);
2509 if (proxy->priv->chunk_sync_idle_id) {
2510 gboolean defer_sync = proxy->priv->defer_sync;
2511 proxy->priv->defer_sync = FALSE;
2512
2513 chunk_sync_idle (proxy);
2514 proxy->priv->defer_sync = defer_sync;
2515 }
2516 gda_mutex_unlock (proxy->priv->mutex);
2517 }
2518
2519 /*
2520 * Emit all the correct signals when switching from proxy->priv->chunk to
2521 * proxy->priv->chunk_to
2522 */
2523 static gboolean
chunk_sync_idle(GdaDataProxy * proxy)2524 chunk_sync_idle (GdaDataProxy *proxy)
2525 {
2526 #define IDLE_STEP 50
2527 gda_mutex_lock (proxy->priv->mutex);
2528
2529 gboolean finished = FALSE;
2530 guint index, max_steps, step;
2531 GdaDataModelIter *iter = NULL;
2532 gint signal_row_offset = proxy->priv->add_null_entry ? 1 : 0;
2533
2534 if (!proxy->priv->defer_sync) {
2535 if (proxy->priv->chunk_sync_idle_id) {
2536 g_idle_remove_by_data (proxy);
2537 proxy->priv->chunk_sync_idle_id = 0;
2538 }
2539 max_steps = G_MAXINT;
2540 }
2541
2542 if (!proxy->priv->chunk_to) {
2543 gda_mutex_unlock (proxy->priv->mutex);
2544 return FALSE; /* nothing to do */
2545 }
2546
2547 max_steps = 0;
2548 if (proxy->priv->chunk_proxy_nb_rows < 0)
2549 proxy->priv->chunk_proxy_nb_rows = proxy->priv->model_nb_rows + g_slist_length (proxy->priv->new_rows);
2550 if (proxy->priv->chunk_to->mapping)
2551 max_steps = MAX (max_steps, proxy->priv->chunk_to->mapping->len - proxy->priv->chunk_sep + 1);
2552 else
2553 max_steps = MAX (max_steps, (guint)(proxy->priv->chunk_proxy_nb_rows - proxy->priv->chunk_sep + 1));
2554 if (proxy->priv->chunk)
2555 max_steps = MAX (max_steps, proxy->priv->chunk->mapping->len - proxy->priv->chunk_sep + 1);
2556 else
2557 max_steps = MAX (max_steps, (guint)(proxy->priv->chunk_proxy_nb_rows - proxy->priv->chunk_sep + 1));
2558
2559 if (proxy->priv->defer_sync)
2560 max_steps = MIN (max_steps, IDLE_STEP);
2561
2562 #ifdef DEBUG_SYNC
2563 g_print ("////////// %s(defer_sync = %d)\n", __FUNCTION__, proxy->priv->defer_sync);
2564 display_chunks_dump (proxy);
2565 #endif
2566
2567 for (index = proxy->priv->chunk_sep, step = 0;
2568 step < max_steps && !finished;
2569 step++) {
2570 gint cur_row, repl_row;
2571
2572 if (proxy->priv->chunk) {
2573 if (index < proxy->priv->chunk->mapping->len)
2574 cur_row = g_array_index (proxy->priv->chunk->mapping, gint, index);
2575 else
2576 cur_row = -1;
2577 }
2578 else {
2579 cur_row = index;
2580 if (cur_row >= proxy->priv->chunk_proxy_nb_rows)
2581 cur_row = -1;
2582 }
2583
2584 if (proxy->priv->chunk_to->mapping) {
2585 if (index < proxy->priv->chunk_to->mapping->len)
2586 repl_row = g_array_index (proxy->priv->chunk_to->mapping, gint, index);
2587 else
2588 repl_row = -1;
2589 }
2590 else {
2591 repl_row = index;
2592 if (!iter)
2593 iter = gda_data_model_create_iter (proxy->priv->model);
2594 if (!gda_data_model_iter_move_to_row (iter, repl_row)) {
2595 if (gda_data_model_iter_get_row (iter) != repl_row)
2596 repl_row = -1;
2597 }
2598 }
2599
2600 #ifdef DEBUG_SYNC
2601 g_print ("INDEX=%d step=%d max_steps=%d cur_row=%d repl_row=%d\n", index, step, max_steps, cur_row, repl_row);
2602 #endif
2603 if ((cur_row >= 0) && (repl_row >= 0)) {
2604 /* emit the GdaDataModel::"row-updated" signal */
2605 if (proxy->priv->chunk) {
2606 g_array_insert_val (proxy->priv->chunk->mapping, index, repl_row);
2607 g_array_remove_index (proxy->priv->chunk->mapping, index + 1);
2608 }
2609 proxy->priv->chunk_sep++;
2610
2611 if (cur_row != repl_row)
2612 if (proxy->priv->notify_changes) {
2613 #ifdef DEBUG_SYNC
2614 g_print ("Signal: Update row %d\n", index + signal_row_offset);
2615 #endif
2616 gda_data_model_row_updated ((GdaDataModel *) proxy, index + signal_row_offset);
2617 }
2618 index++;
2619 }
2620 else if ((cur_row >= 0) && (repl_row < 0)) {
2621 /* emit the GdaDataModel::"row-removed" signal */
2622 if (proxy->priv->chunk)
2623 g_array_remove_index (proxy->priv->chunk->mapping, index);
2624 proxy->priv->chunk_proxy_nb_rows--;
2625 if (proxy->priv->notify_changes) {
2626 #ifdef DEBUG_SYNC
2627 g_print ("Signal: Remove row %d\n", index + signal_row_offset);
2628 #endif
2629 gda_data_model_row_removed ((GdaDataModel *) proxy, index + signal_row_offset);
2630 }
2631 }
2632 else if ((cur_row < 0) && (repl_row >= 0)) {
2633 /* emit GdaDataModel::"row-inserted" insert signal */
2634 if (proxy->priv->chunk)
2635 g_array_insert_val (proxy->priv->chunk->mapping, index, repl_row);
2636 proxy->priv->chunk_sep++;
2637 if (proxy->priv->notify_changes) {
2638 #ifdef DEBUG_SYNC
2639 g_print ("Signal: Insert row %d\n", index + signal_row_offset);
2640 #endif
2641 gda_data_model_row_inserted ((GdaDataModel *) proxy, index + signal_row_offset);
2642 }
2643 index++;
2644 }
2645 else
2646 finished = TRUE;
2647 }
2648
2649 if (iter)
2650 g_object_unref (iter);
2651
2652 if (finished) {
2653 if (proxy->priv->chunk_sync_idle_id) {
2654 g_idle_remove_by_data (proxy);
2655 proxy->priv->chunk_sync_idle_id = 0;
2656 }
2657
2658 if (! proxy->priv->chunk_to->mapping) {
2659 if (proxy->priv->chunk) {
2660 display_chunk_free (proxy->priv->chunk);
2661 proxy->priv->chunk = NULL;
2662 }
2663 display_chunk_free (proxy->priv->chunk_to);
2664 proxy->priv->chunk_to = NULL;
2665 }
2666 else {
2667 if (proxy->priv->chunk)
2668 display_chunk_free (proxy->priv->chunk);
2669 proxy->priv->chunk = proxy->priv->chunk_to;
2670 proxy->priv->chunk_to = NULL;
2671 }
2672 #ifdef DEBUG_SYNC
2673 g_print ("Sync. is now finished\n");
2674 #endif
2675 }
2676 #ifdef DEBUG_SYNC
2677 else
2678 g_print ("Sync. is NOT finished yet\n");
2679 #endif
2680 gda_mutex_unlock (proxy->priv->mutex);
2681 return !finished;
2682 }
2683
2684 static DisplayChunk *
compute_display_chunk(GdaDataProxy * proxy)2685 compute_display_chunk (GdaDataProxy *proxy)
2686 {
2687 DisplayChunk *ret_chunk = NULL;
2688
2689 gda_mutex_lock (proxy->priv->mutex);
2690 if (proxy->priv->filtered_rows) {
2691 /* REM: when there is a filter applied, the new rows are mixed with the
2692 * existing ones => no need to treat them appart
2693 */
2694 gint nb_rows = gda_data_model_get_n_rows (proxy->priv->filtered_rows);
2695 gint i, new_nb_rows = 0;
2696
2697 g_assert (nb_rows >= 0); /* the number of rows IS known here */
2698 if (proxy->priv->sample_size > 0) {
2699 if (proxy->priv->sample_first_row >= nb_rows)
2700 proxy->priv->sample_first_row = proxy->priv->sample_size *
2701 ((nb_rows - 1) / proxy->priv->sample_size);
2702
2703 proxy->priv->sample_last_row = proxy->priv->sample_first_row +
2704 proxy->priv->sample_size - 1;
2705 if (proxy->priv->sample_last_row >= nb_rows)
2706 proxy->priv->sample_last_row = nb_rows - 1;
2707 new_nb_rows = proxy->priv->sample_last_row - proxy->priv->sample_first_row + 1;
2708 }
2709 else {
2710 proxy->priv->sample_first_row = 0;
2711 proxy->priv->sample_last_row = nb_rows - 1;
2712 new_nb_rows = nb_rows;
2713 }
2714
2715 ret_chunk = display_chunk_new (proxy->priv->sample_size > 0 ?
2716 proxy->priv->sample_size : nb_rows);
2717 for (i = 0; i < new_nb_rows; i++) {
2718 const GValue *value;
2719 gint val;
2720
2721 g_assert (i + proxy->priv->sample_first_row < nb_rows);
2722 value = gda_data_model_get_value_at (proxy->priv->filtered_rows,
2723 0, i + proxy->priv->sample_first_row, NULL);
2724 g_assert (value);
2725 g_assert (G_VALUE_TYPE (value) == G_TYPE_INT);
2726 val = g_value_get_int (value);
2727 g_array_append_val (ret_chunk->mapping, val);
2728 }
2729 }
2730 else {
2731 gint i, new_nb_rows = 0;
2732
2733 if (proxy->priv->model_nb_rows >= 0) {
2734 /* known number of rows */
2735 if (proxy->priv->sample_size > 0) {
2736 if (proxy->priv->sample_first_row >= proxy->priv->model_nb_rows)
2737 proxy->priv->sample_first_row = proxy->priv->sample_size *
2738 ((proxy->priv->model_nb_rows - 1) / proxy->priv->sample_size);
2739
2740 proxy->priv->sample_last_row = proxy->priv->sample_first_row +
2741 proxy->priv->sample_size - 1;
2742 if (proxy->priv->sample_last_row >= proxy->priv->model_nb_rows)
2743 proxy->priv->sample_last_row = proxy->priv->model_nb_rows - 1;
2744 new_nb_rows = proxy->priv->sample_last_row - proxy->priv->sample_first_row + 1;
2745 ret_chunk = display_chunk_new (proxy->priv->sample_size);
2746 }
2747 else {
2748 /* no chunk_to->mapping needed */
2749 ret_chunk = g_new0 (DisplayChunk, 1);
2750
2751 proxy->priv->sample_first_row = 0;
2752 proxy->priv->sample_last_row = proxy->priv->model_nb_rows - 1;
2753 new_nb_rows = proxy->priv->model_nb_rows;
2754 }
2755 }
2756 else {
2757 /* no chunk_to->mapping needed */
2758 ret_chunk = g_new0 (DisplayChunk, 1);
2759
2760 if (proxy->priv->model_nb_rows == 0 ) {
2761 /* known number of rows */
2762 proxy->priv->sample_first_row = 0;
2763 proxy->priv->sample_last_row = 0;
2764 new_nb_rows = 0;
2765 }
2766 else {
2767 /* unknown number of rows */
2768 if (proxy->priv->sample_size > 0) {
2769 proxy->priv->sample_last_row = proxy->priv->sample_first_row +
2770 proxy->priv->sample_size - 1;
2771 new_nb_rows = proxy->priv->sample_last_row - proxy->priv->sample_first_row + 1;
2772 }
2773 else {
2774 proxy->priv->sample_first_row = 0;
2775 proxy->priv->sample_last_row = G_MAXINT - 1;
2776 new_nb_rows = G_MAXINT;
2777 }
2778 }
2779 }
2780 /* fill @chunk_to is it exists */
2781 if (ret_chunk && ret_chunk->mapping) {
2782 for (i = 0; i < new_nb_rows; i++) {
2783 g_assert (i + proxy->priv->sample_first_row < proxy->priv->model_nb_rows);
2784 gint val = model_row_to_absolute_row (proxy, i + proxy->priv->sample_first_row);
2785 g_array_append_val (ret_chunk->mapping, val);
2786 }
2787 GSList *list;
2788 for (i++, list = proxy->priv->new_rows; list; list = list->next, i++) {
2789 gint val = row_modif_to_absolute_row (proxy, ROW_MODIF (list->data));
2790 g_array_append_val (ret_chunk->mapping, val);
2791 }
2792 }
2793 }
2794
2795 gda_mutex_unlock (proxy->priv->mutex);
2796 return ret_chunk;
2797 }
2798
2799 /*
2800 * Adjusts the values of the first and last rows to be displayed depending
2801 * on the sample size.
2802 *
2803 * Some rows may be added or removed during the adjustment.
2804 */
2805 static void
adjust_displayed_chunk(GdaDataProxy * proxy)2806 adjust_displayed_chunk (GdaDataProxy *proxy)
2807 {
2808 g_return_if_fail (proxy->priv->model);
2809
2810 gda_mutex_lock (proxy->priv->mutex);
2811
2812 /*
2813 * Stop idle adding of rows if necessary
2814 */
2815 if (proxy->priv->chunk_sync_idle_id) {
2816 g_idle_remove_by_data (proxy);
2817 proxy->priv->chunk_sync_idle_id = 0;
2818 }
2819
2820 /* compute new DisplayChunk */
2821 if (proxy->priv->chunk_to) {
2822 display_chunk_free (proxy->priv->chunk_to);
2823 proxy->priv->chunk_to = NULL;
2824 }
2825 proxy->priv->chunk_to = compute_display_chunk (proxy);
2826 if (!proxy->priv->chunk_to) {
2827 gda_mutex_unlock (proxy->priv->mutex);
2828 return; /* nothing to do */
2829 }
2830
2831 /* determine if chunking has changed */
2832 gboolean equal = FALSE;
2833 if (proxy->priv->chunk && proxy->priv->chunk_to->mapping) {
2834 /* compare the 2 chunks */
2835 if (proxy->priv->chunk->mapping->len == proxy->priv->chunk_to->mapping->len) {
2836 gsize i;
2837 equal = TRUE;
2838 for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
2839 if (g_array_index (proxy->priv->chunk->mapping, gint, i) !=
2840 g_array_index (proxy->priv->chunk_to->mapping, gint, i)) {
2841 equal = FALSE;
2842 break;
2843 }
2844 }
2845 }
2846 }
2847 else if (!proxy->priv->chunk && !proxy->priv->chunk_to->mapping)
2848 equal = TRUE;
2849
2850 /* handle new display chunk (which may be NULL) */
2851 if (! equal) {
2852 #ifdef DEBUG_SYNC
2853 g_print ("////////// %s(%d)\n", __FUNCTION__, __LINE__);
2854 #endif
2855 display_chunks_dump (proxy);
2856 /* signal sample changed if necessary */
2857 g_signal_emit (G_OBJECT (proxy),
2858 gda_data_proxy_signals[SAMPLE_CHANGED],
2859 0, proxy->priv->sample_first_row, proxy->priv->sample_last_row);
2860
2861 /* sync proxy->priv->chunk to proxy->priv->chunk_to */
2862 proxy->priv->chunk_sep = 0;
2863 proxy->priv->chunk_proxy_nb_rows = -1;
2864 if (!proxy->priv->defer_sync)
2865 chunk_sync_idle (proxy);
2866 else
2867 proxy->priv->chunk_sync_idle_id = g_idle_add ((GSourceFunc) chunk_sync_idle, proxy);
2868 }
2869 else {
2870 /* nothing to adjust => destroy proxy->priv->chunk_to if necessary */
2871 if (proxy->priv->chunk_to) {
2872 display_chunk_free (proxy->priv->chunk_to);
2873 proxy->priv->chunk_to = NULL;
2874 }
2875 }
2876
2877 gda_mutex_unlock (proxy->priv->mutex);
2878 }
2879
2880 /**
2881 * gda_data_proxy_apply_all_changes:
2882 * @proxy: a #GdaDataProxy object
2883 * @error: a place to store errors, or %NULL
2884 *
2885 * Apply all the changes stored in the proxy to the proxied data model. The changes are done row
2886 * after row, and if an error
2887 * occurs, then it is possible that not all the changes to all the rows have been applied.
2888 *
2889 * Returns: TRUE if no error occurred
2890 */
2891 gboolean
gda_data_proxy_apply_all_changes(GdaDataProxy * proxy,GError ** error)2892 gda_data_proxy_apply_all_changes (GdaDataProxy *proxy, GError **error)
2893 {
2894 gboolean allok = TRUE;
2895
2896 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2897 g_return_val_if_fail (proxy->priv, FALSE);
2898
2899 gda_mutex_lock (proxy->priv->mutex);
2900
2901 /* ensure that there is no sync to be done */
2902 ensure_chunk_sync (proxy);
2903
2904 gda_data_model_send_hint (proxy->priv->model, GDA_DATA_MODEL_HINT_START_BATCH_UPDATE, NULL);
2905
2906 while (proxy->priv->all_modifs && allok)
2907 allok = commit_row_modif (proxy, ROW_MODIF (proxy->priv->all_modifs->data), FALSE, error);
2908
2909 gda_data_model_send_hint (proxy->priv->model, GDA_DATA_MODEL_HINT_END_BATCH_UPDATE, NULL);
2910 adjust_displayed_chunk (proxy);
2911
2912 gda_mutex_unlock (proxy->priv->mutex);
2913
2914 return allok;
2915 }
2916
2917 /**
2918 * gda_data_proxy_cancel_all_changes:
2919 * @proxy: a #GdaDataProxy object
2920 *
2921 * Cancel all the changes stored in the proxy (the @proxy will be reset to its state
2922 * as it was just after creation). Note that if there are some cached changes (i.e. not applied
2923 * to the current proxied data model), then these cached changes are not cleared (set the "cache-changes"
2924 * property to %FALSE for this).
2925 *
2926 * Returns: TRUE if no error occurred
2927 */
2928 gboolean
gda_data_proxy_cancel_all_changes(GdaDataProxy * proxy)2929 gda_data_proxy_cancel_all_changes (GdaDataProxy *proxy)
2930 {
2931 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2932 g_return_val_if_fail (proxy->priv, FALSE);
2933
2934 gda_mutex_lock (proxy->priv->mutex);
2935
2936 /* ensure that there is no sync to be done */
2937 ensure_chunk_sync (proxy);
2938 g_assert (!proxy->priv->chunk_to);
2939
2940 /* new rows are first treated and removed (no memory de-allocation here, though) */
2941 if (proxy->priv->new_rows) {
2942 if (proxy->priv->chunk) {
2943 /* Using a chunk */
2944 proxy->priv->chunk_to = display_chunk_new (proxy->priv->chunk->mapping->len);
2945 g_array_append_vals (proxy->priv->chunk_to->mapping,
2946 proxy->priv->chunk->mapping->data, proxy->priv->chunk->mapping->len);
2947
2948 while (proxy->priv->new_rows) {
2949 gint proxy_row;
2950
2951 proxy_row = row_modif_to_proxy_row (proxy, (ROW_MODIF (proxy->priv->new_rows->data)));
2952 proxy->priv->new_rows = g_slist_delete_link (proxy->priv->new_rows, proxy->priv->new_rows);
2953
2954 if ((proxy_row >= 0) && proxy->priv->chunk_to)
2955 g_array_remove_index (proxy->priv->chunk_to->mapping,
2956 proxy_row - (proxy->priv->add_null_entry ? 1 : 0));
2957 }
2958
2959 if (proxy->priv->chunk_to) {
2960 /* sync proxy->priv->chunk to proxy->priv->chunk_to */
2961 gboolean defer_sync = proxy->priv->defer_sync;
2962 proxy->priv->defer_sync = FALSE;
2963 proxy->priv->chunk_sep = 0;
2964 proxy->priv->chunk_proxy_nb_rows = -1;
2965 chunk_sync_idle (proxy);
2966 proxy->priv->defer_sync = defer_sync;
2967 }
2968 }
2969 else {
2970 /* no chunk used */
2971 gint nrows = gda_data_proxy_get_n_rows ((GdaDataModel *) proxy);
2972 while (proxy->priv->new_rows) {
2973 proxy->priv->new_rows = g_slist_delete_link (proxy->priv->new_rows, proxy->priv->new_rows);
2974 if (proxy->priv->notify_changes) {
2975 gda_data_model_row_removed ((GdaDataModel *) proxy, nrows-1);
2976 nrows--;
2977 }
2978 }
2979 }
2980 }
2981
2982 /* all modified rows are then treated (including memory de-allocation for new rows) */
2983 while (proxy->priv->all_modifs) {
2984 gint model_row = ROW_MODIF (proxy->priv->all_modifs->data)->model_row;
2985 gint proxy_row = -1;
2986
2987 if (proxy->priv->notify_changes && (model_row >= 0))
2988 proxy_row = row_modif_to_proxy_row (proxy, (ROW_MODIF (proxy->priv->all_modifs->data)));
2989
2990 row_modifs_free (ROW_MODIF (proxy->priv->all_modifs->data));
2991 if (model_row >= 0) {
2992 gint tmp;
2993 tmp = model_row;
2994 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2995 }
2996 proxy->priv->all_modifs = g_slist_delete_link (proxy->priv->all_modifs, proxy->priv->all_modifs);
2997
2998 if ((proxy_row >= 0) && proxy->priv->notify_changes)
2999 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
3000 }
3001
3002 gda_mutex_unlock (proxy->priv->mutex);
3003
3004 return TRUE;
3005 }
3006
3007 static gboolean
sql_where_foreach(GdaSqlAnyPart * part,GdaDataProxy * proxy,G_GNUC_UNUSED GError ** error)3008 sql_where_foreach (GdaSqlAnyPart *part, GdaDataProxy *proxy, G_GNUC_UNUSED GError **error)
3009 {
3010 if (part->type == GDA_SQL_ANY_EXPR) {
3011 GdaSqlExpr *expr = (GdaSqlExpr*) part;
3012 if (expr->value && (G_VALUE_TYPE (expr->value) == G_TYPE_STRING)) {
3013 const gchar *cstr = g_value_get_string (expr->value);
3014 if (*cstr == '_') {
3015 const gchar *ptr;
3016 for (ptr = cstr+1; *ptr; ptr++)
3017 if ((*ptr < '0') || (*ptr > '9'))
3018 break;
3019 if (!*ptr) {
3020 /* column name is "_<number>", use column: <number> - 1 */
3021 gint colnum;
3022 colnum = atoi (cstr+1) - 1; /* Flawfinder: ignore */
3023 if ((colnum >= 0) &&
3024 (colnum < gda_data_model_get_n_columns ((GdaDataModel*) proxy))) {
3025 GdaColumn *col = gda_data_model_describe_column ((GdaDataModel*) proxy,
3026 colnum);
3027 const gchar *cname = gda_column_get_name (col);
3028 if (cname && *cname) {
3029 g_value_take_string (expr->value,
3030 gda_sql_identifier_quote (cname,
3031 proxy->priv->filter_vcnc,
3032 NULL,
3033 FALSE, FALSE));
3034 }
3035 }
3036 }
3037 }
3038 }
3039 }
3040 return TRUE;
3041 }
3042
3043 /*
3044 * Applies proxy->priv->filter_stmt
3045 */
3046 static gboolean
apply_filter_statement(GdaDataProxy * proxy,GError ** error)3047 apply_filter_statement (GdaDataProxy *proxy, GError **error)
3048 {
3049 GdaConnection *vcnc;
3050 GdaDataModel *filtered_rows = NULL;
3051 GdaStatement *stmt = NULL;
3052
3053 if (proxy->priv->filter_stmt) {
3054 stmt = proxy->priv->filter_stmt;
3055 proxy->priv->filter_stmt = NULL;
3056 }
3057
3058 /* ensure that there is no sync to be done */
3059 ensure_chunk_sync (proxy);
3060
3061 if (!stmt)
3062 goto clean_previous_filter;
3063
3064 g_mutex_lock (&provider_mutex);
3065 if (!virtual_provider)
3066 virtual_provider = gda_vprovider_data_model_new ();
3067 g_mutex_unlock (&provider_mutex);
3068
3069 /* Force direct data access where proxy_row <=> absolute_row */
3070 proxy->priv->force_direct_mapping = TRUE;
3071
3072 vcnc = proxy->priv->filter_vcnc;
3073 if (!vcnc) {
3074 GError *lerror = NULL;
3075 vcnc = gda_virtual_connection_open (virtual_provider, &lerror);
3076 if (! vcnc) {
3077 g_print ("Virtual ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
3078 if (lerror)
3079 g_error_free (lerror);
3080 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3081 "%s", _("Could not create virtual connection"));
3082 proxy->priv->force_direct_mapping = FALSE;
3083 goto clean_previous_filter;
3084 }
3085
3086 proxy->priv->filter_vcnc = vcnc;
3087 }
3088
3089 /* Add the @proxy to the virtual connection.
3090 *
3091 * REM: use a GdaDataModelWrapper to force the viewing of the un-modified columns of @proxy,
3092 * otherwise the GdaVconnectionDataModel will just take into account the modified columns
3093 */
3094 GdaDataModel *wrapper;
3095 wrapper = gda_data_access_wrapper_new ((GdaDataModel*) proxy);
3096 if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (vcnc), wrapper,
3097 "proxy", error)) {
3098 g_object_unref (wrapper);
3099 proxy->priv->force_direct_mapping = FALSE;
3100 goto clean_previous_filter;
3101 }
3102 g_object_unref (wrapper);
3103
3104 /* remork the statement for column names */
3105 GdaSqlStatement *sqlst;
3106 g_object_get (G_OBJECT (stmt), "structure", &sqlst, NULL);
3107 g_assert (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT);
3108 gda_sql_any_part_foreach (GDA_SQL_ANY_PART (sqlst->contents), (GdaSqlForeachFunc) sql_where_foreach, proxy, NULL);
3109 g_object_set (G_OBJECT (stmt), "structure", sqlst, NULL);
3110 #ifdef GDA_DEBUG_NO
3111 gchar *ser;
3112 ser = gda_sql_statement_serialize (sqlst);
3113 g_print ("Modified Filter: %s\n", ser);
3114 g_free (ser);
3115 #endif
3116 gda_sql_statement_free (sqlst);
3117
3118 /* execute statement */
3119 GError *lerror = NULL;
3120 filtered_rows = gda_connection_statement_execute_select (vcnc, stmt, NULL, &lerror);
3121 if (!filtered_rows) {
3122 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3123 _("Error in filter expression: %s"), lerror && lerror->message ? lerror->message : _("No detail"));
3124 g_clear_error (&lerror);
3125 proxy->priv->force_direct_mapping = FALSE;
3126 gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (vcnc), "proxy", NULL);
3127 goto clean_previous_filter;
3128 }
3129
3130 /* copy filtered_rows and remove virtual table */
3131 GdaDataModel *copy;
3132 copy = (GdaDataModel*) gda_data_model_array_copy_model (filtered_rows, NULL);
3133 g_object_unref (filtered_rows);
3134 gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (vcnc), "proxy", NULL);
3135 if (!copy) {
3136 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3137 "%s", _("Error in filter expression"));
3138 proxy->priv->force_direct_mapping = FALSE;
3139 filtered_rows = NULL;
3140 goto clean_previous_filter;
3141 }
3142 filtered_rows = copy;
3143 proxy->priv->force_direct_mapping = FALSE;
3144
3145 clean_previous_filter:
3146 if (proxy->priv->filter_expr) {
3147 g_free (proxy->priv->filter_expr);
3148 proxy->priv->filter_expr = NULL;
3149 }
3150 if (proxy->priv->filtered_rows) {
3151 g_object_unref (proxy->priv->filtered_rows);
3152 proxy->priv->filtered_rows = NULL;
3153 }
3154 #define FILTER_SELECT_WHERE "SELECT __gda_row_nb FROM proxy WHERE "
3155 #define FILTER_SELECT_NOWHERE "SELECT __gda_row_nb FROM proxy "
3156 if (filtered_rows) {
3157 gchar *sql;
3158 sql = gda_statement_to_sql (stmt, NULL, NULL);
3159 if (sql) {
3160 if (!g_ascii_strncasecmp (sql, FILTER_SELECT_WHERE, strlen (FILTER_SELECT_WHERE)))
3161 proxy->priv->filter_expr = g_strdup (sql + strlen (FILTER_SELECT_WHERE));
3162 else if (!g_ascii_strncasecmp (sql, FILTER_SELECT_NOWHERE, strlen (FILTER_SELECT_NOWHERE)))
3163 proxy->priv->filter_expr = g_strdup (sql + strlen (FILTER_SELECT_NOWHERE));
3164 g_free (sql);
3165 }
3166 proxy->priv->filtered_rows = filtered_rows;
3167 proxy->priv->filter_stmt = stmt;
3168 }
3169 else if (stmt)
3170 g_object_unref (stmt);
3171
3172 g_signal_emit (G_OBJECT (proxy),
3173 gda_data_proxy_signals[FILTER_CHANGED],
3174 0);
3175 gda_data_model_reset (GDA_DATA_MODEL (proxy));
3176
3177 adjust_displayed_chunk (proxy);
3178
3179 if (!stmt)
3180 return TRUE;
3181 else
3182 return filtered_rows ? TRUE : FALSE;
3183 }
3184
3185 /**
3186 * gda_data_proxy_set_filter_expr:
3187 * @proxy: a #GdaDataProxy object
3188 * @filter_expr: (nullable): an SQL based expression which will filter the contents of @proxy, or %NULL to remove any previous filter
3189 * @error: a place to store errors, or %NULL
3190 *
3191 * Sets a filter among the rows presented by @proxy. The filter is defined by a filter expression
3192 * which can be any SQL valid expression using @proxy's columns. For instance if @proxy has the "id" and
3193 * "name" columns, then a filter can be "length(name) < 5" to filter only the rows where the length of the
3194 * name is strictly inferior to 5, or "id >= 1000 and id < 2000 order by name limit 50" to filter only the rows where the id
3195 * is between 1000 and 2000, ordered by name and limited to 50 rows.
3196 *
3197 * Note about column names: real column names can be used (double quoted if necessary), but columns can also be named
3198 * "_<column number>" with column numbers starting at 1.
3199 *
3200 * Note that any previous filter expression is replaced with the new @filter_expr if no error occurs
3201 * (if an error occurs, then any previous filter is left unchanged).
3202 *
3203 * Returns: TRUE if no error occurred
3204 */
3205 gboolean
gda_data_proxy_set_filter_expr(GdaDataProxy * proxy,const gchar * filter_expr,GError ** error)3206 gda_data_proxy_set_filter_expr (GdaDataProxy *proxy, const gchar *filter_expr, GError **error)
3207 {
3208 gchar *sql;
3209 GdaStatement *stmt = NULL;
3210
3211 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
3212 g_return_val_if_fail (proxy->priv, FALSE);
3213
3214 gda_mutex_lock (proxy->priv->mutex);
3215
3216 if (!filter_expr) {
3217 if (proxy->priv->filter_stmt)
3218 g_object_unref (proxy->priv->filter_stmt);
3219 proxy->priv->filter_stmt = NULL;
3220
3221 gboolean retval = apply_filter_statement (proxy, error);
3222 gda_mutex_unlock (proxy->priv->mutex);
3223 return retval;
3224 }
3225
3226 /* generate SQL with a special case if expression starts with "ORDER BY" */
3227 gchar *tmp;
3228 const gchar *ptr;
3229 gint i;
3230 tmp = g_strdup (filter_expr);
3231 for (i = 0, ptr = filter_expr; *ptr && (i < 7); ptr++) {
3232 if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n')) {
3233 }
3234 else {
3235 tmp [i] = *ptr;
3236 i++;
3237 }
3238 }
3239 if (! g_ascii_strncasecmp (tmp, "orderby", 7))
3240 sql = g_strdup_printf (FILTER_SELECT_NOWHERE "%s", filter_expr);
3241 else
3242 sql = g_strdup_printf (FILTER_SELECT_WHERE "%s", filter_expr);
3243 g_free (tmp);
3244
3245 g_mutex_lock (&parser_mutex);
3246 stmt = gda_sql_parser_parse_string (internal_parser, sql, &ptr, NULL);
3247 g_mutex_unlock (&parser_mutex);
3248 g_free (sql);
3249 if (ptr || !stmt || (gda_statement_get_statement_type (stmt) != GDA_SQL_STATEMENT_SELECT)) {
3250 /* also catches problems with multiple statements in @filter_expr, such as SQL code injection */
3251 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3252 "%s", _("Incorrect filter expression"));
3253 if (stmt)
3254 g_object_unref (stmt);
3255 proxy->priv->force_direct_mapping = FALSE;
3256
3257 gda_mutex_unlock (proxy->priv->mutex);
3258 return FALSE;
3259 }
3260
3261 if (proxy->priv->filter_stmt)
3262 g_object_unref (proxy->priv->filter_stmt);
3263 proxy->priv->filter_stmt = stmt;
3264
3265 gboolean retval = apply_filter_statement (proxy, error);
3266 gda_mutex_unlock (proxy->priv->mutex);
3267 return retval;
3268 }
3269
3270 /**
3271 * gda_data_proxy_set_ordering_column:
3272 * @proxy: a #GdaDataProxy object
3273 * @col: the column number to order from
3274 * @error: a place to store errors, or %NULL
3275 *
3276 * Orders by the @col column
3277 *
3278 * Returns: TRUE if no error occurred
3279 */
3280 gboolean
gda_data_proxy_set_ordering_column(GdaDataProxy * proxy,gint col,GError ** error)3281 gda_data_proxy_set_ordering_column (GdaDataProxy *proxy, gint col, GError **error)
3282 {
3283 gboolean retval;
3284 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
3285 g_return_val_if_fail (proxy->priv, FALSE);
3286 g_return_val_if_fail (col >= 0, FALSE);
3287 g_return_val_if_fail (col < gda_data_model_get_n_columns ((GdaDataModel*) proxy), FALSE);
3288
3289 gda_mutex_lock (proxy->priv->mutex);
3290
3291 if (proxy->priv->filter_stmt) {
3292 GdaSqlStatement *sqlst;
3293 GdaSqlStatementSelect *selst;
3294 gboolean replaced = FALSE;
3295 const gchar *cname;
3296 gchar *colname;
3297
3298 cname = gda_column_get_name (gda_data_model_describe_column ((GdaDataModel*) proxy, col));
3299 if (cname && *cname)
3300 colname = gda_sql_identifier_quote (cname, proxy->priv->filter_vcnc, NULL, FALSE, FALSE);
3301 else
3302 colname = g_strdup_printf ("_%d", col + 1);
3303
3304 g_object_get (G_OBJECT (proxy->priv->filter_stmt), "structure", &sqlst, NULL);
3305 g_assert (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT);
3306 g_free (sqlst->sql);
3307 sqlst->sql = NULL;
3308 selst = (GdaSqlStatementSelect*) sqlst->contents;
3309
3310 /* test if we can actually toggle the sort ASC <-> DESC */
3311 if (selst->order_by && !selst->order_by->next) {
3312 GdaSqlSelectOrder *order_by = (GdaSqlSelectOrder*) selst->order_by->data;
3313 if (order_by->expr && order_by->expr->value &&
3314 (G_VALUE_TYPE (order_by->expr->value) == G_TYPE_STRING) &&
3315 gda_identifier_equal (g_value_get_string (order_by->expr->value), colname)) {
3316 order_by->asc = !order_by->asc;
3317 replaced = TRUE;
3318 g_free (colname);
3319 }
3320 }
3321
3322 if (!replaced) {
3323 /* replace the whole ordering part */
3324 if (selst->order_by) {
3325 g_slist_foreach (selst->order_by, (GFunc) gda_sql_select_order_free, NULL);
3326 g_slist_free (selst->order_by);
3327 selst->order_by = NULL;
3328 }
3329
3330 GdaSqlSelectOrder *order_by;
3331 GdaSqlExpr *expr;
3332 order_by = gda_sql_select_order_new (GDA_SQL_ANY_PART (selst));
3333 selst->order_by = g_slist_prepend (NULL, order_by);
3334 expr = gda_sql_expr_new (GDA_SQL_ANY_PART (order_by));
3335 order_by->expr = expr;
3336 order_by->asc = TRUE;
3337 expr->value = gda_value_new (G_TYPE_STRING);
3338 g_value_take_string (expr->value, colname);
3339 }
3340
3341 g_object_set (G_OBJECT (proxy->priv->filter_stmt), "structure", sqlst, NULL);
3342 #ifdef GDA_DEBUG_NO
3343 gchar *ser;
3344 ser = gda_sql_statement_serialize (sqlst);
3345 g_print ("Modified Filter: %s\n", ser);
3346 g_free (ser);
3347 #endif
3348 gda_sql_statement_free (sqlst);
3349 retval = apply_filter_statement (proxy, error);
3350 }
3351 else {
3352 gchar *str;
3353 str = g_strdup_printf ("ORDER BY _%d", col + 1);
3354 retval = gda_data_proxy_set_filter_expr (proxy, str, error);
3355 g_free (str);
3356 }
3357
3358 gda_mutex_unlock (proxy->priv->mutex);
3359 return retval;
3360 }
3361
3362 /**
3363 * gda_data_proxy_get_filter_expr:
3364 * @proxy: a #GdaDataProxy object
3365 *
3366 * Get the current filter expression used by @proxy.
3367 *
3368 * Returns: the current filter expression or %NULL if no filter has been set
3369 */
3370 const gchar *
gda_data_proxy_get_filter_expr(GdaDataProxy * proxy)3371 gda_data_proxy_get_filter_expr (GdaDataProxy *proxy)
3372 {
3373 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), NULL);
3374 g_return_val_if_fail (proxy->priv, NULL);
3375
3376 return proxy->priv->filter_expr;
3377 }
3378
3379 /**
3380 * gda_data_proxy_get_filtered_n_rows:
3381 * @proxy: a #GdaDataProxy object
3382 *
3383 * Get the total number of filtered rows in @proxy if a filter has been applied. As new rows
3384 * (rows added to the proxy and not yet added to the proxied data model) and rows to remove
3385 * (rows marked for removal but not yet removed from the proxied data model) are also filtered,
3386 * the returned number also contains references to new rows and rows to be removed.
3387 *
3388 * Returns: the number of filtered rows in @proxy, or -1 if no filter has been applied
3389 */
3390 gint
gda_data_proxy_get_filtered_n_rows(GdaDataProxy * proxy)3391 gda_data_proxy_get_filtered_n_rows (GdaDataProxy *proxy)
3392 {
3393 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
3394 g_return_val_if_fail (proxy->priv, -1);
3395
3396 gda_mutex_lock (proxy->priv->mutex);
3397 if (! proxy->priv->filtered_rows) {
3398 gda_mutex_unlock (proxy->priv->mutex);
3399 return -1;
3400 }
3401 else {
3402 gint n = gda_data_model_get_n_rows (proxy->priv->filtered_rows);
3403 gda_mutex_unlock (proxy->priv->mutex);
3404 return n;
3405 }
3406 }
3407
3408 /*
3409 * GdaDataModel interface implementation
3410 */
3411 static gint
gda_data_proxy_get_n_rows(GdaDataModel * model)3412 gda_data_proxy_get_n_rows (GdaDataModel *model)
3413 {
3414 gint nbrows;
3415 GdaDataProxy *proxy;
3416 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
3417 proxy = GDA_DATA_PROXY (model);
3418 g_return_val_if_fail (proxy->priv, -1);
3419
3420 gda_mutex_lock (proxy->priv->mutex);
3421
3422 if (proxy->priv->chunk && !proxy->priv->force_direct_mapping)
3423 nbrows = proxy->priv->chunk->mapping->len;
3424 else {
3425 if (proxy->priv->model_nb_rows >= 0) {
3426 if (proxy->priv->chunk_to && proxy->priv->chunk_to->mapping) {
3427 nbrows = proxy->priv->chunk_proxy_nb_rows;
3428 }
3429 else
3430 nbrows = proxy->priv->model_nb_rows +
3431 g_slist_length (proxy->priv->new_rows);
3432 }
3433 else
3434 return -1; /* unknown number of rows */
3435 }
3436 if (!proxy->priv->force_direct_mapping && proxy->priv->add_null_entry)
3437 nbrows += 1;
3438
3439 gda_mutex_unlock (proxy->priv->mutex);
3440
3441 return nbrows;
3442 }
3443
3444 static gint
gda_data_proxy_get_n_columns(GdaDataModel * model)3445 gda_data_proxy_get_n_columns (GdaDataModel *model)
3446 {
3447 GdaDataProxy *proxy;
3448 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
3449 proxy = GDA_DATA_PROXY (model);
3450 g_return_val_if_fail (proxy->priv, -1);
3451
3452 return 2 * proxy->priv->model_nb_cols;
3453 }
3454
3455
3456 typedef struct {
3457 gchar *name;
3458 GType type;
3459 } ExtraColAttrs;
3460
create_columns(GdaDataProxy * proxy)3461 static void create_columns (GdaDataProxy *proxy)
3462 {
3463 gint i;
3464 if (proxy->priv->columns)
3465 return;
3466
3467 proxy->priv->columns = g_new0 (GdaColumn *, 2 * proxy->priv->model_nb_cols);
3468
3469 /* current proxy's values */
3470 for (i = 0; i < proxy->priv->model_nb_cols; i++) {
3471 GdaColumn *orig;
3472
3473 orig = gda_data_model_describe_column (proxy->priv->model, i);
3474 proxy->priv->columns[i] = gda_column_copy (orig);
3475 gda_column_set_position (proxy->priv->columns[i], i);
3476 }
3477
3478 /* proxied data model's values (original values), again reference columns from proxied data model */
3479 for (; i < 2 * proxy->priv->model_nb_cols; i++) {
3480 GdaColumn *orig;
3481 const gchar *cname;
3482 gchar *newname, *id;
3483 gint k;
3484
3485 orig = gda_data_model_describe_column (proxy->priv->model,
3486 i - proxy->priv->model_nb_cols);
3487 proxy->priv->columns[i] = gda_column_copy (orig);
3488 g_object_get ((GObject*) proxy->priv->columns[i], "id", &id, NULL);
3489 if (id) {
3490 gchar *newid;
3491 newid = g_strdup_printf ("pre%s", id);
3492 g_object_set ((GObject*) proxy->priv->columns[i], "id", newid, NULL);
3493
3494 /* make sure there is no duplicate ID */
3495 for (k = 0; ; k++) {
3496 gint j;
3497 for (j = 0; j < i; j++) {
3498 gchar *id2;
3499 g_object_get ((GObject*) proxy->priv->columns[j], "id", &id2, NULL);
3500 if (id2 && *id2 && !strcmp (id2, newid)) {
3501 g_free (id2);
3502 break;
3503 }
3504 }
3505 if (j == i)
3506 break;
3507
3508 g_free (newid);
3509 newid = g_strdup_printf ("pre%s_%d", id, k);
3510 g_object_set ((GObject*) proxy->priv->columns[i], "id", newid, NULL);
3511 }
3512 g_free (newid);
3513 g_free (id);
3514 }
3515
3516 cname = gda_column_get_name (orig);
3517 if (cname && *cname)
3518 newname = g_strdup_printf ("pre%s", cname);
3519 else
3520 newname = g_strdup_printf ("pre%d", i);
3521
3522 /* make sure there is no duplicate name */
3523 for (k = 0; ; k++) {
3524 gint j;
3525 for (j = 0; j < i; j++) {
3526 const gchar *cname2;
3527 cname2 = gda_column_get_name (proxy->priv->columns[j]);
3528 if (cname2 && *cname2 && !strcmp (cname2, newname))
3529 break;
3530 }
3531 if (j == i)
3532 break;
3533 g_free (newname);
3534 if (cname && *cname)
3535 newname = g_strdup_printf ("pre%s_%d", cname, k);
3536 else
3537 newname = g_strdup_printf ("pre%d_%d", i, k);
3538 }
3539 gda_column_set_name (proxy->priv->columns[i], newname);
3540 gda_column_set_description (proxy->priv->columns[i], newname);
3541 g_free (newname);
3542 gda_column_set_position (proxy->priv->columns[i], i);
3543 }
3544 }
3545
3546 static GdaColumn *
gda_data_proxy_describe_column(GdaDataModel * model,gint col)3547 gda_data_proxy_describe_column (GdaDataModel *model, gint col)
3548 {
3549 GdaDataProxy *proxy;
3550 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), NULL);
3551 proxy = GDA_DATA_PROXY (model);
3552 g_return_val_if_fail (proxy->priv, NULL);
3553
3554 gda_mutex_lock (proxy->priv->mutex);
3555 if (!proxy->priv->columns)
3556 create_columns (proxy);
3557 gda_mutex_unlock (proxy->priv->mutex);
3558 if ((col < 0) || (col >= 2 * proxy->priv->model_nb_cols)) {
3559 g_warning (_("Column %d out of range (0-%d)"), col,
3560 gda_data_model_get_n_columns (model) - 1);
3561 return NULL;
3562 }
3563 else
3564 return proxy->priv->columns [col];
3565 }
3566
3567 static const GValue *
gda_data_proxy_get_value_at(GdaDataModel * model,gint column,gint proxy_row,GError ** error)3568 gda_data_proxy_get_value_at (GdaDataModel *model, gint column, gint proxy_row, GError **error)
3569 {
3570 gint model_row;
3571 GValue *retval = NULL;
3572 GdaDataProxy *proxy;
3573 static GValue *null_value = NULL;
3574
3575 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), NULL);
3576 proxy = GDA_DATA_PROXY (model);
3577 g_return_val_if_fail (proxy->priv, NULL);
3578 g_return_val_if_fail (proxy_row >= 0, NULL);
3579
3580 gda_mutex_lock (proxy->priv->mutex);
3581
3582 if ((proxy_row == 0) && proxy->priv->add_null_entry) {
3583 if (!null_value)
3584 null_value = gda_value_new_null ();
3585 gda_mutex_unlock (proxy->priv->mutex);
3586 return null_value;
3587 }
3588
3589 model_row = proxy_row_to_model_row (proxy, proxy_row);
3590
3591 /* current proxy's values (values may be different than the ones in the proxied data model) */
3592 if (column < proxy->priv->model_nb_cols) {
3593 RowModif *rm;
3594 gint model_col = column % proxy->priv->model_nb_cols;
3595 gboolean value_has_modifs = FALSE;
3596
3597 rm = proxy_row_to_row_modif (proxy, proxy_row);
3598 if (rm && rm->modify_values) {
3599 /* there are some modifications to the row, see if there are some for the column */
3600 GSList *list;
3601 RowValue *rv = NULL;
3602
3603 list = rm->modify_values;
3604 while (list && !rv) {
3605 if (ROW_VALUE (list->data)->model_column == model_col)
3606 rv = ROW_VALUE (list->data);
3607 list = g_slist_next (list);
3608 }
3609 if (rv) {
3610 value_has_modifs = TRUE;
3611 retval = rv->value;
3612 if (!retval) {
3613 if (!null_value)
3614 null_value = gda_value_new_null ();
3615 retval = null_value;
3616 }
3617 }
3618 }
3619
3620 if (!value_has_modifs) {
3621 /* value has not been modified */
3622 if (model_row >= 0) {
3623 /* existing row */
3624 retval = (GValue *) gda_data_model_get_value_at (proxy->priv->model, column, model_row, error);
3625 }
3626 else {
3627 /* non existing row, return NULL */
3628 gint n;
3629 n = gda_data_model_get_n_rows (model);
3630 if (n > 0)
3631 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3632 _("Row %d out of range (0-%d)"), proxy_row, n - 1);
3633 else
3634 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3635 _("Row %d not found (empty data model)"), proxy_row);
3636 retval = NULL;
3637 }
3638 }
3639
3640 gda_mutex_unlock (proxy->priv->mutex);
3641 return retval;
3642 }
3643
3644 /* proxied data model's values (original values) */
3645 if (column < 2 *proxy->priv->model_nb_cols) {
3646 RowModif *rm;
3647 gint model_col = column % proxy->priv->model_nb_cols;
3648
3649 rm = proxy_row_to_row_modif (proxy, proxy_row);
3650 if (rm) {
3651 if (rm->orig_values)
3652 retval = rm->orig_values [model_col];
3653 else {
3654 if (!null_value)
3655 null_value = gda_value_new_null ();
3656 retval = null_value;
3657 }
3658 }
3659 else {
3660 if (model_row >= 0) {
3661 /* existing row */
3662 retval = (GValue *) gda_data_model_get_value_at (proxy->priv->model, model_col, model_row, error);
3663 }
3664 else {
3665 /* non existing row, return NULL */
3666 gint n;
3667 n = gda_data_model_get_n_rows (model);
3668 if (n > 0)
3669 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3670 _("Row %d out of range (0-%d)"), proxy_row, n - 1);
3671 else
3672 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3673 _("Row %d not found (empty data model)"), proxy_row);
3674 retval = NULL;
3675 }
3676 }
3677
3678 gda_mutex_unlock (proxy->priv->mutex);
3679 return retval;
3680 }
3681
3682 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_COLUMN_OUT_OF_RANGE_ERROR,
3683 _("Column %d out of range (0-%d)"), column, 2 *proxy->priv->model_nb_cols - 1);
3684
3685 gda_mutex_unlock (proxy->priv->mutex);
3686 return NULL;
3687 }
3688
3689 static GdaValueAttribute
gda_data_proxy_get_attributes_at(GdaDataModel * model,gint col,gint row)3690 gda_data_proxy_get_attributes_at (GdaDataModel *model, gint col, gint row)
3691 {
3692 GdaValueAttribute attrs;
3693 GdaDataProxy *proxy;
3694
3695 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
3696 proxy = (GdaDataProxy*) model;
3697 g_return_val_if_fail (proxy->priv, FALSE);
3698
3699 gda_mutex_lock (proxy->priv->mutex);
3700 attrs = gda_data_proxy_get_value_attributes ((GdaDataProxy *) model, row, col);
3701 gda_mutex_unlock (proxy->priv->mutex);
3702 return attrs;
3703 }
3704
3705 static gint
gda_data_proxy_find_row_from_values(GdaDataModel * model,GSList * values,gint * cols_index)3706 gda_data_proxy_find_row_from_values (GdaDataModel *model, GSList *values, gint *cols_index)
3707 {
3708 gboolean found = FALSE;
3709 gint proxy_row;
3710 gint current_nb_rows;
3711 GdaDataProxy *proxy;
3712
3713 proxy = (GdaDataProxy*) model;
3714 g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
3715 g_return_val_if_fail (proxy->priv, FALSE);
3716 g_return_val_if_fail (values, FALSE);
3717
3718 gda_mutex_lock (proxy->priv->mutex);
3719
3720 /* ensure that there is no sync to be done */
3721 ensure_chunk_sync (proxy);
3722
3723 /* FIXME: use a virtual connection here with some SQL, it'll be much easier and will avoid
3724 * much code
3725 */
3726 /*TO_IMPLEMENT;*/
3727
3728 /* if there are still some rows waiting to be added in the idle loop, then force them to be added
3729 * first, otherwise we might not find what we are looking for!
3730 */
3731 if (proxy->priv->chunk_sync_idle_id) {
3732 g_idle_remove_by_data (proxy);
3733 proxy->priv->chunk_sync_idle_id = 0;
3734 while (chunk_sync_idle (proxy)) ;
3735 }
3736
3737 current_nb_rows = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy);
3738 for (proxy_row = 0; proxy_row < current_nb_rows; proxy_row++) {
3739 gboolean allequal = TRUE;
3740 GSList *list;
3741 gint index;
3742 const GValue *value;
3743
3744 for (list = values, index = 0;
3745 list;
3746 list = list->next, index++) {
3747 if (cols_index)
3748 g_return_val_if_fail (cols_index [index] < proxy->priv->model_nb_cols, FALSE);
3749 value = gda_data_proxy_get_value_at ((GdaDataModel *) proxy,
3750 cols_index ? cols_index [index] :
3751 index, proxy_row, NULL);
3752 if ((!value) || !list->data ||
3753 (G_VALUE_TYPE (value) != G_VALUE_TYPE ((GValue *) list->data)) ||
3754 gda_value_compare ((GValue *) (list->data), (GValue *) value)) {
3755 allequal = FALSE;
3756 break;
3757 }
3758 }
3759 if (allequal) {
3760 found = TRUE;
3761 break;
3762 }
3763 }
3764
3765 gda_mutex_unlock (proxy->priv->mutex);
3766 return found ? proxy_row : -1;
3767 }
3768
3769 static GdaDataModelAccessFlags
gda_data_proxy_get_access_flags(GdaDataModel * model)3770 gda_data_proxy_get_access_flags (GdaDataModel *model)
3771 {
3772 GdaDataProxy *proxy;
3773 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), 0);
3774 proxy = GDA_DATA_PROXY (model);
3775 g_return_val_if_fail (proxy->priv, 0);
3776
3777 if (proxy->priv->model) {
3778 GdaDataModelAccessFlags flags;
3779 gda_mutex_lock (proxy->priv->mutex);
3780 flags = gda_data_model_get_access_flags (proxy->priv->model) | GDA_DATA_MODEL_ACCESS_RANDOM;
3781 gda_mutex_unlock (proxy->priv->mutex);
3782 return flags;
3783 }
3784 else
3785 return 0;
3786 }
3787
3788 static gboolean
gda_data_proxy_set_value_at(GdaDataModel * model,gint col,gint proxy_row,const GValue * value,GError ** error)3789 gda_data_proxy_set_value_at (GdaDataModel *model, gint col, gint proxy_row, const GValue *value,
3790 GError **error)
3791 {
3792 GdaDataProxy *proxy;
3793
3794 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
3795 proxy = GDA_DATA_PROXY (model);
3796 g_return_val_if_fail (proxy->priv, FALSE);
3797 g_return_val_if_fail (proxy_row >= 0, FALSE);
3798 g_return_val_if_fail (value, FALSE);
3799
3800 gda_mutex_lock (proxy->priv->mutex);
3801
3802 /* ensure that there is no sync to be done */
3803 ensure_chunk_sync (proxy);
3804
3805 if ((proxy_row == 0) && proxy->priv->add_null_entry) {
3806 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_ROW,
3807 "%s", _("The first row is an empty row artificially prepended and cannot be altered"));
3808 gda_mutex_unlock (proxy->priv->mutex);
3809 return FALSE;
3810 }
3811
3812 /* current proxy's values (values may be different than the ones in the proxied data model) */
3813 if ((col >= 0) && (col < proxy->priv->model_nb_cols)) {
3814 /* Storing a GValue value */
3815 RowModif *rm;
3816 RowValue *rv = NULL;
3817 const GValue *cmp_value;
3818
3819 /* compare with the current stored value */
3820 cmp_value = gda_data_proxy_get_value_at ((GdaDataModel *) proxy, col, proxy_row, error);
3821 if (!cmp_value) {
3822 GdaValueAttribute attrs;
3823 attrs = gda_data_proxy_get_value_attributes (proxy, proxy_row, col);
3824 if (attrs & GDA_VALUE_ATTR_NO_MODIF) {
3825 gda_mutex_unlock (proxy->priv->mutex);
3826 return FALSE;
3827 }
3828 else {
3829 GType exptype;
3830 exptype = gda_column_get_g_type (gda_data_model_describe_column ((GdaDataModel *) proxy,
3831 col));
3832 if ((G_VALUE_TYPE (value) != GDA_TYPE_NULL) &&
3833 (exptype != GDA_TYPE_NULL) &&
3834 (exptype != G_VALUE_TYPE (value))) {
3835 gda_mutex_unlock (proxy->priv->mutex);
3836 g_warning (_("Wrong value type: expected '%s' and got '%s'"),
3837 g_type_name (exptype),
3838 g_type_name (G_VALUE_TYPE (value)));
3839 return FALSE;
3840 }
3841 }
3842 }
3843 else if ((G_VALUE_TYPE (cmp_value) != GDA_TYPE_NULL) &&
3844 (G_VALUE_TYPE (value) != GDA_TYPE_NULL) &&
3845 (G_VALUE_TYPE (value) != G_VALUE_TYPE (cmp_value))) {
3846 gda_mutex_unlock (proxy->priv->mutex);
3847 g_warning (_("Wrong value type: expected '%s' and got '%s'"),
3848 g_type_name (G_VALUE_TYPE (cmp_value)),
3849 g_type_name (G_VALUE_TYPE (value)));
3850 return FALSE;
3851 }
3852 else if (! gda_value_compare ((GValue *) value, (GValue *) cmp_value)) {
3853 /* nothing to do: values are equal */
3854 gda_mutex_unlock (proxy->priv->mutex);
3855 return TRUE;
3856 }
3857
3858 /* from now on we have a new value for the row */
3859 rm = find_or_create_row_modif (proxy, proxy_row, col, &rv);
3860
3861 if (rv) {
3862 /* compare with the original value (before modifications) and either
3863 * delete the RowValue or alter it */
3864 if (rv->value) {
3865 gda_value_free (rv->value);
3866 rv->value = NULL;
3867 }
3868
3869 if (rm->orig_values && (col < rm->orig_values_size) &&
3870 rm->orig_values [col] &&
3871 ! gda_value_compare ((GValue *) value, rm->orig_values [col])) {
3872 /* remove the RowValue */
3873 rm->modify_values = g_slist_remove (rm->modify_values, rv);
3874 g_free (rv);
3875 rv = NULL;
3876 }
3877 else {
3878 /* simply alter the RowValue */
3879 GdaValueAttribute flags = rv->attributes;
3880
3881 if (value && !gda_value_is_null ((GValue *) value)) {
3882 flags &= ~GDA_VALUE_ATTR_IS_NULL;
3883 rv->value = gda_value_copy ((GValue*) value);
3884 }
3885 else
3886 flags |= GDA_VALUE_ATTR_IS_NULL;
3887 rv->attributes = flags;
3888 }
3889 }
3890 else {
3891 /* create a new RowValue */
3892 GdaValueAttribute flags = 0;
3893
3894 rv = g_new0 (RowValue, 1);
3895 rv->row_modif = rm;
3896 rv->model_column = col;
3897 rv->attributes = proxy->priv->columns_attrs [col];
3898 flags = rv->attributes;
3899
3900 if (value && !gda_value_is_null ((GValue*) value)) {
3901 rv->value = gda_value_copy ((GValue*) value);
3902 flags &= ~GDA_VALUE_ATTR_IS_NULL;
3903 }
3904 else
3905 flags |= GDA_VALUE_ATTR_IS_NULL;
3906 if (rm->model_row >= 0)
3907 flags |= GDA_VALUE_ATTR_HAS_VALUE_ORIG;
3908 else
3909 flags &= ~GDA_VALUE_ATTR_HAS_VALUE_ORIG;
3910 rv->attributes = flags;
3911 rm->modify_values = g_slist_prepend (rm->modify_values, rv);
3912 }
3913
3914 if (rv) {
3915 GdaValueAttribute flags = rv->attributes;
3916 flags &= ~GDA_VALUE_ATTR_IS_UNCHANGED;
3917 flags &= ~GDA_VALUE_ATTR_IS_DEFAULT;
3918 rv->attributes = flags;
3919 }
3920
3921 if (!rm->to_be_deleted && !rm->modify_values && (rm->model_row >= 0)) {
3922 /* remove that RowModif, it's useless */
3923 gint tmp;
3924 tmp = rm->model_row;
3925 g_hash_table_remove (proxy->priv->modify_rows, &tmp);
3926 proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
3927 row_modifs_free (rm);
3928 }
3929
3930 if (proxy->priv->notify_changes)
3931 gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
3932 }
3933 else {
3934 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_VALUE,
3935 _("Trying to change read-only column: %d"), col);
3936 gda_mutex_unlock (proxy->priv->mutex);
3937 return FALSE;
3938 }
3939
3940 gda_mutex_unlock (proxy->priv->mutex);
3941 return TRUE;
3942 }
3943
3944 static gboolean
gda_data_proxy_set_values(GdaDataModel * model,gint row,GList * values,GError ** error)3945 gda_data_proxy_set_values (GdaDataModel *model, gint row, GList *values, GError **error)
3946 {
3947 GdaDataProxy *proxy;
3948 gboolean notify_changes;
3949 gint col;
3950 GList *list;
3951 gboolean err = FALSE;
3952
3953 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
3954 proxy = GDA_DATA_PROXY (model);
3955 g_return_val_if_fail (proxy->priv, FALSE);
3956 if (!values)
3957 return TRUE;
3958
3959 g_return_val_if_fail ((gint)g_list_length (values) <= gda_data_proxy_get_n_columns (model), FALSE);
3960
3961 /* check values */
3962 col = 0;
3963 list = values;
3964 while (list && !err) {
3965 GValue *value = (GValue *)(list->data);
3966
3967 if (value && !gda_value_is_null (value)) {
3968 GdaColumn *column;
3969 column = gda_data_model_describe_column (model, col);
3970 if (gda_column_get_g_type (column) != G_VALUE_TYPE (value)) {
3971 g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_VALUE_TYPE_ERROR,
3972 _("Value type mismatch %s instead of %s"),
3973 gda_g_type_to_string (G_VALUE_TYPE (value)),
3974 gda_g_type_to_string (gda_column_get_g_type (column)));
3975 err = TRUE;
3976 }
3977 }
3978 col++;
3979 list = g_list_next (list);
3980 }
3981
3982 /* stop here if there is a value error */
3983 if (err)
3984 return FALSE;
3985
3986 gda_mutex_lock (proxy->priv->mutex);
3987
3988 /* temporary disable changes notification */
3989 notify_changes = proxy->priv->notify_changes;
3990 proxy->priv->notify_changes = FALSE;
3991
3992 for (col = 0, list = values; list; col ++, list = list->next) {
3993 if (list->data &&
3994 !gda_data_proxy_set_value_at (model, col, row, (GValue *)(list->data), error)) {
3995 err = TRUE;
3996 break;
3997 }
3998 }
3999
4000 proxy->priv->notify_changes = notify_changes;
4001 if (col && proxy->priv->notify_changes)
4002 /* at least one successful value change occurred */
4003 gda_data_model_row_updated (model, row);
4004
4005 gda_mutex_unlock (proxy->priv->mutex);
4006 return !err;
4007 }
4008
4009 static gint
gda_data_proxy_append_values(GdaDataModel * model,const GList * values,GError ** error)4010 gda_data_proxy_append_values (GdaDataModel *model, const GList *values, GError **error)
4011 {
4012 GdaDataProxy *proxy;
4013 gint newrow;
4014 gboolean notify_changes;
4015
4016 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
4017 proxy = GDA_DATA_PROXY (model);
4018 g_return_val_if_fail (proxy->priv, -1);
4019
4020 gda_mutex_lock (proxy->priv->mutex);
4021
4022 /* ensure that there is no sync to be done */
4023 ensure_chunk_sync (proxy);
4024
4025 /* temporary disable changes notification */
4026 notify_changes = proxy->priv->notify_changes;
4027 proxy->priv->notify_changes = FALSE;
4028
4029 newrow = gda_data_proxy_append (proxy);
4030 if (! gda_data_proxy_set_values (model, newrow, (GList *) values, error)) {
4031 gda_data_proxy_remove_row (model, newrow, NULL);
4032 proxy->priv->notify_changes = notify_changes;
4033 gda_mutex_unlock (proxy->priv->mutex);
4034 return -1;
4035 }
4036 else {
4037 proxy->priv->notify_changes = notify_changes;
4038 if (proxy->priv->notify_changes)
4039 gda_data_model_row_inserted (model, newrow);
4040 gda_mutex_unlock (proxy->priv->mutex);
4041 return newrow;
4042 }
4043 }
4044
4045 static gint
gda_data_proxy_append_row(GdaDataModel * model,G_GNUC_UNUSED GError ** error)4046 gda_data_proxy_append_row (GdaDataModel *model, G_GNUC_UNUSED GError **error)
4047 {
4048 GdaDataProxy *proxy;
4049 gint i;
4050 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
4051 proxy = GDA_DATA_PROXY (model);
4052 g_return_val_if_fail (proxy->priv, -1);
4053
4054 gda_mutex_lock (proxy->priv->mutex);
4055 i = gda_data_proxy_append (proxy);
4056 gda_mutex_unlock (proxy->priv->mutex);
4057 return i;
4058 }
4059
4060 static gboolean
gda_data_proxy_remove_row(GdaDataModel * model,gint row,GError ** error)4061 gda_data_proxy_remove_row (GdaDataModel *model, gint row, GError **error)
4062 {
4063 GdaDataProxy *proxy;
4064 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
4065 proxy = GDA_DATA_PROXY (model);
4066 g_return_val_if_fail (proxy->priv, FALSE);
4067
4068 gda_mutex_lock (proxy->priv->mutex);
4069
4070 if (proxy->priv->add_null_entry && row == 0) {
4071 g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_ROW,
4072 "%s", _("The first row is an empty row artificially prepended and cannot be removed"));
4073 gda_mutex_unlock (proxy->priv->mutex);
4074 return FALSE;
4075 }
4076
4077 gda_data_proxy_delete (proxy, row);
4078 gda_mutex_unlock (proxy->priv->mutex);
4079 return TRUE;
4080 }
4081
4082 static void
gda_data_proxy_set_notify(GdaDataModel * model,gboolean do_notify_changes)4083 gda_data_proxy_set_notify (GdaDataModel *model, gboolean do_notify_changes)
4084 {
4085 GdaDataProxy *proxy;
4086 g_return_if_fail (GDA_IS_DATA_PROXY (model));
4087 proxy = GDA_DATA_PROXY (model);
4088 g_return_if_fail (proxy->priv);
4089
4090 gda_mutex_lock (proxy->priv->mutex);
4091 proxy->priv->notify_changes = do_notify_changes;
4092 gda_mutex_unlock (proxy->priv->mutex);
4093 }
4094
4095 static gboolean
gda_data_proxy_get_notify(GdaDataModel * model)4096 gda_data_proxy_get_notify (GdaDataModel *model)
4097 {
4098 GdaDataProxy *proxy;
4099 g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
4100 proxy = GDA_DATA_PROXY (model);
4101 g_return_val_if_fail (proxy->priv, FALSE);
4102
4103 return proxy->priv->notify_changes;
4104 }
4105
4106 static void
gda_data_proxy_send_hint(GdaDataModel * model,GdaDataModelHint hint,const GValue * hint_value)4107 gda_data_proxy_send_hint (GdaDataModel *model, GdaDataModelHint hint, const GValue *hint_value)
4108 {
4109 GdaDataProxy *proxy;
4110 g_return_if_fail (GDA_IS_DATA_PROXY (model));
4111 proxy = GDA_DATA_PROXY (model);
4112 g_return_if_fail (proxy->priv);
4113
4114 if (proxy->priv->model)
4115 gda_data_model_send_hint (proxy->priv->model, hint, hint_value);
4116 }
4117
4118 /*
4119 * Cache management
4120 */
4121 static void
clean_cached_changes(GdaDataProxy * proxy)4122 clean_cached_changes (GdaDataProxy *proxy)
4123 {
4124 while (proxy->priv->cached_modifs) {
4125 row_modifs_free (ROW_MODIF (proxy->priv->cached_modifs->data));
4126 proxy->priv->cached_modifs = g_slist_delete_link (proxy->priv->cached_modifs,
4127 proxy->priv->cached_modifs);
4128 }
4129 while (proxy->priv->cached_inserts) {
4130 row_modifs_free (ROW_MODIF (proxy->priv->cached_inserts->data));
4131 proxy->priv->cached_inserts = g_slist_delete_link (proxy->priv->cached_inserts,
4132 proxy->priv->cached_inserts);
4133 }
4134 }
4135
4136 static void
migrate_current_changes_to_cache(GdaDataProxy * proxy)4137 migrate_current_changes_to_cache (GdaDataProxy *proxy)
4138 {
4139 while (proxy->priv->all_modifs) {
4140 RowModif *rm;
4141 rm = (RowModif*) proxy->priv->all_modifs->data;
4142 #ifdef GDA_DEBUG_NO
4143 g_print ("=== cached RM %p for row %d\n", rm, rm->model_row);
4144 #endif
4145 if (rm->model_row == -1)
4146 proxy->priv->cached_inserts = g_slist_prepend (proxy->priv->cached_inserts, rm);
4147 else
4148 proxy->priv->cached_modifs = g_slist_prepend (proxy->priv->cached_modifs, rm);
4149 proxy->priv->all_modifs = g_slist_delete_link (proxy->priv->all_modifs,
4150 proxy->priv->all_modifs);
4151 }
4152 g_hash_table_remove_all (proxy->priv->modify_rows);
4153 if (proxy->priv->new_rows) {
4154 g_slist_free (proxy->priv->new_rows);
4155 proxy->priv->new_rows = NULL;
4156 }
4157 }
4158
4159 static void
fetch_current_cached_changes(GdaDataProxy * proxy)4160 fetch_current_cached_changes (GdaDataProxy *proxy)
4161 {
4162 GSList *list;
4163 gint ncols;
4164 g_return_if_fail (proxy->priv->model);
4165
4166 ncols = proxy->priv->model_nb_cols;
4167
4168 /* handle INSERT Row Modifs */
4169 for (list = proxy->priv->cached_inserts; list;) {
4170 RowModif *rm = (RowModif*) list->data;
4171 if (rm->orig_values_size != ncols) {
4172 list = list->next;
4173 continue;
4174 }
4175
4176 gint i;
4177 for (i = 0; i < ncols; i++) {
4178 GdaColumn *gcol;
4179 const GValue *cv = NULL;
4180 gcol = gda_data_model_describe_column (proxy->priv->model, i);
4181
4182 GSList *rvl;
4183 for (rvl = rm->modify_values; rvl; rvl = rvl->next) {
4184 RowValue *rv = ROW_VALUE (rvl->data);
4185 if (rv->model_column == i) {
4186 cv = rv->value;
4187 break;
4188 }
4189 }
4190
4191 if (cv && (G_VALUE_TYPE (cv) != GDA_TYPE_NULL) &&
4192 G_VALUE_TYPE (cv) != gda_column_get_g_type (gcol))
4193 break;
4194 }
4195
4196 if (i == ncols) {
4197 /* Matched! => move that Row Modif from cache */
4198 GSList *nlist;
4199 nlist = list->next;
4200 proxy->priv->cached_inserts = g_slist_delete_link (proxy->priv->cached_inserts,
4201 list);
4202 proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
4203 proxy->priv->new_rows = g_slist_append (proxy->priv->new_rows, rm);
4204 #ifdef GDA_DEBUG_NO
4205 g_print ("=== fetched RM %p for row %d\n", rm, rm->model_row);
4206 #endif
4207 list = nlist;
4208 }
4209 else
4210 list = list->next;
4211 }
4212
4213 /* handle UPDATE and DELETE Row Modifs */
4214 if (! proxy->priv->cached_modifs)
4215 return;
4216
4217 GdaDataModelIter *iter;
4218 iter = gda_data_model_create_iter (proxy->priv->model);
4219 while (gda_data_model_iter_move_next (iter)) {
4220 for (list = proxy->priv->cached_modifs; list; list = list->next) {
4221 RowModif *rm = (RowModif*) list->data;
4222 const GValue *v1, *v2;
4223 if (rm->orig_values_size != ncols)
4224 continue;
4225
4226 gint i;
4227 for (i = 0; i < ncols; i++) {
4228 v1 = gda_data_model_iter_get_value_at (iter, i);
4229 v2 = rm->orig_values [i];
4230
4231 if ((v1 && !v2) || (!v1 && v2))
4232 break;
4233 else if (v1 && v2) {
4234 if ((G_VALUE_TYPE (v1) != G_VALUE_TYPE (v2)) ||
4235 gda_value_differ (v1, v2))
4236 break;
4237 }
4238 }
4239
4240 if (i == ncols) {
4241 /* Matched! => move that Row Modif from cache */
4242 proxy->priv->cached_modifs = g_slist_delete_link (proxy->priv->cached_modifs,
4243 list);
4244 proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
4245
4246 gint *ptr;
4247 ptr = g_new (gint, 1);
4248 #ifdef GDA_DEBUG_NO
4249 g_print ("=== fetched RM %p for row %d (old %d)\n", rm, gda_data_model_iter_get_row (iter), rm->model_row);
4250 #endif
4251 rm->model_row = gda_data_model_iter_get_row (iter);
4252 *ptr = rm->model_row;
4253 g_hash_table_insert (proxy->priv->modify_rows, ptr, rm);
4254 break; /* the FOR over cached_modifs, as there can only be 1 Row Modif
4255 * for the row iter is on */
4256 }
4257 }
4258 }
4259 g_object_unref (iter);
4260 }
4261