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>
26 #include <gtk/gtkbuilder.h>
27 #include <gtk/gtkfilechooserbutton.h>
28 #include <gtk/gtkcombobox.h>
29 #include <gtk/gtkstock.h>
30 #include <gtk/gtktreestore.h>
31 #include <gtk/gtktreeselection.h>
32 #include <gtk/gtkcellrenderertoggle.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtklabel.h>
45 /*===========================================*/
46 /* Private data types */
47 /*===========================================*/
49 struct _glMergePropertiesDialogPrivate {
56 GtkWidget *type_combo;
57 GtkWidget *location_vbox;
63 GtkWidget *select_all_button;
64 GtkWidget *unselect_all_button;
76 /* Invisible columns */
78 DATA_COLUMN, /* points to glMergeRecord */
84 /*===========================================*/
86 /*===========================================*/
89 /*===========================================*/
90 /* Local function prototypes */
91 /*===========================================*/
93 static void gl_merge_properties_dialog_finalize (GObject *object);
94 static void gl_merge_properties_dialog_construct (glMergePropertiesDialog *dialog,
98 static void type_changed_cb (GtkWidget *widget,
99 glMergePropertiesDialog *dialog);
101 static void src_changed_cb (GtkWidget *widget,
102 glMergePropertiesDialog *dialog);
104 static void response_cb (glMergePropertiesDialog *dialog,
108 static void load_tree (GtkTreeStore *store,
111 static void record_select_toggled_cb (GtkCellRendererToggle *cell,
113 GtkTreeStore *store);
115 static void select_all_button_clicked_cb (GtkWidget *widget,
116 glMergePropertiesDialog *dialog);
118 static void unselect_all_button_clicked_cb (GtkWidget *widget,
119 glMergePropertiesDialog *dialog);
122 /*****************************************************************************/
123 /* Boilerplate object stuff. */
124 /*****************************************************************************/
125 G_DEFINE_TYPE (glMergePropertiesDialog, gl_merge_properties_dialog, GTK_TYPE_DIALOG);
129 gl_merge_properties_dialog_class_init (glMergePropertiesDialogClass *class)
131 GObjectClass *object_class = G_OBJECT_CLASS (class);
133 gl_debug (DEBUG_MERGE, "");
135 gl_merge_properties_dialog_parent_class = g_type_class_peek_parent (class);
137 object_class->finalize = gl_merge_properties_dialog_finalize;
142 gl_merge_properties_dialog_init (glMergePropertiesDialog *dialog)
144 gl_debug (DEBUG_MERGE, "START");
146 dialog->priv = g_new0 (glMergePropertiesDialogPrivate, 1);
148 gtk_container_set_border_width (GTK_CONTAINER(dialog), GL_HIG_PAD2);
150 gtk_dialog_set_has_separator (GTK_DIALOG(dialog), FALSE);
151 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
152 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
153 GTK_STOCK_OK, GTK_RESPONSE_OK,
156 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
158 g_signal_connect(G_OBJECT (dialog), "response",
159 G_CALLBACK (response_cb), NULL);
162 gl_debug (DEBUG_MERGE, "END");
167 gl_merge_properties_dialog_finalize (GObject *object)
169 glMergePropertiesDialog* dialog = GL_MERGE_PROPERTIES_DIALOG (object);
171 gl_debug (DEBUG_MERGE, "START");
173 g_return_if_fail (object != NULL);
174 g_return_if_fail (GL_IS_MERGE_PROPERTIES_DIALOG (dialog));
175 g_return_if_fail (dialog->priv != NULL);
177 if (dialog->priv->merge != NULL) {
178 g_object_unref (G_OBJECT (dialog->priv->merge));
180 if (dialog->priv->builder != NULL) {
181 g_object_unref (G_OBJECT (dialog->priv->builder));
183 g_free (dialog->priv);
185 G_OBJECT_CLASS (gl_merge_properties_dialog_parent_class)->finalize (object);
186 gl_debug (DEBUG_MERGE, "END");
190 /*****************************************************************************/
191 /* NEW merge properties dialog. */
192 /*****************************************************************************/
194 gl_merge_properties_dialog_new (glLabel *label,
199 gl_debug (DEBUG_MERGE, "START");
201 dialog = GTK_WIDGET (g_object_new (GL_TYPE_MERGE_PROPERTIES_DIALOG, NULL));
203 gl_merge_properties_dialog_construct (GL_MERGE_PROPERTIES_DIALOG (dialog),
206 gl_debug (DEBUG_MERGE, "END");
212 /*--------------------------------------------------------------------------*/
213 /* PRIVATE. Create merge widgets. */
214 /*--------------------------------------------------------------------------*/
216 gl_merge_properties_dialog_construct (glMergePropertiesDialog *dialog,
221 static gchar *object_ids[] = { "merge_properties_vbox", NULL };
222 GError *error = NULL;
224 glMergeSrcType src_type;
229 GtkCellRenderer *renderer;
230 GtkTreeViewColumn *column;
231 GtkTreeSelection *selection;
233 gl_debug (DEBUG_MERGE, "START");
235 g_return_if_fail (GL_IS_MERGE_PROPERTIES_DIALOG (dialog));
236 g_return_if_fail (dialog->priv != NULL);
239 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(window));
240 gtk_window_set_destroy_with_parent (GTK_WINDOW(dialog), TRUE);
243 builder = gtk_builder_new ();
244 gtk_builder_add_objects_from_file (builder,
245 GLABELS_BUILDER_DIR "merge-properties-dialog.builder",
249 g_critical ("%s\n\ngLabels may not be installed correctly!", error->message);
250 g_error_free (error);
254 gl_util_get_builder_widgets (builder,
255 "merge_properties_vbox", &vbox,
256 "type_combo", &dialog->priv->type_combo,
257 "location_vbox", &dialog->priv->location_vbox,
258 "treeview", &dialog->priv->treeview,
259 "select_all_button", &dialog->priv->select_all_button,
260 "unselect_all_button", &dialog->priv->unselect_all_button,
263 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
264 dialog->priv->builder = builder;
266 gl_util_combo_box_add_text_model (GTK_COMBO_BOX (dialog->priv->type_combo));
268 dialog->priv->label = label;
270 dialog->priv->merge = gl_label_get_merge (dialog->priv->label);
271 description = gl_merge_get_description (dialog->priv->merge);
272 src_type = gl_merge_get_src_type (dialog->priv->merge);
273 src = gl_merge_get_src (dialog->priv->merge);
275 /* --- Window title --- */
276 name = gl_label_get_short_name (label);
277 title = g_strdup_printf ("%s %s", name, _("Merge Properties"));
278 gtk_window_set_title (GTK_WINDOW (dialog), title);
282 texts = gl_merge_get_descriptions ();
283 gl_debug (DEBUG_MERGE, "DESCRIPTIONS:");
287 for (p=texts; p!=NULL; p=p->next) {
288 gl_debug (DEBUG_MERGE, " \"%s\"", p->data);
291 gl_util_combo_box_set_strings (GTK_COMBO_BOX (dialog->priv->type_combo),
293 gl_merge_free_descriptions (&texts);
294 gl_util_combo_box_set_active_text (GTK_COMBO_BOX (dialog->priv->type_combo),
296 g_signal_connect (G_OBJECT (dialog->priv->type_combo), "changed",
297 G_CALLBACK (type_changed_cb), dialog);
299 gl_debug (DEBUG_MERGE, "Src_type = %d", src_type);
301 case GL_MERGE_SRC_IS_FILE:
302 dialog->priv->src_entry =
303 gtk_file_chooser_button_new (_("Select merge-database source"),
304 GTK_FILE_CHOOSER_ACTION_OPEN);
305 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog->priv->src_entry),
307 g_signal_connect (G_OBJECT (dialog->priv->src_entry),
309 G_CALLBACK (src_changed_cb), dialog);
312 dialog->priv->src_entry = gtk_label_new (_("N/A"));
313 gtk_misc_set_alignment (GTK_MISC (dialog->priv->src_entry), 0.0, 0.5);
316 gtk_box_pack_start (GTK_BOX (dialog->priv->location_vbox),
317 dialog->priv->src_entry, FALSE, FALSE, 0);
318 gtk_widget_show_all (GTK_WIDGET (dialog->priv->location_vbox));
320 dialog->priv->store = gtk_tree_store_new (N_COLUMNS,
321 G_TYPE_BOOLEAN, /* Record selector */
322 G_TYPE_STRING, /* Record/Field name */
323 G_TYPE_STRING, /* Field value */
324 G_TYPE_BOOLEAN, /* Is Record? */
325 G_TYPE_POINTER /* Pointer to record */);
326 load_tree (dialog->priv->store, dialog->priv->merge);
328 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->treeview),
329 GTK_TREE_MODEL (dialog->priv->store));
331 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (dialog->priv->treeview),
333 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->treeview));
334 gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
335 renderer = gtk_cell_renderer_toggle_new ();
336 g_signal_connect (G_OBJECT (renderer), "toggled",
337 G_CALLBACK (record_select_toggled_cb), dialog->priv->store);
338 column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer,
339 "active", SELECT_COLUMN,
340 "visible", IS_RECORD_COLUMN,
342 gtk_tree_view_column_set_clickable (column, TRUE);
343 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
344 renderer = gtk_cell_renderer_text_new ();
345 g_object_set (G_OBJECT (renderer), "yalign", 0.0, NULL);
346 column = gtk_tree_view_column_new_with_attributes (_("Record/Field"), renderer,
347 "text", RECORD_FIELD_COLUMN,
349 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
350 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
351 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
352 renderer = gtk_cell_renderer_text_new ();
353 g_object_set (G_OBJECT (renderer), "yalign", 0.0, NULL);
354 column = gtk_tree_view_column_new_with_attributes (_("Data"), renderer,
355 "text", VALUE_COLUMN,
357 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
358 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview), column);
360 g_signal_connect (G_OBJECT (dialog->priv->select_all_button),
362 G_CALLBACK (select_all_button_clicked_cb), dialog);
364 g_signal_connect (G_OBJECT (dialog->priv->unselect_all_button),
366 G_CALLBACK (unselect_all_button_clicked_cb), dialog);
370 g_free (description);
372 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
374 gl_debug (DEBUG_MERGE, "END");
378 /*--------------------------------------------------------------------------*/
379 /* PRIVATE. type "changed" callback. */
380 /*--------------------------------------------------------------------------*/
382 type_changed_cb (GtkWidget *widget,
383 glMergePropertiesDialog *dialog)
388 glMergeSrcType src_type;
390 gl_debug (DEBUG_MERGE, "START");
392 description = gtk_combo_box_get_active_text (GTK_COMBO_BOX (dialog->priv->type_combo));
393 name = gl_merge_description_to_name (description);
395 src = gl_merge_get_src (dialog->priv->merge); /* keep current src if possible */
397 gl_debug (DEBUG_MERGE, "Saving src = \"%s\"", src);
398 g_free (dialog->priv->saved_src);
399 dialog->priv->saved_src = src;
402 if (dialog->priv->merge != NULL) {
403 g_object_unref (G_OBJECT(dialog->priv->merge));
405 dialog->priv->merge = gl_merge_new (name);
407 gtk_widget_destroy (dialog->priv->src_entry);
408 src_type = gl_merge_get_src_type (dialog->priv->merge);
410 case GL_MERGE_SRC_IS_FILE:
411 dialog->priv->src_entry =
412 gtk_file_chooser_button_new (_("Select merge-database source"),
413 GTK_FILE_CHOOSER_ACTION_OPEN);
414 if (dialog->priv->saved_src != NULL) {
415 gl_debug (DEBUG_MERGE, "Setting src = \"%s\"", dialog->priv->saved_src);
416 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog->priv->src_entry),
417 dialog->priv->saved_src);
418 gl_merge_set_src (dialog->priv->merge, dialog->priv->saved_src);
420 g_signal_connect (G_OBJECT (dialog->priv->src_entry),
422 G_CALLBACK (src_changed_cb), dialog);
424 case GL_MERGE_SRC_IS_FIXED:
425 dialog->priv->src_entry = gtk_label_new (_("Fixed"));
426 gtk_misc_set_alignment (GTK_MISC (dialog->priv->src_entry), 0.0, 0.5);
428 gl_debug (DEBUG_MERGE, "Setting src = \"%s\"", dialog->priv->saved_src);
429 gl_merge_set_src (dialog->priv->merge, "Fixed");
432 dialog->priv->src_entry = gtk_label_new (_("N/A"));
433 gtk_misc_set_alignment (GTK_MISC (dialog->priv->src_entry), 0.0, 0.5);
436 gtk_box_pack_start( GTK_BOX (dialog->priv->location_vbox),
437 dialog->priv->src_entry, FALSE, FALSE, 0);
438 gtk_widget_show_all (dialog->priv->location_vbox);
440 load_tree (dialog->priv->store, dialog->priv->merge);
442 g_free (description);
445 gl_debug (DEBUG_MERGE, "END");
449 /*--------------------------------------------------------------------------*/
450 /* PRIVATE. source "changed" callback. */
451 /*--------------------------------------------------------------------------*/
453 src_changed_cb (GtkWidget *widget,
454 glMergePropertiesDialog *dialog)
456 gchar *src, *orig_src;
458 gl_debug (DEBUG_MERGE, "START");
460 orig_src = gl_merge_get_src (dialog->priv->merge);
461 src = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog->priv->src_entry));
463 gl_debug (DEBUG_MERGE, "orig=\"%s\", new=\"%s\"\n", orig_src, src);
465 if (((orig_src == NULL) && (src != NULL)) ||
466 ((orig_src != NULL) && (src == NULL)) ||
467 ((orig_src != NULL) && (src != NULL) && strcmp (src, orig_src)))
469 gl_merge_set_src (dialog->priv->merge, src);
470 load_tree (dialog->priv->store, dialog->priv->merge);
477 gl_debug (DEBUG_MERGE, "END");
481 /*--------------------------------------------------------------------------*/
482 /* PRIVATE. response callback. */
483 /*--------------------------------------------------------------------------*/
485 response_cb (glMergePropertiesDialog *dialog,
489 gl_debug (DEBUG_MERGE, "START");
493 case GTK_RESPONSE_OK:
494 gl_label_set_merge (dialog->priv->label, dialog->priv->merge);
495 gtk_widget_hide (GTK_WIDGET (dialog));
497 case GTK_RESPONSE_CANCEL:
498 /* Let the dialog get rebuilt next time to recover state. */
499 gtk_widget_destroy (GTK_WIDGET (dialog));
501 case GTK_RESPONSE_DELETE_EVENT:
504 g_print ("response = %d", response);
505 g_assert_not_reached ();
508 gl_debug (DEBUG_MERGE, "END");
512 /*--------------------------------------------------------------------------*/
513 /* PRIVATE. Load tree store from merge data. */
514 /*--------------------------------------------------------------------------*/
516 load_tree (GtkTreeStore *store,
519 const GList *record_list;
520 GList *p_rec, *p_field;
521 glMergeRecord *record;
523 GtkTreeIter iter1, iter2;
525 gchar *primary_value;
527 gl_debug (DEBUG_MERGE, "START");
529 gtk_tree_store_clear (store);
531 primary_key = gl_merge_get_primary_key (merge);
532 record_list = gl_merge_get_record_list (merge);
534 for ( p_rec=(GList *)record_list; p_rec!=NULL; p_rec=p_rec->next ) {
535 record = (glMergeRecord *)p_rec->data;
537 primary_value = gl_merge_eval_key (record, primary_key);
539 gtk_tree_store_append (store, &iter1, NULL);
540 gtk_tree_store_set (store, &iter1,
541 SELECT_COLUMN, record->select_flag,
542 RECORD_FIELD_COLUMN, primary_value,
543 IS_RECORD_COLUMN, TRUE,
547 g_free (primary_value);
549 for ( p_field=record->field_list; p_field!=NULL; p_field=p_field->next ) {
550 field = (glMergeField *)p_field->data;
552 gtk_tree_store_append (store, &iter2, &iter1);
553 gtk_tree_store_set (store, &iter2,
554 RECORD_FIELD_COLUMN, field->key,
555 VALUE_COLUMN, field->value,
556 IS_RECORD_COLUMN, FALSE,
561 g_free (primary_key);
563 gl_debug (DEBUG_MERGE, "END");
567 /*--------------------------------------------------------------------------*/
568 /* PRIVATE. Record select toggled. */
569 /*--------------------------------------------------------------------------*/
571 record_select_toggled_cb (GtkCellRendererToggle *cell,
577 glMergeRecord *record;
579 gl_debug (DEBUG_MERGE, "START");
581 /* get toggled iter */
582 path = gtk_tree_path_new_from_string (path_str);
583 gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
585 /* get current data */
586 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
587 DATA_COLUMN, &record,
590 /* toggle the select flag within the record */
591 record->select_flag ^= 1;
593 /* set new value in store */
594 gtk_tree_store_set (store, &iter,
595 SELECT_COLUMN, record->select_flag,
599 gtk_tree_path_free (path);
601 gl_debug (DEBUG_MERGE, "END");
605 /*--------------------------------------------------------------------------*/
606 /* PRIVATE. "Select All" button callback. */
607 /*--------------------------------------------------------------------------*/
609 select_all_button_clicked_cb (GtkWidget *widget,
610 glMergePropertiesDialog *dialog)
612 GtkTreeModel *store = GTK_TREE_MODEL (dialog->priv->store);
614 glMergeRecord *record;
617 gl_debug (DEBUG_MERGE, "START");
619 for ( good = gtk_tree_model_get_iter_first (store, &iter);
621 good = gtk_tree_model_iter_next (store, &iter) )
624 /* get current data */
625 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
626 DATA_COLUMN, &record,
630 /* Set select flag within the record */
631 record->select_flag = TRUE;
633 /* set new value in store */
634 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
635 SELECT_COLUMN, record->select_flag,
640 gl_debug (DEBUG_MERGE, "END");
644 /*--------------------------------------------------------------------------*/
645 /* PRIVATE. "Unselect All" button callback. */
646 /*--------------------------------------------------------------------------*/
648 unselect_all_button_clicked_cb (GtkWidget *widget,
649 glMergePropertiesDialog *dialog)
651 GtkTreeModel *store = GTK_TREE_MODEL (dialog->priv->store);
653 glMergeRecord *record;
656 gl_debug (DEBUG_MERGE, "START");
658 for ( good = gtk_tree_model_get_iter_first (store, &iter);
660 good = gtk_tree_model_iter_next (store, &iter) )
663 /* get current data */
664 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
665 DATA_COLUMN, &record,
669 /* Set select flag within the record */
670 record->select_flag = FALSE;
672 /* set new value in store */
673 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
674 SELECT_COLUMN, record->select_flag,
679 gl_debug (DEBUG_MERGE, "END");
685 * Local Variables: -- emacs
687 * c-basic-offset: 8 -- emacs
688 * tab-width: 8 -- emacs
689 * indent-tabs-mode: nil -- emacs