Fix lockup when a scroll event is received outside the drawing area
[geda-pcb/pcjc2.git] / src / pcb-printf.c
blobe53df6405befca482a3c95a8caafc3b1f444b048
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 2011 Andrew Poelstra
7 * This program 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 2 of the License, or
10 * (at your option) any later version.
12 * This program 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 this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
23 * asp11@sfu.ca
27 /*! \file <pcb-printf.c>
28 * \brief Implementation of printf wrapper to output pcb coords and angles
29 * \par Description
30 * For details of all supported specifiers, see the comment at the
31 * top of pcb-printf.h
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
38 #include "global.h"
40 #include "pcb-printf.h"
42 /* Helper macros for tables */
43 #define MM_TO_COORD3(a,b,c) MM_TO_COORD (a), MM_TO_COORD (b), MM_TO_COORD (c)
44 #define MIL_TO_COORD3(a,b,c) MIL_TO_COORD (a), MIL_TO_COORD (b), MIL_TO_COORD (c)
45 #define MM_TO_COORD5(a,b,c,d,e) MM_TO_COORD (a), MM_TO_COORD (b), MM_TO_COORD (c), \
46 MM_TO_COORD (d), MM_TO_COORD (e)
47 #define MIL_TO_COORD5(a,b,c,d,e) MIL_TO_COORD (a), MIL_TO_COORD (b), MIL_TO_COORD (c), \
48 MIL_TO_COORD (d), MIL_TO_COORD (e)
50 /* These should be kept in order of smallest scale_factor
51 * to largest -- the code uses this ordering when finding
52 * the best scale to use for a group of measures. */
54 static Unit Units[] = {
55 { 0, "km", NULL, 'k', 0.000001, METRIC, ALLOW_KM, 5,
56 0.00005, 0.0005, 0.0025, 0.05, 0.25,
57 { "" } },
58 { 0, "m", NULL, 'f', 0.001, METRIC, ALLOW_M, 5,
59 0.0005, 0.005, 0.025, 0.5, 2.5,
60 { "" } },
61 { 0, "cm", NULL, 'e', 0.1, METRIC, ALLOW_CM, 5,
62 0.005, 0.05, 0.25, 5, 25,
63 { "" } },
64 { 0, "mm", NULL, 'm', 1, METRIC, ALLOW_MM, 4,
65 0.005, 0.05, 0.25, 5, 25,
66 { "" } },
67 { 0, "um", NULL, 'u', 1000, METRIC, ALLOW_UM, 2,
68 0.005, 0.05, 0.25, 5, 25,
69 { "" } },
70 { 0, "nm", NULL, 'n', 1000000, METRIC, ALLOW_NM, 0,
71 5, 50, 2500, 5000, 25000,
72 { "" } },
73 /* Hack: Pixels get parsed like nanometers. If the value of the
74 * resulting integer is sufficiently small, the code interprets
75 * it a screen pixels. This affects rat thickness. */
76 { 0, "px", NULL, 'n', 1000000, METRIC, ALLOW_NM, 0,
77 5, 50, 2500, 5000, 25000,
78 { "" } },
81 { 0, "in", NULL, 'i', 0.001, IMPERIAL, ALLOW_IN, 5,
82 0.1, 1.0, 5.0, 25, 100,
83 { "inch" } },
84 { 0, "mil", NULL, 'l', 1, IMPERIAL, ALLOW_MIL, 2,
85 0.1, 1.0, 10, 100, 1000,
86 { "" } },
87 { 0, "dmil", NULL, 't', 10, IMPERIAL, ALLOW_DMIL, 0,
88 1, 10, 100, 1000, 10000,
89 { "" } },
90 { 0, "cmil", NULL, 'c', 100, IMPERIAL, ALLOW_CMIL, 0,
91 1, 10, 100, 1000, 10000,
92 { "pcb" } }
94 #define N_UNITS ((int) (sizeof Units / sizeof Units[0]))
95 /* \brief Initialize non-static data for pcb-printf
96 * \par Function Description
97 * Assigns each unit its index for quick access through the
98 * main units array, and internationalize the units for GUI
99 * display.
101 void initialize_units()
103 int i;
104 for (i = 0; i < N_UNITS; ++i)
106 Units[i].index = i;
107 Units[i].in_suffix = _(Units[i].suffix);
111 /* TABLE FORMAT | default | min | max
112 * grid | | |
113 * size | | |
114 * line | | |
115 * clear | | |
117 static Increments increments_metric = {
118 "mm",
119 MM_TO_COORD3 (0.1, 0.01, 1.0),
120 MM_TO_COORD3 (0.2, 0.01, 0.5),
121 MM_TO_COORD3 (0.1, 0.005, 0.5),
122 MM_TO_COORD3 (0.05, 0.005, 0.5)
124 static Increments increments_imperial = {
125 "mil",
126 MIL_TO_COORD3 (5, 1, 25),
127 MIL_TO_COORD3 (10, 1, 10),
128 MIL_TO_COORD3 (5, 0.5, 10),
129 MIL_TO_COORD3 (2, 0.5, 10)
132 /* \brief Obtain a unit object from its suffix
133 * \par Function Description
134 * Looks up a given suffix in the main units array. Internationalized
135 * unit suffixes are not supported, though pluralized units are, for
136 * backward-compatibility.
138 * \param [in] const_suffix The suffix to look up
140 * \return A const pointer to the Unit struct, or NULL if none was found
142 const Unit *get_unit_struct (const char *const_suffix)
144 int i;
145 int s_len = 0;
146 /* Turn given suffix into something we can modify... */
147 char *m_suffix = g_strdup (const_suffix);
148 /* ...and store this in a pointer we can move. */
149 char *suffix = m_suffix;
151 /* Determine bounds */
152 while (isspace (*suffix))
153 ++suffix;
154 while (isalnum (suffix[s_len]))
155 ++s_len;
157 /* Also understand plural suffixes: "inches", "mils" */
158 if (s_len > 2)
160 if (suffix[s_len - 2] == 'e' && suffix[s_len - 1] == 's')
161 suffix[s_len - 2] = 0;
162 else if (suffix[s_len - 1] == 's')
163 suffix[s_len - 1] = 0;
166 /* Do lookup */
167 if (*suffix && s_len > 0)
168 for (i = 0; i < N_UNITS; ++i)
169 if (strncmp (suffix, Units[i].suffix, s_len) == 0 ||
170 strncmp (suffix, Units[i].alias[0], s_len) == 0)
172 g_free (m_suffix);
173 return &Units[i];
175 g_free (m_suffix);
176 return NULL;
179 void copy_nonzero_increments (Increments *dst, const Increments *src)
181 if (src->grid >= dst->grid_min && src->grid <= dst->grid_max)
182 dst->grid = src->grid;
183 if (src->line >= dst->line_min && src->line <= dst->line_max)
184 dst->line = src->line;
185 if (src->size >= dst->size_min && src->size <= dst->size_max)
186 dst->size = src->size;
187 if (src->clear >= dst->clear_min && src->clear <= dst->clear_max)
188 dst->clear = src->clear;
191 /* ACCESSORS */
192 /* \brief Returns the master unit list. This may not be modified. */
193 const Unit *get_unit_list (void)
195 return Units;
197 /* \brief Returns the length of the master unit list. */
198 int get_n_units (void)
200 return N_UNITS;
203 /* \brief Obtain the increment values for a given family of units
204 * \par Function Description
206 * \param [in] family One of METRIC or IMPERIAL.
208 * \return A pointer to the appropriate increments structure.
210 Increments *get_increments_struct (enum e_family family)
212 switch (family)
214 case METRIC:
215 return &increments_metric;
216 case IMPERIAL:
217 return &increments_imperial;
219 return NULL;
222 /* \brief Convert a pcb coord to the given unit
224 * \param [in] unit The unit to convert to
225 * \param [in] x The quantity to convert
227 * \return The converted measure
229 double coord_to_unit (const Unit *unit, Coord x)
231 double base;
232 if (unit == NULL)
233 return -1;
234 base = unit->family == METRIC
235 ? COORD_TO_MM (1)
236 : COORD_TO_MIL (1);
237 return x * unit->scale_factor * base;
240 /* \brief Convert a given unit to pcb coords
242 * \param [in] unit The unit to convert from
243 * \param [in] x The quantity to convert
245 * \return The converted measure
247 Coord unit_to_coord (const Unit *unit, double x)
249 return x / coord_to_unit (unit, 1);
252 static int min_sig_figs(double d)
254 char buf[50];
255 int rv;
257 if(d == 0) return 0;
259 /* Normalize to x.xxxx... form */
260 if(d < 0) d *= -1;
261 while(d >= 10) d /= 10;
262 while(d < 1) d *= 10;
264 rv = sprintf(buf, "%g", d);
265 return rv;
268 /* \brief Internal coord-to-string converter for pcb-printf
269 * \par Function Description
270 * Converts a (group of) measurement(s) to a comma-deliminated
271 * string, with appropriate units. If more than one coord is
272 * given, the list is enclosed in parens to make the scope of
273 * the unit suffix clear.
275 * \param [in] coord Array of coords to convert
276 * \param [in] n_coords Number of coords in array
277 * \param [in] printf_spec printf sub-specifier to use with %f
278 * \param [in] e_allow Bitmap of units the function may use
279 * \param [in] suffix_type Whether to add a suffix
281 * \return A string containing the formatted coords. Must be freed with g_free.
283 static gchar *CoordsToString(Coord coord[], int n_coords, const char *printf_spec, enum e_allow allow, enum e_suffix suffix_type)
285 GString *buff;
286 gchar *printf_buff;
287 gchar filemode_buff[G_ASCII_DTOSTR_BUF_SIZE];
288 enum e_family family;
289 double *value;
290 const char *suffix;
291 int i, n;
293 value = malloc (n_coords * sizeof *value);
294 buff = g_string_new ("");
296 /* Sanity checks */
297 if (buff == NULL || value == NULL)
298 return NULL;
299 if (allow == 0)
300 allow = ALLOW_ALL;
301 if (printf_spec == NULL)
302 printf_spec = "";
304 /* Check our freedom in choosing units */
305 if ((allow & ALLOW_IMPERIAL) == 0)
306 family = METRIC;
307 else if ((allow & ALLOW_METRIC) == 0)
308 family = IMPERIAL;
309 else
311 int met_votes = 0,
312 imp_votes = 0;
314 for (i = 0; i < n_coords; ++i)
315 if(min_sig_figs(COORD_TO_MIL(coord[i])) < min_sig_figs(COORD_TO_MM(coord[i])))
316 ++imp_votes;
317 else
318 ++met_votes;
320 if (imp_votes > met_votes)
321 family = IMPERIAL;
322 else
323 family = METRIC;
326 /* Set base unit */
327 for (i = 0; i < n_coords; ++i)
329 switch (family)
331 case METRIC: value[i] = COORD_TO_MM (coord[i]); break;
332 case IMPERIAL: value[i] = COORD_TO_MIL (coord[i]); break;
336 /* Determine scale factor -- find smallest unit that brings
337 * the whole group above unity */
338 for (n = 0; n < N_UNITS; ++n)
340 if ((Units[n].allow & allow) != 0 && (Units[n].family == family))
342 int n_above_one = 0;
344 for (i = 0; i < n_coords; ++i)
345 if (fabs(value[i] * Units[n].scale_factor) > 1)
346 ++n_above_one;
347 if (n_above_one == n_coords)
348 break;
351 /* If nothing worked, wind back to the smallest allowable unit */
352 if (n == N_UNITS)
354 do {
355 --n;
356 } while ((Units[n].allow & allow) == 0 || Units[n].family != family);
359 /* Apply scale factor */
360 suffix = Units[n].suffix;
361 for (i = 0; i < n_coords; ++i)
362 value[i] = value[i] * Units[n].scale_factor;
364 /* Create sprintf specifier, using default_prec no precision is given */
365 i = 0;
366 while (printf_spec[i] == '%' || isdigit(printf_spec[i]) ||
367 printf_spec[i] == '-' || printf_spec[i] == '+' ||
368 printf_spec[i] == '#')
369 ++i;
370 if (printf_spec[i] == '.')
371 printf_buff = g_strdup_printf (", %sf", printf_spec);
372 else
373 printf_buff = g_strdup_printf (", %s.%df", printf_spec, Units[n].default_prec);
375 /* Actually sprintf the values in place
376 * (+ 2 skips the ", " for first value) */
377 if (n_coords > 1)
378 g_string_append_c (buff, '(');
379 if (suffix_type == FILE_MODE || suffix_type == FILE_MODE_NO_SUFFIX)
381 g_ascii_formatd (filemode_buff, sizeof filemode_buff, printf_buff + 2, value[0]);
382 g_string_append_printf (buff, "%s", filemode_buff);
384 else
385 g_string_append_printf (buff, printf_buff + 2, value[0]);
386 for (i = 1; i < n_coords; ++i)
388 if (suffix_type == FILE_MODE || suffix_type == FILE_MODE_NO_SUFFIX)
390 g_ascii_formatd (filemode_buff, sizeof filemode_buff, printf_buff, value[i]);
391 g_string_append_printf (buff, "%s", filemode_buff);
393 else
394 g_string_append_printf (buff, printf_buff, value[i]);
396 if (n_coords > 1)
397 g_string_append_c (buff, ')');
398 /* Append suffix */
399 if (value[0] != 0 || n_coords > 1)
401 switch (suffix_type)
403 case NO_SUFFIX:
404 case FILE_MODE_NO_SUFFIX:
405 break;
406 case SUFFIX:
407 g_string_append_printf (buff, " %s", suffix);
408 break;
409 case FILE_MODE:
410 g_string_append_printf (buff, "%s", suffix);
411 break;
415 g_free (printf_buff);
416 free (value);
417 /* Return just the gchar* part of our string */
418 return g_string_free (buff, FALSE);
421 /* \brief Main pcb-printf function
422 * \par Function Description
423 * This is a printf wrapper that accepts new format specifiers to
424 * output pcb coords as various units. See the comment at the top
425 * of pcb-printf.h for full details.
427 * \param [in] fmt Format specifier
428 * \param [in] args Arguments to specifier
430 * \return A formatted string. Must be freed with g_free.
432 gchar *pcb_vprintf(const char *fmt, va_list args)
434 GString *string = g_string_new ("");
435 GString *spec = g_string_new ("");
437 enum e_allow mask = ALLOW_ALL;
439 if (string == NULL || spec == NULL)
440 return NULL;
442 while(*fmt)
444 enum e_suffix suffix = NO_SUFFIX;
446 if(*fmt == '%')
448 gchar *unit_str = NULL;
449 const char *ext_unit = "";
450 Coord value[10];
451 int count, i;
453 g_string_assign (spec, "");
455 /* Get printf sub-specifiers */
456 g_string_append_c (spec, *fmt++);
457 while(isdigit(*fmt) || *fmt == '.' || *fmt == ' ' || *fmt == '*'
458 || *fmt == '#' || *fmt == 'l' || *fmt == 'L'
459 || *fmt == 'h' || *fmt == '+' || *fmt == '-')
461 if (*fmt == '*')
463 g_string_append_printf (spec, "%d", va_arg (args, int));
464 fmt++;
466 else
467 g_string_append_c (spec, *fmt++);
469 /* Get our sub-specifiers */
470 if(*fmt == '#')
472 mask = ALLOW_CMIL; /* This must be pcb's base unit */
473 fmt++;
475 if(*fmt == '$')
477 suffix = (suffix == NO_SUFFIX) ? SUFFIX : FILE_MODE;
478 fmt++;
480 if(*fmt == '`')
482 suffix = (suffix == SUFFIX) ? FILE_MODE : FILE_MODE_NO_SUFFIX;
483 fmt++;
485 /* Tack full specifier onto specifier */
486 if (*fmt != 'm')
487 g_string_append_c (spec, *fmt);
488 switch(*fmt)
490 /* Printf specs */
491 case 'o': case 'i': case 'd':
492 case 'u': case 'x': case 'X':
493 if(spec->str[1] == 'l')
495 if(spec->str[2] == 'l')
496 unit_str = g_strdup_printf (spec->str, va_arg(args, long long));
497 else
498 unit_str = g_strdup_printf (spec->str, va_arg(args, long));
500 else
502 unit_str = g_strdup_printf (spec->str, va_arg(args, int));
504 break;
505 case 'e': case 'E': case 'f':
506 case 'g': case 'G':
507 if (strchr (spec->str, '*'))
509 int prec = va_arg(args, int);
510 unit_str = g_strdup_printf (spec->str, va_arg(args, double), prec);
512 else
513 unit_str = g_strdup_printf (spec->str, va_arg(args, double));
514 break;
515 case 'c':
516 if(spec->str[1] == 'l' && sizeof(int) <= sizeof(wchar_t))
517 unit_str = g_strdup_printf (spec->str, va_arg(args, wchar_t));
518 else
519 unit_str = g_strdup_printf (spec->str, va_arg(args, int));
520 break;
521 case 's':
522 if(spec->str[0] == 'l')
523 unit_str = g_strdup_printf (spec->str, va_arg(args, wchar_t *));
524 else
525 unit_str = g_strdup_printf (spec->str, va_arg(args, char *));
526 break;
527 case 'n':
528 /* Depending on gcc settings, this will probably break with
529 * some silly "can't put %n in writeable data space" message */
530 unit_str = g_strdup_printf (spec->str, va_arg(args, int *));
531 break;
532 case 'p':
533 unit_str = g_strdup_printf (spec->str, va_arg(args, void *));
534 break;
535 case '%':
536 g_string_append_c (string, '%');
537 break;
538 /* Our specs */
539 case 'm':
540 ++fmt;
541 if (*fmt == '*')
542 ext_unit = va_arg(args, const char *);
543 if (*fmt != '+' && *fmt != 'a')
544 value[0] = va_arg(args, Coord);
545 count = 1;
546 switch(*fmt)
548 case 's': unit_str = CoordsToString(value, 1, spec->str, ALLOW_MM | ALLOW_MIL, suffix); break;
549 case 'S': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_ALL, suffix); break;
550 case 'M': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_METRIC, suffix); break;
551 case 'L': unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_IMPERIAL, suffix); break;
552 case 'r': unit_str = CoordsToString(value, 1, spec->str, ALLOW_READABLE, FILE_MODE); break;
553 /* All these fallthroughs are deliberate */
554 case '9': value[count++] = va_arg(args, Coord);
555 case '8': value[count++] = va_arg(args, Coord);
556 case '7': value[count++] = va_arg(args, Coord);
557 case '6': value[count++] = va_arg(args, Coord);
558 case '5': value[count++] = va_arg(args, Coord);
559 case '4': value[count++] = va_arg(args, Coord);
560 case '3': value[count++] = va_arg(args, Coord);
561 case '2':
562 case 'D':
563 value[count++] = va_arg(args, Coord);
564 unit_str = CoordsToString(value, count, spec->str, mask & ALLOW_ALL, suffix);
565 break;
566 case 'd':
567 value[1] = va_arg(args, Coord);
568 unit_str = CoordsToString(value, 2, spec->str, ALLOW_MM | ALLOW_MIL, suffix);
569 break;
570 case '*':
571 for (i = 0; i < N_UNITS; ++i)
572 if (strcmp (ext_unit, Units[i].suffix) == 0)
573 unit_str = CoordsToString(value, 1, spec->str, Units[i].allow, suffix);
574 if (unit_str == NULL)
575 unit_str = CoordsToString(value, 1, spec->str, mask & ALLOW_ALL, suffix);
576 break;
577 case 'a':
578 g_string_append (spec, ".0f");
579 if (suffix == SUFFIX)
580 g_string_append (spec, " deg");
581 unit_str = g_strdup_printf (spec->str, (double) va_arg(args, Angle));
582 break;
583 case '+':
584 mask = va_arg(args, enum e_allow);
585 break;
586 default:
587 for (i = 0; i < N_UNITS; ++i)
588 if (*fmt == Units[i].printf_code)
589 unit_str = CoordsToString(value, 1, spec->str, Units[i].allow, suffix);
590 if (unit_str == NULL)
591 unit_str = CoordsToString(value, 1, spec->str, ALLOW_ALL, suffix);
592 break;
594 break;
596 if (unit_str != NULL)
598 g_string_append (string, unit_str);
599 g_free (unit_str);
602 else
603 g_string_append_c (string, *fmt);
604 ++fmt;
606 g_string_free (spec, TRUE);
607 /* Return just the gchar* part of our string */
608 return g_string_free (string, FALSE);
612 /* \brief Wrapper for pcb_vprintf that outputs to a string
614 * \param [in] string Pointer to string to output into
615 * \param [in] fmt Format specifier
617 * \return The length of the written string
619 int pcb_sprintf(char *string, const char *fmt, ...)
621 gchar *tmp;
623 va_list args;
624 va_start(args, fmt);
626 tmp = pcb_vprintf (fmt, args);
627 strcpy (string, tmp);
628 g_free (tmp);
630 va_end(args);
631 return strlen (string);
634 /* \brief Wrapper for pcb_vprintf that outputs to a file
636 * \param [in] fh File to output to
637 * \param [in] fmt Format specifier
639 * \return The length of the written string
641 int pcb_fprintf(FILE *fh, const char *fmt, ...)
643 int rv;
644 gchar *tmp;
646 va_list args;
647 va_start(args, fmt);
649 if (fh == NULL)
650 rv = -1;
651 else
653 tmp = pcb_vprintf (fmt, args);
654 rv = fprintf (fh, "%s", tmp);
655 g_free (tmp);
658 va_end(args);
659 return rv;
662 /* \brief Wrapper for pcb_vprintf that outputs to stdout
664 * \param [in] fmt Format specifier
666 * \return The length of the written string
668 int pcb_printf(const char *fmt, ...)
670 int rv;
671 gchar *tmp;
673 va_list args;
674 va_start(args, fmt);
676 tmp = pcb_vprintf (fmt, args);
677 rv = printf ("%s", tmp);
678 g_free (tmp);
680 va_end(args);
681 return rv;
684 /* \brief Wrapper for pcb_vprintf that outputs to a newly allocated string
686 * \param [in] fmt Format specifier
688 * \return The newly allocated string. Must be freed with g_free.
690 char *pcb_g_strdup_printf(const char *fmt, ...)
692 gchar *tmp;
694 va_list args;
695 va_start(args, fmt);
696 tmp = pcb_vprintf (fmt, args);
697 va_end(args);
698 return tmp;