2 * merge-properties-dialog.c
3 * Copyright (C) 2001-2009 Jim Evins <evins@snaught.com>.
5 * This file is part of gLabels.
7 * gLabels is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * gLabels is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with gLabels. If not, see <http://www.gnu.org/licenses/>.
23 #include "merge-properties-dialog.h"
25 #include <glib/gi18n.h>
31 #include "combo-util.h"
32 #include "builder-util.h"
38 /*===========================================*/
39 /* Private data types */
40 /*===========================================*/
42 struct _glMergePropertiesDialogPrivate {
49 GtkWidget *type_combo;
50 GtkWidget *location_vbox;
56 GtkWidget *select_all_button;
57 GtkWidget *unselect_all_button;
71 /* Invisible columns */
73 DATA_COLUMN, /* points to glMergeRecord */
79 /*===========================================*/
81 /*===========================================*/
84 /*===========================================*/
85 /* Local function prototypes */
86 /*===========================================*/
88 static void gl_merge_properties_dialog_finalize (GObject *object);
89 static void gl_merge_properties_dialog_construct (glMergePropertiesDialog *dialog,
93 static void type_changed_cb (GtkWidget *widget,
94 glMergePropertiesDialog *dialog);
96 static void src_changed_cb (GtkWidget *widget,
97 glMergePropertiesDialog *dialog);
99 static void response_cb (glMergePropertiesDialog *dialog,
103 static void load_tree (GtkTreeStore *store,
106 static void record_select_toggled_cb (GtkCellRendererToggle *cell,
108 GtkTreeStore *store);
110 static void select_all_button_clicked_cb (GtkWidget *widget,
111 glMergePropertiesDialog *dialog);
113 static void unselect_all_button_clicked_cb (GtkWidget *widget,
114 glMergePropertiesDialog *dialog);
117 /*****************************************************************************/
118 /* Boilerplate object stuff. */
119 /*****************************************************************************/
120 G_DEFINE_TYPE (glMergePropertiesDialog, gl_merge_properties_dialog, GTK_TYPE_DIALOG);
123 /*****************************************************************************/
124 /* Class Init Function. */
125 /*****************************************************************************/
127 gl_merge_properties_dialog_class_init (glMergePropertiesDialogClass *class)
129 GObjectClass *object_class = G_OBJECT_CLASS (class);
131 gl_debug (DEBUG_MERGE, "");
133 gl_merge_properties_dialog_parent_class = g_type_class_peek_parent (class);
135 object_class->finalize = gl_merge_properties_dialog_finalize;
139 /*****************************************************************************/
140 /* Object Instance Init Function. */
141 /*****************************************************************************/
143 gl_merge_properties_dialog_init (glMergePropertiesDialog *dialog)
145 gchar *builder_filename;
147 static gchar *object_ids[] = { "merge_properties_vbox", NULL };
148 GError *error = NULL;
150 GtkWidget *merge_properties_vbox;
152 gl_debug (DEBUG_MERGE, "START");
154 dialog->priv = g_new0 (glMergePropertiesDialogPrivate, 1);
156 vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
158 gtk_container_set_border_width (GTK_CONTAINER(dialog), GL_HIG_PAD1);
160 gtk_dialog_set_has_separator (GTK_DIALOG(dialog), FALSE);
161 gtk_dialog_add_button (GTK_DIALOG (dialog),
162 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
163 dialog->priv->ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
164 GTK_STOCK_OK, GTK_RESPONSE_OK);
166 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
168 builder = gtk_builder_new ();
169 builder_filename = g_build_filename (GLABELS_DATA_DIR, "ui", "merge-properties-dialog.ui", NULL);
170 gtk_builder_add_objects_from_file (builder, builder_filename, object_ids, &error);
171 g_free (builder_filename);
173 g_critical ("%s\n\ngLabels may not be installed correctly!", error->message);
174 g_error_free (error);
178 gl_builder_util_get_widgets (builder,
179 "merge_properties_vbox", &merge_properties_vbox,
180 "type_combo", &dialog->priv->type_combo,
181 "location_vbox", &dialog->priv->location_vbox,
182 "treeview", &dialog->priv->treeview,
183 "select_all_button", &dialog->priv->select_all_button,
184 "unselect_all_button", &dialog->priv->unselect_all_button,
187 gtk_container_add (GTK_CONTAINER (vbox), merge_properties_vbox);
188 dialog->priv->builder = builder;
190 gl_combo_util_add_text_model (GTK_COMBO_BOX (dialog->priv->type_combo));
193 gl_debug (DEBUG_MERGE, "END");
197 /*****************************************************************************/
198 /* Finalize Function. */
199 /*****************************************************************************/
201 gl_merge_properties_dialog_finalize (GObject *object)
203 glMergePropertiesDialog* dialog = GL_MERGE_PROPERTIES_DIALOG (object);
205 gl_debug (DEBUG_MERGE, "START");
207 g_return_if_fail (object != NULL);
208 g_return_if_fail (GL_IS_MERGE_PROPERTIES_DIALOG (dialog));
209 g_return_if_fail (dialog->priv != NULL);
211 if (dialog->priv->merge != NULL) {
212 g_object_unref (G_OBJECT (dialog->priv->merge));
214 if (dialog->priv->builder != NULL) {
215 g_object_unref (G_OBJECT (dialog->priv->builder));
217 g_free (dialog->priv);
219 G_OBJECT_CLASS (gl_merge_properties_dialog_parent_class)->finalize (object);
220 gl_debug (DEBUG_MERGE, "END");
224 /*****************************************************************************/
225 /* NEW merge properties dialog. */
226 /*****************************************************************************/
228 gl_merge_properties_dialog_new (glLabel *label,
233 gl_debug (DEBUG_MERGE, "START");
235 dialog = GTK_WIDGET (g_object_new (GL_TYPE_MERGE_PROPERTIES_DIALOG, NULL));
237 gl_merge_properties_dialog_construct (GL_MERGE_PROPERTIES_DIALOG (dialog),
240 gl_debug (DEBUG_MERGE, "END");
246 /*--------------------------------------------------------------------------*/
247 /* PRIVATE. Create merge widgets. */
248 /*--------------------------------------------------------------------------*/
250 gl_merge_properties_dialog_construct (glMergePropertiesDialog *dialog,
255 glMergeSrcType src_type;
259 GtkCellRenderer *renderer;
260 GtkTreeViewColumn *column;
261 GtkTreeSelection *selection;
263 gl_debug (DEBUG_MERGE, "START");
265 g_return_if_fail (GL_IS_MERGE_PROPERTIES_DIALOG (dialog));
266 g_return_if_fail (dialog->priv != NULL);
269 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(window));
270 gtk_window_set_destroy_with_parent (GTK_WINDOW(dialog), TRUE);
273 dialog->priv->label = label;
275 dialog->priv->merge = gl_label_get_merge (dialog->priv->label);
276 description = gl_merge_get_description (dialog->priv->merge);
277 src_type = gl_merge_get_src_type (dialog->priv->merge);
278 src = gl_merge_get_src (dialog->priv->merge);
280 /* --- Window title --- */
281 name = gl_label_get_short_name (label);
282 title = g_strdup_printf ("%s %s", name, _("Merge Properties"));
283 gtk_window_set_title (GTK_WINDOW (dialog), title);
287 texts = gl_merge_get_descriptions ();
288 gl_debug (DEBUG_MERGE, "DESCRIPTIONS:");
292 for (p=texts; p!=NULL; p=p->next) {
293 gl_debug (DEBUG_MERGE, " \"%s\"", p->data);
296 gl_combo_util_set_strings (GTK_COMBO_BOX (dialog->priv->type_combo),
298 gl_merge_free_descriptions (&texts);
299 gl_combo_util_set_active_text (GTK_COMBO_BOX (dialog->priv->type_combo),
301 g_signal_connect (G_OBJECT (dialog->priv->type_combo), "changed",
302 G_CALLBACK (type_changed_cb), dialog);
304 gl_debug (DEBUG_MERGE, "Src_type = %d", src_type);
306 case GL_MERGE_SRC_IS_FILE:
307 dialog->priv->src_entry =
308 gtk_file_chooser_button_new (_("Select merge-database source"),
309 GTK_FILE_CHOOSER_ACTION_OPEN);
310 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog->priv->src_entry),
312 gtk_file_chooser_button_set_focus_on_click (GTK_FILE_CHOOSER_BUTTON (dialog->priv->src_entry),
314 g_signal_connect (G_OBJECT (dialog->priv->src_entry),
316 G_CALLBACK (src_changed_cb), dialog);
319 dialog->priv->src_entry = gtk_label_new (_("N/A"));
320 gtk_misc_set_alignment (GTK_MISC (dialog->priv->src_entry), 0.0, 0.5);
323 gtk_box_pack_start (GTK_BOX (dialog->priv->location_vbox),
324 dialog->priv->src_entry, FALSE, FALSE, 0);
325 gtk_widget_show_all (GTK_WIDGET (dialog->priv->location_vbox));
327 dialog->priv->store = gtk_tree_store_new (N_COLUMNS,
328 G_TYPE_BOOLEAN, /* Record selector */
329 G_TYPE_STRING, /* Record/Field name */
330 G_TYPE_STRING, /* Field value */
331 G_TYPE_BOOLEAN, /* Is Record? */
332 G_TYPE_POINTER /* Pointer to record */);
333 load_tree (dialog->priv->store, dialog->priv->merge);
335 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->treeview),
336 GTK_TREE_MODEL (dialog->priv->store));
338 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (dialog->priv->treeview),
340 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->treeview));
341 gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
342 renderer = gtk_cell_renderer_toggle_new ();
343 g_signal_connect (G_OBJECT (renderer), "toggled",
344 G_CALLBACK (record_select_toggled_cb), dialog->priv->store);
345 column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer,
346 "active", SELECT_COLUMN,
347 "visible", IS_RECORD_COLUMN,
349 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
350 renderer = gtk_cell_renderer_text_new ();
351 g_object_set (G_OBJECT (renderer), "yalign", 0.0, NULL);
352 column = gtk_tree_view_column_new_with_attributes (_("Record/Field"), renderer,
353 "text", RECORD_FIELD_COLUMN,
355 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
356 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
357 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
358 renderer = gtk_cell_renderer_text_new ();
359 g_object_set (G_OBJECT (renderer), "yalign", 0.0, NULL);
360 column = gtk_tree_view_column_new_with_attributes (_("Data"), renderer,
361 "text", VALUE_COLUMN,
363 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
364 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
366 g_signal_connect (G_OBJECT (dialog->priv->select_all_button),
368 G_CALLBACK (select_all_button_clicked_cb), dialog);
370 g_signal_connect (G_OBJECT (dialog->priv->unselect_all_button),
372 G_CALLBACK (unselect_all_button_clicked_cb), dialog);
376 g_free (description);
378 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
380 gtk_widget_grab_focus (dialog->priv->ok_button);
381 gtk_widget_grab_default (dialog->priv->ok_button);
383 g_signal_connect(G_OBJECT (dialog), "response",
384 G_CALLBACK (response_cb), NULL);
386 gl_debug (DEBUG_MERGE, "END");
390 /*--------------------------------------------------------------------------*/
391 /* PRIVATE. type "changed" callback. */
392 /*--------------------------------------------------------------------------*/
394 type_changed_cb (GtkWidget *widget,
395 glMergePropertiesDialog *dialog)
400 glMergeSrcType src_type;
402 gl_debug (DEBUG_MERGE, "START");
404 description = gtk_combo_box_get_active_text (GTK_COMBO_BOX (dialog->priv->type_combo));
405 name = gl_merge_description_to_name (description);
407 src = gl_merge_get_src (dialog->priv->merge); /* keep current src if possible */
409 gl_debug (DEBUG_MERGE, "Saving src = \"%s\"", src);
410 g_free (dialog->priv->saved_src);
411 dialog->priv->saved_src = src;
414 if (dialog->priv->merge != NULL) {
415 g_object_unref (G_OBJECT(dialog->priv->merge));
417 dialog->priv->merge = gl_merge_new (name);
419 gtk_widget_destroy (dialog->priv->src_entry);
420 src_type = gl_merge_get_src_type (dialog->priv->merge);
422 case GL_MERGE_SRC_IS_FILE:
423 dialog->priv->src_entry =
424 gtk_file_chooser_button_new (_("Select merge-database source"),
425 GTK_FILE_CHOOSER_ACTION_OPEN);
426 gtk_file_chooser_button_set_focus_on_click (GTK_FILE_CHOOSER_BUTTON (dialog->priv->src_entry),
428 if (dialog->priv->saved_src != NULL) {
429 gl_debug (DEBUG_MERGE, "Setting src = \"%s\"", dialog->priv->saved_src);
430 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog->priv->src_entry),
431 dialog->priv->saved_src);
432 gl_merge_set_src (dialog->priv->merge, dialog->priv->saved_src);
434 g_signal_connect (G_OBJECT (dialog->priv->src_entry),
436 G_CALLBACK (src_changed_cb), dialog);
438 case GL_MERGE_SRC_IS_FIXED:
439 /* Translators: "Fixed" here means that for the given document-merge method, there are
440 * no further choices to further define the source of data. E.g. if you select the
441 * evolution-data-server, there are no further choices -- the source is "Fixed."
443 dialog->priv->src_entry = gtk_label_new (_("Fixed"));
444 gtk_misc_set_alignment (GTK_MISC (dialog->priv->src_entry), 0.0, 0.5);
446 gl_debug (DEBUG_MERGE, "Setting src = \"%s\"", dialog->priv->saved_src);
447 gl_merge_set_src (dialog->priv->merge, "Fixed");
450 dialog->priv->src_entry = gtk_label_new (_("N/A"));
451 gtk_misc_set_alignment (GTK_MISC (dialog->priv->src_entry), 0.0, 0.5);
454 gtk_box_pack_start( GTK_BOX (dialog->priv->location_vbox),
455 dialog->priv->src_entry, FALSE, FALSE, 0);
456 gtk_widget_show_all (dialog->priv->location_vbox);
458 load_tree (dialog->priv->store, dialog->priv->merge);
460 g_free (description);
463 gl_debug (DEBUG_MERGE, "END");
467 /*--------------------------------------------------------------------------*/
468 /* PRIVATE. source "changed" callback. */
469 /*--------------------------------------------------------------------------*/
471 src_changed_cb (GtkWidget *widget,
472 glMergePropertiesDialog *dialog)
474 gchar *src, *orig_src;
476 gl_debug (DEBUG_MERGE, "START");
478 orig_src = gl_merge_get_src (dialog->priv->merge);
479 src = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog->priv->src_entry));
481 gl_debug (DEBUG_MERGE, "orig=\"%s\", new=\"%s\"\n", orig_src, src);
483 if (((orig_src == NULL) && (src != NULL)) ||
484 ((orig_src != NULL) && (src == NULL)) ||
485 ((orig_src != NULL) && (src != NULL) && strcmp (src, orig_src)))
487 gl_merge_set_src (dialog->priv->merge, src);
488 load_tree (dialog->priv->store, dialog->priv->merge);
495 gl_debug (DEBUG_MERGE, "END");
499 /*--------------------------------------------------------------------------*/
500 /* PRIVATE. response callback. */
501 /*--------------------------------------------------------------------------*/
503 response_cb (glMergePropertiesDialog *dialog,
507 gl_debug (DEBUG_MERGE, "START");
511 case GTK_RESPONSE_OK:
512 gl_label_set_merge (dialog->priv->label, dialog->priv->merge, TRUE);
513 gtk_widget_hide (GTK_WIDGET (dialog));
515 case GTK_RESPONSE_CANCEL:
516 /* Let the dialog get rebuilt next time to recover state. */
517 gtk_widget_destroy (GTK_WIDGET (dialog));
519 case GTK_RESPONSE_DELETE_EVENT:
522 g_print ("response = %d", response);
523 g_assert_not_reached ();
526 gl_debug (DEBUG_MERGE, "END");
530 /*--------------------------------------------------------------------------*/
531 /* PRIVATE. Load tree store from merge data. */
532 /*--------------------------------------------------------------------------*/
534 load_tree (GtkTreeStore *store,
537 const GList *record_list;
538 GList *p_rec, *p_field;
539 glMergeRecord *record;
541 GtkTreeIter iter1, iter2;
543 gchar *primary_value;
545 gl_debug (DEBUG_MERGE, "START");
547 gtk_tree_store_clear (store);
549 primary_key = gl_merge_get_primary_key (merge);
550 record_list = gl_merge_get_record_list (merge);
552 for ( p_rec=(GList *)record_list; p_rec!=NULL; p_rec=p_rec->next ) {
553 record = (glMergeRecord *)p_rec->data;
555 primary_value = gl_merge_eval_key (record, primary_key);
557 gtk_tree_store_append (store, &iter1, NULL);
558 gtk_tree_store_set (store, &iter1,
559 SELECT_COLUMN, record->select_flag,
560 RECORD_FIELD_COLUMN, primary_value,
561 IS_RECORD_COLUMN, TRUE,
565 g_free (primary_value);
567 for ( p_field=record->field_list; p_field!=NULL; p_field=p_field->next ) {
568 field = (glMergeField *)p_field->data;
570 gtk_tree_store_append (store, &iter2, &iter1);
571 gtk_tree_store_set (store, &iter2,
572 RECORD_FIELD_COLUMN, field->key,
573 VALUE_COLUMN, field->value,
574 IS_RECORD_COLUMN, FALSE,
579 g_free (primary_key);
581 gl_debug (DEBUG_MERGE, "END");
585 /*--------------------------------------------------------------------------*/
586 /* PRIVATE. Record select toggled. */
587 /*--------------------------------------------------------------------------*/
589 record_select_toggled_cb (GtkCellRendererToggle *cell,
595 glMergeRecord *record;
597 gl_debug (DEBUG_MERGE, "START");
599 /* get toggled iter */
600 path = gtk_tree_path_new_from_string (path_str);
601 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
603 /* get current data */
604 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
605 DATA_COLUMN, &record,
608 /* toggle the select flag within the record */
609 record->select_flag ^= 1;
611 /* set new value in store */
612 gtk_tree_store_set (store, &iter,
613 SELECT_COLUMN, record->select_flag,
617 gtk_tree_path_free (path);
619 gl_debug (DEBUG_MERGE, "END");
623 /*--------------------------------------------------------------------------*/
624 /* PRIVATE. "Select All" button callback. */
625 /*--------------------------------------------------------------------------*/
627 select_all_button_clicked_cb (GtkWidget *widget,
628 glMergePropertiesDialog *dialog)
630 GtkTreeModel *store = GTK_TREE_MODEL (dialog->priv->store);
632 glMergeRecord *record;
635 gl_debug (DEBUG_MERGE, "START");
637 for ( good = gtk_tree_model_get_iter_first (store, &iter);
639 good = gtk_tree_model_iter_next (store, &iter) )
642 /* get current data */
643 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
644 DATA_COLUMN, &record,
648 /* Set select flag within the record */
649 record->select_flag = TRUE;
651 /* set new value in store */
652 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
653 SELECT_COLUMN, record->select_flag,
658 gl_debug (DEBUG_MERGE, "END");
662 /*--------------------------------------------------------------------------*/
663 /* PRIVATE. "Unselect All" button callback. */
664 /*--------------------------------------------------------------------------*/
666 unselect_all_button_clicked_cb (GtkWidget *widget,
667 glMergePropertiesDialog *dialog)
669 GtkTreeModel *store = GTK_TREE_MODEL (dialog->priv->store);
671 glMergeRecord *record;
674 gl_debug (DEBUG_MERGE, "START");
676 for ( good = gtk_tree_model_get_iter_first (store, &iter);
678 good = gtk_tree_model_iter_next (store, &iter) )
681 /* get current data */
682 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
683 DATA_COLUMN, &record,
687 /* Set select flag within the record */
688 record->select_flag = FALSE;
690 /* set new value in store */
691 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
692 SELECT_COLUMN, record->select_flag,
697 gl_debug (DEBUG_MERGE, "END");
703 * Local Variables: -- emacs
705 * c-basic-offset: 8 -- emacs
706 * tab-width: 8 -- emacs
707 * indent-tabs-mode: nil -- emacs