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