1b6fafc4a0ead16660ac2922ed66c3b667adee82
[claws.git] / src / gtk / gtkunit.c
1 /* LIBGTK - The GTK Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimpunit.c
5  * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
6  *
7  * This library is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 3 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <http://www.gnu.org/licenses/>.
20  */
21
22 #include "config.h"
23
24 #include "gtkunit.h"
25
26 #include <string.h>
27
28 #include <glib-object.h>
29
30
31 GtkCMUnitVtable _gtk_unit_vtable = { NULL, };
32
33
34 void
35 gtk_base_init (GtkCMUnitVtable *vtable)
36 {
37   static gboolean gtk_base_initialized = FALSE;
38
39   g_return_if_fail (vtable != NULL);
40
41   if (gtk_base_initialized)
42     g_error ("gtk_base_init() must only be called once!");
43
44   _gtk_unit_vtable = *vtable;
45
46   gtk_base_initialized = TRUE;
47 }
48
49
50 /**
51  * SECTION: gimpunit
52  * @title: gimpunit
53  * @short_description: Provides a collection of predefined units and
54  *                     functions for creating user-defined units.
55  * @see_also: #GtkCMUnitMenu, #GtkSizeEntry.
56  *
57  * Provides a collection of predefined units and functions for
58  * creating user-defined units.
59  **/
60
61
62 static void   unit_to_string (const GValue *src_value,
63                               GValue       *dest_value);
64 static void   string_to_unit (const GValue *src_value,
65                               GValue       *dest_value);
66
67 GType
68 gtk_unit_get_type (void)
69 {
70   static GType unit_type = 0;
71
72   if (! unit_type)
73     {
74       const GTypeInfo type_info = { 0, };
75
76       unit_type = g_type_register_static (G_TYPE_INT, "GtkCMUnit",
77                                           &type_info, 0);
78
79       g_value_register_transform_func (unit_type, G_TYPE_STRING,
80                                        unit_to_string);
81       g_value_register_transform_func (G_TYPE_STRING, unit_type,
82                                        string_to_unit);
83     }
84
85   return unit_type;
86 }
87
88 static void
89 unit_to_string (const GValue *src_value,
90                 GValue       *dest_value)
91 {
92   GtkCMUnit unit = (GtkCMUnit) g_value_get_int (src_value);
93
94   g_value_set_string (dest_value, gtk_unit_get_identifier (unit));
95 }
96
97 static void
98 string_to_unit (const GValue *src_value,
99                 GValue       *dest_value)
100 {
101   const gchar *str;
102   gint         num_units;
103   gint         i;
104
105   str = g_value_get_string (src_value);
106
107   if (!str || !*str)
108     goto error;
109
110   num_units = gtk_unit_get_number_of_units ();
111
112   for (i = CM_UNIT_PIXEL; i < num_units; i++)
113     if (strcmp (str, gtk_unit_get_identifier (i)) == 0)
114       break;
115
116   if (i == num_units)
117     {
118       if (strcmp (str, gtk_unit_get_identifier (CM_UNIT_PERCENT)) == 0)
119         i = CM_UNIT_PERCENT;
120       else
121         goto error;
122     }
123
124   g_value_set_int (dest_value, i);
125   return;
126
127  error:
128   g_warning ("Can't convert string to GtkCMUnit.");
129 }
130
131
132 /**
133  * gtk_unit_get_number_of_units:
134  *
135  * Returns the number of units which are known to the #GtkCMUnit system.
136  *
137  * Returns: The number of defined units.
138  **/
139 gint
140 gtk_unit_get_number_of_units (void)
141 {
142   g_return_val_if_fail (_gtk_unit_vtable.unit_get_number_of_units != NULL,
143                         CM_UNIT_END);
144
145   return _gtk_unit_vtable.unit_get_number_of_units ();
146 }
147
148 /**
149  * gtk_unit_get_number_of_built_in_units:
150  *
151  * Returns the number of #GtkCMUnit's which are hardcoded in the unit system
152  * (UNIT_INCH, UNIT_MM, UNIT_POINT, UNIT_PICA and the two "pseudo unit"
153  *  UNIT_PIXEL).
154  *
155  * Returns: The number of built-in units.
156  **/
157 gint
158 gtk_unit_get_number_of_built_in_units (void)
159 {
160   g_return_val_if_fail (_gtk_unit_vtable.unit_get_number_of_built_in_units
161                         != NULL, CM_UNIT_END);
162
163   return _gtk_unit_vtable.unit_get_number_of_built_in_units ();
164 }
165
166 /**
167  * gtk_unit_new:
168  * @identifier: The unit's identifier string.
169  * @factor: The unit's factor (how many units are in one inch).
170  * @digits: The unit's suggested number of digits (see gtk_unit_get_digits()).
171  * @symbol: The symbol of the unit (e.g. "''" for inch).
172  * @abbreviation: The abbreviation of the unit.
173  * @singular: The singular form of the unit.
174  * @plural: The plural form of the unit.
175  *
176  * Returns the integer ID of the new #GtkCMUnit.
177  *
178  * Note that a new unit is always created with it's deletion flag
179  * set to %TRUE. You will have to set it to %FALSE with
180  * gtk_unit_set_deletion_flag() to make the unit definition persistent.
181  *
182  * Returns: The ID of the new unit.
183  **/
184 GtkCMUnit
185 gtk_unit_new (gchar   *identifier,
186                gdouble  factor,
187                gint     digits,
188                gchar   *symbol,
189                gchar   *abbreviation,
190                gchar   *singular,
191                gchar   *plural)
192 {
193   g_return_val_if_fail (_gtk_unit_vtable.unit_new != NULL, CM_UNIT_INCH);
194
195   return _gtk_unit_vtable.unit_new (identifier, factor, digits,
196                                      symbol, abbreviation, singular, plural);
197 }
198
199 /**
200  * gtk_unit_get_deletion_flag:
201  * @unit: The unit you want to know the @deletion_flag of.
202  *
203  * Returns: The unit's @deletion_flag.
204  **/
205 gboolean
206 gtk_unit_get_deletion_flag (GtkCMUnit unit)
207 {
208   g_return_val_if_fail (_gtk_unit_vtable.unit_get_deletion_flag != NULL, FALSE);
209
210   return _gtk_unit_vtable.unit_get_deletion_flag (unit);
211 }
212
213 /**
214  * gtk_unit_set_deletion_flag:
215  * @unit: The unit you want to set the @deletion_flag for.
216  * @deletion_flag: The new deletion_flag.
217  *
218  * Sets a #GtkCMUnit's @deletion_flag. If the @deletion_flag of a unit is
219  * %TRUE when GTK exits, this unit will not be saved in the users's
220  * "unitrc" file.
221  *
222  * Trying to change the @deletion_flag of a built-in unit will be silently
223  * ignored.
224  **/
225 void
226 gtk_unit_set_deletion_flag (GtkCMUnit unit,
227                              gboolean deletion_flag)
228 {
229   g_return_if_fail (_gtk_unit_vtable.unit_set_deletion_flag != NULL);
230
231   _gtk_unit_vtable.unit_set_deletion_flag (unit, deletion_flag);
232 }
233
234 /**
235  * gtk_unit_get_factor:
236  * @unit: The unit you want to know the factor of.
237  *
238  * A #GtkCMUnit's @factor is defined to be:
239  *
240  * distance_in_units == (@factor * distance_in_inches)
241  *
242  * Returns 0 for @unit == CM_UNIT_PIXEL.
243  *
244  * Returns: The unit's factor.
245  **/
246 gdouble
247 gtk_unit_get_factor (GtkCMUnit unit)
248 {
249   g_return_val_if_fail (_gtk_unit_vtable.unit_get_factor != NULL, 1.0);
250
251   return _gtk_unit_vtable.unit_get_factor (unit);
252 }
253
254 /**
255  * gtk_unit_get_digits:
256  * @unit: The unit you want to know the digits.
257  *
258  * Returns the number of digits an entry field should provide to get
259  * approximately the same accuracy as an inch input field with two digits.
260  *
261  * Returns 0 for @unit == CM_UNIT_PIXEL.
262  *
263  * Returns: The suggested number of digits.
264  **/
265 gint
266 gtk_unit_get_digits (GtkCMUnit unit)
267 {
268   g_return_val_if_fail (_gtk_unit_vtable.unit_get_digits != NULL, 2);
269
270   return _gtk_unit_vtable.unit_get_digits (unit);
271 }
272
273 /**
274  * gtk_unit_get_identifier:
275  * @unit: The unit you want to know the identifier of.
276  *
277  * This is an unstranslated string and must not be changed or freed.
278  *
279  * Returns: The unit's identifier.
280  **/
281 const gchar *
282 gtk_unit_get_identifier (GtkCMUnit unit)
283 {
284   g_return_val_if_fail (_gtk_unit_vtable.unit_get_identifier != NULL, NULL);
285
286   return _gtk_unit_vtable.unit_get_identifier (unit);
287 }
288
289 /**
290  * gtk_unit_get_symbol:
291  * @unit: The unit you want to know the symbol of.
292  *
293  * This is e.g. "''" for UNIT_INCH.
294  *
295  * NOTE: This string must not be changed or freed.
296  *
297  * Returns: The unit's symbol.
298  **/
299 const gchar *
300 gtk_unit_get_symbol (GtkCMUnit unit)
301 {
302   g_return_val_if_fail (_gtk_unit_vtable.unit_get_symbol != NULL, NULL);
303
304   return _gtk_unit_vtable.unit_get_symbol (unit);
305 }
306
307 /**
308  * gtk_unit_get_abbreviation:
309  * @unit: The unit you want to know the abbreviation of.
310  *
311  * For built-in units, this function returns the translated abbreviation
312  * of the unit.
313  *
314  * NOTE: This string must not be changed or freed.
315  *
316  * Returns: The unit's abbreviation.
317  **/
318 const gchar *
319 gtk_unit_get_abbreviation (GtkCMUnit unit)
320 {
321   g_return_val_if_fail (_gtk_unit_vtable.unit_get_abbreviation != NULL, NULL);
322
323   return _gtk_unit_vtable.unit_get_abbreviation (unit);
324 }
325
326 /**
327  * gtk_unit_get_singular:
328  * @unit: The unit you want to know the singular form of.
329  *
330  * For built-in units, this function returns the translated singular form
331  * of the unit's name.
332  *
333  * NOTE: This string must not be changed or freed.
334  *
335  * Returns: The unit's singular form.
336  **/
337 const gchar *
338 gtk_unit_get_singular (GtkCMUnit unit)
339 {
340   g_return_val_if_fail (_gtk_unit_vtable.unit_get_singular != NULL, NULL);
341
342   return _gtk_unit_vtable.unit_get_singular (unit);
343 }
344
345 /**
346  * gtk_unit_get_plural:
347  * @unit: The unit you want to know the plural form of.
348  *
349  * For built-in units, this function returns the translated plural form
350  * of the unit's name.
351  *
352  * NOTE: This string must not be changed or freed.
353  *
354  * Returns: The unit's plural form.
355  **/
356 const gchar *
357 gtk_unit_get_plural (GtkCMUnit unit)
358 {
359   g_return_val_if_fail (_gtk_unit_vtable.unit_get_plural != NULL, NULL);
360
361   return _gtk_unit_vtable.unit_get_plural (unit);
362 }
363
364 static gint
365 print (gchar       *buf,
366        gint         len,
367        gint         start,
368        const gchar *fmt,
369        ...)
370 {
371   va_list args;
372   gint printed;
373
374   va_start (args, fmt);
375
376   printed = g_vsnprintf (buf + start, len - start, fmt, args);
377   if (printed < 0)
378     printed = len - start;
379
380   va_end (args);
381
382   return printed;
383 }
384
385 /**
386  * gtk_unit_format_string:
387  * @format: A printf-like format string which is used to create the unit
388  *          string.
389  * @unit:   A unit.
390  *
391  * The @format string supports the following percent expansions:
392  *
393  * <informaltable pgwide="1" frame="none" role="enum">
394  *   <tgroup cols="2"><colspec colwidth="1*"/><colspec colwidth="8*"/>
395  *     <tbody>
396  *       <row>
397  *         <entry>% f</entry>
398  *         <entry>Factor (how many units make up an inch)</entry>
399  *        </row>
400  *       <row>
401  *         <entry>% y</entry>
402  *         <entry>Symbol (e.g. "''" for CM_UNIT_INCH)</entry>
403  *       </row>
404  *       <row>
405  *         <entry>% a</entry>
406  *         <entry>Abbreviation</entry>
407  *       </row>
408  *       <row>
409  *         <entry>% s</entry>
410  *         <entry>Singular</entry>
411  *       </row>
412  *       <row>
413  *         <entry>% p</entry>
414  *         <entry>Plural</entry>
415  *       </row>
416  *       <row>
417  *         <entry>%%</entry>
418  *         <entry>Literal percent</entry>
419  *       </row>
420  *     </tbody>
421  *   </tgroup>
422  * </informaltable>
423  *
424  * Returns: A newly allocated string with above percent expressions
425  *          replaced with the resp. strings for @unit.
426  *
427  * Since: GTK 2.8
428  **/
429 gchar *
430 gtk_unit_format_string (const gchar *format,
431                          GtkCMUnit     unit)
432 {
433   gchar buffer[1024];
434   gint  i = 0;
435
436   g_return_val_if_fail (format != NULL, NULL);
437   g_return_val_if_fail (unit == CM_UNIT_PERCENT ||
438                         (unit >= CM_UNIT_PIXEL &&
439                          unit < gtk_unit_get_number_of_units ()), NULL);
440
441   while (i < (sizeof (buffer) - 1) && *format)
442     {
443       switch (*format)
444         {
445         case '%':
446           format++;
447           switch (*format)
448             {
449             case 0:
450               g_warning ("%s: unit-menu-format string ended within %%-sequence",
451                          G_STRFUNC);
452               break;
453
454             case '%':
455               buffer[i++] = '%';
456               break;
457
458             case 'f': /* factor (how many units make up an inch) */
459               i += print (buffer, sizeof (buffer), i, "%f",
460                           gtk_unit_get_factor (unit));
461               break;
462
463             case 'y': /* symbol ("''" for inch) */
464               i += print (buffer, sizeof (buffer), i, "%s",
465                           gtk_unit_get_symbol (unit));
466               break;
467
468             case 'a': /* abbreviation */
469               i += print (buffer, sizeof (buffer), i, "%s",
470                           gtk_unit_get_abbreviation (unit));
471               break;
472
473             case 's': /* singular */
474               i += print (buffer, sizeof (buffer), i, "%s",
475                           gtk_unit_get_singular (unit));
476               break;
477
478             case 'p': /* plural */
479               i += print (buffer, sizeof (buffer), i, "%s",
480                           gtk_unit_get_plural (unit));
481               break;
482
483             default:
484               g_warning ("%s: unit-menu-format contains unknown format "
485                          "sequence '%%%c'", G_STRFUNC, *format);
486               break;
487             }
488           break;
489
490         default:
491           buffer[i++] = *format;
492           break;
493         }
494
495       format++;
496     }
497
498   buffer[MIN (i, sizeof (buffer) - 1)] = 0;
499
500   return g_strdup (buffer);
501 }
502
503 /*
504  * GTK_TYPE_PARAM_UNIT
505  */
506
507 #define GTK_PARAM_SPEC_UNIT(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GTK_TYPE_PARAM_UNIT, GtkParamSpecUnit))
508
509 typedef struct _GtkParamSpecUnit GtkParamSpecUnit;
510
511 struct _GtkParamSpecUnit
512 {
513   GParamSpecInt parent_instance;
514
515   gboolean      allow_percent;
516 };
517
518 static void      gtk_param_unit_class_init     (GParamSpecClass *class);
519 static gboolean  gtk_param_unit_value_validate (GParamSpec      *pspec,
520                                                  GValue          *value);
521
522 /**
523  * gtk_param_unit_get_type:
524  *
525  * Reveals the object type
526  *
527  * Returns: the #GType for a unit param object
528  *
529  * Since: GTK 2.4
530  **/
531 GType
532 gtk_param_unit_get_type (void)
533 {
534   static GType spec_type = 0;
535
536   if (! spec_type)
537     {
538       const GTypeInfo type_info =
539       {
540         sizeof (GParamSpecClass),
541         NULL, NULL,
542         (GClassInitFunc) gtk_param_unit_class_init,
543         NULL, NULL,
544         sizeof (GtkParamSpecUnit),
545         0, NULL, NULL
546       };
547
548       spec_type = g_type_register_static (G_TYPE_PARAM_INT,
549                                           "GtkParamUnit",
550                                           &type_info, 0);
551     }
552
553   return spec_type;
554 }
555
556 static void
557 gtk_param_unit_class_init (GParamSpecClass *class)
558 {
559   class->value_type     = GTK_TYPE_UNIT;
560   class->value_validate = gtk_param_unit_value_validate;
561 }
562
563 static gboolean
564 gtk_param_unit_value_validate (GParamSpec *pspec,
565                                 GValue     *value)
566 {
567   GParamSpecInt     *ispec = G_PARAM_SPEC_INT (pspec);
568   GtkParamSpecUnit *uspec = GTK_PARAM_SPEC_UNIT (pspec);
569   gint               oval  = value->data[0].v_int;
570
571   if (uspec->allow_percent && value->data[0].v_int == CM_UNIT_PERCENT)
572     {
573       value->data[0].v_int = value->data[0].v_int;
574     }
575   else
576     {
577       value->data[0].v_int = CLAMP (value->data[0].v_int,
578                                     ispec->minimum,
579                                     gtk_unit_get_number_of_units () - 1);
580     }
581
582   return value->data[0].v_int != oval;
583 }
584
585 /**
586  * gtk_param_spec_unit:
587  * @name:          Canonical name of the param
588  * @nick:          Nickname of the param
589  * @blurb:         Brief desciption of param.
590  * @allow_pixels:  Whether "pixels" is an allowed unit.
591  * @allow_percent: Whether "perecent" is an allowed unit.
592  * @default_value: Unit to use if none is assigned.
593  * @flags:         a combination of #GParamFlags
594  *
595  * Creates a param spec to hold a units param.
596  * See g_param_spec_internal() for more information.
597  *
598  * Returns: a newly allocated #GParamSpec instance
599  *
600  * Since: GTK 2.4
601  **/
602 GParamSpec *
603 gtk_param_spec_unit (const gchar *name,
604                       const gchar *nick,
605                       const gchar *blurb,
606                       gboolean     allow_pixels,
607                       gboolean     allow_percent,
608                       GtkCMUnit     default_value,
609                       GParamFlags  flags)
610 {
611   GtkParamSpecUnit *pspec;
612   GParamSpecInt     *ispec;
613
614   pspec = g_param_spec_internal (GTK_TYPE_PARAM_UNIT,
615                                  name, nick, blurb, flags);
616
617   ispec = G_PARAM_SPEC_INT (pspec);
618
619   ispec->default_value = default_value;
620   ispec->minimum       = allow_pixels ? CM_UNIT_PIXEL : CM_UNIT_INCH;
621   ispec->maximum       = CM_UNIT_PERCENT - 1;
622
623   pspec->allow_percent = allow_percent;
624
625   return G_PARAM_SPEC (pspec);
626 }
627
628 /**
629  * gtk_pixels_to_units:
630  * @pixels:     value in pixels
631  * @unit:       unit to convert to
632  * @resolution: resloution in DPI
633  *
634  * Converts a @value specified in pixels to @unit.
635  *
636  * Returns: @pixels converted to units.
637  *
638  * Since: GTK 2.8
639  **/
640 gdouble
641 gtk_pixels_to_units (gdouble  pixels,
642                       GtkCMUnit unit,
643                       gdouble  resolution)
644 {
645   if (unit == CM_UNIT_PIXEL)
646     return pixels;
647
648   return pixels * gtk_unit_get_factor (unit) / resolution;
649 }
650
651 /**
652  * gtk_units_to_pixels:
653  * @value:      value in units
654  * @unit:       unit of @value
655  * @resolution: resloution in DPI
656  *
657  * Converts a @value specified in @unit to pixels.
658  *
659  * Returns: @value converted to pixels.
660  *
661  * Since: GTK 2.8
662  **/
663 gdouble
664 gtk_units_to_pixels (gdouble  value,
665                       GtkCMUnit unit,
666                       gdouble  resolution)
667 {
668   if (unit == CM_UNIT_PIXEL)
669     return value;
670
671   return value * resolution / gtk_unit_get_factor (unit);
672 }
673
674 /**
675  * gtk_units_to_points:
676  * @value:      value in units
677  * @unit:       unit of @value
678  * @resolution: resloution in DPI
679  *
680  * Converts a @value specified in @unit to points.
681  *
682  * Returns: @value converted to points.
683  *
684  * Since: GTK 2.8
685  **/
686 gdouble
687 gtk_units_to_points (gdouble  value,
688                       GtkCMUnit unit,
689                       gdouble  resolution)
690 {
691   if (unit == CM_UNIT_POINT)
692     return value;
693
694   if (unit == CM_UNIT_PIXEL)
695     return (value * gtk_unit_get_factor (CM_UNIT_POINT) / resolution);
696
697   return (value *
698           gtk_unit_get_factor (CM_UNIT_POINT) / gtk_unit_get_factor (unit));
699 }