Don't add "Go to last error" to filtering log window's context menu.
[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 #include "claws-features.h"
24
25 #include "gtkunit.h"
26
27 #include <string.h>
28
29 #include <glib-object.h>
30
31
32 GtkCMUnitVtable _gtk_unit_vtable = { NULL, };
33
34
35 void
36 gtk_base_init (GtkCMUnitVtable *vtable)
37 {
38   static gboolean gtk_base_initialized = FALSE;
39
40   g_return_if_fail (vtable != NULL);
41
42   if (gtk_base_initialized)
43     g_error ("gtk_base_init() must only be called once!");
44
45   _gtk_unit_vtable = *vtable;
46
47   gtk_base_initialized = TRUE;
48 }
49
50
51 /**
52  * SECTION: gimpunit
53  * @title: gimpunit
54  * @short_description: Provides a collection of predefined units and
55  *                     functions for creating user-defined units.
56  * @see_also: #GtkCMUnitMenu, #GtkSizeEntry.
57  *
58  * Provides a collection of predefined units and functions for
59  * creating user-defined units.
60  **/
61
62
63 static void   unit_to_string (const GValue *src_value,
64                               GValue       *dest_value);
65 static void   string_to_unit (const GValue *src_value,
66                               GValue       *dest_value);
67
68 GType
69 gtk_unit_get_type (void)
70 {
71   static GType unit_type = 0;
72
73   if (! unit_type)
74     {
75       const GTypeInfo type_info = { 0, };
76
77       unit_type = g_type_register_static (G_TYPE_INT, "GtkCMUnit",
78                                           &type_info, 0);
79
80       g_value_register_transform_func (unit_type, G_TYPE_STRING,
81                                        unit_to_string);
82       g_value_register_transform_func (G_TYPE_STRING, unit_type,
83                                        string_to_unit);
84     }
85
86   return unit_type;
87 }
88
89 static void
90 unit_to_string (const GValue *src_value,
91                 GValue       *dest_value)
92 {
93   GtkCMUnit unit = (GtkCMUnit) g_value_get_int (src_value);
94
95   g_value_set_string (dest_value, gtk_unit_get_identifier (unit));
96 }
97
98 static void
99 string_to_unit (const GValue *src_value,
100                 GValue       *dest_value)
101 {
102   const gchar *str;
103   gint         num_units;
104   gint         i;
105
106   str = g_value_get_string (src_value);
107
108   if (!str || !*str)
109     goto error;
110
111   num_units = gtk_unit_get_number_of_units ();
112
113   for (i = CM_UNIT_PIXEL; i < num_units; i++)
114     if (strcmp (str, gtk_unit_get_identifier (i)) == 0)
115       break;
116
117   if (i == num_units)
118     {
119       if (strcmp (str, gtk_unit_get_identifier (CM_UNIT_PERCENT)) == 0)
120         i = CM_UNIT_PERCENT;
121       else
122         goto error;
123     }
124
125   g_value_set_int (dest_value, i);
126   return;
127
128  error:
129   g_warning ("Can't convert string to GtkCMUnit.");
130 }
131
132
133 /**
134  * gtk_unit_get_number_of_units:
135  *
136  * Returns the number of units which are known to the #GtkCMUnit system.
137  *
138  * Returns: The number of defined units.
139  **/
140 gint
141 gtk_unit_get_number_of_units (void)
142 {
143   g_return_val_if_fail (_gtk_unit_vtable.unit_get_number_of_units != NULL,
144                         CM_UNIT_END);
145
146   return _gtk_unit_vtable.unit_get_number_of_units ();
147 }
148
149 /**
150  * gtk_unit_get_number_of_built_in_units:
151  *
152  * Returns the number of #GtkCMUnit's which are hardcoded in the unit system
153  * (UNIT_INCH, UNIT_MM, UNIT_POINT, UNIT_PICA and the two "pseudo unit"
154  *  UNIT_PIXEL).
155  *
156  * Returns: The number of built-in units.
157  **/
158 gint
159 gtk_unit_get_number_of_built_in_units (void)
160 {
161   g_return_val_if_fail (_gtk_unit_vtable.unit_get_number_of_built_in_units
162                         != NULL, CM_UNIT_END);
163
164   return _gtk_unit_vtable.unit_get_number_of_built_in_units ();
165 }
166
167 /**
168  * gtk_unit_new:
169  * @identifier: The unit's identifier string.
170  * @factor: The unit's factor (how many units are in one inch).
171  * @digits: The unit's suggested number of digits (see gtk_unit_get_digits()).
172  * @symbol: The symbol of the unit (e.g. "''" for inch).
173  * @abbreviation: The abbreviation of the unit.
174  * @singular: The singular form of the unit.
175  * @plural: The plural form of the unit.
176  *
177  * Returns the integer ID of the new #GtkCMUnit.
178  *
179  * Note that a new unit is always created with it's deletion flag
180  * set to %TRUE. You will have to set it to %FALSE with
181  * gtk_unit_set_deletion_flag() to make the unit definition persistent.
182  *
183  * Returns: The ID of the new unit.
184  **/
185 GtkCMUnit
186 gtk_unit_new (gchar   *identifier,
187                gdouble  factor,
188                gint     digits,
189                gchar   *symbol,
190                gchar   *abbreviation,
191                gchar   *singular,
192                gchar   *plural)
193 {
194   g_return_val_if_fail (_gtk_unit_vtable.unit_new != NULL, CM_UNIT_INCH);
195
196   return _gtk_unit_vtable.unit_new (identifier, factor, digits,
197                                      symbol, abbreviation, singular, plural);
198 }
199
200 /**
201  * gtk_unit_get_deletion_flag:
202  * @unit: The unit you want to know the @deletion_flag of.
203  *
204  * Returns: The unit's @deletion_flag.
205  **/
206 gboolean
207 gtk_unit_get_deletion_flag (GtkCMUnit unit)
208 {
209   g_return_val_if_fail (_gtk_unit_vtable.unit_get_deletion_flag != NULL, FALSE);
210
211   return _gtk_unit_vtable.unit_get_deletion_flag (unit);
212 }
213
214 /**
215  * gtk_unit_set_deletion_flag:
216  * @unit: The unit you want to set the @deletion_flag for.
217  * @deletion_flag: The new deletion_flag.
218  *
219  * Sets a #GtkCMUnit's @deletion_flag. If the @deletion_flag of a unit is
220  * %TRUE when GTK exits, this unit will not be saved in the users's
221  * "unitrc" file.
222  *
223  * Trying to change the @deletion_flag of a built-in unit will be silently
224  * ignored.
225  **/
226 void
227 gtk_unit_set_deletion_flag (GtkCMUnit unit,
228                              gboolean deletion_flag)
229 {
230   g_return_if_fail (_gtk_unit_vtable.unit_set_deletion_flag != NULL);
231
232   _gtk_unit_vtable.unit_set_deletion_flag (unit, deletion_flag);
233 }
234
235 /**
236  * gtk_unit_get_factor:
237  * @unit: The unit you want to know the factor of.
238  *
239  * A #GtkCMUnit's @factor is defined to be:
240  *
241  * distance_in_units == (@factor * distance_in_inches)
242  *
243  * Returns 0 for @unit == CM_UNIT_PIXEL.
244  *
245  * Returns: The unit's factor.
246  **/
247 gdouble
248 gtk_unit_get_factor (GtkCMUnit unit)
249 {
250   g_return_val_if_fail (_gtk_unit_vtable.unit_get_factor != NULL, 1.0);
251
252   return _gtk_unit_vtable.unit_get_factor (unit);
253 }
254
255 /**
256  * gtk_unit_get_digits:
257  * @unit: The unit you want to know the digits.
258  *
259  * Returns the number of digits an entry field should provide to get
260  * approximately the same accuracy as an inch input field with two digits.
261  *
262  * Returns 0 for @unit == CM_UNIT_PIXEL.
263  *
264  * Returns: The suggested number of digits.
265  **/
266 gint
267 gtk_unit_get_digits (GtkCMUnit unit)
268 {
269   g_return_val_if_fail (_gtk_unit_vtable.unit_get_digits != NULL, 2);
270
271   return _gtk_unit_vtable.unit_get_digits (unit);
272 }
273
274 /**
275  * gtk_unit_get_identifier:
276  * @unit: The unit you want to know the identifier of.
277  *
278  * This is an unstranslated string and must not be changed or freed.
279  *
280  * Returns: The unit's identifier.
281  **/
282 const gchar *
283 gtk_unit_get_identifier (GtkCMUnit unit)
284 {
285   g_return_val_if_fail (_gtk_unit_vtable.unit_get_identifier != NULL, NULL);
286
287   return _gtk_unit_vtable.unit_get_identifier (unit);
288 }
289
290 /**
291  * gtk_unit_get_symbol:
292  * @unit: The unit you want to know the symbol of.
293  *
294  * This is e.g. "''" for UNIT_INCH.
295  *
296  * NOTE: This string must not be changed or freed.
297  *
298  * Returns: The unit's symbol.
299  **/
300 const gchar *
301 gtk_unit_get_symbol (GtkCMUnit unit)
302 {
303   g_return_val_if_fail (_gtk_unit_vtable.unit_get_symbol != NULL, NULL);
304
305   return _gtk_unit_vtable.unit_get_symbol (unit);
306 }
307
308 /**
309  * gtk_unit_get_abbreviation:
310  * @unit: The unit you want to know the abbreviation of.
311  *
312  * For built-in units, this function returns the translated abbreviation
313  * of the unit.
314  *
315  * NOTE: This string must not be changed or freed.
316  *
317  * Returns: The unit's abbreviation.
318  **/
319 const gchar *
320 gtk_unit_get_abbreviation (GtkCMUnit unit)
321 {
322   g_return_val_if_fail (_gtk_unit_vtable.unit_get_abbreviation != NULL, NULL);
323
324   return _gtk_unit_vtable.unit_get_abbreviation (unit);
325 }
326
327 /**
328  * gtk_unit_get_singular:
329  * @unit: The unit you want to know the singular form of.
330  *
331  * For built-in units, this function returns the translated singular form
332  * of the unit's name.
333  *
334  * NOTE: This string must not be changed or freed.
335  *
336  * Returns: The unit's singular form.
337  **/
338 const gchar *
339 gtk_unit_get_singular (GtkCMUnit unit)
340 {
341   g_return_val_if_fail (_gtk_unit_vtable.unit_get_singular != NULL, NULL);
342
343   return _gtk_unit_vtable.unit_get_singular (unit);
344 }
345
346 /**
347  * gtk_unit_get_plural:
348  * @unit: The unit you want to know the plural form of.
349  *
350  * For built-in units, this function returns the translated plural form
351  * of the unit's name.
352  *
353  * NOTE: This string must not be changed or freed.
354  *
355  * Returns: The unit's plural form.
356  **/
357 const gchar *
358 gtk_unit_get_plural (GtkCMUnit unit)
359 {
360   g_return_val_if_fail (_gtk_unit_vtable.unit_get_plural != NULL, NULL);
361
362   return _gtk_unit_vtable.unit_get_plural (unit);
363 }
364
365 static gint
366 print (gchar       *buf,
367        gint         len,
368        gint         start,
369        const gchar *fmt,
370        ...)
371 {
372   va_list args;
373   gint printed;
374
375   va_start (args, fmt);
376
377   printed = g_vsnprintf (buf + start, len - start, fmt, args);
378   if (printed < 0)
379     printed = len - start;
380
381   va_end (args);
382
383   return printed;
384 }
385
386 /**
387  * gtk_unit_format_string:
388  * @format: A printf-like format string which is used to create the unit
389  *          string.
390  * @unit:   A unit.
391  *
392  * The @format string supports the following percent expansions:
393  *
394  * <informaltable pgwide="1" frame="none" role="enum">
395  *   <tgroup cols="2"><colspec colwidth="1*"/><colspec colwidth="8*"/>
396  *     <tbody>
397  *       <row>
398  *         <entry>% f</entry>
399  *         <entry>Factor (how many units make up an inch)</entry>
400  *        </row>
401  *       <row>
402  *         <entry>% y</entry>
403  *         <entry>Symbol (e.g. "''" for CM_UNIT_INCH)</entry>
404  *       </row>
405  *       <row>
406  *         <entry>% a</entry>
407  *         <entry>Abbreviation</entry>
408  *       </row>
409  *       <row>
410  *         <entry>% s</entry>
411  *         <entry>Singular</entry>
412  *       </row>
413  *       <row>
414  *         <entry>% p</entry>
415  *         <entry>Plural</entry>
416  *       </row>
417  *       <row>
418  *         <entry>%%</entry>
419  *         <entry>Literal percent</entry>
420  *       </row>
421  *     </tbody>
422  *   </tgroup>
423  * </informaltable>
424  *
425  * Returns: A newly allocated string with above percent expressions
426  *          replaced with the resp. strings for @unit.
427  *
428  * Since: GTK 2.8
429  **/
430 gchar *
431 gtk_unit_format_string (const gchar *format,
432                          GtkCMUnit     unit)
433 {
434   gchar buffer[1024];
435   gint  i = 0;
436
437   g_return_val_if_fail (format != NULL, NULL);
438   g_return_val_if_fail (unit == CM_UNIT_PERCENT ||
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       value->data[0].v_int = CLAMP (value->data[0].v_int,
573                                     ispec->minimum,
574                                     gtk_unit_get_number_of_units () - 1);
575   }
576
577   return value->data[0].v_int != oval;
578 }
579
580 /**
581  * gtk_param_spec_unit:
582  * @name:          Canonical name of the param
583  * @nick:          Nickname of the param
584  * @blurb:         Brief desciption of param.
585  * @allow_pixels:  Whether "pixels" is an allowed unit.
586  * @allow_percent: Whether "perecent" is an allowed unit.
587  * @default_value: Unit to use if none is assigned.
588  * @flags:         a combination of #GParamFlags
589  *
590  * Creates a param spec to hold a units param.
591  * See g_param_spec_internal() for more information.
592  *
593  * Returns: a newly allocated #GParamSpec instance
594  *
595  * Since: GTK 2.4
596  **/
597 GParamSpec *
598 gtk_param_spec_unit (const gchar *name,
599                       const gchar *nick,
600                       const gchar *blurb,
601                       gboolean     allow_pixels,
602                       gboolean     allow_percent,
603                       GtkCMUnit     default_value,
604                       GParamFlags  flags)
605 {
606   GtkParamSpecUnit *pspec;
607   GParamSpecInt     *ispec;
608
609   pspec = g_param_spec_internal (GTK_TYPE_PARAM_UNIT,
610                                  name, nick, blurb, flags);
611
612   ispec = G_PARAM_SPEC_INT (pspec);
613
614   ispec->default_value = default_value;
615   ispec->minimum       = allow_pixels ? CM_UNIT_PIXEL : CM_UNIT_INCH;
616   ispec->maximum       = CM_UNIT_PERCENT - 1;
617
618   pspec->allow_percent = allow_percent;
619
620   return G_PARAM_SPEC (pspec);
621 }
622
623 /**
624  * gtk_pixels_to_units:
625  * @pixels:     value in pixels
626  * @unit:       unit to convert to
627  * @resolution: resloution in DPI
628  *
629  * Converts a @value specified in pixels to @unit.
630  *
631  * Returns: @pixels converted to units.
632  *
633  * Since: GTK 2.8
634  **/
635 gdouble
636 gtk_pixels_to_units (gdouble  pixels,
637                       GtkCMUnit unit,
638                       gdouble  resolution)
639 {
640   if (unit == CM_UNIT_PIXEL)
641     return pixels;
642
643   return pixels * gtk_unit_get_factor (unit) / resolution;
644 }
645
646 /**
647  * gtk_units_to_pixels:
648  * @value:      value in units
649  * @unit:       unit of @value
650  * @resolution: resloution in DPI
651  *
652  * Converts a @value specified in @unit to pixels.
653  *
654  * Returns: @value converted to pixels.
655  *
656  * Since: GTK 2.8
657  **/
658 gdouble
659 gtk_units_to_pixels (gdouble  value,
660                       GtkCMUnit unit,
661                       gdouble  resolution)
662 {
663   if (unit == CM_UNIT_PIXEL)
664     return value;
665
666   return value * resolution / gtk_unit_get_factor (unit);
667 }
668
669 /**
670  * gtk_units_to_points:
671  * @value:      value in units
672  * @unit:       unit of @value
673  * @resolution: resloution in DPI
674  *
675  * Converts a @value specified in @unit to points.
676  *
677  * Returns: @value converted to points.
678  *
679  * Since: GTK 2.8
680  **/
681 gdouble
682 gtk_units_to_points (gdouble  value,
683                       GtkCMUnit unit,
684                       gdouble  resolution)
685 {
686   if (unit == CM_UNIT_POINT)
687     return value;
688
689   if (unit == CM_UNIT_PIXEL)
690     return (value * gtk_unit_get_factor (CM_UNIT_POINT) / resolution);
691
692   return (value *
693           gtk_unit_get_factor (CM_UNIT_POINT) / gtk_unit_get_factor (unit));
694 }