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/>.
25 #include <glib/gi18n.h>
26 #include <gobject/gvaluecollector.h>
29 #include <libglabels.h>
33 /*========================================================*/
35 /*========================================================*/
37 struct _glMergePrivate {
41 glMergeSrcType src_type;
55 glMergeSrcType src_type;
62 /*========================================================*/
63 /* Private globals. */
64 /*========================================================*/
66 static GList *backends = NULL;
68 /*========================================================*/
69 /* Private function prototypes. */
70 /*========================================================*/
72 static void gl_merge_finalize (GObject *object);
74 static void merge_open (glMerge *merge);
76 static void merge_close (glMerge *merge);
78 static glMergeRecord *merge_get_record (glMerge *merge);
80 static void merge_free_record (glMergeRecord **record);
82 static glMergeRecord *merge_dup_record (glMergeRecord *record);
84 static void merge_free_record_list (GList **record_list);
86 static GList *merge_dup_record_list (GList *record_list);
91 /*****************************************************************************/
92 /* Register a new merge backend. */
93 /*****************************************************************************/
95 gl_merge_register_backend (GType type,
98 glMergeSrcType src_type,
99 const gchar *first_arg_name,
108 guint n_params = 0, n_alloced_params = 16;
110 backend = g_new0 (Backend, 1);
112 backend->type = type;
113 backend->name = g_strdup (name);
114 backend->description = g_strdup (description);
115 backend->src_type = src_type;
117 params = g_new (GParameter, n_alloced_params);
118 va_start (args, first_arg_name);
119 for ( pname=first_arg_name; pname != NULL; pname=va_arg (args,gchar *) ) {
122 class = g_type_class_ref (type);
124 g_message ("%s: unknown object type %d",
125 G_STRLOC, (int)type);
128 pspec = g_object_class_find_property (class, pname);
130 g_message ("%s: object class `%s' has no property named `%s'",
131 G_STRLOC, g_type_name (type), pname);
134 if (n_params >= n_alloced_params) {
135 n_alloced_params += 16;
136 params = g_renew (GParameter, params, n_alloced_params);
138 params[n_params].name = pname;
139 params[n_params].value.g_type = 0;
140 g_value_init (¶ms[n_params].value, pspec->value_type);
141 G_VALUE_COLLECT (¶ms[n_params].value, args, 0, &error);
143 g_message ("%s: %s", G_STRLOC, error);
152 backend->n_params = n_params;
153 backend->params = params;
155 backends = g_list_append (backends, backend);
159 /*****************************************************************************/
160 /* Get list of registered backend descriptions. */
161 /*****************************************************************************/
163 gl_merge_get_descriptions (void)
165 GList *descriptions = NULL;
169 /* Translators: "None" here means that no document-merge source or
170 * method has been selected. */
171 descriptions = g_list_append (descriptions, g_strdup(_("None")));
173 for ( p=backends; p!=NULL; p=p->next) {
174 backend = (Backend *)p->data;
175 descriptions = g_list_append (descriptions,
176 g_strdup(backend->description));
182 /*****************************************************************************/
183 /* Free list of descriptions. */
184 /*****************************************************************************/
186 gl_merge_free_descriptions (GList **descriptions)
190 gl_debug (DEBUG_MERGE, "START");
192 for (p = *descriptions; p != NULL; p = p->next) {
197 g_list_free (*descriptions);
198 *descriptions = NULL;
200 gl_debug (DEBUG_MERGE, "END");
203 /*****************************************************************************/
204 /* Lookup name of backend from description. */
205 /*****************************************************************************/
207 gl_merge_description_to_name (gchar *description)
212 if (lgl_str_utf8_casecmp(description, _("None")) == 0) {
213 return g_strdup("None");
216 for ( p=backends; p!=NULL; p=p->next) {
217 backend = (Backend *)p->data;
218 if (lgl_str_utf8_casecmp(description, backend->description) == 0) {
219 return g_strdup(backend->name);
223 return g_strdup("None");
226 /*****************************************************************************/
227 /* Boilerplate object stuff. */
228 /*****************************************************************************/
229 G_DEFINE_TYPE (glMerge, gl_merge, G_TYPE_OBJECT);
232 gl_merge_class_init (glMergeClass *class)
234 GObjectClass *object_class = G_OBJECT_CLASS (class);
236 gl_debug (DEBUG_MERGE, "START");
238 gl_merge_parent_class = g_type_class_peek_parent (class);
240 object_class->finalize = gl_merge_finalize;
242 gl_debug (DEBUG_MERGE, "END");
246 gl_merge_init (glMerge *merge)
248 gl_debug (DEBUG_MERGE, "START");
250 merge->priv = g_new0 (glMergePrivate, 1);
252 gl_debug (DEBUG_MERGE, "END");
256 gl_merge_finalize (GObject *object)
258 glMerge *merge = GL_MERGE (object);
260 gl_debug (DEBUG_MERGE, "START");
262 g_return_if_fail (object && GL_IS_MERGE (object));
264 merge_free_record_list (&merge->priv->record_list);
265 g_free (merge->priv->name);
266 g_free (merge->priv->description);
267 g_free (merge->priv->src);
268 g_free (merge->priv);
270 G_OBJECT_CLASS (gl_merge_parent_class)->finalize (object);
272 gl_debug (DEBUG_MERGE, "END");
275 /*****************************************************************************/
276 /* New merge object. */
277 /*****************************************************************************/
279 gl_merge_new (gchar *name)
281 glMerge *merge = NULL;
285 gl_debug (DEBUG_MERGE, "START");
287 for (p=backends; p!=NULL; p=p->next) {
288 backend = (Backend *)p->data;
290 if (g_ascii_strcasecmp(name, backend->name) == 0) {
292 merge = GL_MERGE (g_object_newv (backend->type,
296 merge->priv->name = g_strdup (name);
297 merge->priv->description = g_strdup (backend->description);
298 merge->priv->src_type = backend->src_type;
304 if ( (merge == NULL) && (g_ascii_strcasecmp (name, "None") != 0)) {
305 g_message ("Unknown merge backend \"%s\"", name);
308 gl_debug (DEBUG_MERGE, "END");
313 /*****************************************************************************/
314 /* Duplicate merge. */
315 /*****************************************************************************/
317 gl_merge_dup (glMerge *src_merge)
321 gl_debug (DEBUG_MERGE, "START");
323 if (src_merge == NULL) {
324 gl_debug (DEBUG_MERGE, "END (NULL)");
328 g_return_val_if_fail (GL_IS_MERGE (src_merge), NULL);
330 dst_merge = g_object_new (G_OBJECT_TYPE(src_merge), NULL);
331 dst_merge->priv->name = g_strdup (src_merge->priv->name);
332 dst_merge->priv->description = g_strdup (src_merge->priv->description);
333 dst_merge->priv->src = g_strdup (src_merge->priv->src);
334 dst_merge->priv->src_type = src_merge->priv->src_type;
335 dst_merge->priv->record_list
336 = merge_dup_record_list (src_merge->priv->record_list);
338 if ( GL_MERGE_GET_CLASS(src_merge)->copy != NULL ) {
340 /* We have an object specific method, use it */
341 GL_MERGE_GET_CLASS(src_merge)->copy (dst_merge, src_merge);
345 gl_debug (DEBUG_MERGE, "END");
350 /*****************************************************************************/
351 /* Get name of merge. */
352 /*****************************************************************************/
354 gl_merge_get_name (glMerge *merge)
356 gl_debug (DEBUG_MERGE, "");
359 return g_strdup("None");
362 g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup("None"));
364 return g_strdup(merge->priv->name);
367 /*****************************************************************************/
368 /* Get description of merge. */
369 /*****************************************************************************/
371 gl_merge_get_description (glMerge *merge)
373 gl_debug (DEBUG_MERGE, "");
376 return g_strdup(_("None"));
379 g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup(_("None")));
381 return g_strdup(merge->priv->description);
384 /*****************************************************************************/
385 /* Get source type of merge. */
386 /*****************************************************************************/
388 gl_merge_get_src_type (glMerge *merge)
390 gl_debug (DEBUG_MERGE, "");
393 return GL_MERGE_SRC_IS_FIXED;
396 g_return_val_if_fail (GL_IS_MERGE (merge), GL_MERGE_SRC_IS_FIXED);
398 return merge->priv->src_type;
401 /*****************************************************************************/
402 /* Set src of merge. */
403 /*****************************************************************************/
405 gl_merge_set_src (glMerge *merge,
408 GList *record_list = NULL;
409 glMergeRecord *record;
411 gl_debug (DEBUG_MERGE, "START");
415 gl_debug (DEBUG_MERGE, "END (NULL)");
419 g_return_if_fail (GL_IS_MERGE (merge));
424 if ( merge->priv->src != NULL )
426 g_free (merge->priv->src);
428 merge->priv->src = NULL;
429 merge_free_record_list (&merge->priv->record_list);
435 if ( merge->priv->src != NULL )
437 g_free(merge->priv->src);
439 merge->priv->src = g_strdup (src);
441 merge_free_record_list (&merge->priv->record_list);
444 while ( (record = merge_get_record (merge)) != NULL )
446 record_list = g_list_append( record_list, record );
449 merge->priv->record_list = record_list;
454 gl_debug (DEBUG_MERGE, "END");
457 /*****************************************************************************/
458 /* Get src of merge. */
459 /*****************************************************************************/
461 gl_merge_get_src (glMerge *merge)
463 gl_debug (DEBUG_MERGE, "");
469 g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
471 return g_strdup(merge->priv->src);
474 /*****************************************************************************/
476 /*****************************************************************************/
478 gl_merge_get_key_list (glMerge *merge)
480 GList *key_list = NULL;
482 gl_debug (DEBUG_MERGE, "START");
488 g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
490 if ( GL_MERGE_GET_CLASS(merge)->get_key_list != NULL ) {
492 key_list = GL_MERGE_GET_CLASS(merge)->get_key_list (merge);
496 gl_debug (DEBUG_MERGE, "END");
501 /*****************************************************************************/
502 /* Free a list of keys. */
503 /*****************************************************************************/
505 gl_merge_free_key_list (GList **key_list)
509 gl_debug (DEBUG_MERGE, "START");
511 for (p = *key_list; p != NULL; p = p->next) {
516 g_list_free (*key_list);
519 gl_debug (DEBUG_MERGE, "END");
522 /*****************************************************************************/
524 /*****************************************************************************/
526 gl_merge_get_primary_key (glMerge *merge)
530 gl_debug (DEBUG_MERGE, "START");
536 g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
538 if ( GL_MERGE_GET_CLASS(merge)->get_primary_key != NULL ) {
540 key = GL_MERGE_GET_CLASS(merge)->get_primary_key (merge);
544 gl_debug (DEBUG_MERGE, "END");
549 /*---------------------------------------------------------------------------*/
550 /* Open merge source. */
551 /*---------------------------------------------------------------------------*/
553 merge_open (glMerge *merge)
555 gl_debug (DEBUG_MERGE, "START");
557 g_return_if_fail (merge && GL_IS_MERGE (merge));
559 if ( GL_MERGE_GET_CLASS(merge)->open != NULL ) {
561 GL_MERGE_GET_CLASS(merge)->open (merge);
565 gl_debug (DEBUG_MERGE, "END");
568 /*---------------------------------------------------------------------------*/
569 /* Close merge source. */
570 /*---------------------------------------------------------------------------*/
572 merge_close (glMerge *merge)
574 gl_debug (DEBUG_MERGE, "START");
576 g_return_if_fail (merge && GL_IS_MERGE (merge));
578 if ( GL_MERGE_GET_CLASS(merge)->close != NULL ) {
580 GL_MERGE_GET_CLASS(merge)->close (merge);
584 gl_debug (DEBUG_MERGE, "END");
587 /*---------------------------------------------------------------------------*/
588 /* Get next record (list of fields) from opened merge source. */
589 /*---------------------------------------------------------------------------*/
590 static glMergeRecord *
591 merge_get_record (glMerge *merge)
593 glMergeRecord *record = NULL;
595 gl_debug (DEBUG_MERGE, "START");
597 g_return_val_if_fail (merge && GL_IS_MERGE (merge), NULL);
599 if ( GL_MERGE_GET_CLASS(merge)->get_record != NULL ) {
601 record = GL_MERGE_GET_CLASS(merge)->get_record (merge);
605 gl_debug (DEBUG_MERGE, "END");
610 /*---------------------------------------------------------------------------*/
611 /* Free a merge record (list of fields) */
612 /*---------------------------------------------------------------------------*/
614 merge_free_record (glMergeRecord **record)
619 gl_debug (DEBUG_MERGE, "START");
621 for (p = (*record)->field_list; p != NULL; p = p->next) {
622 field = (glMergeField *) p->data;
626 g_free (field->value);
633 g_list_free ((*record)->field_list);
634 (*record)->field_list = NULL;
639 gl_debug (DEBUG_MERGE, "END");
642 /*---------------------------------------------------------------------------*/
643 /* Duplicate a merge record (list of fields) */
644 /*---------------------------------------------------------------------------*/
645 static glMergeRecord *
646 merge_dup_record (glMergeRecord *record)
648 glMergeRecord *dest_record;
650 glMergeField *dest_field, *field;
652 gl_debug (DEBUG_MERGE, "START");
654 dest_record = g_new0 (glMergeRecord, 1);
655 dest_record->select_flag = record->select_flag;
657 for (p = record->field_list; p != NULL; p = p->next) {
658 field = (glMergeField *) p->data;
660 dest_field = g_new0 (glMergeField, 1);
662 dest_field->key = g_strdup (field->key);
663 dest_field->value = g_strdup (field->value);
665 dest_record->field_list =
666 g_list_append (dest_record->field_list, dest_field);
670 gl_debug (DEBUG_MERGE, "END");
675 /*****************************************************************************/
676 /* Find key in given record and evaluate. */
677 /*****************************************************************************/
679 gl_merge_eval_key (glMergeRecord *record,
687 gl_debug (DEBUG_MERGE, "START");
689 if ( (record != NULL) && (key != NULL) ) {
690 for (p = record->field_list; p != NULL; p = p->next) {
691 field = (glMergeField *) p->data;
693 if (strcmp (key, field->key) == 0) {
694 val = g_strdup (field->value);
700 gl_debug (DEBUG_MERGE, "END");
705 /*****************************************************************************/
706 /* Read all records from merge source. */
707 /*****************************************************************************/
709 gl_merge_get_record_list (glMerge *merge)
711 gl_debug (DEBUG_MERGE, "");
713 if ( merge != NULL ) {
714 return merge->priv->record_list;
720 /*---------------------------------------------------------------------------*/
721 /* Free a list of records. */
722 /*---------------------------------------------------------------------------*/
724 merge_free_record_list (GList **record_list)
727 glMergeRecord *record;
729 gl_debug (DEBUG_MERGE, "START");
731 for (p = *record_list; p != NULL; p = p->next) {
732 record = (glMergeRecord *) p->data;
734 merge_free_record( &record );
738 g_list_free (*record_list);
741 gl_debug (DEBUG_MERGE, "END");
744 /*---------------------------------------------------------------------------*/
745 /* Duplicate a list of records. */
746 /*---------------------------------------------------------------------------*/
748 merge_dup_record_list (GList *record_list)
750 GList *dest_list = NULL, *p;
751 glMergeRecord *dest_record, *record;
753 gl_debug (DEBUG_MERGE, "START");
755 for (p = record_list; p != NULL; p = p->next) {
756 record = (glMergeRecord *) p->data;
758 dest_record = merge_dup_record( record );
759 dest_list = g_list_append (dest_list, dest_record);
763 gl_debug (DEBUG_MERGE, "END");
768 /*****************************************************************************/
769 /* Count selected records. */
770 /*****************************************************************************/
772 gl_merge_get_record_count (glMerge *merge)
775 glMergeRecord *record;
778 gl_debug (DEBUG_MERGE, "START");
781 for ( p=merge->priv->record_list; p!=NULL; p=p->next ) {
782 record = (glMergeRecord *)p->data;
784 if ( record->select_flag ) count ++;
787 gl_debug (DEBUG_MERGE, "END");
795 * Local Variables: -- emacs
797 * c-basic-offset: 8 -- emacs
798 * tab-width: 8 -- emacs
799 * indent-tabs-mode: nil -- emacs