Merge branch 'rewrite-sheet' of ssh://jmd@git.sv.gnu.org/srv/git/pspp into rewrite...
[pspp-builds.git] / src / ui / gui / psppire-data-store.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2006, 2008  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <gettext.h>
21 #define _(msgid) gettext (msgid)
22 #define N_(msgid) msgid
23
24 #include <data/datasheet.h>
25 #include <data/data-out.h>
26 #include <data/variable.h>
27
28 #include <gtksheet/gsheetmodel.h>
29
30 #include <pango/pango-context.h>
31
32 #include "psppire-data-store.h"
33 #include "psppire-case-file.h"
34 #include "helper.h"
35
36 #include <data/dictionary.h>
37 #include <data/missing-values.h>
38 #include <data/value-labels.h>
39 #include <data/data-in.h>
40 #include <data/format.h>
41
42
43 static void psppire_data_store_init            (PsppireDataStore      *data_store);
44 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
45 static void psppire_data_store_sheet_model_init (GSheetModelIface *iface);
46
47 static void psppire_data_store_finalize        (GObject           *object);
48 static void psppire_data_store_dispose        (GObject           *object);
49
50 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
51                                           glong row, glong column);
52
53
54 static GObjectClass *parent_class = NULL;
55
56
57 enum  {
58        BACKEND_CHANGED,
59        n_SIGNALS};
60
61 static guint signals [n_SIGNALS];
62
63
64 GType
65 psppire_data_store_get_type (void)
66 {
67   static GType data_store_type = 0;
68
69   if (!data_store_type)
70     {
71       static const GTypeInfo data_store_info =
72       {
73         sizeof (PsppireDataStoreClass),
74         NULL,           /* base_init */
75         NULL,           /* base_finalize */
76         (GClassInitFunc) psppire_data_store_class_init,
77         NULL,           /* class_finalize */
78         NULL,           /* class_data */
79         sizeof (PsppireDataStore),
80         0,
81         (GInstanceInitFunc) psppire_data_store_init,
82       };
83
84       static const GInterfaceInfo sheet_model_info =
85       {
86         (GInterfaceInitFunc) psppire_data_store_sheet_model_init,
87         NULL,
88         NULL
89       };
90
91
92       data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore",
93                                                 &data_store_info, 0);
94
95       g_type_add_interface_static (data_store_type,
96                                    G_TYPE_SHEET_MODEL,
97                                    &sheet_model_info);
98
99     }
100
101   return data_store_type;
102 }
103
104
105 static void
106 psppire_data_store_class_init (PsppireDataStoreClass *class)
107 {
108   GObjectClass *object_class;
109
110   parent_class = g_type_class_peek_parent (class);
111   object_class = (GObjectClass*) class;
112
113   object_class->finalize = psppire_data_store_finalize;
114   object_class->dispose = psppire_data_store_dispose;
115
116   signals [BACKEND_CHANGED] =
117     g_signal_new ("backend-changed",
118                   G_TYPE_FROM_CLASS (class),
119                   G_SIGNAL_RUN_FIRST,
120                   0,
121                   NULL, NULL,
122                   g_cclosure_marshal_VOID__VOID,
123                   G_TYPE_NONE,
124                   0);
125 }
126
127
128
129 static glong
130 psppire_data_store_get_var_count (const GSheetModel *model)
131 {
132   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
133
134   return psppire_dict_get_var_cnt (store->dict);
135 }
136
137 casenumber
138 psppire_data_store_get_case_count (const PsppireDataStore *store)
139 {
140   return psppire_case_file_get_case_count (store->case_file);
141 }
142
143 size_t
144 psppire_data_store_get_value_count (const PsppireDataStore *store)
145 {
146   return psppire_dict_get_value_cnt (store->dict);
147 }
148
149 casenumber
150 psppire_data_store_get_case_count_wrapper (const GSheetModel *model)
151 {
152   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
153   return psppire_data_store_get_case_count (store);
154 }
155
156 static void
157 psppire_data_store_init (PsppireDataStore *data_store)
158 {
159   data_store->dict = 0;
160   data_store->case_file = NULL;
161   data_store->dispose_has_run = FALSE;
162 }
163
164 static inline gchar *
165 psppire_data_store_get_string_wrapper (const GSheetModel *model, glong row,
166                                        glong column)
167 {
168   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
169 }
170
171
172 static inline gboolean
173 psppire_data_store_set_string_wrapper (GSheetModel *model,
174                                        const gchar *text,
175                                        glong row, glong column)
176 {
177   return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
178                                         row, column);
179 }
180
181
182
183 static gchar * get_column_subtitle (const GSheetModel *model, gint col);
184 static gchar * get_column_button_label (const GSheetModel *model, gint col);
185 static gboolean get_column_sensitivity (const GSheetModel *model, gint col);
186 static GtkJustification get_column_justification (const GSheetModel *model, gint col);
187
188 static gchar * get_row_button_label (const GSheetModel *model, gint row);
189 static gboolean get_row_sensitivity (const GSheetModel *model, gint row);
190
191
192 static void
193 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
194 {
195   iface->free_strings = TRUE;
196   iface->get_string = psppire_data_store_get_string_wrapper;
197   iface->set_string = psppire_data_store_set_string_wrapper;
198   iface->clear_datum = psppire_data_store_clear_datum;
199   iface->is_editable = NULL;
200   iface->get_foreground = NULL;
201   iface->get_background = NULL;
202   iface->get_cell_border = NULL;
203   iface->get_column_count = psppire_data_store_get_var_count;
204   iface->get_row_count = psppire_data_store_get_case_count_wrapper;
205
206   iface->get_column_subtitle = get_column_subtitle;
207   iface->get_column_title = get_column_button_label;
208   iface->get_column_sensitivity = get_column_sensitivity;
209   iface->get_column_justification = get_column_justification;
210
211   iface->get_row_title = get_row_button_label;
212   iface->get_row_sensitivity = get_row_sensitivity;
213 }
214
215 static void
216 delete_cases_callback (GtkWidget *w,
217          casenumber first, casenumber n_cases, gpointer data)
218 {
219   PsppireDataStore *store  ;
220
221   g_return_if_fail (data);
222
223   store  = PSPPIRE_DATA_STORE (data);
224
225   g_assert (first >= 0);
226
227   g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
228 }
229
230
231 static void
232 insert_case_callback (GtkWidget *w, casenumber casenum, gpointer data)
233 {
234   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
235
236   g_return_if_fail (data);
237
238   g_print ("%s\n", __FUNCTION__);
239
240   g_sheet_model_range_changed (G_SHEET_MODEL (store),
241                                casenum, -1,
242                                psppire_case_file_get_case_count (store->case_file),
243                                -1);
244
245   g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
246 }
247
248
249 static void
250 changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
251 {
252   PsppireDataStore *store  ;
253   g_return_if_fail (data);
254
255   store  = PSPPIRE_DATA_STORE (data);
256
257   g_sheet_model_range_changed (G_SHEET_MODEL (store),
258                                  casenum, -1,
259                                  casenum, -1);
260 }
261
262
263 /*
264    A callback which occurs after a variable has been deleted.
265  */
266 static void
267 delete_variable_callback (GObject *obj, gint dict_index,
268                           gint case_index, gint val_cnt,
269                           gpointer data)
270 {
271   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
272
273 #if AXIS_TRANSITION
274   g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
275
276   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
277                                    dict_index, -1);
278 #endif
279 }
280
281
282 static void
283 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
284 {
285   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
286
287 #if AXIS_TRANSITION
288   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
289                                   var_num, 1);
290
291
292   g_sheet_model_range_changed (G_SHEET_MODEL (store),
293                                -1, var_num,
294                                -1, var_num);
295 #endif
296 }
297
298 static void
299 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
300 {
301   PsppireDataStore *store;
302   gint posn;
303
304   g_return_if_fail (data);
305
306   store  = PSPPIRE_DATA_STORE (data);
307
308   if ( var_num > 0 )
309     {
310       struct variable *variable =
311         psppire_dict_get_variable (store->dict, var_num);
312
313       g_assert (variable != NULL);
314
315       posn = var_get_case_index (variable);
316     }
317   else
318     {
319       posn = 0;
320     }
321
322   psppire_case_file_insert_values (store->case_file, 1, posn);
323
324 #if AXIS_TRANSITION
325   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
326                                   var_num, 1);
327 #endif
328
329   g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
330 }
331
332
333 static void
334 dict_size_change_callback (GObject *obj,
335                           gint posn, gint adjustment, gpointer data)
336 {
337   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
338
339   const struct variable *v = psppire_dict_get_variable (store->dict, posn);
340
341   const gint new_val_width = value_cnt_from_width (var_get_width (v));
342
343   if ( adjustment > 0 )
344     psppire_case_file_insert_values (store->case_file, adjustment,
345                                      new_val_width - adjustment +
346                                      var_get_case_index(v));
347 }
348
349
350
351 /**
352  * psppire_data_store_new:
353  * @dict: The dictionary for this data_store.
354  *
355  *
356  * Return value: a new #PsppireDataStore
357  **/
358 PsppireDataStore *
359 psppire_data_store_new (PsppireDict *dict)
360 {
361   PsppireDataStore *retval;
362
363   retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
364
365   psppire_data_store_set_dictionary (retval, dict);
366
367   return retval;
368 }
369
370
371 void
372 psppire_data_store_set_case_file (PsppireDataStore *ds,
373                                   PsppireCaseFile *cf)
374 {
375   gint i;
376   if ( ds->case_file)  g_object_unref (ds->case_file);
377   ds->case_file = cf;
378
379   g_sheet_model_range_changed (G_SHEET_MODEL (ds),
380                                -1, -1, -1, -1);
381
382   for (i = 0 ; i < n_cf_signals ; ++i )
383     {
384       if ( ds->cf_handler_id [i] > 0 )
385         g_signal_handler_disconnect (ds->case_file,
386                                      ds->cf_handler_id[i]);
387     }
388
389
390   if ( ds->dict )
391     for (i = 0 ; i < n_dict_signals; ++i )
392       {
393         if ( ds->dict_handler_id [i] > 0)
394           {
395             g_signal_handler_unblock (ds->dict,
396                                       ds->dict_handler_id[i]);
397           }
398       }
399
400   ds->cf_handler_id [CASES_DELETED] =
401     g_signal_connect (ds->case_file, "cases-deleted",
402                       G_CALLBACK (delete_cases_callback),
403                       ds);
404
405   ds->cf_handler_id [CASE_INSERTED] =
406     g_signal_connect (ds->case_file, "case-inserted",
407                       G_CALLBACK (insert_case_callback),
408                       ds);
409
410   ds->cf_handler_id [CASE_CHANGED] =
411     g_signal_connect (ds->case_file, "case-changed",
412                       G_CALLBACK (changed_case_callback),
413                       ds);
414
415   g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
416 }
417
418
419 /**
420  * psppire_data_store_replace_set_dictionary:
421  * @data_store: The variable store
422  * @dict: The dictionary to set
423  *
424  * If a dictionary is already associated with the data-store, then it will be
425  * destroyed.
426  **/
427 void
428 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
429 {
430   int i;
431
432   /* Disconnect any existing handlers */
433   if ( data_store->dict )
434     for (i = 0 ; i < n_dict_signals; ++i )
435       {
436         g_signal_handler_disconnect (data_store->dict,
437                                      data_store->dict_handler_id[i]);
438       }
439
440   data_store->dict = dict;
441
442   if ( dict != NULL)
443     {
444
445       data_store->dict_handler_id [VARIABLE_INSERTED] =
446         g_signal_connect (dict, "variable-inserted",
447                           G_CALLBACK (insert_variable_callback),
448                           data_store);
449
450       data_store->dict_handler_id [VARIABLE_DELETED] =
451         g_signal_connect (dict, "variable-deleted",
452                           G_CALLBACK (delete_variable_callback),
453                           data_store);
454
455       data_store->dict_handler_id [VARIABLE_CHANGED] =
456         g_signal_connect (dict, "variable-changed",
457                           G_CALLBACK (variable_changed_callback),
458                           data_store);
459
460       data_store->dict_handler_id [SIZE_CHANGED] =
461         g_signal_connect (dict, "dict-size-changed",
462                           G_CALLBACK (dict_size_change_callback),
463                           data_store);
464     }
465
466
467
468   /* The entire model has changed */
469   g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
470
471 #if AXIS_TRANSITION
472   g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
473 #endif
474
475   if ( data_store->dict )
476     for (i = 0 ; i < n_dict_signals; ++i )
477       {
478         if ( data_store->dict_handler_id [i] > 0)
479           {
480             g_signal_handler_block (data_store->dict,
481                                     data_store->dict_handler_id[i]);
482           }
483       }
484 }
485
486 static void
487 psppire_data_store_finalize (GObject *object)
488 {
489
490   /* must chain up */
491   (* parent_class->finalize) (object);
492 }
493
494
495 static void
496 psppire_data_store_dispose (GObject *object)
497 {
498   PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
499
500   if (ds->dispose_has_run)
501     return;
502
503   if (ds->case_file) g_object_unref (ds->case_file);
504
505   /* must chain up */
506   (* parent_class->dispose) (object);
507
508   ds->dispose_has_run = TRUE;
509 }
510
511
512 gboolean
513 psppire_data_store_delete_cases (PsppireDataStore *ds,
514                                  casenumber first, casenumber count)
515 {
516   g_return_val_if_fail (ds, FALSE);
517
518   return psppire_case_file_delete_cases (ds->case_file, count, first);
519 }
520
521
522
523 /* Insert a blank case before POSN */
524 gboolean
525 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
526 {
527   gboolean result;
528   gint val_cnt, v;
529   struct ccase cc;
530   g_return_val_if_fail (ds, FALSE);
531
532   val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
533
534   g_return_val_if_fail (val_cnt > 0, FALSE);
535
536   g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
537
538   case_create (&cc, val_cnt);
539
540   memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
541
542   for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
543     {
544       const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
545       if ( var_is_alpha (pv))
546         continue;
547
548       case_data_rw (&cc, pv)->f = SYSMIS;
549     }
550
551   result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
552
553   case_destroy (&cc);
554
555   return result;
556 }
557
558
559 gchar *
560 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
561 {
562   gint idx;
563   char *text;
564   const struct fmt_spec *fp ;
565   const struct variable *pv ;
566   union value *v ;
567   GString *s;
568
569   g_return_val_if_fail (store->dict, NULL);
570   g_return_val_if_fail (store->case_file, NULL);
571
572   if (column >= psppire_dict_get_var_cnt (store->dict))
573     return NULL;
574
575   if ( row >= psppire_case_file_get_case_count (store->case_file))
576     return NULL;
577
578   pv = psppire_dict_get_variable (store->dict, column);
579
580   g_assert (pv);
581
582   idx = var_get_case_index (pv);
583
584   g_assert (idx >= 0);
585
586   v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
587                                    var_get_width (pv));
588
589   g_return_val_if_fail (v, NULL);
590
591   if ( store->show_labels)
592     {
593       const gchar *label = var_lookup_value_label (pv, v);
594       if (label)
595         {
596           free (v);
597           return pspp_locale_to_utf8 (label, -1, 0);
598         }
599     }
600
601   fp = var_get_write_format (pv);
602
603   s = g_string_sized_new (fp->w + 1);
604   g_string_set_size (s, fp->w);
605
606   memset (s->str, 0, fp->w);
607
608   g_assert (fp->w == s->len);
609
610   /* Converts binary value V into printable form in the exactly
611      FP->W character in buffer S according to format specification
612      FP.  No null terminator is appended to the buffer.  */
613   data_out (v, fp, s->str);
614
615   text = pspp_locale_to_utf8 (s->str, fp->w, 0);
616   g_string_free (s, TRUE);
617
618   g_strchomp (text);
619
620   free (v);
621   return text;
622 }
623
624
625 static gboolean
626 psppire_data_store_clear_datum (GSheetModel *model,
627                                           glong row, glong col)
628 {
629   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
630
631   union value v;
632   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
633
634   const gint index = var_get_case_index (pv) ;
635
636   if ( var_is_numeric (pv))
637     v.f = SYSMIS;
638   else
639     memcpy (v.s, "", MAX_SHORT_STRING);
640
641   psppire_case_file_set_value (store->case_file, row, index, &v,
642                               var_get_width (pv));
643
644   return TRUE;
645 }
646
647
648 /* Attempts to update that part of the variable store which corresponds
649    to ROW, COL with  the value TEXT.
650    Returns true if anything was updated, false otherwise.
651 */
652 gboolean
653 psppire_data_store_set_string (PsppireDataStore *store,
654                                const gchar *text, glong row, glong col)
655 {
656   glong n_cases;
657   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
658   g_return_val_if_fail (pv, FALSE);
659
660   n_cases = psppire_data_store_get_case_count (store);
661
662   if ( row > n_cases)
663     return FALSE;
664
665   if (row == n_cases)
666     psppire_data_store_insert_new_case (store, row);
667
668   psppire_case_file_data_in (store->case_file, row,
669                              var_get_case_index (pv), ss_cstr (text),
670                              var_get_write_format (pv));
671
672   return TRUE;
673 }
674
675
676
677 void
678 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
679 {
680   g_return_if_fail (store);
681   g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
682
683   store->show_labels = show_labels;
684
685   g_sheet_model_range_changed (G_SHEET_MODEL (store),
686                                  -1, -1, -1, -1);
687 }
688
689
690 void
691 psppire_data_store_clear (PsppireDataStore *data_store)
692 {
693   psppire_case_file_clear (data_store->case_file);
694
695   psppire_dict_clear (data_store->dict);
696 }
697
698
699
700 /* Return a casereader made from this datastore */
701 struct casereader *
702 psppire_data_store_get_reader (PsppireDataStore *ds)
703 {
704   int i;
705   struct casereader *reader ;
706
707   for (i = 0 ; i < n_cf_signals ; ++i )
708     {
709       g_signal_handler_disconnect (ds->case_file, ds->cf_handler_id[i]);
710       ds->cf_handler_id[i] = 0 ;
711     }
712
713   if ( ds->dict )
714     for (i = 0 ; i < n_dict_signals; ++i )
715       {
716         g_signal_handler_block (ds->dict,
717                                 ds->dict_handler_id[i]);
718       }
719
720   reader = psppire_case_file_make_reader (ds->case_file);
721
722   return reader;
723 }
724
725
726
727 /* Column related funcs */
728
729
730 static const gchar null_var_name[]=N_("var");
731
732 \f
733
734 /* Row related funcs */
735
736 static gchar *
737 get_row_button_label (const GSheetModel *model, gint unit)
738 {
739   gchar *text;
740   gchar *s;
741   PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
742
743   s = g_strdup_printf (_("%d"), unit + 1);
744
745   text =  pspp_locale_to_utf8 (s, -1, 0);
746
747   g_free (s);
748
749   return text;
750 }
751
752
753 static gboolean
754 get_row_sensitivity (const GSheetModel *model, gint unit)
755 {
756   PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
757
758   return (unit < psppire_case_file_get_case_count (ds->case_file));
759 }
760
761
762 \f
763
764 /* Column related stuff */
765
766 static gchar *
767 get_column_subtitle (const GSheetModel *model, gint col)
768 {
769   gchar *text;
770   const struct variable *v ;
771   PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
772
773   if ( col >= psppire_dict_get_var_cnt (ds->dict) )
774     return NULL;
775
776   v = psppire_dict_get_variable (ds->dict, col);
777
778   if ( ! var_has_label (v))
779     return NULL;
780
781   text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
782
783   return text;
784 }
785
786 static gchar *
787 get_column_button_label (const GSheetModel *model, gint col)
788 {
789   gchar *text;
790   struct variable *pv ;
791   PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
792
793   if ( col >= psppire_dict_get_var_cnt (ds->dict) )
794     return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
795
796   pv = psppire_dict_get_variable (ds->dict, col);
797
798   text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
799
800   return text;
801 }
802
803 static gboolean
804 get_column_sensitivity (const GSheetModel *model, gint col)
805 {
806   PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
807
808   return (col < psppire_dict_get_var_cnt (ds->dict));
809 }
810
811
812
813 static GtkJustification
814 get_column_justification (const GSheetModel *model, gint col)
815 {
816   PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
817   const struct variable *pv ;
818
819   if ( col >= psppire_dict_get_var_cnt (ds->dict) )
820     return GTK_JUSTIFY_LEFT;
821
822   pv = psppire_dict_get_variable (ds->dict, col);
823
824   return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
825           : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
826           : GTK_JUSTIFY_CENTER);
827 }
828
829