1 /*
2   tuxpaint.c
3 
4   Tux Paint - A simple drawing program for children.
5 
6   Copyright (c) 2002-2021
7   by various contributors; see AUTHORS.txt
8   http://www.tuxpaint.org/
9 
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   (See COPYING.txt)
24 
25   June 14, 2002 - April 19, 2021
26 */
27 
28 #include "platform.h"
29 
30 /* (Note: VER_VERSION and VER_DATE are now handled by Makefile) */
31 
32 
33 /* FIXME: */
34 
35 /* Use this in places where we can only (or only want to, for whatever reason)
36    use 'unlink()' to delete files, rather than trying to put them in the
37    desktop enivronment's "trash" -bjk 2011.04.18 */
38 /* #define UNLINK_ONLY */
39 
40 /* Color depth for Tux Paint to run in, and store canvases in: */
41 
42 /* *INDENT-OFF* */
43 
44 #if defined(NOKIA_770)
45 #define VIDEO_BPP 16
46 #endif
47 
48 #if defined(OLPC_XO)
49 #define VIDEO_BPP 15
50 #endif
51 
52 #ifndef VIDEO_BPP
53     /*# define VIDEO_BPP 15 *//* saves memory */
54     /*# define VIDEO_BPP 16 *//* causes discoloration */
55     /*# define VIDEO_BPP 24 *//* compromise */
56     #define VIDEO_BPP 32      /* might be fastest, if conversion funcs removed */
57 #endif
58 
59 /* Method for printing images: */
60 
61 #define PRINTMETHOD_PS          /* Direct to PostScript */
62 /*#define PRINTMETHOD_PNM_PS *//* Output PNM, assuming it gets printed */
63 /*#define PRINTMETHOD_PNG_PNM_PS *//* Output PNG, assuming it gets printed */
64 
65 #define MAX_PATH 256
66 
67 
68 /* Compile-time options: */
69 
70 #include "debug.h"
71 
72 #ifdef NOKIA_770
73 #define LOW_QUALITY_THUMBNAILS
74 #define LOW_QUALITY_STAMP_OUTLINE
75 #define NO_PROMPT_SHADOWS
76 #define USE_HWSURFACE
77 #else
78 /* #define DEBUG_MALLOC */
79 /* #define LOW_QUALITY_THUMBNAILS */
80 /* #define LOW_QUALITY_COLOR_SELECTOR */
81 /* #define LOW_QUALITY_STAMP_OUTLINE */
82 /* #define NO_PROMPT_SHADOWS */
83 /* #define USE_HWSURFACE */
84 
85 /* FIXME: Deal with this option properly -bjk 2010.02.25 */
86 #define GAMMA_CORRECTED_THUMBNAILS
87 #endif
88 
89 #define ALLOW_STAMP_OVERSCAN
90 
91 
92 /* Disable fancy cursors in fullscreen mode, to avoid SDL bug: */
93 /* (This bug is still around, as of SDL 1.2.9, October 2005) */
94 /* (Is it still in SDL 1.2.11 in May 2007, though!? -bjk) */
95 /* #define LARGE_CURSOR_FULLSCREEN_BUG */
96 
97 /* control the color selector */
98 #define COLORSEL_DISABLE 0      /* disable and draw the (greyed out) colors */
99 #define COLORSEL_ENABLE  1      /* enable and draw the colors */
100 #define COLORSEL_CLOBBER 2      /* colors get scribbled over */
101 #define COLORSEL_REFRESH 4      /* redraw the colors, either on or off */
102 #define COLORSEL_CLOBBER_WIPE 8 /* draw the (greyed out) colors, but don't disable */
103 #define COLORSEL_FORCE_REDRAW 16        /* enable, and force redraw (to make color picker work) */
104 
105 /* FIXME: Why are we checking this BEFORE the #include "SDL.h"!? Does this even work? -bjk 2010.04.24 */
106 /* Setting the amask value based on endianness*/
107 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
108 #define TPAINT_AMASK 0xff000000
109 #else
110 #define TPAINT_AMASK 0x000000ff
111 #endif
112 
113 /* *INDENT-ON* */
114 
115 static unsigned draw_colors(unsigned action);
116 
117 
118 /* hide all scale-related values here */
119 typedef struct scaleparams
120 {
121   unsigned numer, denom;
122 } scaleparams;
123 
124 static scaleparams scaletable[] = {
125   {1, 256},                     /*  0.00390625 */
126   {3, 512},                     /*  0.005859375 */
127   {1, 128},                     /*  0.0078125 */
128   {3, 256},                     /*  0.01171875 */
129   {1, 64},                      /*  0.015625 */
130   {3, 128},                     /*  0.0234375 */
131   {1, 32},                      /*  0.03125 */
132   {3, 64},                      /*  0.046875 */
133   {1, 16},                      /*  0.0625 */
134   {3, 32},                      /*  0.09375 */
135   {1, 8},                       /*  0.125 */
136   {3, 16},                      /*  0.1875 */
137   {1, 4},                       /*  0.25 */
138   {3, 8},                       /*  0.375 */
139   {1, 2},                       /*  0.5 */
140   {3, 4},                       /*  0.75 */
141   {1, 1},                       /*  1 */
142   {3, 2},                       /*  1.5 */
143   {2, 1},                       /*  2 */
144   {3, 1},                       /*  3 */
145   {4, 1},                       /*  4 */
146   {6, 1},                       /*  6 */
147   {8, 1},                       /*  8 */
148   {12, 1},                      /* 12 */
149   {16, 1},                      /* 16 */
150   {24, 1},                      /* 24 */
151   {32, 1},                      /* 32 */
152   {48, 1},                      /* 48 */
153 };
154 
155 
156 /* Macros: */
157 
158 #define HARD_MIN_STAMP_SIZE 0   /* bottom of scaletable */
159 #define HARD_MAX_STAMP_SIZE (sizeof scaletable / sizeof scaletable[0] - 1)
160 
161 #define MIN_STAMP_SIZE (stamp_data[stamp_group][cur_stamp[stamp_group]]->min)
162 #define MAX_STAMP_SIZE (stamp_data[stamp_group][cur_stamp[stamp_group]]->max)
163 
164 /* to scale some offset, in pixels, like the current stamp is scaled */
165 #define SCALE_LIKE_STAMP(x) ( ((x) * scaletable[stamp_data[stamp_group][cur_stamp[stamp_group]]->size].numer + scaletable[stamp_data[stamp_group][cur_stamp[stamp_group]]->size].denom-1) / scaletable[stamp_data[stamp_group][cur_stamp[stamp_group]]->size].denom )
166 
167 /* pixel dimensions of the current stamp, as scaled */
168 #define CUR_STAMP_W SCALE_LIKE_STAMP(active_stamp->w)
169 #define CUR_STAMP_H SCALE_LIKE_STAMP(active_stamp->h)
170 
171 
172 #define REPEAT_SPEED 300        /* Initial repeat speed for scrollbars */
173 #define CURSOR_BLINK_SPEED 500  /* Initial repeat speed for cursor */
174 
175 
176 #ifndef _GNU_SOURCE
177 #define _GNU_SOURCE             /* for strcasestr() */
178 #endif
179 
180 #include <stdio.h>
181 #include <stdlib.h>
182 #include <string.h>
183 #include <ctype.h>
184 #include <time.h>
185 #include <libgen.h>             /* EP added this include for basename() */
186 
187 /* On Linux, we can use 'wordexp()' to expand env. vars. in settings
188    pulled from config. files */
189 #ifdef __linux__
190 #include <wordexp.h>
191 #endif
192 
193 
194 /* Check if features.h did its 'magic', in which case strcasestr() is
195    likely available; if not using GNU, you can set HAVE_STRCASESTR to
196    avoid trying to redefine it -bjk 2006.06.02 */
197 
198 #if !defined(__USE_GNU) && !defined(HAVE_STRCASESTR)
199 #warning "Attempting to define strcasestr(); if errors, build with -DHAVE_STRCASESTR"
200 
strcasestr(const char * haystack,const char * needle)201 char *strcasestr(const char *haystack, const char *needle)
202 {
203   char *uphaystack, *upneedle, *result;
204   unsigned int i;
205 
206   uphaystack = strdup(haystack);
207   upneedle = strdup(needle);
208 
209   if (uphaystack == NULL || upneedle == NULL)
210     return (NULL);
211 
212   for (i = 0; i < strlen(uphaystack); i++)
213     uphaystack[i] = toupper(uphaystack[i]);
214 
215   for (i = 0; i < strlen(upneedle); i++)
216     upneedle[i] = toupper(upneedle[i]);
217 
218   result = strstr(uphaystack, upneedle);
219 
220   if (result != NULL)
221     return (result - uphaystack + (char *)haystack);
222   else
223     return NULL;
224 }
225 
226 #endif
227 
228 
229 /* math.h makes y1 an obscure function! */
230 #define y1 evil_y1
231 #include <math.h>
232 #undef y1
233 
234 #include <locale.h>
235 
236 #ifdef __HAIKU__
237 #include <zlib.h>
238 #include <zconf.h>
239 #include <FindDirectory.h>
240 #include <fs_info.h>
241 #endif
242 #if defined __BEOS__ || defined __HAIKU__ || defined __APPLE__
243 #include <wchar.h>
244 #include <stdbool.h>
245 #ifndef __HAIKU__
246 #define FALSE false
247 #define TRUE true
248 #endif
249 #else
250 #include <wchar.h>
251 #include <wctype.h>
252 #endif
253 
254 #include <libintl.h>
255 #ifndef gettext_noop
256 #define gettext_noop(String) String
257 #endif
258 
259 #ifdef DEBUG
260 #undef gettext                  /* EP to avoid warning on following line */
261 #define gettext(String) debug_gettext(String)
262 #endif
263 
264 
265 #ifndef M_PI
266 #define M_PI 3.14159265358979323846
267 #endif
268 
269 #include <sys/types.h>
270 #include <sys/stat.h>
271 
272 #ifndef WIN32
273 
274 /* Not Windows: */
275 
276 #include <unistd.h>
277 #include <dirent.h>
278 #include <signal.h>
279 
280 #if defined __BEOS__ || defined __HAIKU__
281 
282 /* BeOS */
283 
284 #include "BeOS_print.h"
285 
286 /* workaround dirent handling bug in TuxPaint code */
287 typedef struct safer_dirent
288 {
289   dev_t d_dev;
290   dev_t d_pdev;
291   ino_t d_ino;
292   ino_t d_pino;
293   unsigned short d_reclen;
294   char d_name[FILENAME_MAX];
295 } safer_dirent;
296 
297 #define dirent safer_dirent
298 
299 #else /* __BEOS__ */
300 
301 /* Not BeOS */
302 
303 #if defined(__MACOS__)
304 
305 /* macOS */
306 
307 #include "macos_print.h"
308 
309 #elif defined(__IOS__)
310 
311 /* iOS */
312 
313 #include "ios_print.h"
314 
315 #else /* __MACOS__, __IOS__ */
316 
317 /* Not Windows, not BeOS, not macOS, not iOS */
318 
319 #include "postscript_print.h"
320 
321 #endif /* __MACOS__, __IOS__ */
322 
323 #endif /* __BEOS__ */
324 
325 #else /* WIN32 */
326 
327 /* Windows */
328 
329 #include <unistd.h>
330 #include <dirent.h>
331 #include <malloc.h>
332 #include "win32_print.h"
333 #include <io.h>
334 #include <direct.h>
335 #include <iconv.h>
336 
337 #define mkdir(path,access)    _mkdir(path)
338 
339 /**
340  * FIXME
341  */
mtw(wchar_t * wtok,char * tok)342 static void mtw(wchar_t * wtok, char *tok)
343 {
344   /* workaround using iconv to get a functionallity somewhat approximate as mbstowcs() */
345   Uint16 *ui16;
346 
347   ui16 = malloc(255);
348   char *wrptr = (char *)ui16;
349   size_t n, in, out;
350   iconv_t trans;
351   wchar_t *wch;
352 
353   n = 255;
354   in = 250;
355   out = 250;
356   wch = malloc(255);
357 
358   trans = iconv_open("WCHAR_T", "UTF-8");
359   iconv(trans, (const char **)&tok, &in, &wrptr, &out);
360   *((wchar_t *) wrptr) = L'\0';
361   swprintf(wtok, L"%ls", ui16);
362   free(ui16);
363   iconv_close(trans);
364 }
365 
366 #endif /* WIN32 */
367 
368 #if defined(__MACOS__)
369 #include "macos.h"
370 #elif defined(__IOS__)
371 #include "ios.h"
372 #endif
373 
374 #include <errno.h>
375 #include <sys/stat.h>
376 
377 #include "SDL.h"
378 #include "SDL_thread.h"
379 #if !defined(_SDL_H)
380 #error "---------------------------------------------------"
381 #error "If you installed SDL from a package, be sure to get"
382 #error "the development package, as well!"
383 #error "(e.g., 'libsdl1.2-devel.rpm')"
384 #error "---------------------------------------------------"
385 #endif
386 
387 #include "SDL_image.h"
388 #if !defined(_SDL_IMAGE_H) && !defined(_IMG_h)
389 #error "---------------------------------------------------"
390 #error "If you installed SDL_image from a package, be sure"
391 #error "to get the development package, as well!"
392 #error "(e.g., 'libsdl-image1.2-devel.rpm')"
393 #error "---------------------------------------------------"
394 #endif
395 
396 #include "SDL_ttf.h"
397 #if !defined(_SDL_TTF_H) && !defined(_SDLttf_h)
398 #error "---------------------------------------------------"
399 #error "If you installed SDL_ttf from a package, be sure"
400 #error "to get the development package, as well!"
401 #error "(e.g., 'libsdl-ttf1.2-devel.rpm')"
402 #error "---------------------------------------------------"
403 #endif
404 
405 
406 #ifndef NO_SDLPANGO
407 
408 /*
409  The following section renames global variables defined in SDL_Pango.h to avoid errors during linking.
410  It is okay to rename these variables because they are constants.
411  SDL_Pango.h is included by tuxpaint.c.
412  */
413 #define _MATRIX_WHITE_BACK _MATRIX_WHITE_BACK0
414 #define MATRIX_WHITE_BACK MATRIX_WHITE_BACK0
415 #define _MATRIX_BLACK_BACK _MATRIX_BLACK_BACK0
416 #define MATRIX_BLACK_BACK MATRIX_BLACK_BACK0
417 #define _MATRIX_TRANSPARENT_BACK_BLACK_LETTER _MATRIX_TRANSPARENT_BACK_BLACK_LETTER0
418 #define MATRIX_TRANSPARENT_BACK_BLACK_LETTER MATRIX_TRANSPARENT_BACK_BLACK_LETTER0
419 #define _MATRIX_TRANSPARENT_BACK_WHITE_LETTER _MATRIX_TRANSPARENT_BACK_WHITE_LETTER0
420 #define MATRIX_TRANSPARENT_BACK_WHITE_LETTER MATRIX_TRANSPARENT_BACK_WHITE_LETTER0
421 #define _MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER _MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER0
422 #define MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER0
423 /*
424  The renaming ends here.
425  */
426 
427 #include "SDL_Pango.h"
428 #if !defined(SDL_PANGO_H)
429 #error "---------------------------------------------------"
430 #error "If you installed SDL_Pango from a package, be sure"
431 #error "to get the development package, as well!"
432 #error "(e.g., 'libsdl-pango1-dev.rpm')"
433 #error "---------------------------------------------------"
434 #endif
435 
436 #endif
437 
438 
439 #ifndef NOSOUND
440 #include "SDL_mixer.h"
441 #if !defined(_SDL_MIXER_H) && !defined(_MIXER_H_)
442 #error "---------------------------------------------------"
443 #error "If you installed SDL_mixer from a package, be sure"
444 #error "to get the development package, as well!"
445 #error "(e.g., 'libsdl-mixer1.2-devel.rpm')"
446 #error "---------------------------------------------------"
447 #endif
448 #endif
449 
450 #ifndef NOSVG
451 
452 #ifdef OLD_SVG
453 #include "cairo.h"
454 #include "svg.h"
455 #include "svg-cairo.h"
456 #if !defined(CAIRO_H) || !defined(SVG_H) || !defined(SVG_CAIRO_H)
457 #error "---------------------------------------------------"
458 #error "If you installed Cairo, libSVG or svg-cairo from packages, be sure"
459 #error "to get the development package, as well!"
460 #error "(e.g., 'libcairo-dev.rpm')"
461 #error "---------------------------------------------------"
462 #endif
463 
464 #else
465 
466 #include <librsvg/rsvg.h>
467 #if defined (__MINGW32__) && (__GNUC__ <= 4 )
468 #include <librsvg/rsvg-cairo.h>
469 #endif
470 
471 #if !defined(RSVG_H) || !defined(RSVG_CAIRO_H)
472 #error "---------------------------------------------------"
473 #error "If you installed libRSVG from packages, be sure"
474 #error "to get the development package, as well!"
475 #error "(e.g., 'librsvg2-dev.rpm')"
476 #error "---------------------------------------------------"
477 #endif
478 
479 #endif
480 
481 #endif
482 
483 #include <zlib.h>               /* EP added for PNG upgrade from 1.2 to 1.5 */
484 #define PNG_INTERNAL
485 #include <png.h>
486 #define FNAME_EXTENSION ".png"
487 #ifndef PNG_H
488 #error "---------------------------------------------------"
489 #error "If you installed the PNG libraries from a package,"
490 #error "be sure to get the development package, as well!"
491 #error "(e.g., 'libpng2-devel.rpm')"
492 #error "---------------------------------------------------"
493 #endif
494 
495 #include "libimagequant.h"
496 
497 #include "SDL_getenv.h"
498 
499 #include "i18n.h"
500 #include "cursor.h"
501 #include "pixels.h"
502 #include "rgblinear.h"
503 #include "playsound.h"
504 #include "progressbar.h"
505 #include "fonts.h"
506 #include "dirwalk.h"
507 #include "get_fname.h"
508 #include "onscreen_keyboard.h"
509 #include "gifenc.h"
510 
511 #include "tools.h"
512 #include "titles.h"
513 #include "colors.h"
514 #include "shapes.h"
515 #include "sounds.h"
516 #include "tip_tux.h"
517 #include "great.h"
518 
519 #include "fill.h"
520 #include "fill_tools.h"
521 
522 #include "im.h"
523 
524 
525 #ifdef DEBUG_MALLOC
526 #include "malloc.c"
527 #endif
528 
529 #include "parse.h"
530 
531 #include "compiler.h"
532 
533 
534 /* EP added #ifndef __APPLE__ because macros are buggy (shifted by 1 byte), plus the function exists in SDL */
535 #ifndef __APPLE__
536 #if VIDEO_BPP==32
537 #ifdef __GNUC__
538 #define SDL_GetRGBA(p,f,rp,gp,bp,ap) ({ \
539   unsigned u_p = p;                     \
540   *(ap) = (u_p >> 24) & 0xff;           \
541   *(rp) = (u_p >> 16) & 0xff;           \
542   *(gp) = (u_p >>  8) & 0xff;           \
543   *(bp) = (u_p >>  0) & 0xff;           \
544 })
545 #define SDL_GetRGB(p,f,rp,gp,bp) ({ \
546   unsigned u_p = p;                     \
547   *(rp) = (u_p >> 16) & 0xff;           \
548   *(gp) = (u_p >>  8) & 0xff;           \
549   *(bp) = (u_p >>  0) & 0xff;           \
550 })
551 #endif
552 #define SDL_MapRGBA(f,r,g,b,a) ( \
553   (((a) & 0xffu) << 24)          \
554   |                              \
555   (((r) & 0xffu) << 16)          \
556   |                              \
557   (((g) & 0xffu) <<  8)          \
558   |                              \
559   (((b) & 0xffu) <<  0)          \
560 )
561 #define SDL_MapRGB(f,r,g,b) (   \
562   (((r) & 0xffu) << 16)          \
563   |                              \
564   (((g) & 0xffu) <<  8)          \
565   |                              \
566   (((b) & 0xffu) <<  0)          \
567 )
568 #endif
569 #endif
570 
571 
572 int TP_EventFilter(const SDL_Event * event);
573 
574 
575 /* *INDENT-OFF* */
576 /* #define fmemopen_alternative *//* Uncomment this to test the fmemopen alternative in systems were fmemopen exists */
577 /* *INDENT-ON* */
578 
579 #if defined (WIN32) || defined (__APPLE__) || defined(__NetBSD__) || defined(__sun)     /* MINGW/MSYS, NetBSD, and MacOSX need it, at least for now */
580 #define fmemopen_alternative
581 #endif
582 
583 #ifdef fmemopen_alternative
584 #undef fmemopen
585 
586 FILE *my_fmemopen(unsigned char *data, size_t size, const char *mode);
587 
588 /**
589  * Open memory as a stream.  Some platforms do not support
590  * fmemopen(), so this simply dumps data to a temp file,
591  * then opens it back up and returns the FILE pointer.
592  *
593  * @param data Data to perform I/O on.
594  * @param size Size of the data
595  * @param mode I/O mode (as in fopen(3))
596  */
my_fmemopen(unsigned char * data,size_t size,const char * mode)597 FILE *my_fmemopen(unsigned char *data, size_t size, const char *mode)
598 {
599   unsigned int i;
600   char *fname;
601   FILE *fi;
602 
603 #ifndef WIN32
604   fname = get_fname("tmpfile", DIR_SAVE);
605 #else
606   fname = get_temp_fname("tmpfile");
607 #endif
608 
609 
610   fi = fopen(fname, "wb");
611   if (fi == NULL)
612     {
613       free(fname);
614       return (NULL);
615     }
616 
617   for (i = 0; i < size; i++)
618     {
619       fwrite(data, 1, 1, fi);
620       data++;
621     }
622 
623   fclose(fi);
624   fi = fopen(fname, mode);
625   free(fname);
626   return (fi);
627 }
628 
629 #define fmemopen my_fmemopen
630 
631 #endif
632 
633 
634 enum
635 {
636   SAVE_OVER_UNSET = -1,
637   SAVE_OVER_PROMPT,
638   SAVE_OVER_ALWAYS,
639   SAVE_OVER_NO
640 };
641 
642 enum
643 {
644   ALTPRINT_MOD,
645   ALTPRINT_ALWAYS,
646   ALTPRINT_NEVER
647 };
648 
649 
650 enum
651 {
652   STARTER_OUTLINE,
653   STARTER_SCENE
654 };
655 
656 enum
657 {
658   LABEL_OFF,
659   LABEL_LABEL,
660   LABEL_SELECT
661     /* , LABEL_ROTATE */
662 };
663 
664 
665 
666 /* Color globals (copied from colors.h, if no colors specified by user) */
667 
668 static int NUM_COLORS;
669 static Uint8 **color_hexes;
670 static char **color_names;
671 
672 
673 /* Show debugging stuff: */
674 
675 /**
676  * Echos debug info to STDERR, if debugging (DEBUG #define) is set.
677  *
678  * @param str text to echo
679  */
debug(const char * const str)680 static void debug(const char *const str)
681 {
682 #ifndef DEBUG
683   (void)str;
684 #else
685   fprintf(stderr, "DEBUG: %s\n", str);
686   fflush(stderr);
687 #endif
688 }
689 
690 /* sizing */
691 
692 /* The old Tux Paint:
693    640x480 screen
694    448x376 canvas
695     40x96  titles near the top
696     48x48  button tiles
697     ??x56  tux area
698     room for 2x7 button tile grids */
699 
700 typedef struct
701 {
702   Uint8 rows, cols;
703 } grid_dims;
704 
705 /* *INDENT-OFF* */
706 /* static SDL_Rect r_screen; *//* was 640x480 @ 0,0  -- but this isn't so useful */
707 /* *INDENT-ON* */
708 
709 static SDL_Rect r_canvas;       /* was 448x376 @ 96,0 */
710 static SDL_Rect r_tools;        /* was 96x336 @ 0,40 */
711 static SDL_Rect r_sfx;
712 static SDL_Rect r_toolopt;      /* was 96x336 @ 544,40 */
713 static SDL_Rect r_colors;       /* was 544x48 @ 96,376 */
714 static SDL_Rect r_ttools;       /* was 96x40 @ 0,0  (title for tools, "Tools") */
715 static SDL_Rect r_tcolors;      /* was 96x48 @ 0,376 (title for colors, "Colors") */
716 static SDL_Rect r_ttoolopt;     /* was 96x40 @ 544,0 (title for tool options) */
717 static SDL_Rect r_tuxarea;      /* was 640x56 */
718 static SDL_Rect r_label;
719 static SDL_Rect old_dest;
720 
721 static int button_w;            /* was 48 */
722 static int button_h;            /* was 48 */
723 static float button_scale;      /* scale factor to be applied to the  size of buttons */
724 static int color_button_w;      /* was 32 */
725 static int color_button_h;      /* was 48 */
726 static int colors_rows;
727 
728 static int buttons_tall;        /* promoted to a global variable from setup_normal_screen_layout() */
729 
730 /* Define button grid dimensions. (in button units) */
731 /* These are the maximum slots -- some may be unused. */
732 static grid_dims gd_tools;      /* was 2x7 */
733 static grid_dims gd_sfx;
734 static grid_dims gd_toolopt;    /* was 2x7 */
735 
736 /* *INDENT-OFF* */
737 /* static grid_dims gd_open; *//* was 4x4 */
738 /* *INDENT-ON* */
739 static grid_dims gd_colors;     /* was 17x1 */
740 
741 #define ORIGINAL_BUTTON_SIZE 48 /* Original Button Size */
742 #define HEIGHTOFFSET (((WINDOW_HEIGHT - 480) / button_h) * button_h)
743 #define TOOLOFFSET (HEIGHTOFFSET / button_h * 2)
744 #define PROMPTOFFSETX (WINDOW_WIDTH - 640) / 2
745 #define PROMPTOFFSETY (HEIGHTOFFSET / 2)
746 
747 #define THUMB_W ((WINDOW_WIDTH - r_ttools.w - r_ttoolopt.w) / 4)
748 #define THUMB_H (((button_h * buttons_tall + r_ttools.h) - button_h - button_h / 2) / 4)
749 
750 #ifdef NOKIA_770
751 static int WINDOW_WIDTH = 800;
752 static int WINDOW_HEIGHT = 480;
753 #elif defined(OLPC_XO)
754 /* ideally we'd support rotation and 2x scaling */
755 static int WINDOW_WIDTH = 1200;
756 static int WINDOW_HEIGHT = 900;
757 #else
758 static int WINDOW_WIDTH = 800;
759 static int WINDOW_HEIGHT = 600;
760 #endif
761 
762 static void magic_putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
763 static Uint32 magic_getpixel(SDL_Surface * surface, int x, int y);
764 static void magic_xorpixel(SDL_Surface * surface, int x, int y);
765 
766 /**
767  * Sets button_scale to the maximum Tux Paint can handle
768  */
set_max_buttonscale(void)769 static void set_max_buttonscale(void)
770 {
771   float max_w, max_h;
772 
773   /* WINDOW_WIDTH / original size of tools columnss + 9 buttons + tooloption columns */
774   max_w = (float)WINDOW_WIDTH / (gd_tools.cols * 48 + 9 * 48 + gd_toolopt.cols * 48);
775 
776   /* WINDOW_HEIGHT / original size of r_ttools.h + 5 buttons + colors rows + tux area */
777   max_h = (float)WINDOW_HEIGHT / (40 + 5 * 48 + gd_colors.rows * 48 + 56);
778 
779   button_scale = min(max_w, max_h);
780   fprintf(stderr, "Will use a button size of %d\n", (int)(button_scale * ORIGINAL_BUTTON_SIZE));
781 }
782 
783 /**
784  * Sets a variety of screen layout globals, based on the
785  * size of the window/screen Tux Paint is being displayed on
786  * (WINDOW_WIDTH & WINDOW_HEIGHT).
787  */
setup_normal_screen_layout(void)788 static void setup_normal_screen_layout(void)
789 {
790   button_w = 48 * button_scale;
791   button_h = 48 * button_scale;
792 
793   gd_toolopt.cols = 2;
794   gd_tools.cols = 2;
795 
796   r_ttools.x = 0;
797   r_ttools.y = 0;
798   r_ttools.w = gd_tools.cols * button_w;
799   r_ttools.h = 40 * button_scale;
800 
801   r_ttoolopt.w = gd_toolopt.cols * button_w;
802   r_ttoolopt.h = 40 * button_scale;
803   r_ttoolopt.x = WINDOW_WIDTH - r_ttoolopt.w;
804   r_ttoolopt.y = 0;
805 
806   gd_colors.rows = colors_rows;
807   gd_colors.cols = (NUM_COLORS + gd_colors.rows - 1) / gd_colors.rows;
808 
809   r_colors.h = 48 * button_scale * gd_colors.rows;
810   color_button_h = r_colors.h / gd_colors.rows;
811   r_tcolors.h = r_colors.h;
812 
813   r_tcolors.x = 0;
814   r_tcolors.w = gd_tools.cols * button_w;
815   r_colors.x = r_tcolors.w;
816   r_colors.w = WINDOW_WIDTH - r_tcolors.w;
817 
818   color_button_w = r_colors.w / gd_colors.cols;
819 
820   /* This would make it contain _just_ the color spots,
821      without any leftover bit on the end. Hmmm... */
822   /* r_colors.w = color_button_w * gd_colors.cols; */
823 
824   r_canvas.x = gd_tools.cols * button_w;
825   r_canvas.y = 0;
826   r_canvas.w = WINDOW_WIDTH - (gd_tools.cols + gd_toolopt.cols) * button_w;
827 
828   r_tuxarea.x = 0;
829   r_tuxarea.w = WINDOW_WIDTH;
830 
831   /* need 56 minimum for the Tux area */
832   buttons_tall = (WINDOW_HEIGHT - r_ttoolopt.h - 56 * button_scale - r_colors.h) / button_h;
833   if (buttons_tall < 5) {
834     fprintf(stderr, "Button size '%d' with window size '%dx%d' is not reasonable (not tall enough).\n",
835       button_w, WINDOW_WIDTH, WINDOW_HEIGHT);
836     set_max_buttonscale();
837     setup_normal_screen_layout();
838   }
839 
840   if (r_canvas.w < button_w * 9) {
841     fprintf(stderr, "Button size '%d' with window size '%dx%d' is not reasonable (not wide enough).\n",
842       button_w, WINDOW_WIDTH, WINDOW_HEIGHT);
843     set_max_buttonscale();
844     setup_normal_screen_layout();
845   }
846 
847   gd_tools.rows = buttons_tall;
848   gd_toolopt.rows = buttons_tall;
849 
850   r_canvas.h = r_ttoolopt.h + buttons_tall * button_h;
851 
852   r_label = r_canvas;
853 
854   r_colors.y = r_canvas.h + r_canvas.y;
855   r_tcolors.y = r_canvas.h + r_canvas.y;
856 
857   r_tuxarea.y = r_colors.y + r_colors.h;
858   r_tuxarea.h = WINDOW_HEIGHT - r_tuxarea.y;
859 
860   r_sfx.x = r_tuxarea.x;
861   r_sfx.y = r_tuxarea.y;
862   r_sfx.w = button_w;           /* Two half-sized buttons across */
863   r_sfx.h = button_h >> 1;      /* One half-sized button down */
864 
865   gd_sfx.rows = 1;
866   gd_sfx.cols = 2;
867 
868   r_tools.x = 0;
869   r_tools.y = r_ttools.h + r_ttools.y;
870   r_tools.w = gd_tools.cols * button_w;
871   r_tools.h = gd_tools.rows * button_h;
872 
873   r_toolopt.w = gd_toolopt.cols * button_w;
874   r_toolopt.h = gd_toolopt.rows * button_h;
875   r_toolopt.x = WINDOW_WIDTH - r_ttoolopt.w;
876   r_toolopt.y = r_ttoolopt.h + r_ttoolopt.y;
877 
878   /* TODO: dialog boxes */
879 }
880 
881 #ifdef DEBUG
882 /**
883  * Debug output that shows a layout rectangle's position & dimensions
884  * (Used as a #define macro, by print_layout(), below)
885  *
886  * @param r The rectange
887  * @param name The name of the rect object
888  */
debug_rect(SDL_Rect * r,char * name)889 static void debug_rect(SDL_Rect * r, char *name)
890 {
891   /* FIXME: Send to stderr, not stdout? */
892   printf("%-12s %dx%d @ %d,%d\n", name, r->w, r->h, r->x, r->y);
893 }
894 
895 #define DR(x) debug_rect(&x, #x)
896 
897 /**
898  * Debug output that shows a layout grid's dimensions
899  * (Used as a #define macro, by print_layout(), below)
900  *
901  * @param g The grid
902  * @param name The name of the grid object
903  */
debug_dims(grid_dims * g,char * name)904 static void debug_dims(grid_dims * g, char *name)
905 {
906   /* FIXME: Send to stderr, not stdout? */
907   printf("%-12s %dx%d\n", name, g->cols, g->rows);
908 }
909 
910 #define DD(x) debug_dims(&x, #x)
911 
912 /**
913  * Debug output that shows Tux Paint's layout
914  */
print_layout(void)915 static void print_layout(void)
916 {
917   /* FIXME: Send to stderr, not stdout? */
918   printf("\n--- layout ---\n");
919   DR(r_canvas);
920   DR(r_tools);
921   DR(r_toolopt);
922   DR(r_colors);
923   DR(r_ttools);
924   DR(r_tcolors);
925   DR(r_ttoolopt);
926   DR(r_tuxarea);
927   DD(gd_tools);
928   DD(gd_toolopt);
929   DD(gd_colors);
930   /* FIXME: Send to stderr, not stdout? */
931   printf("buttons are %dx%d\n", button_w, button_h);
932   printf("color buttons are %dx%d\n", color_button_w, color_button_h);
933 }
934 
935 #undef DD
936 #undef DR
937 #endif
938 
939 /**
940  * Set up (and display, if debugging is enabled), the
941  * position, size, and layout of Tux Paint's UI
942  */
setup_screen_layout(void)943 static void setup_screen_layout(void)
944 {
945   /* can do right-to-left, colors at the top, extra tool option columns, etc. */
946   setup_normal_screen_layout();
947 #ifdef DEBUG
948   print_layout();
949 #endif
950 }
951 
952 static SDL_Surface *screen = NULL;
953 static SDL_Surface *canvas = NULL;
954 static SDL_Surface *label = NULL;
955 static SDL_Surface *save_canvas = NULL;
956 static SDL_Surface *canvas_back = NULL;
957 static SDL_Surface *img_starter = NULL, *img_starter_bkgd = NULL;
958 
959 /**
960  * Update a rect. based on two x/y coords (not necessarly in order)
961  * (calls SDL_UpdateRect())
962  *
963  * @param x1 X of first coordinate
964  * @param y1 Y of first coordinate
965  * @param x2 X of second coordinate
966  * @param y2 Y of second coordinate
967  */
update_screen(int x1,int y1,int x2,int y2)968 static void update_screen(int x1, int y1, int x2, int y2)
969 {
970   int tmp;
971 
972   if (x1 > x2)
973     {
974       tmp = x1;
975       x1 = x2;
976       x2 = tmp;
977     }
978 
979   if (y1 > y2)
980     {
981       tmp = y1;
982       y1 = y2;
983       y2 = tmp;
984     }
985 
986   x1 = x1 - 1;
987   x2 = x2 + 1;
988   y1 = y1 - 1;
989   y2 = y2 + 1;
990 
991 
992   if (x1 < 0)
993     x1 = 0;
994   if (x2 < 0)
995     x2 = 0;
996   if (y1 < 0)
997     y1 = 0;
998   if (y2 < 0)
999     y2 = 0;
1000 
1001   if (x1 >= WINDOW_WIDTH)
1002     x1 = WINDOW_WIDTH - 1;
1003   if (x2 >= WINDOW_WIDTH)
1004     x2 = WINDOW_WIDTH - 1;
1005   if (y1 >= WINDOW_HEIGHT)
1006     y1 = WINDOW_HEIGHT - 1;
1007   if (y2 >= WINDOW_HEIGHT)
1008     y2 = WINDOW_HEIGHT - 1;
1009 
1010   SDL_UpdateRect(screen, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
1011 }
1012 
1013 
1014 /**
1015  * Update a rect. area of the screen
1016  * (calls SDL_UpdateRect())
1017  *
1018  * @param r The rect
1019  */
update_screen_rect(SDL_Rect * r)1020 static void update_screen_rect(SDL_Rect * r)
1021 {
1022   SDL_UpdateRect(screen, r->x, r->y, r->w, r->h);
1023 }
1024 
1025 
1026 /**
1027  * Test whether an x/y coordinate is within a given rect.
1028  *
1029  * @param r The rect
1030  * @param x X coordinate
1031  * @param y Y coordinate
1032  * @return true if a hit, else false
1033  */
hit_test(const SDL_Rect * const r,unsigned x,unsigned y)1034 static int hit_test(const SDL_Rect * const r, unsigned x, unsigned y)
1035 {
1036   /* note the use of unsigned math: no need to check for negative */
1037   return x - r->x < r->w && y - r->y < r->h;
1038 }
1039 
1040 #define HIT(r) hit_test(&(r), event.button.x, event.button.y)
1041 
1042 
1043 /**
1044  * Returns which item in a grid was clicked, if any.
1045  *
1046  * @param r The rectangle containing the grid on the scren
1047  * @param x X coordinate (mouse location) of a click
1048  * @param y Y coordinate (mouse location) of a click
1049  * @param gd The grid of items
1050  * @returns The item clicked, or -1 if click was outside the grid.
1051  */
grid_hit_gd(const SDL_Rect * const r,unsigned x,unsigned y,grid_dims * gd)1052 static int grid_hit_gd(const SDL_Rect * const r, unsigned x, unsigned y, grid_dims * gd)
1053 {
1054   unsigned item_w = r->w / gd->cols;
1055   unsigned item_h = r->h / gd->rows;
1056   unsigned col = (x - r->x) / item_w;
1057   unsigned row = (y - r->y) / item_h;
1058 
1059 #ifdef DEBUG
1060   /* FIXME: Send to stderr, not stdout? */
1061   printf("%d,%d resolves to %d,%d in a %dx%d grid, index is %d\n", x, y, col,
1062          row, gd->cols, gd->rows, col + row * gd->cols);
1063 #endif
1064   if (col >= gd->cols || row >= gd->rows)
1065     return -1;
1066   return col + row * gd->cols;
1067 }
1068 
1069 /* test an SDL_Rect r for a grid location, based on a grid_dims gd */
1070 #define GRIDHIT_GD(r,gd) grid_hit_gd(&(r), event.button.x, event.button.y, &(gd))
1071 
1072 /* One global variable defined here so that update_canvas() need not be moved below */
1073 
1074 #if VIDEO_BPP != 32
1075 static int disable_label = 1;
1076 #else
1077 static int disable_label;
1078 #endif
1079 
1080 /**
1081  * Update the contents of a rectangular region of the drawing canvas.
1082  * If overlaying Starter Image exists, and/or Labels are in
1083  * the image, draw them over the picture.
1084  *
1085  * FIXME: Subtly different from update_canvas_ex(), below.
1086  *
1087  * @param x1 Left side of area to update
1088  * @param y1 Top side of area to update
1089  * @param x2 Right side of area to update
1090  * @param y2 Bottom side of area to update
1091  * @param screen_too If true, show updated canvas on the screen
1092  */
update_canvas_ex_r(int x1,int y1,int x2,int y2,int screen_too)1093 static void update_canvas_ex_r(int x1, int y1, int x2, int y2, int screen_too)
1094 {
1095   SDL_Rect src, dest;
1096 
1097   src.x = x1;
1098   src.y = y1;
1099   src.w = x2 - x1 + 1;
1100   src.h = y2 - y1 + 1;
1101 
1102   dest.x = x1;
1103   dest.y = y1;
1104   dest.w = src.w;
1105   dest.h = src.h;
1106 
1107   if (img_starter != NULL)
1108     {
1109       /* If there was a starter, cover this part of the drawing with
1110          the corresponding part of the starter's foreground! */
1111 
1112       SDL_BlitSurface(img_starter, &dest, canvas, &dest);
1113     }
1114 
1115   dest.x = x1 + r_ttools.w;
1116 
1117   SDL_BlitSurface(canvas, &src, screen, &dest);
1118 
1119   /* If label is not disabled, cover canvas with label layer */
1120 
1121   if (!disable_label)
1122     SDL_BlitSurface(label, &src, screen, &dest);
1123 
1124   if (screen_too)
1125     update_screen(x1 + r_ttools.w, y1, x2 + r_ttools.w, y2);
1126 }
1127 
1128 /**
1129  * Update the contents of a rectangular region of the drawing canvas.
1130  * If overlaying Starter Image exists, and/or Labels are in
1131  * the image, draw them over the picture.
1132  *
1133  * FIXME: Subtly different from update_canvas_ex_r(), above.
1134  *
1135  * @param x1 Left side of area to update
1136  * @param y1 Top side of area to update
1137  * @param x2 Right side of area to update
1138  * @param y2 Bottom side of area to update
1139  * @param screen_too If true, show updated canvas on the screen
1140  */
update_canvas_ex(int x1,int y1,int x2,int y2,int screen_too)1141 static void update_canvas_ex(int x1, int y1, int x2, int y2, int screen_too)
1142 {
1143   SDL_Rect src, dest;
1144 
1145   if (img_starter != NULL)
1146     {
1147       /* If there was a starter, cover this part of the drawing with
1148          the corresponding part of the starter's foreground! */
1149 
1150       src.x = x1;
1151       src.y = y1;
1152       src.w = x2 - x1 + 1;
1153       src.h = y2 - y1 + 1;
1154 
1155       dest.x = x1;
1156       dest.y = y1;
1157       dest.w = src.w;
1158       dest.h = src.h;
1159 
1160       SDL_BlitSurface(img_starter, &dest, canvas, &dest);
1161     }
1162 
1163   SDL_BlitSurface(canvas, NULL, screen, &r_canvas);
1164 
1165   /* If label is not disabled, cover canvas with label layer */
1166 
1167   if (!disable_label)
1168     SDL_BlitSurface(label, NULL, screen, &r_label);
1169 
1170   if (screen_too)
1171     update_screen(x1 + r_ttools.w, y1, x2 + r_ttools.w, y2);
1172 }
1173 
1174 /**
1175  * Update the screen with the new canvas.
1176  * (Wrapper for update_canvas_ex(), above, with `screen_too` = true)
1177  *
1178  * @param x1 Left side of area to update
1179  * @param y1 Top side of area to update
1180  * @param x2 Right side of area to update
1181  * @param y2 Bottom side of area to update
1182  */
update_canvas(int x1,int y1,int x2,int y2)1183 static void update_canvas(int x1, int y1, int x2, int y2)
1184 {
1185   update_canvas_ex(x1, y1, x2, y2, 1);
1186 }
1187 
1188 
1189 /* Globals: */
1190 static int emulate_button_pressed = 0;
1191 static int mouseaccessibility = 0;
1192 static int onscreen_keyboard = 0;
1193 static char *onscreen_keyboard_layout = NULL;
1194 static on_screen_keyboard *kbd = NULL;
1195 static int onscreen_keyboard_disable_change = 0;
1196 static int joystick_low_threshold = 3200;
1197 static int joystick_slowness = 15;
1198 static int joystick_maxsteps = 7;
1199 static int joystick_hat_slowness = 15;
1200 static Uint32 joystick_hat_timeout = 1000;
1201 static int joystick_button_escape = 255;
1202 static int joystick_button_selectbrushtool = 255;
1203 static int joystick_button_selectstamptool = 255;
1204 static int joystick_button_selectlinestool = 255;
1205 static int joystick_button_selectshapestool = 255;
1206 static int joystick_button_selecttexttool = 255;
1207 static int joystick_button_selectlabeltool = 255;
1208 static int joystick_button_selectmagictool = 255;
1209 static int joystick_button_undo = 255;
1210 static int joystick_button_redo = 255;
1211 static int joystick_button_selecterasertool = 255;
1212 static int joystick_button_new = 255;
1213 static int joystick_button_open = 255;
1214 static int joystick_button_save = 255;
1215 static int joystick_button_pagesetup = 255;
1216 static int joystick_button_print = 255;
1217 static int joystick_buttons_ignore_len = 0;
1218 static int joystick_buttons_ignore[256];
1219 static Uint32 old_hat_ticks = 0;
1220 static int oldpos_x;
1221 static int oldpos_y;
1222 static int disable_screensaver;
1223 
1224 #ifdef NOKIA_770
1225 static int fullscreen = 1;
1226 #else
1227 static int fullscreen;
1228 #endif
1229 static int native_screensize;
1230 static int grab_input;
1231 static int rotate_orientation;
1232 static int joystick_dev = 0;
1233 
1234 static int disable_print;
1235 static int print_delay;
1236 static int use_print_config = 1;
1237 static int alt_print_command_default = ALTPRINT_MOD;
1238 static int want_alt_printcommand;
1239 
1240 static int wheely = 1;
1241 static int keymouse = 0;
1242 static int no_button_distinction;
1243 static int button_down;
1244 static int scrolling;
1245 
1246 static int promptless_save = SAVE_OVER_UNSET;
1247 static int _promptless_save_over, _promptless_save_over_ask, _promptless_save_over_new;
1248 static int disable_quit;
1249 
1250 static int noshortcuts;
1251 static int disable_save;
1252 static int ok_to_use_lockfile = 1;
1253 static int start_blank;
1254 static int autosave_on_quit;
1255 static int no_prompt_on_quit = 0;
1256 
1257 static int dont_do_xor;
1258 static int dont_load_stamps;
1259 static int mirrorstamps;
1260 static int disable_stamp_controls;
1261 static int stamp_size_override = -1;
1262 static int new_colors_last;
1263 
1264 #ifdef NOKIA_770
1265 static int simple_shapes = 1;
1266 #else
1267 static int simple_shapes;
1268 #endif
1269 static int only_uppercase;
1270 
1271 static int disable_magic_controls;
1272 static int disable_shape_controls;
1273 
1274 static int shape_mode = SHAPEMODE_CENTER;
1275 
1276 static int starter_mirrored;
1277 static int starter_flipped;
1278 static int starter_personal;
1279 static int template_personal;
1280 static int starter_modified;
1281 
1282 static Uint8 canvas_color_r, canvas_color_g, canvas_color_b;
1283 static Uint8 *touched;
1284 static Uint8 *sim_flood_touched;
1285 int sim_flood_x1 = 0, sim_flood_y1 = 0, sim_flood_x2 = 0, sim_flood_y2 = 0;
1286 int fill_x, fill_y;
1287 static int last_print_time = 0;
1288 
1289 static int shape_radius;
1290 
1291 /* Text label tool struct to hold information about text on the label layer */
1292 typedef struct label_node
1293 {
1294   unsigned int save_texttool_len;
1295   wchar_t save_texttool_str[256];
1296   SDL_Color save_color;
1297   int save_width;
1298   int save_height;
1299   Uint16 save_x;
1300   Uint16 save_y;
1301   int save_cur_font;
1302   char *save_font_type;
1303   int save_text_state;
1304   unsigned save_text_size;
1305   int save_undoid;
1306   int is_enabled;
1307   struct label_node *disables;
1308   struct label_node *next_to_up_label_node;
1309   struct label_node *next_to_down_label_node;
1310   SDL_Surface *label_node_surface;
1311 } label_node;
1312 
1313 
1314 static struct label_node *start_label_node;
1315 static struct label_node *current_label_node;
1316 static struct label_node *first_label_node_in_redo_stack;
1317 static struct label_node *label_node_to_edit;
1318 static struct label_node *highlighted_label_node;
1319 
1320 static unsigned int select_texttool_len;
1321 static wchar_t select_texttool_str[256];
1322 static unsigned select_color;
1323 static int select_width;
1324 static int select_height;
1325 static Uint16 select_x;
1326 static Uint16 select_y;
1327 static int select_cur_font;
1328 static int select_text_state;
1329 static unsigned select_text_size;
1330 static int coming_from_undo_or_redo = FALSE;
1331 
1332 
1333 static void add_label_node(int, int, Uint16, Uint16, SDL_Surface * label_node_surface);
1334 static void load_info_about_label_surface(FILE * lfi);
1335 
1336 static struct label_node *search_label_list(struct label_node **, Uint16, Uint16, int hover);
1337 static void highlight_label_nodes(void);
1338 static void cycle_highlighted_label_node(void);
1339 static int are_labels(void);
1340 
1341 static void do_undo_label_node(void);
1342 static void do_redo_label_node(void);
1343 static void rec_undo_label(void);
1344 
1345 static void render_all_nodes_starting_at(struct label_node **);
1346 static void simply_render_node(struct label_node *);
1347 
1348 static void derender_node(struct label_node **);
1349 
1350 static void delete_label_list(struct label_node **);
1351 
1352 static void myblit(SDL_Surface * src_surf, SDL_Rect * src_rect, SDL_Surface * dest_surf, SDL_Rect * dest_rect);
1353 
1354 static void set_label_fonts(void);
1355 
1356 static void tmp_apply_uncommited_text(void);
1357 static void undo_tmp_applied_text(void);
1358 
1359 static void handle_joyaxismotion(SDL_Event event, int *motioner, int *val_x, int *val_y);
1360 static void handle_joyhatmotion(SDL_Event event, int oldpos_x, int oldpos_y, int *valhat_x, int *valhat_y,
1361                                 int *hat_motioner, Uint32 * old_hat_ticks);
1362 static void handle_joyballmotion(SDL_Event event, int oldpos_x, int oldpos_y);
1363 static void handle_joybuttonupdown(SDL_Event event, int oldpos_x, int oldpos_y);
1364 static void handle_motioners(int oldpos_x, int oldpos_y, int motioner, int hatmotioner, int old_hat_ticks, int val_x,
1365                              int val_y, int valhat_x, int valhat_y);
1366 
1367 static void handle_joybuttonupdownscl(SDL_Event event, int oldpos_x, int oldpos_y, SDL_Rect real_r_tools);
1368 
1369 char * get_xdg_user_dir(const char * dir_type, const char * fallback);
1370 #ifdef WIN32
1371 extern char * GetUserImageDir(void);
1372 #endif
1373 
1374 /* Magic tools API and tool handles: */
1375 
1376 #include "tp_magic_api.h"
1377 
1378 static void update_progress_bar(void);
1379 static void special_notify(int flags);
1380 
1381 typedef struct magic_funcs_s
1382 {
1383   int (*get_tool_count) (magic_api *);
1384   char *(*get_name) (magic_api *, int);
1385   SDL_Surface *(*get_icon) (magic_api *, int);
1386   char *(*get_description) (magic_api *, int, int);
1387   int (*requires_colors) (magic_api *, int);
1388   int (*modes) (magic_api *, int);
1389   void (*set_color) (magic_api *, Uint8, Uint8, Uint8);
1390   int (*init) (magic_api *);
1391    Uint32(*api_version) (void);
1392   void (*shutdown) (magic_api *);
1393   void (*click) (magic_api *, int, int, SDL_Surface *, SDL_Surface *, int, int, SDL_Rect *);
1394   void (*drag) (magic_api *, int, SDL_Surface *, SDL_Surface *, int, int, int, int, SDL_Rect *);
1395   void (*release) (magic_api *, int, SDL_Surface *, SDL_Surface *, int, int, SDL_Rect *);
1396   void (*switchin) (magic_api *, int, int, SDL_Surface *, SDL_Surface *);
1397   void (*switchout) (magic_api *, int, int, SDL_Surface *, SDL_Surface *);
1398 } magic_funcs_t;
1399 
1400 
1401 typedef struct magic_s
1402 {
1403   int place;
1404   int handle_idx;               /* Index to magic funcs for each magic tool (shared objs may report more than 1 tool) */
1405   int idx;                      /* Index to magic tools within shared objects (shared objs may report more than 1 tool) */
1406   int mode;                     /* Current mode (paint or fullscreen) */
1407   int avail_modes;              /* Available modes (paint &/or fullscreen) */
1408   int colors;                   /* Whether magic tool accepts colors */
1409   char *name;                   /* Name of magic tool */
1410   char *tip[MAX_MODES];         /* Description of magic tool, in each possible mode */
1411   SDL_Surface *img_icon;
1412   SDL_Surface *img_name;
1413 } magic_t;
1414 
1415 
1416 /* FIXME: Drop the 512 constants :^P */
1417 
1418 static int num_plugin_files;    /* How many shared object files we went through */
1419 static void *magic_handle[512]; /* Handle to shared object (to be unloaded later) *//* FIXME: Unload them! */
1420 static magic_funcs_t magic_funcs[512];  /* Pointer to shared objects' functions */
1421 
1422 static magic_t magics[512];
1423 static int num_magics;          /* How many magic tools were loaded (note: shared objs may report more than 1 tool) */
1424 
1425 enum
1426 {
1427   MAGIC_PLACE_GLOBAL,
1428   MAGIC_PLACE_LOCAL,
1429 #ifdef __APPLE__
1430   MAGIC_PLACE_ALLUSERS,
1431 #endif
1432   NUM_MAGIC_PLACES
1433 };
1434 
1435 static magic_api *magic_api_struct;     /* Pointer to our internal functions; passed to shared object's functions when we call them */
1436 
1437 
1438 #if !defined(WIN32) && !defined(__APPLE__) && !defined(__BEOS__) && !defined(__HAIKU__)
1439 #include <paper.h>
1440 #if !defined(PAPER_H)
1441 #error "---------------------------------------------------"
1442 #error "If you installed libpaper from a package, be sure"
1443 #error "to get the development package, as well!"
1444 #error "(eg., 'libpaper-dev_1.1.21.deb')"
1445 #error "---------------------------------------------------"
1446 #endif
1447 static const char *printcommand = PRINTCOMMAND;
1448 static const char *altprintcommand = ALTPRINTCOMMAND;
1449 static const char *papersize;
1450 #endif
1451 
1452 
1453 #if 1                           /* FIXME: ifdef for platforms that lack fribidi? */
1454 #include <fribidi/fribidi.h>
1455 #if !defined(_FRIBIDI_H) && !defined(FRIBIDI_H)
1456 #error "---------------------------------------------------"
1457 #error "If you installed libfribidi from a package, be sure"
1458 #error "to get the development package, as well!"
1459 #error "(eg., 'libfribidi-dev')"
1460 #error "---------------------------------------------------"
1461 #endif
1462 #else
1463 /* FIXME: define a noop function */
1464 #endif
1465 
1466 enum
1467 {
1468   UNDO_STARTER_NONE,
1469   UNDO_STARTER_MIRRORED,
1470   UNDO_STARTER_FLIPPED
1471 };
1472 
1473 #define NUM_UNDO_BUFS 20
1474 static SDL_Surface *undo_bufs[NUM_UNDO_BUFS];
1475 static int undo_starters[NUM_UNDO_BUFS];
1476 static int cur_undo, oldest_undo, newest_undo;
1477 static int text_undo[NUM_UNDO_BUFS];
1478 static int have_to_rec_label_node;
1479 static int have_to_rec_label_node_back;
1480 static SDL_Surface *img_title, *img_title_credits, *img_title_tuxpaint;
1481 static SDL_Surface *img_btn_up, *img_btn_down, *img_btn_off, *img_btn_hold;
1482 static SDL_Surface *img_btnsm_up, *img_btnsm_off, *img_btnsm_down, *img_btnsm_hold;
1483 static SDL_Surface *img_btn_nav, *img_btnsm_nav;
1484 static SDL_Surface *img_prev, *img_next;
1485 static SDL_Surface *img_mirror, *img_flip;
1486 static SDL_Surface *img_dead40x40;
1487 static SDL_Surface *img_black, *img_grey;
1488 static SDL_Surface *img_yes, *img_no;
1489 static SDL_Surface *img_sfx, *img_speak;
1490 static SDL_Surface *img_open, *img_erase, *img_back, *img_trash, *img_pict_export;
1491 static SDL_Surface *img_slideshow, *img_play, *img_gif_export, *img_select_digits;
1492 static SDL_Surface *img_printer, *img_printer_wait;
1493 static SDL_Surface *img_save_over, *img_popup_arrow;
1494 static SDL_Surface *img_cursor_up, *img_cursor_down;
1495 static SDL_Surface *img_cursor_starter_up, *img_cursor_starter_down;
1496 static SDL_Surface *img_scroll_up, *img_scroll_down;
1497 static SDL_Surface *img_scroll_up_off, *img_scroll_down_off;
1498 static SDL_Surface *img_grow, *img_shrink;
1499 static SDL_Surface *img_magic_paint, *img_magic_fullscreen;
1500 static SDL_Surface *img_shapes_corner, *img_shapes_center;
1501 static SDL_Surface *img_bold, *img_italic;
1502 static SDL_Surface *img_label, *img_label_select;
1503 static SDL_Surface *img_color_picker, *img_color_picker_thumb, *img_paintwell, *img_color_sel;
1504 static int color_picker_x, color_picker_y;
1505 
1506 static SDL_Surface *img_title_on, *img_title_off, *img_title_large_on, *img_title_large_off;
1507 static SDL_Surface *img_title_names[NUM_TITLES];
1508 static SDL_Surface *img_tools[NUM_TOOLS], *img_tool_names[NUM_TOOLS];
1509 
1510 static SDL_Surface *img_oskdel, *img_osktab, *img_oskenter, *img_oskcapslock, *img_oskshift;
1511 static SDL_Surface *thumbnail(SDL_Surface * src, int max_x, int max_y, int keep_aspect);
1512 static SDL_Surface *thumbnail2(SDL_Surface * src, int max_x, int max_y, int keep_aspect, int keep_alpha);
1513 
1514 #ifndef NO_BILINEAR
1515 static SDL_Surface *zoom(SDL_Surface * src, int new_x, int new_y);
1516 #endif
1517 
1518 
1519 /**
1520  * Render some text (char's) as a bitmap
1521  *
1522  * @param font The font to use
1523  * @param str The string of text to render
1524  * @param color The color to draw it in
1525  * @return A new surface, containing the rendered text
1526  */
render_text(TuxPaint_Font * restrict font,const char * restrict str,SDL_Color color)1527 static SDL_Surface *render_text(TuxPaint_Font * restrict font, const char *restrict str, SDL_Color color)
1528 {
1529   SDL_Surface *ret = NULL;
1530   int height;
1531 
1532 #ifndef NO_SDLPANGO
1533   SDLPango_Matrix pango_color;
1534 #endif
1535 
1536   if (font == NULL)
1537     {
1538       fprintf(stderr, "render_text() received a NULL font!\n");
1539       fflush(stdout);
1540       return NULL;
1541     }
1542 
1543 #ifndef NO_SDLPANGO
1544   if (font->typ == FONT_TYPE_PANGO)
1545     {
1546       sdl_color_to_pango_color(color, &pango_color);
1547 
1548 #ifdef DEBUG
1549       printf("Calling SDLPango_SetText(\"%s\")\n", str);
1550       fflush(stdout);
1551 #endif
1552 
1553       SDLPango_SetDefaultColor(font->pango_context, &pango_color);
1554       SDLPango_SetText(font->pango_context, str, -1);
1555       ret = SDLPango_CreateSurfaceDraw(font->pango_context);
1556     }
1557 #endif
1558 
1559   if (font->typ == FONT_TYPE_TTF)
1560     {
1561 #ifdef DEBUG
1562       printf("Calling TTF_RenderUTF8_Blended(\"%s\")\n", str);
1563       fflush(stdout);
1564 #endif
1565 
1566       ret = TTF_RenderUTF8_Blended(font->ttf_font, str, color);
1567     }
1568 
1569   if (ret)
1570     return ret;
1571 
1572   /* Sometimes a font will be missing a character we need. Sometimes the library
1573      will substitute a rectangle without telling us. Sometimes it returns NULL.
1574      Probably we should use FreeType directly. For now though... */
1575 
1576   height = 2;
1577 
1578   return thumbnail(img_title_large_off, height * strlen(str) / 2, height, 0);
1579 }
1580 
1581 
1582 /**
1583  * Convert a wide-character string to string of Uint16's.
1584  *
1585  * This conversion is required on platforms where Uint16 doesn't match wchar_t.
1586  * On Windows, wchar_t is 16-bit, elsewhere it is 32-bit.
1587  * Mismatch caused by the use of Uint16 for unicode characters by SDL, SDL_ttf.
1588  * I guess wchar_t is really only suitable for internal use ...
1589  *
1590  * @param str The wide-character string
1591  * @return The string, as Uint16 characters.
1592  */
wcstou16(const wchar_t * str)1593 static Uint16 *wcstou16(const wchar_t * str)
1594 {
1595   unsigned int i, len = wcslen(str);
1596   Uint16 *res = malloc((len + 1) * sizeof(Uint16));
1597 
1598   for (i = 0; i < len + 1; ++i)
1599     {
1600       /* This is a bodge, but it seems unlikely that a case-conversion
1601          will cause a change from one utf16 character into two....
1602          (though at least UTF-8 suffers from this problem) */
1603 
1604       /* FIXME: mangles non-BMP characters rather than using UTF-16 surrogates! */
1605       res[i] = (Uint16) str[i];
1606     }
1607 
1608   return res;
1609 }
1610 
1611 
1612 /**
1613  * Render some text (wide-characters) as a bitmap
1614  *
1615  * @param font The font to use
1616  * @param str The string of text to render
1617  * @param color The color to draw it in
1618  * @return A new surface, containing the rendered text
1619  */
render_text_w(TuxPaint_Font * restrict font,const wchar_t * restrict str,SDL_Color color)1620 static SDL_Surface *render_text_w(TuxPaint_Font * restrict font, const wchar_t * restrict str, SDL_Color color)
1621 {
1622   SDL_Surface *ret = NULL;
1623   int height;
1624   Uint16 *ustr;
1625 
1626 #ifndef NO_SDLPANGO
1627   unsigned int i, j;
1628   int utfstr_max;
1629   char *utfstr;
1630   SDLPango_Matrix pango_color;
1631 #endif
1632 
1633 #ifndef NO_SDLPANGO
1634   if (font->typ == FONT_TYPE_PANGO)
1635     {
1636       sdl_color_to_pango_color(color, &pango_color);
1637 
1638       SDLPango_SetDefaultColor(font->pango_context, &pango_color);
1639 
1640       /* Convert from 16-bit UNICODE to UTF-8 encoded for SDL_Pango: */
1641 
1642       utfstr_max = (sizeof(char) * 4 * (wcslen(str) + 1));
1643       utfstr = (char *)malloc(utfstr_max);
1644       memset(utfstr, 0, utfstr_max);
1645 
1646       j = 0;
1647       for (i = 0; i < wcslen(str); i++)
1648         {
1649           if (str[i] <= 0x0000007F)
1650             {
1651               /* 0x00000000 - 0x0000007F:
1652                  0xxxxxxx */
1653 
1654               utfstr[j++] = (str[i] & 0x7F);
1655             }
1656           else if (str[i] <= 0x000007FF)
1657             {
1658               /* 0x00000080 - 0x000007FF:
1659 
1660                  00000abc defghijk
1661                  110abcde 10fghijk */
1662 
1663               utfstr[j++] = (((str[i] & 0x0700) >> 6) | /* -----abc -------- to ---abc-- */
1664                              ((str[i] & 0x00C0) >> 6) | /* -------- de------ to ------de */
1665                              (0xC0));   /*                  add 110----- */
1666 
1667               utfstr[j++] = (((str[i] & 0x003F)) |      /* -------- --fghijk to --fghijk */
1668                              (0x80));   /*                  add 10------ */
1669             }
1670           else if (str[i] <= 0x0000FFFF)
1671             {
1672               /* 0x00000800 - 0x0000FFFF:
1673 
1674                  abcdefgh ijklmnop
1675                  1110abcd 10efghij 10klmnop */
1676 
1677               utfstr[j++] = (((str[i] & 0xF000) >> 12) |        /* abcd---- -------- to ----abcd */
1678                              (0xE0));   /*                  add 1110---- */
1679               utfstr[j++] = (((str[i] & 0x0FC0) >> 6) | /* ----efgh ij------ to --efghij */
1680                              (0x80));   /*                  add 10------ */
1681               utfstr[j++] = (((str[i] & 0x003F)) |      /* -------- --klmnop to --klmnop */
1682                              (0x80));   /*                  add 10------ */
1683             }
1684           else
1685             {
1686               /* 0x00010000 - 0x001FFFFF:
1687                  11110abc 10defghi 10jklmno 10pqrstu */
1688 
1689               utfstr[j++] = (((str[i] & 0x1C0000) >> 18) |      /* ---abc-- -------- --------  to -----abc */
1690                              (0xF0));   /*                            add 11110000 */
1691               utfstr[j++] = (((str[i] & 0x030000) >> 12) |      /* ------de -------- --------  to --de---- */
1692                              ((str[i] & 0x00F000) >> 12) |      /* -------- fghi---- --------  to ----fghi */
1693                              (0x80));   /*                            add 10------ */
1694               utfstr[j++] = (((str[i] & 0x000F00) >> 6) |       /* -------- ----jklm --------  to --jklm-- */
1695                              ((str[i] & 0x0000C0) >> 6) |       /* -------- -------- no------  to ------no */
1696                              (0x80));   /*                            add 10------ */
1697               utfstr[j++] = ((str[i] & 0x00003F) |      /* -------- -------- --pqrstu  to --prqstu */
1698                              (0x80));   /*                            add 10------ */
1699             }
1700         }
1701       utfstr[j] = '\0';
1702 
1703 
1704       SDLPango_SetText(font->pango_context, utfstr, -1);
1705       ret = SDLPango_CreateSurfaceDraw(font->pango_context);
1706     }
1707 #endif
1708 
1709   if (font->typ == FONT_TYPE_TTF)
1710     {
1711       ustr = wcstou16(str);
1712       ret = TTF_RenderUNICODE_Blended(font->ttf_font, ustr, color);
1713       free(ustr);
1714     }
1715 
1716   if (ret)
1717     return ret;
1718 
1719   /* Sometimes a font will be missing a character we need. Sometimes the library
1720      will substitute a rectangle without telling us. Sometimes it returns NULL.
1721      Probably we should use FreeType directly. For now though... */
1722 
1723   height = 2;
1724   return thumbnail(img_title_large_off, height * wcslen(str) / 2, height, 0);
1725 }
1726 
1727 
1728 typedef struct stamp_type
1729 {
1730   char *stampname;
1731   char *stxt;
1732   Uint8 locale_text;
1733 #ifndef NOSOUND
1734   Mix_Chunk *ssnd;
1735   Mix_Chunk *sdesc;
1736 #endif
1737 
1738   SDL_Surface *thumbnail;
1739   unsigned thumb_mirrored:1;
1740   unsigned thumb_flipped:1;
1741   unsigned thumb_mirrored_flipped:1;
1742   unsigned no_premirror:1;
1743   unsigned no_preflip:1;
1744   unsigned no_premirrorflip:1;
1745 
1746   unsigned processed:1;         /* got *.dat, computed size limits, etc. */
1747 
1748   unsigned no_sound:1;
1749   unsigned no_descsound:1;
1750   unsigned no_txt:1;
1751   /*  unsigned no_local_sound : 1;  *//* to remember, if code written to discard sound */
1752 
1753   unsigned tinter:3;
1754   unsigned colorable:1;
1755   unsigned tintable:1;
1756   unsigned mirrorable:1;
1757   unsigned flipable:1;
1758 
1759   unsigned mirrored:1;
1760   unsigned flipped:1;
1761   unsigned min:5;
1762   unsigned size:5;
1763   unsigned max:5;
1764 
1765   unsigned is_svg:1;
1766 } stamp_type;
1767 
1768 #define MAX_STAMP_GROUPS 256
1769 
1770 static unsigned int stamp_group_dir_depth = 1;  /* How deep (how many slashes in a subdirectory path) we think a new stamp group should be */
1771 
1772 static int stamp_group = 0;
1773 
1774 static const char *load_stamp_basedir;
1775 static int num_stamp_groups = 0;
1776 static int num_stamps[MAX_STAMP_GROUPS];
1777 static int max_stamps[MAX_STAMP_GROUPS];
1778 static stamp_type **stamp_data[MAX_STAMP_GROUPS];
1779 
1780 static SDL_Surface *active_stamp;
1781 
1782 
1783 /**
1784  * Returns whether a particular stamp can be colored.
1785  *
1786  * @param stamp Which stamp?
1787  * @return True/false
1788  */
stamp_colorable(int stamp)1789 static int stamp_colorable(int stamp)
1790 {
1791   return stamp_data[stamp_group][stamp]->colorable;
1792 }
1793 
1794 /**
1795  * Returns whether a particular stamp can be tinted.
1796  *
1797  * @param stamp Which stamp?
1798  * @return True/false
1799  */
stamp_tintable(int stamp)1800 static int stamp_tintable(int stamp)
1801 {
1802   return stamp_data[stamp_group][stamp]->tintable;
1803 }
1804 
1805 
1806 #define SHAPE_BRUSH_NAME "aa_round_03.png"
1807 static int num_brushes, num_brushes_max, shape_brush = 0;
1808 static SDL_Surface **img_brushes, **img_brushes_thumbs;
1809 static int *brushes_frames = NULL;
1810 static int *brushes_spacing = NULL;
1811 static short *brushes_directional = NULL;
1812 
1813 static SDL_Surface *img_shapes[NUM_SHAPES], *img_shape_names[NUM_SHAPES];
1814 static SDL_Surface *img_fills[NUM_FILLS], *img_fill_names[NUM_FILLS];
1815 static SDL_Surface *img_openlabels_open, *img_openlabels_erase,
1816   *img_openlabels_slideshow, *img_openlabels_back, *img_openlabels_play,
1817   *img_openlabels_gif_export, *img_openlabels_pict_export, *img_openlabels_next;
1818 
1819 static SDL_Surface *img_tux[NUM_TIP_TUX];
1820 
1821 static SDL_Surface *img_mouse, *img_mouse_click;
1822 
1823 #ifdef LOW_QUALITY_COLOR_SELECTOR
1824 static SDL_Surface *img_paintcan;
1825 #else
1826 static SDL_Surface **img_color_btns;
1827 static SDL_Surface *img_color_btn_off;
1828 #endif
1829 
1830 static int colors_are_selectable;
1831 
1832 enum
1833 {
1834   BRUSH_DIRECTION_RIGHT,
1835   BRUSH_DIRECTION_DOWN_RIGHT,
1836   BRUSH_DIRECTION_DOWN,
1837   BRUSH_DIRECTION_DOWN_LEFT,
1838   BRUSH_DIRECTION_LEFT,
1839   BRUSH_DIRECTION_UP_LEFT,
1840   BRUSH_DIRECTION_UP,
1841   BRUSH_DIRECTION_UP_RIGHT,
1842   BRUSH_DIRECTION_NONE
1843 };
1844 
1845 static SDL_Surface *img_cur_brush;
1846 static int img_cur_brush_frame_w, img_cur_brush_w, img_cur_brush_h,
1847   img_cur_brush_frames, img_cur_brush_directional, img_cur_brush_spacing;
1848 static int brush_counter, brush_frame;
1849 
1850 #define NUM_ERASERS 16          /* How many sizes of erasers
1851                                    (from ERASER_MIN to _MAX as squares, then again
1852                                    from ERASER_MIN to _MAX as circles; best if a
1853                                    multiple of 4, since selector is 2 buttons across) */
1854 #define ERASER_MIN 5            /* Smaller than 5 will not render as a circle! */
1855 #define ERASER_MAX 128
1856 
1857 
1858 static unsigned cur_color;
1859 static int cur_tool, cur_brush, old_tool;
1860 static int cur_stamp[MAX_STAMP_GROUPS];
1861 static int cur_shape, cur_magic;
1862 static int cur_font, cur_eraser, cur_fill, fill_drag_started;
1863 static int cursor_left, cursor_x, cursor_y, cursor_textwidth;   /* canvas-relative */
1864 static int old_cursor_x, old_cursor_y;
1865 static int cur_label, cur_select;
1866 static int been_saved;
1867 static char file_id[FILENAME_MAX];
1868 static char starter_id[FILENAME_MAX];
1869 static char template_id[FILENAME_MAX];
1870 static int brush_scroll;
1871 static int stamp_scroll[MAX_STAMP_GROUPS];
1872 static int font_scroll, magic_scroll, tool_scroll;
1873 static int eraser_scroll, shape_scroll, fill_scroll;
1874 
1875 static int eraser_sound;
1876 
1877 static IM_DATA im_data;
1878 static wchar_t texttool_str[256];
1879 static unsigned int texttool_len;
1880 
1881 static int tool_avail[NUM_TOOLS], tool_avail_bak[NUM_TOOLS];
1882 
1883 static Uint32 cur_toggle_count;
1884 
1885 static int num_wished_langs;
1886 
1887 typedef struct edge_type
1888 {
1889   int y_upper;
1890   float x_intersect, dx_per_scan;
1891   struct edge_type *next;
1892 } edge;
1893 
1894 
1895 typedef struct point_type
1896 {
1897   int x, y;
1898 } point_type;
1899 
1900 typedef struct fpoint_type
1901 {
1902   float x, y;
1903 } fpoint_type;
1904 
1905 typedef enum
1906 { Left, Right, Bottom, Top } an_edge;
1907 
1908 #define NUM_EDGES 4
1909 
1910 static SDL_Event scrolltimer_event;
1911 
1912 int non_left_click_count = 0;
1913 
1914 
1915 typedef struct dirent2
1916 {
1917   struct dirent f;
1918   int place;
1919 } dirent2;
1920 
1921 SDL_Joystick *joystick;
1922 
1923 /* Local function prototypes: */
1924 
1925 static void mainloop(void);
1926 static void brush_draw(int x1, int y1, int x2, int y2, int update);
1927 static void blit_brush(int x, int y, int direction);
1928 static void stamp_draw(int x, int y);
1929 static void rec_undo_buffer(void);
1930 
1931 void show_version(int details);
1932 void show_usage(int exitcode);
1933 static char *progname;
1934 
1935 static SDL_Cursor *get_cursor(unsigned char *bits, unsigned char *mask_bits,
1936                               unsigned int w, unsigned int h, unsigned int x, unsigned int y);
1937 static void seticon(void);
1938 static SDL_Surface *loadimage(const char *const fname);
1939 static SDL_Surface *do_loadimage(const char *const fname, int abort_on_error);
1940 static void draw_toolbar(void);
1941 static void draw_magic(void);
1942 static void draw_brushes(void);
1943 static void draw_stamps(void);
1944 static void draw_shapes(void);
1945 static void draw_erasers(void);
1946 static void draw_fonts(void);
1947 static void draw_fills(void);
1948 static void draw_none(void);
1949 
1950 static void do_undo(void);
1951 static void do_redo(void);
1952 static void render_brush(void);
1953 static void _xorpixel(SDL_Surface * surf, int x, int y);
1954 static void line_xor(int x1, int y1, int x2, int y2);
1955 static void rect_xor(int x1, int y1, int x2, int y2);
1956 static void draw_blinking_cursor(void);
1957 static void hide_blinking_cursor(void);
1958 
1959 static void reset_brush_counter(void);
1960 
1961 #ifdef LOW_QUALITY_STAMP_OUTLINE
1962 #define stamp_xor(x,y) rect_xor( \
1963   (x) - (CUR_STAMP_W+1)/2, \
1964   (y) - (CUR_STAMP_H+1)/2, \
1965   (x) + (CUR_STAMP_W+1)/2, \
1966   (y) + (CUR_STAMP_H+1)/2 \
1967 )
1968 #define update_stamp_xor()
1969 #else
1970 static void stamp_xor(int x1, int y1);
1971 static void update_stamp_xor(void);
1972 #endif
1973 
1974 static void set_active_stamp(void);
1975 
1976 static int calc_eraser_size(int which_eraser);
1977 static void do_eraser(int x, int y, int update);
1978 static void eraser_draw(int x1, int y1, int x2, int y2);
1979 static void disable_avail_tools(void);
1980 static void enable_avail_tools(void);
1981 static void reset_avail_tools(void);
1982 static int compare_dirent2s(struct dirent2 *f1, struct dirent2 *f2);
1983 static void redraw_tux_text(void);
1984 static void draw_tux_text(int which_tux, const char *const str, int want_right_to_left);
1985 static void draw_tux_text_ex(int which_tux, const char *const str, int want_right_to_left, Uint8 locale_text);
1986 static void wordwrap_text(const char *const str, SDL_Color color, int left, int top, int right, int want_right_to_left);
1987 static void wordwrap_text_ex(const char *const str, SDL_Color color,
1988                              int left, int top, int right, int want_right_to_left, Uint8 locale_text);
1989 static char *loaddesc(const char *const fname, Uint8 * locale_text);
1990 static double loadinfo(const char *const fname, stamp_type * inf);
1991 
1992 #ifndef NOSOUND
1993 static Mix_Chunk *loadsound(const char *const fname);
1994 static Mix_Chunk *loaddescsound(const char *const fname);
1995 static void playstampdesc(int chan);
1996 #endif
1997 static void do_wait(int counter);
1998 static void load_current(void);
1999 static void save_current(void);
2000 static int do_prompt_image_flash(const char *const text,
2001                                  const char *const btn_yes,
2002                                  const char *const btn_no, SDL_Surface * img1,
2003                                  SDL_Surface * img2, SDL_Surface * img3, int animate, int ox, int oy);
2004 static int do_prompt_image_flash_snd(const char *const text,
2005                                      const char *const btn_yes,
2006                                      const char *const btn_no,
2007                                      SDL_Surface * img1, SDL_Surface * img2,
2008                                      SDL_Surface * img3, int animate, int snd, int ox, int oy);
2009 static int do_prompt_image(const char *const text, const char *const btn_yes,
2010                            const char *const btn_no, SDL_Surface * img1,
2011                            SDL_Surface * img2, SDL_Surface * img3, int ox, int oy);
2012 static int do_prompt_image_snd(const char *const text,
2013                                const char *const btn_yes,
2014                                const char *const btn_no, SDL_Surface * img1,
2015                                SDL_Surface * img2, SDL_Surface * img3, int snd, int ox, int oy);
2016 static int do_prompt(const char *const text, const char *const btn_yes, const char *const btn_no, int ox, int oy);
2017 static int do_prompt_snd(const char *const text, const char *const btn_yes,
2018                          const char *const btn_no, int snd, int ox, int oy);
2019 static void cleanup(void);
2020 static void free_surface(SDL_Surface ** surface_array);
2021 static void free_surface_array(SDL_Surface * surface_array[], int count);
2022 
2023 /*static void update_shape(int cx, int ox1, int ox2, int cy, int oy1, int oy2,
2024                           int fixed); */
2025 static void do_shape(int sx, int sy, int nx, int ny, int rotn, int use_brush);
2026 static int shape_rotation(int ctr_x, int ctr_y, int ox, int oy);
2027 static int brush_rotation(int ctr_x, int ctr_y, int ox, int oy);
2028 static int do_save(int tool, int dont_show_success_results);
2029 static int do_png_save(FILE * fi, const char *const fname, SDL_Surface * surf, int embed);
2030 static void load_embedded_data(char *fname, SDL_Surface * org_surf);
2031 static int chunk_is_valid(const char *chunk_name, png_unknown_chunk unknown);
2032 Bytef *get_chunk_data(FILE * fp, char *fname, png_structp png_ptr,
2033                       png_infop info_ptr, const char *chunk_name, png_unknown_chunk unknown, int *unc_size);
2034 static void get_new_file_id(void);
2035 static int do_quit(int tool);
2036 static int do_open(void);
2037 static int do_new_dialog(void);
2038 static int do_new_dialog_add_colors(SDL_Surface * *thumbs, int num_files, int *d_places, char * *d_names,
2039                                     char * *d_exts, int *white_in_palette);
2040 static int do_color_picker(void);
2041 static int do_color_sel(void);
2042 
2043 static int do_slideshow(void);
2044 static void play_slideshow(int *selected, int num_selected, char *dirname, char **d_names, char **d_exts, int speed);
2045 static void draw_selection_digits(int right, int bottom, int n);
2046 
2047 static int export_gif(int *selected, int num_selected, char *dirname, char **d_names, char **d_exts, int speed);
2048 int export_gif_monitor_events(void);
2049 static int export_pict(char * fname);
2050 static char * get_export_filepath(const char * ext);
2051 
2052 static void wait_for_sfx(void);
2053 static void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v);
2054 static void hsvtorgb(float h, float s, float v, Uint8 * r8, Uint8 * g8, Uint8 * b8);
2055 
2056 static SDL_Surface *flip_surface(SDL_Surface * s);
2057 static SDL_Surface *mirror_surface(SDL_Surface * s);
2058 
2059 static void print_image(void);
2060 static void do_print(void);
2061 static void strip_trailing_whitespace(char *buf);
2062 static void do_render_cur_text(int do_blit);
2063 static char *uppercase(const char *restrict const str);
2064 static wchar_t *uppercase_w(const wchar_t * restrict const str);
2065 static char *textdir(const char *const str);
2066 static SDL_Surface *do_render_button_label(const char *const label);
2067 static void create_button_labels(void);
2068 static Uint32 scrolltimer_callback(Uint32 interval, void *param);
2069 static Uint32 drawtext_callback(Uint32 interval, void *param);
2070 static void control_drawtext_timer(Uint32 interval, const char *const text, Uint8 locale_text);
2071 static const char *great_str(void);
2072 static void draw_image_title(int t, SDL_Rect dest);
2073 static void handle_keymouse(SDLKey key, Uint8 updown, int steps, SDL_Rect * area1, SDL_Rect * area2);
2074 static void handle_keymouse_buttons(SDLKey key, int *whicht, int *whichc, SDL_Rect real_r_tools);
2075 static void handle_active(SDL_Event * event);
2076 
2077 /*static char *replace_tilde(const char* const path);*/
2078 #ifdef NO_SDLPANGO
2079 static void anti_carriage_return(int left, int right, int cur_top, int new_top, int cur_bot, int line_width);
2080 #endif
2081 static void load_starter_id(char *saved_id, FILE * fil);
2082 static void load_starter(char *img_id);
2083 static void load_template(char *img_id);
2084 static SDL_Surface *duplicate_surface(SDL_Surface * orig);
2085 static void mirror_starter(void);
2086 static void flip_starter(void);
2087 static int valid_click(Uint8 button);
2088 static int in_circle_rad(int x, int y, int rad);
2089 static int paintsound(int size);
2090 static void load_magic_plugins(void);
2091 static int magic_sort(const void *a, const void *b);
2092 
2093 Mix_Chunk *magic_current_snd_ptr;
2094 static void magic_playsound(Mix_Chunk * snd, int left_right, int up_down);
2095 static void magic_stopsound(void);
2096 static void magic_line_func(void *mapi,
2097                             int which, SDL_Surface * canvas, SDL_Surface * last,
2098                             int x1, int y1, int x2, int y2, int step,
2099                             void (*cb) (void *, int, SDL_Surface *, SDL_Surface *, int, int));
2100 
2101 static Uint8 magic_linear_to_sRGB(float lin);
2102 static float magic_sRGB_to_linear(Uint8 srgb);
2103 static int magic_button_down(void);
2104 static SDL_Surface *magic_scale(SDL_Surface * surf, int w, int h, int aspect);
2105 static void reset_touched(void);
2106 static Uint8 magic_touched(int x, int y);
2107 
2108 static void magic_switchin(SDL_Surface * last);
2109 static void magic_switchout(SDL_Surface * last);
2110 static int magic_modeint(int mode);
2111 
2112 #ifdef DEBUG
2113 static char *debug_gettext(const char *str);
2114 static int charsize(Uint16 c);
2115 #endif
2116 
2117 static SDL_Surface *load_kpx(const char *file);
2118 
2119 #ifndef NOSVG
2120 static SDL_Surface *load_svg(const char *file);
2121 static float pick_best_scape(unsigned int orig_w, unsigned int orig_h, unsigned int max_w, unsigned int max_h);
2122 #endif
2123 static SDL_Surface *myIMG_Load_RWops(const char *file);
2124 static SDL_Surface *myIMG_Load(const char *file);
2125 static int trash(char *path);
2126 int file_exists(char *path);
2127 
2128 int generate_fontconfig_cache_spinner(SDL_Surface * screen);
2129 
2130 char * safe_strncat(char *dest, const char *src, size_t n);
2131 char * safe_strncpy(char *dest, const char *src, size_t n);
2132 int safe_snprintf(char *str, size_t size, const char *format, ...);
2133 
2134 #define MAX_UTF8_CHAR_LENGTH 6
2135 
2136 #define USEREVENT_TEXT_UPDATE 1
2137 #define USEREVENT_PLAYDESCSOUND 2
2138 
2139 #define TP_SDL_MOUSEBUTTONSCROLL (SDL_USEREVENT + 1)
2140 
2141 static int bypass_splash_wait;
2142 
2143 /**
2144  * Wait for a keypress or mouse click.
2145  *
2146  * @param counter How long to wait (in 1/10th of seconds)
2147  */
do_wait(int counter)2148 static void do_wait(int counter)
2149 {
2150   SDL_Event event;
2151   int done;
2152 
2153   if (bypass_splash_wait)
2154     return;
2155 
2156   done = 0;
2157 
2158   do
2159     {
2160       while (SDL_PollEvent(&event))
2161         {
2162           if (event.type == SDL_QUIT)
2163             {
2164               done = 1;
2165 
2166               /* FIXME: Handle SDL_Quit better */
2167             }
2168           else if (event.type == SDL_ACTIVEEVENT)
2169             {
2170               handle_active(&event);
2171             }
2172           else if (event.type == SDL_KEYDOWN)
2173             {
2174               done = 1;
2175             }
2176           else if (event.type == SDL_MOUSEBUTTONDOWN && valid_click(event.button.button))
2177             {
2178               done = 1;
2179             }
2180         }
2181 
2182       counter--;
2183       SDL_Delay(100);
2184     }
2185   while (!done && counter > 0);
2186 }
2187 
2188 
2189 /* Prompt to confirm user wishes to quit */
2190 #define PROMPT_QUIT_TXT gettext_noop("Do you really want to quit?")
2191 
2192 /* Quit prompt positive response (quit) */
2193 #define PROMPT_QUIT_YES gettext_noop("Yes, I’m done!")
2194 
2195 /* Quit prompt negative response (don't quit) */
2196 #define PROMPT_QUIT_NO  gettext_noop("No, take me back!")
2197 
2198 
2199 /* Current picture is not saved; user is quitting */
2200 #define PROMPT_QUIT_SAVE_TXT gettext_noop("If you quit, you’ll lose your picture! Save it?")
2201 #define PROMPT_QUIT_SAVE_YES gettext_noop("Yes, save it!")
2202 #define PROMPT_QUIT_SAVE_NO gettext_noop("No, don’t bother saving!")
2203 
2204 /* Current picture is not saved; user is opening another picture */
2205 #define PROMPT_OPEN_SAVE_TXT gettext_noop("Save your picture first?")
2206 #define PROMPT_OPEN_SAVE_YES gettext_noop("Yes, save it!")
2207 #define PROMPT_OPEN_SAVE_NO gettext_noop("No, don’t bother saving!")
2208 
2209 /* Error opening picture */
2210 #define PROMPT_OPEN_UNOPENABLE_TXT gettext_noop("Can’t open that picture!")
2211 
2212 /* Generic dialog dismissal */
2213 #define PROMPT_OPEN_UNOPENABLE_YES gettext_noop("OK")
2214 
2215 
2216 /* Notification that 'Open' dialog has nothing to show */
2217 #define PROMPT_OPEN_NOFILES_TXT gettext_noop("There are no saved files!")
2218 #define PROMPT_OPEN_NOFILES_YES gettext_noop("OK")
2219 
2220 /* Verification of print action */
2221 #define PROMPT_PRINT_NOW_TXT gettext_noop("Print your picture now?")
2222 #define PROMPT_PRINT_NOW_YES gettext_noop("Yes, print it!")
2223 #define PROMPT_PRINT_NOW_NO gettext_noop("No, take me back!")
2224 
2225 /* Confirmation of successful (we hope) printing */
2226 #define PROMPT_PRINT_TXT gettext_noop("Your picture has been printed!")
2227 #define PROMPT_PRINT_YES gettext_noop("OK")
2228 
2229 /* We got an error printing */
2230 #define PROMPT_PRINT_FAILED_TXT gettext_noop("Sorry! Your picture could not be printed!")
2231 
2232 /* Notification that it's too soon to print again (--printdelay option is in effect) */
2233 #define PROMPT_PRINT_TOO_SOON_TXT gettext_noop("You can’t print yet!")
2234 #define PROMPT_PRINT_TOO_SOON_YES gettext_noop("OK")
2235 
2236 /* Prompt to confirm erasing a picture in the Open dialog */
2237 #define PROMPT_ERASE_TXT gettext_noop("Erase this picture?")
2238 #define PROMPT_ERASE_YES gettext_noop("Yes, erase it!")
2239 #define PROMPT_ERASE_NO gettext_noop("No, don’t erase it!")
2240 
2241 /* Reminder that Mouse Button 1 is the button to use in Tux Paint */
2242 #define PROMPT_TIP_LEFTCLICK_TXT gettext_noop("Remember to use the left mouse button!")
2243 #define PROMPT_TIP_LEFTCLICK_YES gettext_noop("OK")
2244 
2245 /* Confirmation of successful (we hope) image export */
2246 #define PROMPT_PICT_EXPORT_TXT gettext_noop("Your picture has been exported!")
2247 #define PROMPT_GIF_EXPORT_TXT gettext_noop("Your slideshow GIF has been exported!")
2248 #define PROMPT_EXPORT_YES gettext_noop("OK")
2249 
2250 /* We got an error exporting */
2251 #define PROMPT_PICT_EXPORT_FAILED_TXT gettext_noop("Sorry! Your picture could not be exported!")
2252 #define PROMPT_GIF_EXPORT_FAILED_TXT gettext_noop("Sorry! Your slideshow GIF could not be exported!")
2253 
2254 
2255 /* Slideshow instructions */
2256 #define TUX_TIP_SLIDESHOW gettext("Choose the pictures you want, then click “Play”.")
2257 
2258 
2259 enum
2260 {
2261   SHAPE_TOOL_MODE_STRETCH,
2262   SHAPE_TOOL_MODE_ROTATE,
2263   SHAPE_TOOL_MODE_DONE
2264 };
2265 
2266 int shape_reverse;
2267 
2268 
2269 int brushflag, xnew, ynew, eraflag, lineflag, magicflag, keybd_flag, keybd_position, keyglobal, initial_y, gen_key_flag,
2270   ide, activeflag, old_x, old_y;
2271 int cur_thing;
2272 
2273 /**
2274  * --- MAIN LOOP! ---
2275  */
mainloop(void)2276 static void mainloop(void)
2277 {
2278   int done, val_x, val_y, valhat_x, valhat_y, new_x, new_y,
2279     shape_tool_mode, shape_start_x, shape_start_y, shape_current_x, shape_current_y, old_stamp_group, which;
2280   int num_things;
2281   int *thing_scroll;
2282   int do_draw, max;
2283   int ignoring_motion;
2284   int motioner = 0;
2285   int hatmotioner = 0;
2286   int whichc = 0;
2287   int whicht = 0;
2288   int line_start_x = 0;
2289   int line_start_y = 0;
2290   int j = 0;
2291   int stamp_size_selector_clicked = 0;
2292   int stamp_xored = 0;
2293 
2294   unsigned int i = 0;
2295   SDL_TimerID scrolltimer = NULL;
2296   SDL_Event event;
2297   SDLKey key;
2298   SDLMod mod;
2299   Uint32 last_cursor_blink, cur_cursor_blink, pre_event_time, current_event_time;
2300   SDL_Rect update_rect;
2301   SDL_Rect real_r_tools = r_tools;
2302 
2303 #ifdef DEBUG
2304   Uint16 key_unicode;
2305   SDLKey key_down;
2306 #endif
2307   on_screen_keyboard *new_kbd;
2308   SDL_Rect kbd_rect;
2309 
2310   num_things = num_brushes;
2311   thing_scroll = &brush_scroll;
2312   cur_thing = 0;
2313   do_draw = 0;
2314   old_x = 0;
2315   old_y = 0;
2316   which = 0;
2317   shape_start_x = 0;
2318   shape_start_y = 0;
2319   shape_current_x = 0;
2320   shape_current_y = 0;
2321   shape_tool_mode = SHAPE_TOOL_MODE_DONE;
2322   button_down = 0;
2323   last_cursor_blink = cur_toggle_count = 0;
2324   texttool_len = 0;
2325   scrolling = 0;
2326   scrolltimer = 0;
2327   val_x = 0;
2328   val_y = 0;
2329   valhat_x = 0;
2330   valhat_y = 0;
2331   done = 0;
2332   keyglobal = 0;
2333   kbd = NULL;
2334 
2335   if (NUM_TOOLS > buttons_tall * gd_tools.cols)
2336     {
2337       real_r_tools.h = r_tools.h - button_h;
2338       real_r_tools.y = r_tools.y + button_h / 2;
2339     }
2340 
2341   do
2342     {
2343       ignoring_motion = 0;
2344 
2345       pre_event_time = SDL_GetTicks();
2346 
2347 
2348       while (SDL_PollEvent(&event))
2349         {
2350           current_event_time = SDL_GetTicks();
2351 
2352           /* To avoid getting stuck in a 'catching up with mouse motion' interface lock-up */
2353           /* FIXME: Another thing we could do here is peek into events, and 'skip' to the last motion...? Or something... -bjk 2011.04.26 */
2354           if (current_event_time > pre_event_time + 500 && event.type == SDL_MOUSEMOTION)
2355             ignoring_motion = (ignoring_motion + 1) % 3;        /* Ignore every couple of motion events, to keep things moving quickly (but avoid, e.g., attempts to draw "O" from looking like "D") */
2356 
2357 
2358           if (event.type == SDL_QUIT)
2359             {
2360               magic_switchout(canvas);
2361               done = do_quit(cur_tool);
2362               if (!done)
2363                 {
2364                   magic_switchin(canvas);
2365 
2366                   if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2367                     {
2368                       if (onscreen_keyboard && kbd)
2369                         {
2370                           SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
2371                           update_screen_rect(&kbd_rect);
2372                         }
2373                     }
2374                 }
2375             }
2376           else if (event.type == SDL_ACTIVEEVENT)
2377             {
2378               /* Reset Shapes tool and clean the canvas if we lose focus */
2379               if (mouseaccessibility && emulate_button_pressed &&
2380                   ((cur_tool == TOOL_SHAPES && shape_tool_mode != SHAPE_TOOL_MODE_DONE) || cur_tool == TOOL_LINES) &&
2381                   event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE) && event.active.gain == 0)
2382                 {
2383                   do_undo();
2384                   tool_avail[TOOL_REDO] = 0;    /* Don't let them 'redo' to get preview back */
2385                   draw_toolbar();
2386                   update_screen_rect(&r_tools);
2387                   shape_tool_mode = SHAPE_TOOL_MODE_DONE;
2388                 }
2389               handle_active(&event);
2390 
2391             }
2392           else if (event.type == SDL_KEYUP)
2393             {
2394               key = event.key.keysym.sym;
2395 
2396               handle_keymouse(key, SDL_KEYUP, 16, NULL, NULL);
2397             }
2398 
2399           else if (event.type == SDL_KEYDOWN)
2400             {
2401               key = event.key.keysym.sym;
2402               mod = event.key.keysym.mod;
2403 
2404 #ifdef DEBUG
2405 /* FIXME: debug junk */
2406               fprintf(stderr,
2407                       "key 0x%04x mod 0x%04x character 0x%04x %d <%c> is %sprintable, key_down 0x%x\n",
2408                       (unsigned)key,
2409                       (unsigned)mod,
2410                       (unsigned)event.key.keysym.unicode,
2411                       (int)event.key.keysym.unicode,
2412                       (key_unicode > ' ' && key_unicode < 127) ? (char)event.key.keysym.unicode : ' ',
2413                       iswprint(key_unicode) ? "" : "not ", (unsigned)key_down);
2414 #endif
2415               if (cur_tool == TOOL_STAMP)
2416                 {
2417                   SDL_Rect r_stamps_sizesel;
2418 
2419                   r_stamps_sizesel.x = r_canvas.x + r_canvas.w;
2420                   r_stamps_sizesel.y = r_canvas.h - img_btn_up->h;
2421                   r_stamps_sizesel.w = img_btn_up->w * 2;
2422                   r_stamps_sizesel.h = img_btn_up->h;
2423                   handle_keymouse(key, SDL_KEYDOWN, 16, &r_canvas, &r_stamps_sizesel);
2424                 }
2425               else
2426                 handle_keymouse(key, SDL_KEYDOWN, 16, &r_canvas, NULL);
2427 
2428               /* handle_keymouse_buttons will move one button at a time */
2429               handle_keymouse_buttons(key, &whicht, &whichc, real_r_tools);
2430 
2431 
2432               if (key == SDLK_ESCAPE && !disable_quit)
2433                 {
2434                   magic_switchout(canvas);
2435                   done = do_quit(cur_tool);
2436                   if (!done)
2437                     {
2438                       magic_switchin(canvas);
2439 
2440                       if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2441                         {
2442                           if (onscreen_keyboard && kbd)
2443                             {
2444                               SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
2445                               update_screen_rect(&kbd_rect);
2446                             }
2447                         }
2448                     }
2449                 }
2450               else if (key == SDLK_s && (mod & KMOD_ALT))
2451                 {
2452 #ifndef NOSOUND
2453                   if (use_sound)
2454                     {
2455 #ifdef DEBUG
2456                       printf("modstate at mainloop %d, mod %d\n", SDL_GetModState(), mod);
2457 #endif
2458 
2459                       mute = !mute;
2460                       Mix_HaltChannel(-1);
2461 
2462                       if (mute)
2463                         {
2464                           /* Sound has been muted (silenced) via keyboard shortcut */
2465                           draw_tux_text(TUX_BORED, gettext("Sound muted."), 0);
2466                         }
2467                       else
2468                         {
2469                           /* Sound has been unmuted (unsilenced) via keyboard shortcut */
2470                           draw_tux_text(TUX_BORED, gettext("Sound unmuted."), 0);
2471                         }
2472                     }
2473 #endif
2474                 }
2475               else if (key == SDLK_ESCAPE && (mod & KMOD_SHIFT) && (mod & KMOD_CTRL))
2476                 {
2477                   magic_switchout(canvas);
2478                   done = do_quit(cur_tool);
2479                   if (!done)
2480                     magic_switchin(canvas);
2481                 }
2482 #ifdef WIN32
2483               else if (key == SDLK_F4 && (mod & KMOD_ALT))
2484                 {
2485                   magic_switchout(canvas);
2486                   done = do_quit(cur_tool);
2487                   if (!done)
2488                     magic_switchin(canvas);
2489                 }
2490 #endif
2491               else if (key == SDLK_z && (mod & KMOD_CTRL) && !noshortcuts)
2492                 {
2493                   /* Ctrl-Z - Undo */
2494 
2495                   magic_switchout(canvas);
2496 
2497                   if (tool_avail[TOOL_UNDO])
2498                     {
2499                       if (cursor_x != -1 && cursor_y != -1)
2500                         {
2501                           hide_blinking_cursor();
2502                           if (texttool_len > 0)
2503                             {
2504                               rec_undo_buffer();
2505                               do_render_cur_text(1);
2506                               texttool_len = 0;
2507                               cursor_textwidth = 0;
2508                               label_node_to_edit = NULL;
2509                             }
2510                           else if (cur_tool == TOOL_LABEL && label_node_to_edit)
2511                             {
2512                               rec_undo_buffer();
2513                               have_to_rec_label_node = TRUE;
2514                               add_label_node(0, 0, 0, 0, NULL);
2515                               derender_node(&label_node_to_edit);
2516                               label_node_to_edit = NULL;
2517                             }
2518                         }
2519 
2520                       if (cur_undo == newest_undo)
2521                         {
2522                           rec_undo_buffer();
2523                           do_undo();
2524                         }
2525                       do_undo();
2526                       update_screen_rect(&r_tools);
2527                       shape_tool_mode = SHAPE_TOOL_MODE_DONE;
2528                     }
2529 
2530                   magic_switchin(canvas);
2531                 }
2532               else if (key == SDLK_r && (mod & KMOD_CTRL) && !noshortcuts)
2533                 {
2534                   /* Ctrl-R - Redo */
2535 
2536                   magic_switchout(canvas);
2537 
2538                   if (tool_avail[TOOL_REDO])
2539                     {
2540                       hide_blinking_cursor();
2541                       do_redo();
2542                       update_screen_rect(&r_tools);
2543                       shape_tool_mode = SHAPE_TOOL_MODE_DONE;
2544                     }
2545 
2546                   magic_switchin(canvas);
2547                 }
2548               else if (key == SDLK_o && (mod & KMOD_CTRL) && !noshortcuts)
2549                 {
2550                   /* Ctrl-O - Open */
2551 
2552                   magic_switchout(canvas);
2553 
2554                   disable_avail_tools();
2555                   draw_toolbar();
2556                   draw_colors(COLORSEL_CLOBBER_WIPE);
2557                   draw_none();
2558 
2559                   if (do_open() == 0)
2560                     {
2561                       if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2562                         do_render_cur_text(0);
2563                     }
2564 
2565                   enable_avail_tools();
2566 
2567                   draw_toolbar();
2568                   update_screen_rect(&r_tools);
2569                   draw_colors(COLORSEL_REFRESH);
2570 
2571                   if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
2572                     draw_brushes();
2573                   else if (cur_tool == TOOL_MAGIC)
2574                     draw_magic();
2575                   else if (cur_tool == TOOL_STAMP)
2576                     draw_stamps();
2577                   else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2578                     {
2579                       draw_fonts();
2580                       if (onscreen_keyboard && kbd)
2581                         {
2582                           SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
2583                           update_screen_rect(&kbd_rect);
2584                         }
2585                     }
2586                   else if (cur_tool == TOOL_SHAPES)
2587                     draw_shapes();
2588                   else if (cur_tool == TOOL_ERASER)
2589                     draw_erasers();
2590                   else if (cur_tool == TOOL_FILL)
2591                     draw_fills();
2592 
2593                   draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 1);
2594 
2595                   /* FIXME: Make delay configurable: */
2596                   control_drawtext_timer(1000, tool_tips[cur_tool], 0);
2597 
2598                   magic_switchin(canvas);
2599                 }
2600               else if ((key == SDLK_n && (mod & KMOD_CTRL)) && !noshortcuts)
2601                 {
2602                   /* Ctrl-N - New */
2603 
2604                   magic_switchout(canvas);
2605 
2606                   hide_blinking_cursor();
2607                   shape_tool_mode = SHAPE_TOOL_MODE_DONE;
2608 
2609                   disable_avail_tools();
2610                   draw_toolbar();
2611                   draw_colors(COLORSEL_CLOBBER_WIPE);
2612                   draw_none();
2613 
2614                   if (do_new_dialog() == 0)
2615                     {
2616                       draw_tux_text(tool_tux[TUX_DEFAULT], TIP_NEW_ABORT, 1);
2617 
2618                       if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2619                         do_render_cur_text(0);
2620                     }
2621 
2622                   enable_avail_tools();
2623 
2624                   draw_toolbar();
2625                   update_screen_rect(&r_tools);
2626                   draw_colors(COLORSEL_REFRESH);
2627 
2628                   if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
2629                     draw_brushes();
2630                   else if (cur_tool == TOOL_MAGIC)
2631                     draw_magic();
2632                   else if (cur_tool == TOOL_STAMP)
2633                     draw_stamps();
2634                   else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2635                     {
2636                       draw_fonts();
2637                       if (onscreen_keyboard && kbd)
2638                         {
2639                           SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
2640                           update_screen_rect(&kbd_rect);
2641                         }
2642                     }
2643                   else if (cur_tool == TOOL_SHAPES)
2644                     draw_shapes();
2645                   else if (cur_tool == TOOL_ERASER)
2646                     draw_erasers();
2647                   else if (cur_tool == TOOL_FILL)
2648                     draw_fills();
2649 
2650                   update_screen_rect(&r_toolopt);
2651                   update_screen_rect(&r_ttoolopt);
2652                   magic_switchin(canvas);
2653                 }
2654               else if (key == SDLK_s && (mod & KMOD_CTRL) && !noshortcuts)
2655                 {
2656                   /* Ctrl-S - Save */
2657 
2658                   magic_switchout(canvas);
2659                   hide_blinking_cursor();
2660 
2661                   if (do_save(cur_tool, 0))
2662                     {
2663                       /* Only think it's been saved if it HAS been saved :^) */
2664 
2665                       been_saved = 1;
2666                       tool_avail[TOOL_SAVE] = 0;
2667                     }
2668 
2669                   draw_toolbar();
2670                   update_screen_rect(&r_tools);
2671                   if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2672                     {
2673                       if (onscreen_keyboard && kbd)
2674                         {
2675                           SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
2676                           update_screen_rect(&kbd_rect);
2677                         }
2678                     }
2679 
2680                   magic_switchin(canvas);
2681                 }
2682 #ifdef __APPLE__
2683               else if (key == SDLK_p && (mod & KMOD_CTRL) && (mod & KMOD_SHIFT) && !noshortcuts)
2684                 {
2685                   /* Ctrl-Shft-P - Page Setup */
2686                   if (!disable_print)
2687                     DisplayPageSetup(canvas);
2688                 }
2689 #endif
2690               else if (key == SDLK_p && (mod & KMOD_CTRL) && !noshortcuts)
2691                 {
2692                   /* Ctrl-P - Print */
2693 
2694                   if (!disable_print)
2695                     {
2696                       magic_switchout(canvas);
2697 
2698                       /* If they haven't hit [Enter], but clicked 'Print', add their text now -bjk 2007.10.25 */
2699 
2700                       tmp_apply_uncommited_text();
2701                       print_image();
2702                       undo_tmp_applied_text();
2703                       magic_switchin(canvas);
2704 
2705                       if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
2706                         {
2707                           if (onscreen_keyboard && kbd)
2708                             {
2709                               SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
2710                               update_screen_rect(&kbd_rect);
2711                             }
2712                         }
2713 
2714                       draw_toolbar();
2715                       draw_tux_text(TUX_BORED, "", 0);
2716                       update_screen_rect(&r_tools);
2717                     }
2718                 }
2719               else
2720                 {
2721                   /* Handle key in text tool: */
2722 
2723                   if (((cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL) && cursor_x != -1 && cursor_y != -1) ||
2724                       (cur_tool == TOOL_LABEL && cur_label == LABEL_SELECT))
2725                     {
2726                       static int redraw = 0;
2727                       wchar_t *im_cp = im_data.s;
2728 
2729 #ifdef DEBUG
2730                       key_down = key;
2731                       key_unicode = event.key.keysym.unicode;
2732                       printf("character 0x%04x %d <%c> is %d pixels, %sprintable, key_down 0x%x\n",
2733                              (unsigned)event.key.keysym.unicode,
2734                              (int)event.key.keysym.unicode,
2735                              (key_unicode > ' ' && key_unicode < 127) ? (char)event.key.keysym.unicode : ' ',
2736                              (int)charsize(event.key.keysym.unicode),
2737                              iswprint(key_unicode) ? "" : "not ", (unsigned)key_down);
2738 #if 0
2739                       /* this doesn't work for some reason */
2740                       wprintf(L"character 0x%04x %d <%lc> is %d pixels, %lsprintable, key_down 0x%x\n",
2741                               event.key.keysym.unicode,
2742                               event.key.keysym.unicode,
2743                               (key_unicode > L' ') ? event.key.keysym.unicode : L' ',
2744                               charsize(event.key.keysym.unicode), iswprint(key_unicode) ? L"" : L"not ", key_down);
2745 #endif
2746 #endif
2747 
2748                       /* Discard previous # of redraw characters */
2749                       if ((int)texttool_len <= redraw)
2750                         texttool_len = 0;
2751                       else
2752                         texttool_len -= redraw;
2753                       texttool_str[texttool_len] = L'\0';
2754 
2755                       /* Read IM, remember how many to redraw next iteration */
2756                       redraw = im_read(&im_data, event.key.keysym);
2757 
2758                       /* Korean Hangul needs this to refresh when buffered chars gets emptied */
2759                       if (!*im_cp)
2760                         do_render_cur_text(0);
2761 
2762                       /* Queue each character to be displayed */
2763                       while (*im_cp)
2764                         {
2765                           if (*im_cp == L'\b')
2766                             {
2767                               hide_blinking_cursor();
2768                               if (texttool_len > 0)
2769                                 {
2770                                   texttool_len--;
2771                                   texttool_str[texttool_len] = 0;
2772                                   playsound(screen, 0, SND_KEYCLICK, 0, SNDPOS_CENTER, SNDDIST_NEAR);
2773 
2774                                   do_render_cur_text(0);
2775 
2776                                   if (been_saved)
2777                                     {
2778                                       been_saved = 0;
2779 
2780                                       if (!disable_save)
2781                                         tool_avail[TOOL_SAVE] = 1;
2782 
2783                                       draw_toolbar();
2784                                       update_screen_rect(&r_tools);
2785                                     }
2786 
2787                                 }
2788                             }
2789                           else if (*im_cp == L'\r')
2790                             {
2791                               int font_height;
2792 
2793                               font_height = TuxPaint_Font_FontHeight(getfonthandle(cur_font));
2794 
2795                               hide_blinking_cursor();
2796                               if (texttool_len > 0)
2797                                 {
2798                                   rec_undo_buffer();
2799                                   do_render_cur_text(1);
2800                                   label_node_to_edit = NULL;
2801                                   texttool_len = 0;
2802                                   cursor_textwidth = 0;
2803                                   if (cur_tool == TOOL_LABEL)
2804                                     {
2805                                       draw_fonts();
2806                                       update_screen_rect(&r_toolopt);
2807                                     }
2808 
2809                                   if (been_saved)
2810                                     {
2811                                       been_saved = 0;
2812 
2813                                       if (!disable_save)
2814                                         tool_avail[TOOL_SAVE] = 1;
2815 
2816                                       draw_toolbar();
2817                                       update_screen_rect(&r_tools);
2818                                     }
2819 
2820 
2821                                   cursor_x = cursor_left;
2822                                   cursor_y = min(cursor_y + font_height, canvas->h - font_height);
2823 
2824                                   playsound(screen, 0, SND_RETURN, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
2825 
2826                                 }
2827                               else if (cur_tool == TOOL_LABEL && label_node_to_edit)
2828                                 {
2829                                   rec_undo_buffer();
2830                                   have_to_rec_label_node = TRUE;
2831                                   add_label_node(0, 0, 0, 0, NULL);
2832                                   derender_node(&label_node_to_edit);
2833                                   label_node_to_edit = NULL;
2834                                   /* playsound(screen, 0, SND_DELETE_LABEL, 0, SNDPOS_CENTER); *//* FIXME lack of specific sound */
2835 
2836                                   if (been_saved)
2837                                     {
2838                                       been_saved = 0;
2839 
2840                                       if (!disable_save)
2841                                         tool_avail[TOOL_SAVE] = 1;
2842 
2843                                       draw_toolbar();
2844                                       update_screen_rect(&r_tools);
2845                                     }
2846                                 }
2847 
2848                               /* Select a node to edit */
2849                               else if (cur_tool == TOOL_LABEL && cur_label == LABEL_SELECT)
2850                                 {
2851                                   label_node_to_edit =
2852                                     search_label_list(&highlighted_label_node, highlighted_label_node->save_x + 3,
2853                                                       highlighted_label_node->save_y + 3, 0);
2854                                   if (label_node_to_edit)
2855                                     {
2856                                       cur_label = LABEL_LABEL;
2857                                       cur_thing = label_node_to_edit->save_cur_font;
2858                                       do_setcursor(cursor_insertion);
2859                                       i = 0;
2860                                       label_node_to_edit->is_enabled = FALSE;
2861                                       derender_node(&label_node_to_edit);
2862 
2863                                       texttool_len = select_texttool_len;
2864                                       while (i < texttool_len)
2865                                         {
2866                                           texttool_str[i] = select_texttool_str[i];
2867                                           i = i + 1;
2868                                         }
2869                                       texttool_str[i] = L'\0';
2870                                       cur_color = select_color;
2871                                       old_x = select_x;
2872                                       old_y = select_y;
2873                                       cur_font = select_cur_font;
2874                                       text_state = select_text_state;
2875                                       text_size = select_text_size;
2876                                       for (j = 0; j < num_font_families; j++)
2877                                         {
2878                                           if (user_font_families[j] && user_font_families[j]->handle)
2879                                             {
2880                                               TuxPaint_Font_CloseFont(user_font_families[j]->handle);
2881                                               user_font_families[j]->handle = NULL;
2882                                             }
2883                                         }
2884                                       draw_fonts();
2885                                       update_screen_rect(&r_toolopt);
2886 
2887                                       cursor_x = old_x;
2888                                       cursor_y = old_y;
2889                                       cursor_left = old_x;
2890 
2891                                       draw_colors(COLORSEL_REFRESH);
2892                                       draw_fonts();
2893                                     }
2894 
2895 
2896                                   do_render_cur_text(0);
2897 
2898                                 }
2899                               else
2900                                 {
2901                                   cursor_x = cursor_left;
2902                                   cursor_y = min(cursor_y + font_height, canvas->h - font_height);
2903                                 }
2904 
2905 #ifdef SPEECH
2906 #ifdef __APPLE__
2907                               if (use_sound)
2908                                 speak_string(texttool_str);
2909 #endif
2910 #endif
2911                               im_softreset(&im_data);
2912                             }
2913                           else if (*im_cp == L'\t')
2914                             {
2915 
2916                               if (texttool_len > 0)
2917                                 {
2918                                   rec_undo_buffer();
2919                                   do_render_cur_text(1);
2920                                   label_node_to_edit = NULL;
2921                                   cursor_x = min(cursor_x + cursor_textwidth, canvas->w);
2922                                   texttool_len = 0;
2923                                   cursor_textwidth = 0;
2924                                   if (cur_tool == TOOL_LABEL)
2925                                     {
2926                                       draw_fonts();
2927                                       update_screen_rect(&r_toolopt);
2928                                     }
2929 
2930                                   if (been_saved)
2931                                     {
2932                                       been_saved = 0;
2933 
2934                                       if (!disable_save)
2935                                         tool_avail[TOOL_SAVE] = 1;
2936 
2937                                       draw_toolbar();
2938                                       update_screen_rect(&r_tools);
2939                                     }
2940                                 }
2941                               else if (cur_tool == TOOL_LABEL && label_node_to_edit)
2942                                 {
2943                                   rec_undo_buffer();
2944                                   have_to_rec_label_node = TRUE;
2945                                   add_label_node(0, 0, 0, 0, NULL);
2946                                   derender_node(&label_node_to_edit);
2947                                   label_node_to_edit = NULL;
2948                                   /* playsound(screen, 0, SND_DELETE_LABEL, 0, SNDPOS_CENTER); *//* FIXME lack of specific sound */
2949 
2950                                   if (been_saved)
2951                                     {
2952                                       been_saved = 0;
2953 
2954                                       if (!disable_save)
2955                                         tool_avail[TOOL_SAVE] = 1;
2956 
2957                                       draw_toolbar();
2958                                       update_screen_rect(&r_tools);
2959                                     }
2960                                 }
2961                               /* Cycle accross the nodes */
2962                               else if (cur_tool == TOOL_LABEL && cur_label == LABEL_SELECT)
2963                                 {
2964                                   cycle_highlighted_label_node();
2965                                   highlight_label_nodes();
2966                                 }
2967 
2968 
2969 
2970 #ifdef SPEECH
2971 #ifdef __APPLE__
2972                               if (use_sound)
2973                                 speak_string(texttool_str);
2974 #endif
2975 #endif
2976                               im_softreset(&im_data);
2977                             }
2978                           else if (iswprint(*im_cp) && (cur_tool == TOOL_TEXT || cur_label == LABEL_LABEL))
2979                             {
2980                               if (texttool_len < (sizeof(texttool_str) / sizeof(wchar_t)) - 1)
2981                                 {
2982                                   int old_cursor_textwidth = cursor_textwidth;
2983 
2984 #ifdef DEBUG
2985                                   wprintf(L"    key = <%c>\nunicode = <%lc> 0x%04x %d\n\n",
2986                                           key_down, key_unicode, key_unicode, key_unicode);
2987 #endif
2988 
2989                                   texttool_str[texttool_len++] = *im_cp;
2990                                   texttool_str[texttool_len] = 0;
2991 
2992                                   do_render_cur_text(0);
2993 
2994                                   if (been_saved)
2995                                     {
2996                                       been_saved = 0;
2997 
2998                                       if (!disable_save)
2999                                         tool_avail[TOOL_SAVE] = 1;
3000 
3001                                       draw_toolbar();
3002                                       update_screen_rect(&r_tools);
3003                                     }
3004 
3005 
3006                                   if (cursor_x + old_cursor_textwidth <= canvas->w - 50 &&
3007                                       cursor_x + cursor_textwidth > canvas->w - 50)
3008                                     {
3009                                       playsound(screen, 0, SND_KEYCLICKRING, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
3010                                     }
3011                                   else
3012                                     {
3013                                       /* FIXME: Might be fun to position the
3014                                          sound based on keyboard layout...? */
3015 
3016                                       playsound(screen, 0, SND_KEYCLICK, 0, SNDPOS_CENTER, SNDDIST_NEAR);
3017                                     }
3018                                 }
3019                             }
3020 
3021                           im_cp++;
3022                         }       /* while(*im_cp) */
3023 
3024                       /* Show IM tip text */
3025                       if (im_data.tip_text)
3026                         {
3027                           draw_tux_text(TUX_DEFAULT, im_data.tip_text, 1);
3028                         }
3029 
3030                     }
3031                 }
3032             }
3033 
3034           else if (event.type == SDL_JOYAXISMOTION)
3035             handle_joyaxismotion(event, &motioner, &val_x, &val_y);
3036 
3037           else if (event.type == SDL_JOYHATMOTION)
3038             handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
3039 
3040           else if (event.type == SDL_JOYBALLMOTION)
3041             handle_joyballmotion(event, oldpos_x, oldpos_y);
3042 
3043           else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
3044             handle_joybuttonupdownscl(event, oldpos_x, oldpos_y, real_r_tools);
3045 
3046           else if (event.type == SDL_MOUSEBUTTONDOWN &&
3047                    event.button.button >= 2 &&
3048                    event.button.button <= 3 &&
3049                    (no_button_distinction == 0 && !(HIT(r_tools) && GRIDHIT_GD(r_tools, gd_tools) == TOOL_PRINT)))
3050             {
3051               /* They're using the middle or right mouse buttons! */
3052 
3053               non_left_click_count++;
3054 
3055 
3056               if (non_left_click_count == 10 || non_left_click_count == 20 || (non_left_click_count % 50) == 0)
3057                 {
3058                   /* Pop up an informative animation: */
3059 
3060                   hide_blinking_cursor();
3061                   do_prompt_image_flash(PROMPT_TIP_LEFTCLICK_TXT,
3062                                         PROMPT_TIP_LEFTCLICK_YES,
3063                                         "", img_mouse, img_mouse_click, NULL, 1, event.button.x, event.button.y);
3064                   if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
3065                     do_render_cur_text(0);
3066                   draw_tux_text(TUX_BORED, "", 0);
3067                 }
3068             }
3069           else if ((event.type == SDL_MOUSEBUTTONDOWN ||
3070                     event.type == TP_SDL_MOUSEBUTTONSCROLL) && event.button.button <= 3)
3071             {
3072 
3073 
3074               if (HIT(r_tools))
3075                 {
3076 
3077 
3078                   if (HIT(real_r_tools))
3079                     {
3080                       /* A tool on the left has been pressed! */
3081                       brushflag = 0;
3082                       magicflag = 0;
3083                       magic_switchout(canvas);
3084                       whicht = tool_scroll + GRIDHIT_GD(real_r_tools, gd_tools);
3085 
3086                       if (whicht < NUM_TOOLS && tool_avail[whicht] &&
3087                           (valid_click(event.button.button) || whicht == TOOL_PRINT))
3088                         {
3089                           /* Allow middle/right-click on "Print", since [Alt]+click
3090                              on Mac OS X changes it from left click to middle! */
3091 
3092                           /* Render any current text, if switching to a different
3093                              drawing tool: */
3094 
3095                           if ((cur_tool == TOOL_TEXT && whicht != TOOL_TEXT &&
3096                                whicht != TOOL_NEW && whicht != TOOL_OPEN &&
3097                                whicht != TOOL_SAVE && whicht != TOOL_PRINT &&
3098                                whicht != TOOL_QUIT) ||
3099                               (cur_tool == TOOL_LABEL && whicht != TOOL_LABEL &&
3100                                whicht != TOOL_NEW && whicht != TOOL_OPEN &&
3101                                whicht != TOOL_SAVE && whicht != TOOL_PRINT && whicht != TOOL_QUIT))
3102                             {
3103                               if (cursor_x != -1 && cursor_y != -1)
3104                                 {
3105                                   hide_blinking_cursor();
3106                                   if (texttool_len > 0)
3107                                     {
3108                                       rec_undo_buffer();
3109                                       do_render_cur_text(1);
3110                                       texttool_len = 0;
3111                                       cursor_textwidth = 0;
3112                                       label_node_to_edit = NULL;
3113                                     }
3114                                   else if (cur_tool == TOOL_LABEL && label_node_to_edit)
3115                                     {
3116                                       rec_undo_buffer();
3117                                       have_to_rec_label_node = TRUE;
3118                                       add_label_node(0, 0, 0, 0, NULL);
3119                                       derender_node(&label_node_to_edit);
3120                                       label_node_to_edit = NULL;
3121                                     }
3122                                 }
3123                             }
3124                           update_canvas(0, 0, WINDOW_WIDTH - r_ttoolopt.w, (button_h * buttons_tall) + r_ttools.h);
3125 
3126                           old_tool = cur_tool;
3127                           cur_tool = whicht;
3128                           draw_toolbar();
3129                           update_screen_rect(&r_tools);
3130 
3131                           playsound(screen, 1, SND_CLICK, 0, SNDPOS_LEFT, SNDDIST_NEAR);
3132 
3133                           /* FIXME: this "if" is just plain gross */
3134                           if (cur_tool != TOOL_TEXT)
3135                             draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool], 1);
3136 
3137                           /* Draw items for this tool: */
3138 
3139                           if (cur_tool == TOOL_BRUSH)
3140                             {
3141                               keybd_flag = 0;
3142                               cur_thing = cur_brush;
3143                               num_things = num_brushes;
3144                               thing_scroll = &brush_scroll;
3145                               draw_brushes();
3146                               draw_colors(COLORSEL_ENABLE);
3147                             }
3148                           else if (cur_tool == TOOL_STAMP)
3149                             {
3150                               keybd_flag = 0;
3151                               cur_thing = cur_stamp[stamp_group];
3152                               num_things = num_stamps[stamp_group];
3153                               thing_scroll = &(stamp_scroll[stamp_group]);
3154                               draw_stamps();
3155                               draw_colors(stamp_colorable(cur_stamp[stamp_group]) ||
3156                                           stamp_tintable(cur_stamp[stamp_group]));
3157                               set_active_stamp();
3158                               update_stamp_xor();
3159                             }
3160                           else if (cur_tool == TOOL_LINES)
3161                             {
3162                               keybd_flag = 0;
3163                               cur_thing = cur_brush;
3164                               num_things = num_brushes;
3165                               thing_scroll = &brush_scroll;
3166                               draw_brushes();
3167                               draw_colors(COLORSEL_ENABLE);
3168                             }
3169                           else if (cur_tool == TOOL_FILL)
3170                             {
3171                               keybd_flag = 0;
3172                               cur_thing = cur_fill;
3173                               num_things = NUM_FILLS;
3174                               thing_scroll = &fill_scroll;
3175                               draw_fills();
3176                               draw_colors(COLORSEL_ENABLE);
3177                             }
3178                           else if (cur_tool == TOOL_SHAPES)
3179                             {
3180                               keybd_flag = 0;
3181                               cur_thing = cur_shape;
3182                               num_things = NUM_SHAPES;
3183                               thing_scroll = &shape_scroll;
3184                               draw_shapes();
3185                               draw_colors(COLORSEL_ENABLE);
3186                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
3187                             }
3188                           else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
3189                             {
3190                               if (onscreen_keyboard)
3191                                 {
3192                                   if (kbd == NULL)
3193                                     {
3194                                       if (onscreen_keyboard_layout)
3195                                         kbd =
3196                                           osk_create(onscreen_keyboard_layout, screen,
3197                                                      img_btn_up, img_btn_down, img_btn_off,
3198                                                      img_btn_nav, img_btn_hold,
3199                                                      img_oskdel, img_osktab, img_oskenter,
3200                                                      img_oskcapslock, img_oskshift,
3201                                                      img_btnsm_up, img_btnsm_down, img_btnsm_off,
3202                                                      img_btnsm_nav, img_btnsm_hold,
3203                                                      /* FIXME */
3204                                                      img_oskdel, img_osktab, img_oskenter,
3205                                                      img_oskcapslock, img_oskshift,
3206                                                      onscreen_keyboard_disable_change);
3207                                       else
3208                                         kbd =
3209                                           osk_create(strdup("default.layout"), screen,
3210                                                      img_btn_up, img_btn_down, img_btn_off,
3211                                                      img_btn_nav, img_btn_hold,
3212                                                      img_oskdel, img_osktab, img_oskenter,
3213                                                      img_oskcapslock, img_oskshift,
3214                                                      img_btnsm_up, img_btnsm_down, img_btnsm_off,
3215                                                      img_btnsm_nav, img_btnsm_hold,
3216                                                      /* FIXME */
3217                                                      img_oskdel, img_osktab, img_oskenter,
3218                                                      img_oskcapslock, img_oskshift,
3219                                                      onscreen_keyboard_disable_change);
3220                                     }
3221                                   if (kbd == NULL)
3222                                     {
3223                                       fprintf(stderr, "kbd = NULL\n");
3224                                     }
3225                                   else
3226                                     {
3227                                       kbd_rect.x = button_w * 2 + (canvas->w - kbd->surface->w) / 2;
3228                                       if (old_y > canvas->h / 2)
3229                                         kbd_rect.y = 0;
3230                                       else
3231                                         kbd_rect.y = canvas->h - kbd->surface->h;
3232                                       kbd_rect.w = kbd->surface->w;
3233                                       kbd_rect.h = kbd->surface->h;
3234                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
3235                                       update_screen_rect(&kbd_rect);
3236                                     }
3237                                 }
3238                               if (!font_thread_done)
3239                                 {
3240                                   draw_colors(COLORSEL_DISABLE);
3241                                   draw_none();
3242                                   update_screen_rect(&r_toolopt);
3243                                   update_screen_rect(&r_ttoolopt);
3244                                   do_setcursor(cursor_watch);
3245 
3246                                   /* Wait while Text tool finishes loading fonts */
3247                                   draw_tux_text(TUX_WAIT, gettext("Please wait…"), 1);
3248 
3249                                   waiting_for_fonts = 1;
3250 #ifdef FORKED_FONTS
3251                                   receive_some_font_info(screen);
3252 #else
3253                                   while (!font_thread_done && !font_thread_aborted)
3254                                     {
3255                                       /* FIXME: should have a read-depends memory barrier around here */
3256                                       show_progress_bar(screen);
3257                                       SDL_Delay(20);
3258                                     }
3259                                   /* FIXME: should kill this in any case */
3260                                   SDL_WaitThread(font_thread, NULL);
3261 #endif
3262                                   set_label_fonts();
3263                                   do_setcursor(cursor_arrow);
3264                                 }
3265                               draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool], 1);
3266 
3267                               if (num_font_families > 0)
3268                                 {
3269                                   cur_thing = cur_font;
3270                                   num_things = num_font_families;
3271                                   thing_scroll = &font_scroll;
3272                                   cur_label = LABEL_LABEL;
3273 
3274                                   draw_fonts();
3275                                   draw_colors(COLORSEL_ENABLE);
3276                                 }
3277                               else
3278                                 {
3279                                   /* Problem using fonts! */
3280 
3281                                   cur_tool = old_tool;
3282                                   draw_toolbar();
3283                                   update_screen_rect(&r_tools);
3284                                 }
3285                             }
3286                           else if (cur_tool == TOOL_MAGIC)
3287                             {
3288                               keybd_flag = 0;
3289                               cur_thing = cur_magic;
3290                               num_things = num_magics;
3291                               thing_scroll = &magic_scroll;
3292                               magic_current_snd_ptr = NULL;
3293                               draw_magic();
3294                               draw_colors(magics[cur_magic].colors);
3295 
3296                               if (magics[cur_magic].colors)
3297                                 magic_funcs[magics[cur_magic].handle_idx].set_color(magic_api_struct,
3298                                                                                     color_hexes[cur_color][0],
3299                                                                                     color_hexes[cur_color][1],
3300                                                                                     color_hexes[cur_color][2]);
3301                             }
3302                           else if (cur_tool == TOOL_ERASER)
3303                             {
3304                               keybd_flag = 0;
3305                               cur_thing = cur_eraser;
3306                               num_things = NUM_ERASERS;
3307                               thing_scroll = &eraser_scroll;
3308                               draw_erasers();
3309                               draw_colors(COLORSEL_DISABLE);
3310                             }
3311                           else if (cur_tool == TOOL_FILL)
3312                             {
3313                               keybd_flag = 0;
3314                               cur_thing = cur_fill;
3315                               num_things = NUM_FILLS;
3316                               thing_scroll = &fill_scroll;
3317                               draw_fills();
3318                               draw_colors(COLORSEL_DISABLE);
3319                             }
3320                           else if (cur_tool == TOOL_UNDO)
3321                             {
3322                               if (cur_undo == newest_undo)
3323                                 {
3324                                   rec_undo_buffer();
3325                                   do_undo();
3326                                 }
3327                               do_undo();
3328 
3329                               been_saved = 0;
3330 
3331                               if (!disable_save)
3332                                 tool_avail[TOOL_SAVE] = 1;
3333 
3334                               cur_tool = old_tool;
3335                               draw_toolbar();
3336                               update_screen_rect(&r_tools);
3337                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
3338                             }
3339                           else if (cur_tool == TOOL_REDO)
3340                             {
3341                               do_redo();
3342 
3343                               been_saved = 0;
3344 
3345                               if (!disable_save)
3346                                 tool_avail[TOOL_SAVE] = 1;
3347 
3348                               cur_tool = old_tool;
3349                               draw_toolbar();
3350                               update_screen_rect(&r_tools);
3351                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
3352                             }
3353                           else if (cur_tool == TOOL_OPEN)
3354                             {
3355                               disable_avail_tools();
3356                               draw_toolbar();
3357                               draw_colors(COLORSEL_CLOBBER_WIPE);
3358                               draw_none();
3359 
3360                               if (do_open() == 0)
3361                                 {
3362                                   if (old_tool == TOOL_TEXT || old_tool == TOOL_LABEL)
3363                                     do_render_cur_text(0);
3364                                 }
3365 
3366                               enable_avail_tools();
3367 
3368                               cur_tool = old_tool;
3369                               draw_toolbar();
3370                               update_screen_rect(&r_tools);
3371 
3372                               draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 1);
3373 
3374                               draw_colors(COLORSEL_REFRESH);
3375 
3376                               if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
3377                                 draw_brushes();
3378                               else if (cur_tool == TOOL_MAGIC)
3379                                 draw_magic();
3380                               else if (cur_tool == TOOL_STAMP)
3381                                 draw_stamps();
3382                               else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
3383                                 {
3384                                   draw_fonts();
3385                                   if (onscreen_keyboard && kbd)
3386                                     {
3387                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
3388                                       update_screen_rect(&kbd_rect);
3389                                     }
3390                                 }
3391                               else if (cur_tool == TOOL_SHAPES)
3392                                 draw_shapes();
3393                               else if (cur_tool == TOOL_ERASER)
3394                                 draw_erasers();
3395                               else if (cur_tool == TOOL_FILL)
3396                                 draw_fills();
3397                             }
3398                           else if (cur_tool == TOOL_SAVE)
3399                             {
3400                               if (do_save(old_tool, 0))
3401                                 {
3402                                   been_saved = 1;
3403                                   tool_avail[TOOL_SAVE] = 0;
3404                                 }
3405 
3406                               if (old_tool == TOOL_TEXT || old_tool == TOOL_LABEL)
3407                                 {
3408                                   if (onscreen_keyboard && kbd)
3409                                     {
3410                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
3411                                       update_screen_rect(&kbd_rect);
3412                                     }
3413                                 }
3414 
3415                               cur_tool = old_tool;
3416                               draw_toolbar();
3417                               update_screen_rect(&r_tools);
3418                             }
3419                           else if (cur_tool == TOOL_NEW)
3420                             {
3421                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
3422 
3423                               disable_avail_tools();
3424                               draw_toolbar();
3425                               draw_colors(COLORSEL_CLOBBER_WIPE);
3426                               draw_none();
3427 
3428                               if (do_new_dialog() == 0)
3429                                 {
3430                                   cur_tool = old_tool;
3431 
3432                                   draw_tux_text(tool_tux[TUX_DEFAULT], TIP_NEW_ABORT, 1);
3433 
3434                                   if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
3435                                     do_render_cur_text(0);
3436                                 }
3437 
3438                               cur_tool = old_tool;
3439 
3440                               enable_avail_tools();
3441 
3442                               draw_toolbar();
3443                               update_screen_rect(&r_tools);
3444                               draw_colors(COLORSEL_REFRESH);
3445 
3446                               if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
3447                                 draw_brushes();
3448                               else if (cur_tool == TOOL_MAGIC)
3449                                 draw_magic();
3450                               else if (cur_tool == TOOL_STAMP)
3451                                 draw_stamps();
3452                               else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
3453                                 {
3454                                   draw_fonts();
3455                                   if (onscreen_keyboard && kbd)
3456                                     {
3457                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
3458                                       update_screen_rect(&kbd_rect);
3459                                     }
3460                                 }
3461                               else if (cur_tool == TOOL_SHAPES)
3462                                 draw_shapes();
3463                               else if (cur_tool == TOOL_ERASER)
3464                                 draw_erasers();
3465                               else if (cur_tool == TOOL_FILL)
3466                                 draw_fills();
3467                             }
3468                           else if (cur_tool == TOOL_PRINT)
3469                             {
3470                               /* If they haven't hit [Enter], but clicked 'Print', add their text now -bjk 2007.10.25 */
3471                               tmp_apply_uncommited_text();
3472                               /* original print code was here */
3473                               print_image();
3474                               undo_tmp_applied_text();
3475 
3476                               if (old_tool == TOOL_TEXT || old_tool == TOOL_LABEL)
3477                                 {
3478                                   if (onscreen_keyboard && kbd)
3479                                     {
3480                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
3481                                       update_screen_rect(&kbd_rect);
3482                                     }
3483                                 }
3484 
3485                               cur_tool = old_tool;
3486                               draw_toolbar();
3487                               draw_tux_text(TUX_BORED, "", 0);
3488                               update_screen_rect(&r_tools);
3489                             }
3490                           else if (cur_tool == TOOL_QUIT)
3491                             {
3492                               done = do_quit(old_tool);
3493 
3494                               if (old_tool == TOOL_TEXT || old_tool == TOOL_LABEL)
3495                                 {
3496                                   if (onscreen_keyboard && kbd)
3497                                     {
3498                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
3499                                       update_screen_rect(&kbd_rect);
3500                                     }
3501                                 }
3502 
3503                               cur_tool = old_tool;
3504                               draw_toolbar();
3505                               update_screen_rect(&r_tools);
3506                             }
3507                           update_screen_rect(&r_toolopt);
3508                           update_screen_rect(&r_ttoolopt);
3509                         }
3510 
3511                       if (!done)
3512                         magic_switchin(canvas);
3513                     }
3514                   else if ((event.button.y < r_tools.y + button_h / 2) && tool_scroll > 0)
3515                     {
3516                       tool_scroll -= gd_tools.cols;
3517                       playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
3518 
3519                       draw_toolbar();
3520                       update_screen_rect(&r_tools);
3521 
3522                     }
3523                   else if ((event.button.y > real_r_tools.y + real_r_tools.h)
3524                            && (tool_scroll < NUM_TOOLS - buttons_tall * gd_tools.cols + gd_tools.cols))
3525                     {
3526                       tool_scroll += gd_tools.cols;
3527                       draw_toolbar();
3528                       playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
3529 
3530                       update_screen_rect(&r_tools);
3531 
3532                     }
3533                 }
3534 
3535               else if (HIT(r_toolopt) && valid_click(event.button.button))
3536                 {
3537                   /* Options on the right
3538                      WARNING: this must be kept in sync with the mouse-move
3539                      code (for cursor changes) and mouse-scroll code. */
3540 
3541                   if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_STAMP ||
3542                       cur_tool == TOOL_SHAPES || cur_tool == TOOL_LINES ||
3543                       cur_tool == TOOL_MAGIC || cur_tool == TOOL_TEXT ||
3544                       cur_tool == TOOL_ERASER || cur_tool == TOOL_LABEL ||
3545                       cur_tool == TOOL_FILL)
3546                     {
3547                       int num_rows_needed;
3548                       SDL_Rect r_controls;
3549                       SDL_Rect r_notcontrols;
3550                       SDL_Rect r_items; /* = r_notcontrols; */
3551                       int toolopt_changed;
3552                       int select_changed = 0;
3553                       grid_dims gd_controls;    /* might become 2-by-2 */
3554                       grid_dims gd_items;       /* generally becoming 2-by-whatever */
3555 
3556                       gd_controls.rows = 0;
3557                       gd_controls.cols = 0;
3558                       gd_items.rows = 2;
3559                       gd_items.cols = 2;
3560 
3561                       /* Note set of things we're dealing with */
3562                       /* (stamps, brushes, etc.) */
3563 
3564                       if (cur_tool == TOOL_STAMP)
3565                         {
3566                           if (!disable_stamp_controls)
3567                             {
3568                               /* was 2,2 before adding left/right stamp group buttons -bjk 2007.05.15 */
3569                               gd_controls.rows = 3;
3570                               gd_controls.cols = 2;
3571                             }
3572                           else
3573                             {
3574                               /* was left 0,0 before adding left/right stamp group buttons -bjk 2007.05.03 */
3575                               gd_controls.rows = 1;
3576                               gd_controls.cols = 2;
3577                             }
3578                         }
3579                       else if (cur_tool == TOOL_TEXT)
3580                         {
3581                           if (!disable_stamp_controls)
3582                             {
3583                               gd_controls.rows = 2;
3584                               gd_controls.cols = 2;
3585                             }
3586                         }
3587                       else if (cur_tool == TOOL_LABEL)
3588                         {
3589                           if (!disable_stamp_controls)
3590                             {
3591                               gd_controls.rows = 3;
3592                               gd_controls.cols = 2;
3593                             }
3594                           else
3595                             {
3596                               gd_controls.rows = 1;
3597                               gd_controls.cols = 2;
3598                             }
3599                         }
3600 
3601                       else if (cur_tool == TOOL_MAGIC)
3602                         {
3603                           if (!disable_magic_controls)
3604                             {
3605                               gd_controls.rows = 1;
3606                               gd_controls.cols = 2;
3607                             }
3608                         }
3609 
3610                       else if (cur_tool == TOOL_SHAPES)
3611                         {
3612                           if (!disable_shape_controls)
3613                             {
3614                               gd_controls.rows = 1;
3615                               gd_controls.cols = 2;
3616                             }
3617                         }
3618 
3619                       /* number of whole or partial rows that will be needed
3620                          (can make this per-tool if variable columns needed) */
3621                       num_rows_needed = (num_things + gd_items.cols - 1) / gd_items.cols;
3622 
3623                       do_draw = 0;
3624 
3625                       r_controls.w = r_toolopt.w;
3626                       r_controls.h = gd_controls.rows * button_h;
3627                       r_controls.x = r_toolopt.x;
3628                       r_controls.y = r_toolopt.y + r_toolopt.h - r_controls.h;
3629 
3630                       r_notcontrols.w = r_toolopt.w;
3631                       r_notcontrols.h = r_toolopt.h - r_controls.h;
3632                       r_notcontrols.x = r_toolopt.x;
3633                       r_notcontrols.y = r_toolopt.y;
3634 
3635                       r_items.x = r_notcontrols.x;
3636                       r_items.y = r_notcontrols.y;
3637                       r_items.w = r_notcontrols.w;
3638                       r_items.h = r_notcontrols.h;
3639 
3640                       if (num_rows_needed * button_h > r_items.h)
3641                         {
3642                           /* too many; we'll need scroll buttons */
3643                           r_items.h -= button_h;
3644                           r_items.y += button_h / 2;
3645                         }
3646                       gd_items.rows = r_items.h / button_h;
3647 
3648                       toolopt_changed = 0;
3649 
3650                       if (HIT(r_items))
3651                         {
3652                           which = GRIDHIT_GD(r_items, gd_items) + *thing_scroll;
3653 
3654                           if (which < num_things)
3655                             {
3656                               toolopt_changed = 1;
3657 #ifndef NOSOUND
3658                               if (cur_tool != TOOL_STAMP || stamp_data[stamp_group][which]->ssnd == NULL)
3659                                 {
3660                                   playsound(screen, 1, SND_BLEEP, 0, SNDPOS_RIGHT, SNDDIST_NEAR);
3661                                 }
3662 #endif
3663                               cur_thing = which;
3664                               do_draw = 1;
3665                             }
3666                         }
3667                       else if (HIT(r_controls))
3668                         {
3669                           which = GRIDHIT_GD(r_controls, gd_controls);
3670                           if (cur_tool == TOOL_STAMP)
3671                             {
3672                               /* Stamp controls! */
3673                               int control_sound = -1;
3674 
3675                               if (which == 4 || which == 5)
3676                                 {
3677                                   /* Grow/Shrink Controls: */
3678 #ifdef OLD_STAMP_GROW_SHRINK
3679                                   if (which == 5)
3680                                     {
3681                                       /* Bottom right button: Grow: */
3682                                       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size < MAX_STAMP_SIZE)
3683                                         {
3684                                           stamp_data[stamp_group][cur_stamp[stamp_group]]->size++;
3685                                           control_sound = SND_GROW;
3686                                         }
3687                                     }
3688                                   else
3689                                     {
3690                                       /* Bottom left button: Shrink: */
3691                                       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size > MIN_STAMP_SIZE)
3692                                         {
3693                                           stamp_data[stamp_group][cur_stamp[stamp_group]]->size--;
3694                                           control_sound = SND_SHRINK;
3695                                         }
3696                                     }
3697 #else
3698                                   int old_size;
3699 
3700 #ifdef DEBUG
3701                                   float choice;
3702 #endif
3703 
3704                                   old_size = stamp_data[stamp_group][cur_stamp[stamp_group]]->size;
3705 
3706                                   stamp_data[stamp_group][cur_stamp[stamp_group]]->size =
3707                                     (((MAX_STAMP_SIZE - MIN_STAMP_SIZE + 1
3708                                        /* +1 to address lack of ability to get back to max default stamp size (SF Bug #1668235 -bjk 2011.01.08) */
3709                                       ) * (event.button.x - (WINDOW_WIDTH - r_ttoolopt.w))) / r_ttoolopt.w) + MIN_STAMP_SIZE;
3710 
3711 #ifdef DEBUG
3712                                   printf("Old size = %d, Chose %0.4f, New size =%d\n", old_size, choice,
3713                                          stamp_data[stamp_group][cur_stamp[stamp_group]]->size);
3714 #endif
3715 
3716                                   if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size < old_size)
3717                                     control_sound = SND_SHRINK;
3718                                   else if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size > old_size)
3719                                     control_sound = SND_GROW;
3720 #endif
3721                                 }
3722                               else if (which == 2 || which == 3)
3723                                 {
3724                                   /* Mirror/Flip Controls: */
3725                                   if (which == 3)
3726                                     {
3727                                       /* Top right button: Flip: */
3728                                       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->flipable)
3729                                         {
3730                                           stamp_data[stamp_group][cur_stamp[stamp_group]]->flipped =
3731                                             !stamp_data[stamp_group][cur_stamp[stamp_group]]->flipped;
3732                                           control_sound = SND_FLIP;
3733                                         }
3734                                     }
3735                                   else
3736                                     {
3737                                       /* Top left button: Mirror: */
3738                                       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->mirrorable)
3739                                         {
3740                                           stamp_data[stamp_group][cur_stamp[stamp_group]]->mirrored =
3741                                             !stamp_data[stamp_group][cur_stamp[stamp_group]]->mirrored;
3742                                           control_sound = SND_MIRROR;
3743                                         }
3744                                     }
3745                                 }
3746                               else
3747                                 {
3748                                   /* Prev/Next Controls: */
3749 
3750                                   old_stamp_group = stamp_group;
3751 
3752                                   if (which == 1)
3753                                     {
3754                                       /* Next group */
3755                                       stamp_group++;
3756                                       if (stamp_group >= num_stamp_groups)
3757                                         stamp_group = 0;
3758                                       control_sound = SND_CLICK;
3759                                     }
3760                                   else
3761                                     {
3762                                       /* Prev group */
3763                                       stamp_group--;
3764                                       if (stamp_group < 0)
3765                                         stamp_group = num_stamp_groups - 1;
3766                                       control_sound = SND_CLICK;
3767                                     }
3768 
3769                                   if (stamp_group == old_stamp_group)
3770                                     control_sound = -1;
3771                                   else
3772                                     {
3773                                       cur_thing = cur_stamp[stamp_group];
3774                                       num_things = num_stamps[stamp_group];
3775                                       thing_scroll = &(stamp_scroll[stamp_group]);
3776                                     }
3777                                 }
3778 
3779                               if (control_sound != -1)
3780                                 {
3781                                   playsound(screen, 0, control_sound, 0, SNDPOS_CENTER, SNDDIST_NEAR);
3782                                   draw_stamps();
3783                                   update_screen_rect(&r_toolopt);
3784                                   set_active_stamp();
3785                                   update_stamp_xor();
3786                                 }
3787                             }
3788                           else if (cur_tool == TOOL_MAGIC)
3789                             {
3790                               /* Magic controls! */
3791                               if (which == 1 && magics[cur_magic].avail_modes & MODE_FULLSCREEN)
3792                                 {
3793                                   magic_switchout(canvas);
3794                                   magics[cur_magic].mode = MODE_FULLSCREEN;
3795                                   magic_switchin(canvas);
3796                                   draw_magic();
3797                                   update_screen_rect(&r_toolopt);
3798                                 }
3799                               else if (which == 0 && magics[cur_magic].avail_modes & MODE_PAINT)
3800                                 {
3801                                   magic_switchout(canvas);
3802                                   magics[cur_magic].mode = MODE_PAINT;
3803                                   magic_switchin(canvas);
3804                                   draw_magic();
3805                                   update_screen_rect(&r_toolopt);
3806                                 }
3807                               else if (which == 0 && magics[cur_magic].avail_modes & MODE_PAINT_WITH_PREVIEW)
3808                                 {
3809                                   magic_switchout(canvas);
3810                                   magics[cur_magic].mode = MODE_PAINT_WITH_PREVIEW;
3811                                   magic_switchin(canvas);
3812                                   draw_magic();
3813                                   update_screen_rect(&r_toolopt);
3814                                 }
3815                               else if (which == 0 && magics[cur_magic].avail_modes & MODE_ONECLICK)
3816                                 {
3817                                   magic_switchout(canvas);
3818                                   magics[cur_magic].mode = MODE_ONECLICK;
3819                                   magic_switchin(canvas);
3820                                   draw_magic();
3821                                   update_screen_rect(&r_toolopt);
3822                                 }
3823                               /* FIXME: Sfx */
3824                             }
3825                           else if (cur_tool == TOOL_SHAPES)
3826                             {
3827                               /* Shape controls! */
3828                               shape_mode = which;
3829                               draw_shapes();
3830 			      update_screen_rect(&r_toolopt);
3831                               draw_tux_text(TUX_GREAT, shapemode_tips[shape_mode], 1);
3832                               playsound(screen, 0, SND_CLICK, 0, SNDPOS_RIGHT, SNDDIST_NEAR);
3833                               update_screen_rect(&r_tuxarea);
3834                               toolopt_changed = 0;
3835                             }
3836                           else if (cur_tool == TOOL_TEXT)
3837                             {
3838                               /* Text controls! */
3839                               int control_sound = -1;
3840 
3841                               if (which & 2)
3842                                 {
3843                                   /* One of the bottom buttons: */
3844                                   if (which & 1)
3845                                     {
3846                                       /* Bottom right button: Grow: */
3847                                       if (text_size < MAX_TEXT_SIZE)
3848                                         {
3849                                           text_size++;
3850                                           control_sound = SND_GROW;
3851                                           toolopt_changed = 1;
3852                                         }
3853                                     }
3854                                   else
3855                                     {
3856                                       /* Bottom left button: Shrink: */
3857                                       if (text_size > MIN_TEXT_SIZE)
3858                                         {
3859                                           text_size--;
3860                                           control_sound = SND_SHRINK;
3861                                           toolopt_changed = 1;
3862                                         }
3863                                     }
3864                                 }
3865                               else
3866                                 {
3867                                   /* One of the top buttons: */
3868                                   if (which & 1)
3869                                     {
3870                                       /* Top right button: Italic: */
3871                                       if (text_state & TTF_STYLE_ITALIC)
3872                                         {
3873                                           text_state &= ~TTF_STYLE_ITALIC;
3874                                           control_sound = SND_ITALIC_ON;
3875                                         }
3876                                       else
3877                                         {
3878                                           text_state |= TTF_STYLE_ITALIC;
3879                                           control_sound = SND_ITALIC_OFF;
3880                                         }
3881                                     }
3882                                   else
3883                                     {
3884                                       /* Top left button: Bold: */
3885                                       if (text_state & TTF_STYLE_BOLD)
3886                                         {
3887                                           text_state &= ~TTF_STYLE_BOLD;
3888                                           control_sound = SND_THIN;
3889                                         }
3890                                       else
3891                                         {
3892                                           text_state |= TTF_STYLE_BOLD;
3893                                           control_sound = SND_THICK;
3894                                         }
3895                                     }
3896                                   toolopt_changed = 1;
3897                                 }
3898                               if (control_sound != -1)
3899                                 {
3900                                   playsound(screen, 0, control_sound, 0, SNDPOS_CENTER, SNDDIST_NEAR);
3901 
3902 
3903                                   if (cur_tool == TOOL_TEXT)    /* Huh? It had better be! */
3904                                     {
3905                                       /* need to invalidate all the cached user fonts, causing reload on demand */
3906 
3907                                       int i;
3908 
3909                                       for (i = 0; i < num_font_families; i++)
3910                                         {
3911                                           if (user_font_families[i] && user_font_families[i]->handle)
3912                                             {
3913                                               TuxPaint_Font_CloseFont(user_font_families[i]->handle);
3914                                               user_font_families[i]->handle = NULL;
3915                                             }
3916                                         }
3917                                       draw_fonts();
3918                                       update_screen_rect(&r_toolopt);
3919                                     }
3920                                 }
3921                             }
3922 
3923                           /* Label controls! */
3924                           else if (cur_tool == TOOL_LABEL)
3925                             {
3926                               int control_sound = -1;
3927 
3928                               if (which & 4)
3929                                 {
3930                                   /* One of the bottom buttons: */
3931                                   if (which & 1)
3932                                     {
3933                                       /* Bottom right button: Grow: */
3934                                       if (text_size < MAX_TEXT_SIZE)
3935                                         {
3936                                           text_size++;
3937                                           control_sound = SND_GROW;
3938                                           toolopt_changed = 1;
3939                                         }
3940                                     }
3941                                   else
3942                                     {
3943                                       /* Bottom left button: Shrink: */
3944                                       if (text_size > MIN_TEXT_SIZE)
3945                                         {
3946                                           text_size--;
3947                                           control_sound = SND_SHRINK;
3948                                           toolopt_changed = 1;
3949                                         }
3950                                     }
3951                                 }
3952                               else
3953                                 {
3954                                   if (which & 2)
3955                                     {
3956                                       /* One of the middle buttons: */
3957                                       if (which & 1)
3958                                         {
3959                                           /*  right button: Italic: */
3960                                           if (text_state & TTF_STYLE_ITALIC)
3961                                             {
3962                                               text_state &= ~TTF_STYLE_ITALIC;
3963                                               control_sound = SND_ITALIC_ON;
3964                                             }
3965                                           else
3966                                             {
3967                                               text_state |= TTF_STYLE_ITALIC;
3968                                               control_sound = SND_ITALIC_OFF;
3969                                             }
3970                                         }
3971                                       else
3972                                         {
3973                                           /* middle left button: Bold: */
3974                                           if (text_state & TTF_STYLE_BOLD)
3975                                             {
3976                                               text_state &= ~TTF_STYLE_BOLD;
3977                                               control_sound = SND_THIN;
3978                                             }
3979                                           else
3980                                             {
3981                                               text_state |= TTF_STYLE_BOLD;
3982                                               control_sound = SND_THICK;
3983                                             }
3984                                         }
3985                                       toolopt_changed = 1;
3986                                     }
3987 
3988                                   else
3989                                     {
3990                                       /* One of the top buttons: */
3991                                       if (which & 1)
3992                                         {
3993                                           /* Select button: */
3994                                           if (cur_label == LABEL_SELECT)
3995                                             {
3996                                               cur_label = LABEL_LABEL;
3997                                               update_canvas(0, 0, WINDOW_WIDTH - r_ttoolopt.w, (button_h * buttons_tall) + r_ttoolopt.h);
3998                                               if (onscreen_keyboard)
3999                                                 {
4000                                                   SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
4001                                                   update_screen_rect(&kbd_rect);
4002                                                 }
4003                                             }
4004                                           else
4005                                             {
4006                                               if (are_labels())
4007                                                 {
4008                                                   update_canvas_ex_r(kbd_rect.x - r_ttools.w, kbd_rect.y,
4009                                                                      kbd_rect.x + kbd_rect.w, kbd_rect.y + kbd_rect.h,
4010                                                                      1);
4011                                                   if (texttool_len > 0)
4012                                                     {
4013                                                       rec_undo_buffer();
4014                                                       do_render_cur_text(1);
4015                                                       texttool_len = 0;
4016                                                       cursor_textwidth = 0;
4017                                                       label_node_to_edit = NULL;
4018                                                     }
4019                                                   else if (label_node_to_edit)
4020                                                     {
4021                                                       rec_undo_buffer();
4022                                                       have_to_rec_label_node = TRUE;
4023                                                       add_label_node(0, 0, 0, 0, NULL);
4024                                                       label_node_to_edit = NULL;
4025 
4026                                                     }
4027 
4028                                                   cur_label = LABEL_SELECT;
4029                                                   highlight_label_nodes();
4030                                                 }
4031                                             }
4032                                           toolopt_changed = 1;
4033                                         }
4034                                     }
4035                                 }
4036 
4037                               if (control_sound != -1)
4038                                 {
4039                                   playsound(screen, 0, control_sound, 0, SNDPOS_CENTER, SNDDIST_NEAR);
4040 
4041 
4042                                   if (cur_tool == TOOL_LABEL)   /* Huh? It had better be! */
4043                                     {
4044                                       /* need to invalidate all the cached user fonts, causing reload on demand */
4045 
4046                                       int i;
4047 
4048                                       for (i = 0; i < num_font_families; i++)
4049                                         {
4050                                           if (user_font_families[i] && user_font_families[i]->handle)
4051                                             {
4052                                               TuxPaint_Font_CloseFont(user_font_families[i]->handle);
4053                                               user_font_families[i]->handle = NULL;
4054                                             }
4055                                         }
4056                                       draw_fonts();
4057                                       update_screen_rect(&r_toolopt);
4058                                     }
4059                                 }
4060                               draw_fonts();
4061                               update_screen_rect(&r_toolopt);
4062 
4063                             }
4064                         }
4065                       else
4066                         {
4067                           /* scroll button */
4068                           int is_upper = event.button.y < r_toolopt.y + button_h / 2;
4069 
4070                           if ((is_upper && *thing_scroll > 0)   /* upper arrow */
4071                               || (!is_upper && *thing_scroll / gd_items.cols < num_rows_needed - gd_items.rows) /* lower arrow */
4072                             )
4073                             {
4074                               *thing_scroll += is_upper ? -gd_items.cols : gd_items.cols;
4075                               do_draw = 1;
4076                               playsound(screen, 1, SND_SCROLL, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
4077 
4078                               if (scrolltimer != NULL)
4079                                 {
4080                                   SDL_RemoveTimer(scrolltimer);
4081                                   scrolltimer = NULL;
4082                                 }
4083 
4084                               if (!scrolling && event.type == SDL_MOUSEBUTTONDOWN)
4085                                 {
4086                                   DEBUG_PRINTF("Starting scrolling\n");
4087                                   memcpy(&scrolltimer_event, &event, sizeof(SDL_Event));
4088                                   scrolltimer_event.type = TP_SDL_MOUSEBUTTONSCROLL;
4089 
4090                                   /*
4091                                   * We enable the timer subsystem only when needed (e.g., to use SDL_AddTimer() needed
4092                                   * for scrolling) then disable it immediately after (e.g., after the timer has fired or
4093                                   * after SDL_RemoveTimer()) because enabling the timer subsystem in SDL1 has a high
4094                                   * energy impact on the Mac.
4095                                   */
4096 
4097                                   scrolling = 1;
4098                                   SDL_InitSubSystem(SDL_INIT_TIMER);
4099                                   scrolltimer =
4100                                     SDL_AddTimer(REPEAT_SPEED, scrolltimer_callback, (void *)&scrolltimer_event);
4101                                 }
4102                               else
4103                                 {
4104                                   DEBUG_PRINTF("Continuing scrolling\n");
4105                                   scrolltimer =
4106                                     SDL_AddTimer(REPEAT_SPEED / 3, scrolltimer_callback, (void *)&scrolltimer_event);
4107                                 }
4108 
4109                               if (*thing_scroll == 0 || *thing_scroll / gd_items.cols ==  num_rows_needed - gd_items.rows)
4110                                 {
4111                                   do_setcursor(cursor_arrow);
4112                                   if (scrolling)
4113                                     {
4114                                       if (scrolltimer != NULL)
4115                                         {
4116                                           SDL_RemoveTimer(scrolltimer);
4117                                           scrolltimer = NULL;
4118                                         }
4119                                       scrolling = 0;
4120                                       SDL_QuitSubSystem(SDL_INIT_TIMER);
4121                                     }
4122                                 }
4123                             }
4124                         }
4125 
4126 
4127                       /* Assign the change(s), if any / redraw, if needed: */
4128 
4129                       if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
4130                         {
4131                           cur_brush = cur_thing;
4132                           render_brush();
4133 
4134                           if (do_draw)
4135                             draw_brushes();
4136                         }
4137                       else if (cur_tool == TOOL_ERASER)
4138                         {
4139                           cur_eraser = cur_thing;
4140 
4141                           if (do_draw)
4142                             draw_erasers();
4143                         }
4144                       else if (cur_tool == TOOL_FILL)
4145                         {
4146                           cur_fill = cur_thing;
4147                           draw_tux_text(TUX_GREAT, fill_tips[cur_fill], 1);
4148 
4149                           if (do_draw)
4150                             draw_fills();
4151                         }
4152                       else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
4153                         {
4154                           /* FIXME */
4155                           /* char font_tux_text[512]; */
4156 
4157                           cur_font = cur_thing;
4158 
4159                           /* FIXME */
4160                           /*
4161                              safe_snprintf(font_tux_text, sizeof font_tux_text, "%s (%s).",
4162                              TTF_FontFaceFamilyName(getfonthandle(cur_font)),
4163                              TTF_FontFaceStyleName(getfonthandle(cur_font)));
4164                              draw_tux_text(TUX_GREAT, font_tux_text, 1);
4165                            */
4166 
4167                           if (do_draw)
4168                             draw_fonts();
4169 
4170 
4171                           /* Only rerender when picking a different font */
4172                           if (toolopt_changed)
4173                             {
4174                               draw_fonts();
4175                               if (select_changed)
4176                                 {
4177                                   rec_undo_buffer();
4178                                   do_render_cur_text(1);
4179                                   texttool_len = 0;
4180                                 }
4181                               else
4182                                 {
4183                                   do_render_cur_text(0);
4184                                 }
4185                             }
4186                         }
4187                       else if (cur_tool == TOOL_STAMP)
4188                         {
4189 #ifndef NOSOUND
4190                           /* Only play when picking a different stamp */
4191                           if (toolopt_changed && !mute)
4192                             {
4193                               /* If there's an SFX, play it! */
4194 
4195                               if (stamp_data[stamp_group][cur_thing]->ssnd != NULL)
4196                                 {
4197                                   Mix_ChannelFinished(NULL);    /* Prevents multiple clicks from toggling between SFX and desc sound, rather than always playing SFX first, then desc sound... */
4198 
4199                                   Mix_PlayChannel(2, stamp_data[stamp_group][cur_thing]->ssnd, 0);
4200 
4201                                   /* If there's a description sound, play it after the SFX! */
4202 
4203                                   if (stamp_data[stamp_group][cur_thing]->sdesc != NULL)
4204                                     {
4205                                       Mix_ChannelFinished(playstampdesc);
4206                                     }
4207                                 }
4208                               else
4209                                 {
4210                                   /* No SFX?  If there's a description sound, play it now! */
4211 
4212                                   if (stamp_data[stamp_group][cur_thing]->sdesc != NULL)
4213                                     {
4214                                       Mix_PlayChannel(2, stamp_data[stamp_group][cur_thing]->sdesc, 0);
4215                                     }
4216                                 }
4217                             }
4218 #endif
4219 
4220                           if (cur_thing != cur_stamp[stamp_group])
4221                             {
4222                               cur_stamp[stamp_group] = cur_thing;
4223                               set_active_stamp();
4224                               update_stamp_xor();
4225                             }
4226 
4227                           if (do_draw)
4228                             draw_stamps();
4229 
4230                           if (stamp_data[stamp_group][cur_stamp[stamp_group]]->stxt != NULL)
4231                             {
4232 #ifdef DEBUG
4233                               printf("stamp_data[stamp_group][cur_stamp[stamp_group]]->stxt = %s\n",
4234                                      stamp_data[stamp_group][cur_stamp[stamp_group]]->stxt);
4235 #endif
4236 
4237                               draw_tux_text_ex(TUX_GREAT, stamp_data[stamp_group][cur_stamp[stamp_group]]->stxt, 1,
4238                                                stamp_data[stamp_group][cur_stamp[stamp_group]]->locale_text);
4239                             }
4240                           else
4241                             draw_tux_text(TUX_GREAT, "", 0);
4242 
4243                           /* Enable or disable color selector: */
4244                           draw_colors(stamp_colorable(cur_stamp[stamp_group])
4245                                       || stamp_tintable(cur_stamp[stamp_group]));
4246                           if (!scrolling)
4247                             {
4248                               stamp_xor(canvas->w / 2, canvas->h / 2);
4249                               stamp_xored = 1;
4250                               stamp_size_selector_clicked = 1;
4251                               update_screen(canvas->w / 2 - (CUR_STAMP_W + 1) / 2 + r_canvas.x,
4252                                             canvas->h / 2 - (CUR_STAMP_H + 1) / 2 + r_canvas.y,
4253                                             canvas->w / 2 + (CUR_STAMP_W + 1) / 2 + r_canvas.x,
4254                                             canvas->h / 2 + (CUR_STAMP_H + 1) / 2 + r_canvas.y);
4255                             }
4256                         }
4257                       else if (cur_tool == TOOL_SHAPES)
4258                         {
4259                           cur_shape = cur_thing;
4260 
4261                           /* Remove ghost previews an reset the tool */
4262                           if (shape_tool_mode != SHAPE_TOOL_MODE_DONE)
4263                             {
4264                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
4265                               do_undo();
4266                               tool_avail[TOOL_REDO] = 0;        /* Don't let them 'redo' to get preview back */
4267                               draw_toolbar();
4268                               update_screen_rect(&r_tools);
4269                               update_canvas(0, 0, canvas->w, canvas->h);
4270                             }
4271 
4272                           if (toolopt_changed)
4273                             draw_tux_text(TUX_GREAT, shape_tips[cur_shape], 1);
4274 
4275                           if (do_draw)
4276                             draw_shapes();
4277                         }
4278                       else if (cur_tool == TOOL_MAGIC)
4279                         {
4280                           if (cur_thing != cur_magic)
4281                             {
4282                               magic_switchout(canvas);
4283 
4284                               cur_magic = cur_thing;
4285                               draw_colors(magics[cur_magic].colors);
4286 
4287                               if (magics[cur_magic].colors)
4288                                 magic_funcs[magics[cur_magic].handle_idx].set_color(magic_api_struct,
4289                                                                                     color_hexes[cur_color][0],
4290                                                                                     color_hexes[cur_color][1],
4291                                                                                     color_hexes[cur_color][2]);
4292 
4293                               magic_switchin(canvas);
4294                             }
4295 
4296                           draw_tux_text(TUX_GREAT, magics[cur_magic].tip[magic_modeint(magics[cur_magic].mode)], 1);
4297 
4298                           if (do_draw)
4299                             draw_magic();
4300                         }
4301 
4302                       /* Update the screen: */
4303                       if (do_draw)
4304                         update_screen_rect(&r_toolopt);
4305                     }
4306                 }
4307               else if (HIT(r_colors) && colors_are_selectable)
4308                 {
4309                   /* Color! */
4310                   whichc = GRIDHIT_GD(r_colors, gd_colors);
4311 
4312                   if (valid_click(event.button.button))
4313                     {
4314                       // magic_switchout(canvas);
4315 
4316                       if (whichc >= 0 && whichc < NUM_COLORS)
4317                         {
4318                           cur_color = whichc;
4319                           draw_tux_text(TUX_KISS, color_names[cur_color], 1);
4320 
4321                           if (cur_color == (unsigned)(NUM_COLORS - 1) || cur_color == (unsigned)(NUM_COLORS - 2))
4322                             {
4323                               disable_avail_tools();
4324                               draw_toolbar();
4325                               draw_colors(COLORSEL_CLOBBER_WIPE);
4326                               draw_none();
4327 
4328                               if (cur_color == (unsigned)(NUM_COLORS - 1))
4329                                 do_color_picker();
4330                               else
4331                                 do_color_sel();
4332 
4333                               if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
4334                                 {
4335                                   if (onscreen_keyboard && kbd)
4336                                     {
4337                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
4338                                       update_screen_rect(&kbd_rect);
4339                                     }
4340                                 }
4341 
4342                               enable_avail_tools();
4343                               draw_toolbar();
4344                               update_screen_rect(&r_tools);
4345 
4346                               draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 1);
4347 
4348                               draw_colors(COLORSEL_FORCE_REDRAW);
4349 
4350                               if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
4351                                 draw_brushes();
4352                               else if (cur_tool == TOOL_MAGIC)
4353                                 draw_magic();
4354                               else if (cur_tool == TOOL_STAMP)
4355                                 draw_stamps();
4356                               else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
4357                                 draw_fonts();
4358                               else if (cur_tool == TOOL_SHAPES)
4359                                 draw_shapes();
4360                               else if (cur_tool == TOOL_ERASER)
4361                                 draw_erasers();
4362                               else if (cur_tool == TOOL_FILL)
4363                                 draw_fills();
4364 
4365                               playsound(screen, 1, SND_BUBBLE, 1, SNDPOS_CENTER, SNDDIST_NEAR);
4366 
4367                               SDL_Flip(screen);
4368                             }
4369                           else
4370                             {
4371                               draw_colors(COLORSEL_REFRESH);
4372 
4373                               playsound(screen, 1, SND_BUBBLE, 1, event.button.x, SNDDIST_NEAR);
4374                             }
4375 
4376                           render_brush();
4377 
4378 
4379                           if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
4380                             do_render_cur_text(0);
4381                           else if (cur_tool == TOOL_MAGIC)
4382                             magic_funcs[magics[cur_magic].handle_idx].set_color(magic_api_struct,
4383                                                                                 color_hexes[cur_color][0],
4384                                                                                 color_hexes[cur_color][1],
4385                                                                                 color_hexes[cur_color][2]);
4386                         }
4387                     }
4388                 }
4389               else if (HIT(r_canvas) && valid_click(event.button.button) && keyglobal == 0)
4390                 {
4391                   /* Draw something! */
4392                   old_x = event.button.x - r_canvas.x;
4393                   old_y = event.button.y - r_canvas.y;
4394                   /* if (old_y < r_canvas.h/2) */
4395                   /* { */
4396                   /*            keybd_position = 0; */
4397                   /* } */
4398                   /* else */
4399                   /* { */
4400                   /*            keybd_position = 1; */
4401                   /* } */
4402 
4403                   if (been_saved)
4404                     {
4405                       been_saved = 0;
4406 
4407                       if (!disable_save)
4408                         tool_avail[TOOL_SAVE] = 1;
4409 
4410                       draw_toolbar();
4411                       update_screen_rect(&r_tools);
4412                     }
4413 
4414                   if (cur_tool == TOOL_BRUSH)
4415                     {
4416                       /* Start painting! */
4417                       if (!emulate_button_pressed)
4418                         rec_undo_buffer();
4419 
4420                       /* (Arbitrarily large, so we draw once now) */
4421                       reset_brush_counter();
4422 
4423                       /* brush_draw(old_x, old_y, old_x, old_y, 1); fixes SF #1934883? */
4424                       playsound(screen, 0, paintsound(img_cur_brush_w), 1, event.button.x, SNDDIST_NEAR);
4425 
4426                       if (mouseaccessibility)
4427                         emulate_button_pressed = !emulate_button_pressed;
4428                     }
4429                   else if (cur_tool == TOOL_LINES)
4430                     {
4431                       /* Start a line! */
4432 
4433                       if (!emulate_button_pressed)
4434                         {
4435                           rec_undo_buffer();
4436 
4437                           line_start_x = old_x;
4438                           line_start_y = old_y;
4439 
4440                           /* (Arbitrarily large, so we draw once now) */
4441                           reset_brush_counter();
4442 
4443                           /* brush_draw(old_x, old_y, old_x, old_y, 1); fixes sf #1934883? */
4444 
4445                           playsound(screen, 1, SND_LINE_START, 1, event.button.x, SNDDIST_NEAR);
4446                           draw_tux_text(TUX_BORED, TIP_LINE_START, 1);
4447                         }
4448                       if (mouseaccessibility)
4449                         emulate_button_pressed = !emulate_button_pressed;
4450                     }
4451                   else if (cur_tool == TOOL_SHAPES)
4452                     {
4453                       if (shape_tool_mode == SHAPE_TOOL_MODE_DONE)
4454                         {
4455                           /* Start drawing a shape! */
4456 
4457                           rec_undo_buffer();
4458 
4459                           shape_start_x = old_x;
4460                           shape_start_y = old_y;
4461 
4462                           shape_tool_mode = SHAPE_TOOL_MODE_STRETCH;
4463 
4464                           playsound(screen, 1, SND_LINE_START, 1, event.button.x, SNDDIST_NEAR);
4465                           draw_tux_text(TUX_BORED, TIP_SHAPE_START, 1);
4466                           if (mouseaccessibility)
4467                             emulate_button_pressed = 1;
4468                         }
4469                       else if (shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
4470                         {
4471                           /* Draw the shape with the brush! */
4472 
4473                           /* Only draw here in mouse accessibility mode as there IS a mouse */
4474                           /* See #300881 for the reasons that this is deplaced to draw in mouse release */
4475                           if (mouseaccessibility)
4476                             {
4477                               /* (Arbitrarily large...) */
4478                               reset_brush_counter();
4479 
4480                               playsound(screen, 1, SND_LINE_END, 1, event.button.x, SNDDIST_NEAR);
4481                               do_shape(shape_start_x, shape_start_y, shape_current_x, shape_current_y,
4482                                        shape_rotation(shape_start_x, shape_start_y,
4483                                                       event.button.x - r_canvas.x, event.button.y - r_canvas.y), 1);
4484 
4485                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
4486                               draw_tux_text(TUX_GREAT, tool_tips[TOOL_SHAPES], 1);
4487                             }
4488                         }
4489                       else if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
4490                         /* Only reached in accessibility mode */
4491                         emulate_button_pressed = 0;
4492                     }
4493                   else if (cur_tool == TOOL_MAGIC)
4494                     {
4495                       if (!emulate_button_pressed)
4496                         {
4497                           int undo_ctr;
4498                           SDL_Surface *last;
4499 
4500                           /* Start doing magic! */
4501 
4502                           /* These switchout/in are here for Magic tools that abuse the canvas
4503                              by drawing widgets on them; you don't want the widgets recorded as part
4504                              of the canvas in the undo buffer!
4505                              HOWEVER, as Pere noted in 2010.March, this causes many 'normal' Magic
4506                              tools to not work right, because they lose their record of the 'original'
4507                              canvas, before the user started using the tool (e.g., Rails, Perspective, Zoom).
4508                              FIXME: Some in-between solution is needed (a 'clean up the canvas'/'dirty the canvas'
4509                              pair of functions for the widgety abusers?) -bjk 2010.03.22 */
4510 
4511                           /* magic_switchout(canvas); *//* <-- FIXME: I dislike this -bjk 2009.10.13 */
4512                           rec_undo_buffer();
4513                           /* magic_switchin(canvas); *//* <-- FIXME: I dislike this -bjk 2009.10.13 */
4514 
4515                           if (cur_undo > 0)
4516                             undo_ctr = cur_undo - 1;
4517                           else
4518                             undo_ctr = NUM_UNDO_BUFS - 1;
4519 
4520                           last = undo_bufs[undo_ctr];
4521 
4522                           update_rect.x = 0;
4523                           update_rect.y = 0;
4524                           update_rect.w = 0;
4525                           update_rect.h = 0;
4526 
4527                           reset_touched();
4528 
4529                           magic_funcs[magics[cur_magic].handle_idx].click(magic_api_struct,
4530                                                                           magics[cur_magic].idx,
4531                                                                           magics[cur_magic].mode,
4532                                                                           canvas, last, old_x, old_y, &update_rect);
4533 
4534                           draw_tux_text(TUX_GREAT, magics[cur_magic].tip[magic_modeint(magics[cur_magic].mode)], 1);
4535 
4536                           update_canvas(update_rect.x, update_rect.y,
4537                                         update_rect.x + update_rect.w, update_rect.y + update_rect.h);
4538                         }
4539 
4540                       if (mouseaccessibility)
4541                         {
4542                           if (magics[cur_magic].mode != MODE_FULLSCREEN && magics[cur_magic].mode != MODE_ONECLICK)     /* Note: some non-fullscreen tools are also click-only (not click-and-drag) -bjk 2011.04.26 */
4543                             emulate_button_pressed = !emulate_button_pressed;
4544                         }
4545                     }
4546                   else if (cur_tool == TOOL_ERASER)
4547                     {
4548                       /* Erase! */
4549                       if (!emulate_button_pressed)
4550                         rec_undo_buffer();
4551 
4552                       do_eraser(old_x, old_y, 1);
4553 
4554                       if (mouseaccessibility)
4555                         emulate_button_pressed = !emulate_button_pressed;
4556                     }
4557                   else if (cur_tool == TOOL_FILL)
4558                     {
4559                       Uint32 draw_color, canv_color;
4560 
4561                       /* Fill */
4562 
4563                       draw_color = SDL_MapRGB(canvas->format,
4564                                      color_hexes[cur_color][0],
4565                                      color_hexes[cur_color][1],
4566                                      color_hexes[cur_color][2]);
4567                       canv_color = getpixels[canvas->format->BytesPerPixel] (canvas, old_x, old_y);
4568 
4569                       fill_x = old_x;
4570                       fill_y = old_y;
4571 
4572                       if (would_flood_fill(canvas, draw_color, canv_color))
4573                         {
4574                           int x1, y1, x2, y2;
4575                           SDL_Surface * last;
4576                           int undo_ctr;
4577 
4578                           /* We only bother recording an undo buffer
4579                              (which may kill our redos) if we're about
4580                              to actually change the picture */
4581                           rec_undo_buffer();
4582                           x1 = x2 = old_x;
4583                           y1 = y2 = old_y;
4584 
4585                           if (cur_undo > 0)
4586                             undo_ctr = cur_undo - 1;
4587                           else
4588                             undo_ctr = NUM_UNDO_BUFS - 1;
4589 
4590                           last = undo_bufs[undo_ctr];
4591 
4592 
4593                           for (y1 = 0; y1 < canvas->h; y1++) {
4594                             for (x1 = 0; x1 < canvas->w; x1++) {
4595                               sim_flood_touched[(y1 * canvas->w) + x1] = 0;
4596                             }
4597                           }
4598 
4599                           if (cur_fill == FILL_FLOOD)
4600                             {
4601                               /* Flood fill a solid color */
4602                               do_flood_fill(screen, last, canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
4603 
4604                               update_canvas(x1, y1, x2, y2);
4605                             }
4606                           else
4607                             {
4608                               SDL_Surface * tmp_canvas;
4609 
4610                               tmp_canvas = SDL_CreateRGBSurface(canvas->flags,
4611                                 canvas->w, canvas->h, canvas->format->BitsPerPixel,
4612                                 canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask);
4613                               SDL_BlitSurface(canvas, NULL, tmp_canvas, NULL);
4614 
4615                               simulate_flood_fill(screen, last, tmp_canvas, old_x, old_y, draw_color, canv_color, &x1, &y1, &x2, &y2, sim_flood_touched);
4616                               SDL_FreeSurface(tmp_canvas);
4617 
4618                               sim_flood_x1 = x1;
4619                               sim_flood_y1 = y1;
4620                               sim_flood_x2 = x2;
4621                               sim_flood_y2 = y2;
4622 
4623                               if (cur_fill == FILL_GRADIENT_RADIAL)
4624                                 {
4625                                   /* Radial gradient */
4626                                   draw_radial_gradient(canvas, sim_flood_x1, sim_flood_y1, sim_flood_x2, sim_flood_y2,
4627                                     old_x, old_y, draw_color, sim_flood_touched);
4628                                 }
4629                               else if (cur_fill == FILL_GRADIENT_LINEAR)
4630                                 {
4631                                   /* Start a linear gradient */
4632                                   draw_linear_gradient(canvas, canvas, sim_flood_x1, sim_flood_y1, sim_flood_x2, sim_flood_y2,
4633                                     fill_x, fill_y, old_x, old_y + 1, draw_color, sim_flood_touched);
4634                                   fill_drag_started = 1;
4635                                 }
4636 
4637                               update_canvas(x1, y1, x2, y2);
4638                             }
4639 
4640                             draw_tux_text(TUX_GREAT, fill_tips[cur_fill], 1);
4641                         }
4642                     }
4643                   else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
4644                     {
4645                       /* Text and Label Tools! */
4646                       if (cur_tool == TOOL_LABEL && cur_label == LABEL_SELECT)
4647                         {
4648                           label_node_to_edit = search_label_list(&highlighted_label_node, old_x, old_y, 0);
4649                           if (label_node_to_edit)
4650                             {
4651                               cur_label = LABEL_LABEL;
4652                               cur_thing = label_node_to_edit->save_cur_font;
4653                               do_setcursor(cursor_insertion);
4654                               i = 0;
4655                               label_node_to_edit->is_enabled = FALSE;
4656                               derender_node(&label_node_to_edit);
4657 
4658                               texttool_len = select_texttool_len;
4659                               while (i < texttool_len)
4660                                 {
4661                                   texttool_str[i] = select_texttool_str[i];
4662                                   i = i + 1;
4663                                 }
4664                               texttool_str[i] = L'\0';
4665                               cur_color = select_color;
4666                               old_x = select_x;
4667                               old_y = select_y;
4668                               cur_font = select_cur_font;
4669                               text_state = select_text_state;
4670                               text_size = select_text_size;
4671                               // int j;
4672                               for (j = 0; j < num_font_families; j++)
4673                                 {
4674                                   if (user_font_families[j] && user_font_families[j]->handle)
4675                                     {
4676                                       TuxPaint_Font_CloseFont(user_font_families[j]->handle);
4677                                       user_font_families[j]->handle = NULL;
4678                                     }
4679                                 }
4680                               draw_fonts();
4681                               update_screen_rect(&r_toolopt);
4682                               if (onscreen_keyboard)
4683                                 {
4684                                   if (old_y < r_canvas.h / 2)
4685                                     kbd_rect.y = r_canvas.h - kbd->surface->h;
4686                                   else
4687                                     kbd_rect.y = 0;
4688 
4689                                   SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
4690                                   update_screen_rect(&kbd_rect);
4691                                 }
4692 
4693                               do_render_cur_text(0);
4694                               draw_colors(COLORSEL_REFRESH);
4695                               draw_fonts();
4696                             }
4697 
4698                         }
4699                       else
4700                         hide_blinking_cursor();
4701 
4702 
4703 
4704                       if (cursor_x != -1 && cursor_y != -1)
4705                         {
4706                           /*
4707                              if (texttool_len > 0)
4708                              {
4709                              rec_undo_buffer();
4710                              do_render_cur_text(1);
4711                              texttool_len = 0;
4712                              }
4713                            */
4714                         }
4715                       if (onscreen_keyboard && HIT(kbd_rect) && !(cur_tool == TOOL_LABEL && cur_label == LABEL_SELECT))
4716                         {
4717                           new_kbd = osk_clicked(kbd, old_x - kbd_rect.x + r_canvas.x, old_y - kbd_rect.y + r_canvas.y);
4718                           /* keyboard has changed, erase the old, note that the old kbd has yet been freed. */
4719                           if (new_kbd != kbd)
4720                             {
4721                               kbd = new_kbd;
4722                               update_canvas_ex(kbd_rect.x, kbd_rect.y, kbd_rect.x + kbd_rect.w, kbd_rect.y + kbd_rect.h,
4723                                                0);
4724                               /* set kbd_rect dimensions according to the new keyboard */
4725                               kbd_rect.x = button_w * 2 + (canvas->w - kbd->surface->w) / 2;
4726                               if (kbd_rect.y != 0)
4727                                 kbd_rect.y = canvas->h - kbd->surface->h;
4728                               kbd_rect.w = kbd->surface->w;
4729                               kbd_rect.h = kbd->surface->h;
4730                             }
4731                           SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
4732                           update_screen_rect(&kbd_rect);
4733                         }
4734                       else
4735                         {
4736                           cursor_x = old_x;
4737                           cursor_y = old_y;
4738                           cursor_left = old_x;
4739 
4740                           if (onscreen_keyboard && !(cur_tool == TOOL_LABEL && cur_label == LABEL_SELECT))
4741                             {
4742                               if (old_y < r_canvas.h / 2)
4743                                 {
4744                                   if (kbd_rect.y != r_canvas.h - kbd->surface->h)
4745                                     {
4746                                       update_canvas_ex(kbd_rect.x, kbd_rect.y, kbd_rect.x + kbd_rect.w,
4747                                                        kbd_rect.y + kbd_rect.h, 0);
4748                                       update_screen_rect(&kbd_rect);
4749                                       kbd_rect.y = r_canvas.h - kbd->surface->h;
4750                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
4751                                       update_screen_rect(&kbd_rect);
4752                                     }
4753                                 }
4754                               else
4755                                 {
4756                                   if (kbd_rect.y != 0)
4757                                     {
4758                                       update_canvas_ex(kbd_rect.x, kbd_rect.y, kbd_rect.x + kbd_rect.w,
4759                                                        kbd_rect.y + kbd_rect.h, 0);
4760                                       update_screen_rect(&kbd_rect);
4761                                       kbd_rect.y = 0;
4762                                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
4763                                       update_screen_rect(&kbd_rect);
4764                                     }
4765                                 }
4766                             }
4767                         }
4768 
4769                       do_render_cur_text(0);
4770                     }
4771 
4772                   button_down = 1;
4773                 }
4774               else if (HIT(r_sfx) && valid_click(event.button.button))
4775                 {
4776                   /* A sound player button on the lower left has been pressed! */
4777 #ifndef NOSOUND
4778                   if (cur_tool == TOOL_STAMP && use_sound && !mute)
4779                     {
4780                       which = GRIDHIT_GD(r_sfx, gd_sfx);
4781 
4782                       if (which == 0 && !stamp_data[stamp_group][cur_stamp[stamp_group]]->no_sound)
4783                         {
4784                           /* Re-play sound effect: */
4785 
4786                           Mix_ChannelFinished(NULL);
4787                           Mix_PlayChannel(2, stamp_data[stamp_group][cur_thing]->ssnd, 0);
4788                         }
4789                       else if (which == 1 && !stamp_data[stamp_group][cur_stamp[stamp_group]]->no_descsound)
4790                         {
4791                           Mix_ChannelFinished(NULL);
4792                           Mix_PlayChannel(2, stamp_data[stamp_group][cur_thing]->sdesc, 0);
4793                         }
4794 
4795                       magic_switchout(canvas);
4796                     }
4797 #endif
4798                 }
4799             }
4800 
4801           else if (event.type == SDL_MOUSEBUTTONDOWN && wheely && event.button.button >= 4 && event.button.button <= 5)
4802             {
4803               int most = 14;
4804               int num_rows_needed;
4805               SDL_Rect r_controls;
4806               SDL_Rect r_notcontrols;
4807               SDL_Rect r_items; /* = r_notcontrols; */
4808 
4809               /* Scroll wheel code.
4810                  WARNING: this must be kept in sync with the mouse-move
4811                  code (for cursor changes) and mouse-click code. */
4812 
4813               if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_STAMP ||
4814                   cur_tool == TOOL_SHAPES || cur_tool == TOOL_LINES ||
4815                   cur_tool == TOOL_MAGIC || cur_tool == TOOL_TEXT ||
4816                   cur_tool == TOOL_ERASER || cur_tool == TOOL_LABEL ||
4817                   cur_tool == TOOL_FILL)
4818                 {
4819 
4820                   /* Left tools scroll */
4821                   if (HIT(r_tools) && NUM_TOOLS > most + TOOLOFFSET)
4822                     {
4823                       int is_upper = (event.button.button == 4);
4824 
4825                       if (is_upper && tool_scroll > 0)
4826                         {
4827                           tool_scroll -= gd_tools.cols;
4828                           playsound(screen, 1, SND_SCROLL, 1, event.button.x, SNDDIST_NEAR);
4829                           draw_toolbar();
4830                         }
4831                       else if (!is_upper && tool_scroll < NUM_TOOLS - 12 - TOOLOFFSET)
4832                         {
4833                           tool_scroll += gd_tools.cols;
4834                           playsound(screen, 1, SND_SCROLL, 1, event.button.x, SNDDIST_NEAR);
4835                           draw_toolbar();
4836                         }
4837 
4838                       if (event.button.y < r_tools.y + button_h / 2)    // cursor on the upper button
4839                         {
4840                           if (tool_scroll == 0)
4841                             do_setcursor(cursor_arrow);
4842                           else
4843                             do_setcursor(cursor_up);
4844                         }
4845 
4846                       else if (event.button.y > r_tools.y + r_tools.h - button_h / 2)   // cursor on the lower button
4847                         {
4848                           if (tool_scroll < NUM_TOOLS - 12 - TOOLOFFSET)
4849                             do_setcursor(cursor_down);
4850                           else
4851                             do_setcursor(cursor_arrow);
4852                         }
4853 
4854                       else if (tool_avail[((event.button.x - r_tools.x) / button_w) +
4855                                           ((event.button.y -
4856                                             r_tools.y - button_h / 2) / button_h) * gd_tools.cols + tool_scroll])
4857                         {
4858                           do_setcursor(cursor_hand);
4859                         }
4860                       else
4861                         {
4862                           do_setcursor(cursor_arrow);
4863                         }
4864                       update_screen_rect(&r_tools);
4865                     }
4866 
4867                   /* Right tool options scroll */
4868                   else
4869                     {
4870                       grid_dims gd_controls;    /* might become 2-by-2 */
4871                       grid_dims gd_items;       /* generally becoming 2-by-whatever */
4872 
4873                       gd_controls.rows = 0;
4874                       gd_controls.cols = 0;
4875                       gd_items.rows = 2;
4876                       gd_items.cols = 2;
4877 
4878                       /* Note set of things we're dealing with */
4879                       /* (stamps, brushes, etc.) */
4880 
4881                       if (cur_tool == TOOL_STAMP)
4882                         {
4883                           if (!disable_stamp_controls)
4884                             {
4885                               /* was 2,2 before adding left/right stamp group buttons -bjk 2007.05.15 */
4886                               gd_controls.rows = 3;
4887                               gd_controls.cols = 2;
4888                             }
4889                           else
4890                             {
4891                               /* was left 0,0 before adding left/right stamp group buttons -bjk 2007.05.03 */
4892                               gd_controls.rows = 1;
4893                               gd_controls.cols = 2;
4894                             }
4895                         }
4896                       else if (cur_tool == TOOL_TEXT)
4897                         {
4898                           if (!disable_stamp_controls)
4899                             {
4900                               gd_controls.rows = 2;
4901                               gd_controls.cols = 2;
4902                             }
4903                         }
4904                       else if (cur_tool == TOOL_LABEL)
4905                         {
4906                           if (!disable_stamp_controls)
4907                             {
4908                               gd_controls.rows = 3;
4909                               gd_controls.cols = 2;
4910                             }
4911                           else
4912                             {
4913                               gd_controls.rows = 1;
4914                               gd_controls.cols = 2;
4915                             }
4916                         }
4917                       else if (cur_tool == TOOL_MAGIC)
4918                         {
4919                           if (!disable_magic_controls)
4920                             {
4921                               gd_controls.rows = 1;
4922                               gd_controls.cols = 2;
4923                             }
4924                         }
4925                       else if (cur_tool == TOOL_SHAPES)
4926                         {
4927                           if (!disable_shape_controls)
4928                             {
4929                               gd_controls.rows = 1;
4930                               gd_controls.cols = 2;
4931                             }
4932                         }
4933 
4934                       /* number of whole or partial rows that will be needed
4935                          (can make this per-tool if variable columns needed) */
4936                       num_rows_needed = (num_things + gd_items.cols - 1) / gd_items.cols;
4937 
4938                       do_draw = 0;
4939 
4940                       r_controls.w = r_toolopt.w;
4941                       r_controls.h = gd_controls.rows * button_h;
4942                       r_controls.x = r_toolopt.x;
4943                       r_controls.y = r_toolopt.y + r_toolopt.h - r_controls.h;
4944 
4945                       r_notcontrols.w = r_toolopt.w;
4946                       r_notcontrols.h = r_toolopt.h - r_controls.h;
4947                       r_notcontrols.x = r_toolopt.x;
4948                       r_notcontrols.y = r_toolopt.y;
4949 
4950                       r_items.x = r_notcontrols.x;
4951                       r_items.y = r_notcontrols.y;
4952                       r_items.w = r_notcontrols.w;
4953                       r_items.h = r_notcontrols.h;
4954 
4955                       if (num_rows_needed * button_h > r_items.h)
4956                         {
4957                           /* too many; we'll need scroll buttons */
4958                           r_items.h -= button_h;
4959                           r_items.y += button_h / 2;
4960                         }
4961                       gd_items.rows = r_items.h / button_h;
4962 
4963                       if (0)
4964                         {
4965                         }
4966                       else
4967                         {
4968                           /* scroll button */
4969                           int is_upper = (event.button.button == 4);
4970 
4971                           if ((is_upper && *thing_scroll > 0)   /* upper arrow */
4972                               || (!is_upper && *thing_scroll / gd_items.cols < num_rows_needed - gd_items.rows) /* lower arrow */
4973                             )
4974                             {
4975                               *thing_scroll += is_upper ? -gd_items.cols : gd_items.cols;
4976                               do_draw = 1;
4977                               playsound(screen, 1, SND_SCROLL, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
4978                               if (*thing_scroll == 0)
4979                                 {
4980                                   do_setcursor(cursor_arrow);
4981                                 }
4982                             }
4983                         }
4984 
4985 
4986                       /* Assign the change(s), if any / redraw, if needed: */
4987 
4988                       if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
4989                         {
4990                           if (do_draw)
4991                             draw_brushes();
4992                         }
4993                       else if (cur_tool == TOOL_ERASER)
4994                         {
4995                           if (do_draw)
4996                             draw_erasers();
4997                         }
4998                       else if (cur_tool == TOOL_FILL)
4999                         {
5000                           if (do_draw)
5001                             draw_fills();
5002                         }
5003                       else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
5004                         {
5005                           if (do_draw)
5006                             draw_fonts();
5007                         }
5008                       else if (cur_tool == TOOL_STAMP)
5009                         {
5010                           if (do_draw)
5011                             draw_stamps();
5012                         }
5013                       else if (cur_tool == TOOL_SHAPES)
5014                         {
5015                           if (do_draw)
5016                             draw_shapes();
5017                         }
5018                       else if (cur_tool == TOOL_MAGIC)
5019                         {
5020                           if (do_draw)
5021                             draw_magic();
5022                         }
5023 
5024                       /* Update the screen: */
5025                       if (do_draw)
5026                         update_screen_rect(&r_toolopt);
5027 
5028                     }
5029                 }
5030             }
5031           else if (event.type == SDL_USEREVENT)
5032             {
5033               if (event.user.code == USEREVENT_TEXT_UPDATE)
5034                 {
5035                   /* Time to replace "Great!" with old tip text: */
5036 
5037                   if (event.user.data1 != NULL)
5038                     {
5039                       if (((unsigned char *)event.user.data1)[0] == '=')
5040                         {
5041                           draw_tux_text_ex(TUX_GREAT, (char *)event.user.data1 + 1, 1, (int)(intptr_t) event.user.data2);       //EP added (intptr_t) to avoid warning on x64
5042                         }
5043                       else
5044                         {
5045                           draw_tux_text_ex(TUX_GREAT, (char *)event.user.data1, 0, (int)(intptr_t) event.user.data2);   //EP added (intptr_t) to avoid warning on x64
5046                         }
5047                     }
5048                   else
5049                     draw_tux_text(TUX_GREAT, "", 1);
5050                 }
5051               else if (event.user.code == USEREVENT_PLAYDESCSOUND)
5052                 {
5053                   /* Play a stamp's spoken description (because the sound effect just finished) */
5054                   /* (This event is pushed into the queue by playstampdesc(), which
5055                      is a callback from Mix_ChannelFinished() when playing a stamp SFX) */
5056 
5057                   debug("Playing description sound...");
5058 
5059 #ifndef NOSOUND
5060                   Mix_ChannelFinished(NULL);    /* Kill the callback, so we don't get stuck in a loop! */
5061 
5062                   if (event.user.data1 != NULL)
5063                     {
5064                       if ((int)(intptr_t) event.user.data1 == cur_stamp[stamp_group])   /* Don't play old stamp's sound... *///EP added (intptr_t) to avoid warning on x64
5065                         {
5066                           if (!mute && stamp_data[stamp_group][(int)(intptr_t) event.user.data1]->sdesc != NULL)        //EP added (intptr_t) to avoid warning on x64
5067                             Mix_PlayChannel(2, stamp_data[stamp_group][(int)(intptr_t) event.user.data1]->sdesc,        //EP added (intptr_t) to avoid warning on x64
5068                                             0);
5069                         }
5070                     }
5071 #endif
5072                 }
5073             }
5074           else if (event.type == SDL_MOUSEBUTTONUP)
5075             {
5076               if (scrolling)
5077                 {
5078                   if (scrolltimer != NULL)
5079                     {
5080                       SDL_RemoveTimer(scrolltimer);
5081                       scrolltimer = NULL;
5082                     }
5083                   scrolling = 0;
5084                   SDL_QuitSubSystem(SDL_INIT_TIMER);
5085 
5086                   /* printf("Killing scrolling\n"); */
5087                 }
5088               /* Erase the xor drawed at click */
5089               else if (cur_tool == TOOL_STAMP && stamp_xored && event.button.button < 4)
5090                 {
5091                   stamp_xor(canvas->w / 2, canvas->h / 2);
5092                   stamp_xored = 0;
5093                   stamp_size_selector_clicked = 0;
5094                   update_screen(canvas->w / 2 - (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5095                                 canvas->h / 2 - (CUR_STAMP_H + 1) / 2 + r_canvas.y,
5096                                 canvas->w / 2 + (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5097                                 canvas->h / 2 + (CUR_STAMP_H + 1) / 2 + r_canvas.y);
5098                 }
5099 
5100 
5101               if (button_down || emulate_button_pressed)
5102                 {
5103                   if (cur_tool == TOOL_BRUSH)
5104                     {
5105                       /* (Drawing on mouse release to fix single click issue) */
5106                       brush_draw(old_x, old_y, old_x, old_y, 1);
5107                     }
5108                   else if (cur_tool == TOOL_STAMP)
5109                     {
5110                       if (old_x >= 0 && old_y >= 0 && old_x <= r_canvas.w && old_y <= r_canvas.h)
5111                         {
5112                           /* Draw a stamp! */
5113 
5114                           rec_undo_buffer();
5115 
5116                           stamp_draw(old_x, old_y);
5117                           stamp_xor(old_x, old_y);
5118                           playsound(screen, 1, SND_STAMP, 1, event.button.x, SNDDIST_NEAR);
5119 
5120                           draw_tux_text(TUX_GREAT, great_str(), 1);
5121 
5122                           /* FIXME: Make delay configurable: */
5123 
5124                           control_drawtext_timer(1000, stamp_data[stamp_group][cur_stamp[stamp_group]]->stxt,
5125                                                  stamp_data[stamp_group][cur_stamp[stamp_group]]->locale_text);
5126                         }
5127                     }
5128 
5129                   else if (cur_tool == TOOL_LINES)
5130                     {
5131                       if (!mouseaccessibility || (mouseaccessibility && !emulate_button_pressed))
5132                         {
5133                           /* (Arbitrarily large, so we draw once now) */
5134                           reset_brush_counter();
5135 
5136                           brush_draw(line_start_x, line_start_y,
5137                                      event.button.x - r_canvas.x, event.button.y - r_canvas.y, 1);
5138                           brush_draw(event.button.x - r_canvas.x,
5139                                      event.button.y - r_canvas.y,
5140                                      event.button.x - r_canvas.x, event.button.y - r_canvas.y, 1);
5141 
5142                           playsound(screen, 1, SND_LINE_END, 1, event.button.x, SNDDIST_NEAR);
5143                           draw_tux_text(TUX_GREAT, tool_tips[TOOL_LINES], 1);
5144                         }
5145                     }
5146 
5147                   else if (cur_tool == TOOL_SHAPES)
5148                     {
5149                       if (!mouseaccessibility || (mouseaccessibility && !emulate_button_pressed))
5150                         {
5151                           if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
5152                             {
5153                               /* Now we can rotate the shape... */
5154 
5155                               shape_current_x = event.button.x - r_canvas.x;
5156                               shape_current_y = event.button.y - r_canvas.y;
5157 
5158                               if (!simple_shapes && !shape_no_rotate[cur_shape])
5159                                 {
5160                                   shape_tool_mode = SHAPE_TOOL_MODE_ROTATE;
5161 
5162                                   shape_radius =
5163                                     sqrt((shape_start_x - shape_current_x) * (shape_start_x - shape_current_x) +
5164                                          (shape_start_y - shape_current_y) * (shape_start_y - shape_current_y));
5165 
5166                                   SDL_WarpMouse(shape_current_x + r_ttools.w, shape_start_y);
5167                                   do_setcursor(cursor_rotate);
5168 
5169 
5170                                   /* Erase stretchy XOR: */
5171 
5172                                   if (abs(shape_start_x - shape_current_x) > 15 || abs(shape_start_y - shape_current_y) > 15)
5173                                     do_shape(shape_start_x, shape_start_y, old_x, old_y, 0, 0);
5174 
5175                                   /* Make an initial rotation XOR to be erased: */
5176 
5177                                   do_shape(shape_start_x, shape_start_y,
5178                                            shape_current_x, shape_current_y,
5179                                            shape_rotation(shape_start_x, shape_start_y, shape_current_x, shape_current_y), 0);
5180 
5181                                   playsound(screen, 1, SND_LINE_START, 1, event.button.x, SNDDIST_NEAR);
5182                                   draw_tux_text(TUX_BORED, TIP_SHAPE_NEXT, 1);
5183 
5184 
5185                                   /* FIXME: Do something less intensive! */
5186 
5187                                   SDL_Flip(screen);
5188                                 }
5189                               else
5190                                 {
5191                                   reset_brush_counter();
5192 
5193 
5194                                   playsound(screen, 1, SND_LINE_END, 1, event.button.x, SNDDIST_NEAR);
5195                                   do_shape(shape_start_x, shape_start_y, shape_current_x, shape_current_y, 0, 1);
5196 
5197                                   SDL_Flip(screen);
5198 
5199                                   shape_tool_mode = SHAPE_TOOL_MODE_DONE;
5200                                   draw_tux_text(TUX_GREAT, tool_tips[TOOL_SHAPES], 1);
5201                                 }
5202                             }
5203                           else if (shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
5204                             {
5205                               reset_brush_counter();
5206 
5207                               playsound(screen, 1, SND_LINE_END, 1, event.button.x, SNDDIST_NEAR);
5208                               do_shape(shape_start_x, shape_start_y, shape_current_x, shape_current_y,
5209                                        shape_rotation(shape_start_x, shape_start_y,
5210                                                       event.button.x - r_canvas.x, event.button.y - r_canvas.y), 1);
5211 
5212                               shape_tool_mode = SHAPE_TOOL_MODE_DONE;
5213                               draw_tux_text(TUX_GREAT, tool_tips[TOOL_SHAPES], 1);
5214 
5215                               /* FIXME: Do something less intensive! */
5216 
5217                               SDL_Flip(screen);
5218                             }
5219                         }
5220                     }
5221                   else if (cur_tool == TOOL_MAGIC
5222                            && (magics[cur_magic].mode == MODE_PAINT || magics[cur_magic].mode == MODE_ONECLICK
5223                                || magics[cur_magic].mode == MODE_PAINT_WITH_PREVIEW))
5224                     {
5225                       if (!mouseaccessibility || (mouseaccessibility && !emulate_button_pressed))
5226                         {
5227                           int undo_ctr;
5228                           SDL_Surface *last;
5229 
5230                           /* Releasing button: Finish the magic: */
5231 
5232                           if (cur_undo > 0)
5233                             undo_ctr = cur_undo - 1;
5234                           else
5235                             undo_ctr = NUM_UNDO_BUFS - 1;
5236 
5237                           last = undo_bufs[undo_ctr];
5238 
5239                           update_rect.x = 0;
5240                           update_rect.y = 0;
5241                           update_rect.w = 0;
5242                           update_rect.h = 0;
5243 
5244                           magic_funcs[magics[cur_magic].handle_idx].release(magic_api_struct,
5245                                                                             magics[cur_magic].idx,
5246                                                                             canvas, last, old_x, old_y, &update_rect);
5247 
5248                           draw_tux_text(TUX_GREAT, magics[cur_magic].tip[magic_modeint(magics[cur_magic].mode)], 1);
5249 
5250                           update_canvas(update_rect.x, update_rect.y,
5251                                         update_rect.x + update_rect.w, update_rect.y + update_rect.h);
5252                         }
5253                     }
5254                   else if (onscreen_keyboard &&
5255                            (cur_tool == TOOL_TEXT || (cur_tool == TOOL_LABEL && cur_label != LABEL_SELECT)))
5256                     {
5257                       osk_released(kbd);
5258                       SDL_BlitSurface(kbd->surface, &kbd->rect, screen, &kbd_rect);
5259                       update_screen_rect(&kbd_rect);
5260                       //      SDL_Flip(screen);
5261                     }
5262                   else if (cur_tool == TOOL_FILL)
5263                     {
5264                       fill_drag_started = 0;
5265                     }
5266                 }
5267               button_down = 0;
5268             }
5269           else if (event.type == SDL_MOUSEMOTION && !ignoring_motion)
5270             {
5271               new_x = event.button.x - r_canvas.x;
5272               new_y = event.button.y - r_canvas.y;
5273 
5274               oldpos_x = event.motion.x;
5275               oldpos_y = event.motion.y;
5276 
5277 
5278               /* FIXME: Is doing this every event too intensive? */
5279               /* Should I check current cursor first? */
5280 
5281               if (HIT(r_tools))
5282                 {
5283                   int most = buttons_tall * gd_tools.cols;
5284 
5285                   /* Tools: */
5286 
5287                   if (NUM_TOOLS > most)
5288                     {
5289                       if (event.button.y < r_tools.y + button_h / 2)
5290                         {
5291                           if (tool_scroll > 0)
5292                             do_setcursor(cursor_up);
5293                           else
5294                             do_setcursor(cursor_arrow);
5295                         }
5296                       else if (event.button.y > r_tools.y + r_tools.h - button_h / 2)
5297                         {
5298                           if (tool_scroll < NUM_TOOLS - buttons_tall * gd_tools.cols + gd_tools.cols)
5299                             do_setcursor(cursor_down);
5300                           else
5301                             do_setcursor(cursor_arrow);
5302                         }
5303 
5304                       else if (tool_avail[((event.button.x - r_tools.x) / button_w) +
5305                                           ((event.button.y -
5306                                             r_tools.y - button_h / 2) / button_h) * gd_tools.cols + tool_scroll])
5307                         {
5308                           do_setcursor(cursor_hand);
5309                         }
5310                       else
5311                         {
5312                           do_setcursor(cursor_arrow);
5313                         }
5314                     }
5315 
5316                   else
5317                     {
5318                       if (tool_avail[((event.button.x - r_tools.x) / button_w) +
5319                                      ((event.button.y - r_tools.y) / button_h) * gd_tools.cols])
5320                         {
5321                           do_setcursor(cursor_hand);
5322                         }
5323                       else
5324                         {
5325                           do_setcursor(cursor_arrow);
5326                         }
5327                     }
5328                 }
5329               else if (HIT(r_sfx))
5330                 {
5331                   /* Sound player buttons: */
5332 
5333                   if (cur_tool == TOOL_STAMP && use_sound && !mute &&
5334                       ((GRIDHIT_GD(r_sfx, gd_sfx) == 0 &&
5335                         !stamp_data[stamp_group][cur_stamp[stamp_group]]->no_sound) ||
5336                        (GRIDHIT_GD(r_sfx, gd_sfx) == 1 &&
5337                         !stamp_data[stamp_group][cur_stamp[stamp_group]]->no_descsound)))
5338                     {
5339                       do_setcursor(cursor_hand);
5340                     }
5341                   else
5342                     {
5343                       do_setcursor(cursor_arrow);
5344                     }
5345                 }
5346               else if (HIT(r_colors))
5347                 {
5348                   /* Color picker: */
5349                   if (colors_are_selectable)
5350                     do_setcursor(cursor_hand);
5351                   else
5352                     do_setcursor(cursor_arrow);
5353                 }
5354               else if (HIT(r_toolopt))
5355                 {
5356                   /* mouse cursor code
5357                      WARNING: this must be kept in sync with the mouse-click
5358                      and mouse-click code. (it isn't, currently!) */
5359 
5360                   /* Note set of things we're dealing with */
5361                   /* (stamps, brushes, etc.) */
5362                   if (cur_tool == TOOL_STAMP)
5363                     {
5364                     }
5365                   else if (cur_tool == TOOL_TEXT || cur_tool == TOOL_LABEL)
5366                     {
5367                     }
5368 
5369 		  int control_rows = 0;
5370                   if (cur_tool == TOOL_STAMP && !disable_stamp_controls)
5371 		    control_rows = 3;
5372                   if (cur_tool == TOOL_LABEL)
5373                     {
5374 		      control_rows = 1;
5375                       if (!disable_stamp_controls)
5376 			control_rows = 3;
5377                     }
5378 
5379                   if (cur_tool == TOOL_TEXT && !disable_stamp_controls)
5380 		    control_rows = 2;
5381                   if (cur_tool == TOOL_MAGIC && !disable_magic_controls)
5382 		    control_rows = 1;
5383                   if (cur_tool == TOOL_SHAPES && !disable_shape_controls)
5384 		    control_rows = 1;
5385 		  int num_places = buttons_tall * gd_toolopt.cols - control_rows * gd_toolopt.cols;
5386 
5387                   if (num_things > num_places)
5388                     {
5389                       /* Are there scroll buttons? */
5390 		      num_places = num_places - gd_toolopt.cols; /* Two scroll buttons occupy one row */
5391                       if (event.button.y < r_ttoolopt.h + img_scroll_up->h)
5392                         {
5393                           /* Up button; is it available? */
5394 
5395                           if (*thing_scroll > 0)
5396                             do_setcursor(cursor_up);
5397                           else
5398                             do_setcursor(cursor_arrow);
5399                         }
5400                       else if (event.button.y >
5401                                (button_h * (num_places / gd_toolopt.cols) + r_ttoolopt.h + img_scroll_up->h)
5402                                && event.button.y <= (button_h * (num_places / gd_toolopt.cols) + r_ttoolopt.h + img_scroll_up->h + img_scroll_up->h))
5403                         {
5404                           /* Down button; is it available? */
5405 
5406                           if (*thing_scroll < num_things - num_places)
5407                             do_setcursor(cursor_down);
5408                           else
5409                             do_setcursor(cursor_arrow);
5410                         }
5411                       else
5412                         {
5413                           /* One of the selectors: */
5414 
5415                           which = ((event.button.y - r_ttoolopt.h - img_scroll_up->h) / button_h) * 2 + (event.button.x - (WINDOW_WIDTH - r_ttoolopt.w)) / button_w;
5416 
5417                           if (which < num_things)
5418                             do_setcursor(cursor_hand);
5419                           else
5420                             do_setcursor(cursor_arrow);
5421                         }
5422                     }
5423                   else
5424                     {
5425                       /* No scroll buttons - must be a selector: */
5426 
5427                       which = ((event.button.y - r_ttoolopt.h) / button_h) * 2 + (event.button.x - (WINDOW_WIDTH - r_ttoolopt.w)) / button_w;
5428 
5429                       if (which < num_things)
5430                         do_setcursor(cursor_hand);
5431                       else
5432                         do_setcursor(cursor_arrow);
5433                     }
5434                 }
5435               else if (HIT(r_canvas) && keyglobal == 0)
5436                 {
5437                   /* Canvas: */
5438 
5439                   if (cur_tool == TOOL_BRUSH)
5440                     do_setcursor(cursor_brush);
5441                   else if (cur_tool == TOOL_STAMP)
5442                     do_setcursor(cursor_tiny);
5443                   else if (cur_tool == TOOL_LINES || cur_tool == TOOL_FILL)
5444                     do_setcursor(cursor_crosshair);
5445                   else if (cur_tool == TOOL_SHAPES)
5446                     {
5447                       if (shape_tool_mode != SHAPE_TOOL_MODE_ROTATE)
5448                         do_setcursor(cursor_crosshair);
5449                       else
5450                         do_setcursor(cursor_rotate);
5451                     }
5452                   else if (cur_tool == TOOL_TEXT)
5453                     {
5454                       if (onscreen_keyboard && HIT(kbd_rect))
5455                         do_setcursor(cursor_hand);
5456                       else
5457                         do_setcursor(cursor_insertion);
5458                     }
5459                   else if (cur_tool == TOOL_LABEL)
5460                     {
5461                       if (cur_label == LABEL_LABEL)
5462                         if (onscreen_keyboard && HIT(kbd_rect))
5463                           do_setcursor(cursor_hand);
5464                         else
5465                           do_setcursor(cursor_insertion);
5466                       else if (cur_label == LABEL_SELECT)
5467                         {
5468                           if (search_label_list(&current_label_node, event.button.x - r_ttools.w, event.button.y, 1))
5469                             do_setcursor(cursor_hand);
5470                           else
5471                             do_setcursor(cursor_arrow);
5472                         }
5473                     }
5474                   else if (cur_tool == TOOL_MAGIC)
5475                     do_setcursor(cursor_wand);
5476                   else if (cur_tool == TOOL_ERASER)
5477                     do_setcursor(cursor_tiny);
5478                 }
5479               else
5480                 {
5481                   do_setcursor(cursor_arrow);
5482                 }
5483 
5484 
5485               if (button_down || emulate_button_pressed)
5486                 {
5487                   if (cur_tool == TOOL_BRUSH)
5488                     {
5489                       /* Pushing button and moving: Draw with the brush: */
5490 
5491                       brush_draw(old_x, old_y, new_x, new_y, 1);
5492 
5493                       playsound(screen, 0, paintsound(img_cur_brush_w), 0, event.button.x, SNDDIST_NEAR);
5494                     }
5495                   else if (cur_tool == TOOL_LINES)
5496                     {
5497                       /* Still pushing button, while moving:
5498                          Draw XOR where line will go: */
5499                       update_screen(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
5500                       line_xor(line_start_x, line_start_y, old_x, old_y);
5501 
5502                       line_xor(line_start_x, line_start_y, new_x, new_y);
5503 
5504                       update_screen(line_start_x + r_canvas.x,
5505                                     line_start_y + r_canvas.y, old_x + r_canvas.x, old_y + r_canvas.y);
5506                       update_screen(line_start_x + r_canvas.x,
5507                                     line_start_y + r_canvas.y, new_x + r_canvas.x, new_y + r_canvas.y);
5508                       update_screen(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
5509                     }
5510                   else if (cur_tool == TOOL_SHAPES)
5511                     {
5512                       /* Still pushing button, while moving:
5513                          Draw XOR where shape will go: */
5514 
5515                       if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
5516                         {
5517                           do_shape(shape_start_x, shape_start_y, old_x, old_y, 0, 0);
5518 
5519                           do_shape(shape_start_x, shape_start_y, new_x, new_y, 0, 0);
5520 
5521                           shape_reverse = (new_x < shape_start_x);
5522 
5523 
5524                           /* FIXME: Fix update shape function! */
5525 
5526                           /* update_shape(shape_start_x, old_x, new_x,
5527                              shape_start_y, old_y, new_y,
5528                              shape_locked[cur_shape]); */
5529 
5530                           SDL_Flip(screen);
5531                         }
5532                     }
5533                   else if (cur_tool == TOOL_MAGIC
5534                            && (magics[cur_magic].mode == MODE_PAINT || magics[cur_magic].mode == MODE_ONECLICK
5535                                || magics[cur_magic].mode == MODE_PAINT_WITH_PREVIEW))
5536                     {
5537                       int undo_ctr;
5538                       SDL_Surface *last;
5539 
5540                       /* Pushing button and moving: Continue doing the magic: */
5541 
5542                       if (cur_undo > 0)
5543                         undo_ctr = cur_undo - 1;
5544                       else
5545                         undo_ctr = NUM_UNDO_BUFS - 1;
5546 
5547                       last = undo_bufs[undo_ctr];
5548 
5549                       update_rect.x = 0;
5550                       update_rect.y = 0;
5551                       update_rect.w = 0;
5552                       update_rect.h = 0;
5553 
5554                       magic_funcs[magics[cur_magic].handle_idx].drag(magic_api_struct,
5555                                                                      magics[cur_magic].idx,
5556                                                                      canvas, last,
5557                                                                      old_x, old_y, new_x, new_y, &update_rect);
5558 
5559                       update_canvas(update_rect.x, update_rect.y,
5560                                     update_rect.x + update_rect.w, update_rect.y + update_rect.h);
5561                     }
5562                   else if (cur_tool == TOOL_ERASER)
5563                     {
5564                       int sz;
5565 
5566                       /* Still pushing, and moving - Erase! */
5567 
5568                       eraser_draw(old_x, old_y, new_x, new_y);
5569 
5570                       sz = calc_eraser_size(cur_eraser);
5571                       rect_xor(new_x - sz / 2, new_y - sz / 2, new_x + sz / 2, new_y + sz / 2);
5572                     }
5573                   else if (cur_tool == TOOL_FILL && cur_fill == FILL_GRADIENT_LINEAR && fill_drag_started)
5574                     {
5575                       Uint32 draw_color, canv_color;
5576                       int undo_ctr;
5577                       SDL_Surface * last;
5578 
5579                       if (cur_undo > 0)
5580                         undo_ctr = cur_undo - 1;
5581                       else
5582                         undo_ctr = NUM_UNDO_BUFS - 1;
5583 
5584                       last = undo_bufs[undo_ctr];
5585 
5586                       /* Pushing button and moving: Update the gradient: */
5587 
5588                       draw_color = SDL_MapRGB(canvas->format,
5589                                      color_hexes[cur_color][0],
5590                                      color_hexes[cur_color][1],
5591                                      color_hexes[cur_color][2]);
5592                       draw_linear_gradient(canvas, last, sim_flood_x1, sim_flood_y1, sim_flood_x2, sim_flood_y2,
5593                         fill_x, fill_y, new_x, new_y, draw_color, sim_flood_touched);
5594 
5595                       update_canvas(sim_flood_x1, sim_flood_y1, sim_flood_x2, sim_flood_y2);
5596                     }
5597                 }
5598 
5599 
5600               if (cur_tool == TOOL_STAMP ||
5601                   ((cur_tool == TOOL_ERASER && !button_down) &&
5602                    (!mouseaccessibility || (mouseaccessibility && !emulate_button_pressed))))
5603                 {
5604                   int w = 0;
5605                   int h = 0;
5606 
5607                   /* Moving: Draw XOR where stamp/eraser will apply: */
5608 
5609 
5610                   if (cur_tool == TOOL_STAMP)
5611                     {
5612                       w = active_stamp->w;
5613                       h = active_stamp->h;
5614                     }
5615                   else
5616                     {
5617                       if (cur_eraser < NUM_ERASERS / 2)
5618                         {
5619                           w = (ERASER_MIN +
5620                                (((NUM_ERASERS / 2) - cur_eraser - 1) *
5621                                 ((ERASER_MAX - ERASER_MIN) / ((NUM_ERASERS / 2) - 1))));
5622                         }
5623                       else
5624                         {
5625                           w = (ERASER_MIN +
5626                                (((NUM_ERASERS / 2) - (cur_eraser - NUM_ERASERS / 2) - 1) *
5627                                 ((ERASER_MAX - ERASER_MIN) / ((NUM_ERASERS / 2) - 1))));
5628                         }
5629 
5630                       h = w;
5631                     }
5632 
5633                   if (old_x >= 0 && old_x < r_canvas.w && old_y >= 0 && old_y < r_canvas.h)
5634                     {
5635                       if (cur_tool == TOOL_STAMP)
5636                         {
5637                           stamp_xor(old_x, old_y);
5638 
5639                           update_screen(old_x - (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5640                                         old_y - (CUR_STAMP_H + 1) / 2 + r_canvas.y,
5641                                         old_x + (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5642                                         old_y + (CUR_STAMP_H + 1) / 2 + r_canvas.y);
5643                         }
5644 
5645                       else
5646                         {
5647                           rect_xor(old_x - w / 2, old_y - h / 2, old_x + w / 2, old_y + h / 2);
5648 
5649                           update_screen(old_x - w / 2 + r_canvas.x,
5650                                         old_y - h / 2 + r_canvas.y,
5651                                         old_x + w / 2 + r_canvas.x, old_y + h / 2 + r_canvas.y);
5652                         }
5653                     }
5654 
5655                   if (new_x >= 0 && new_x < r_canvas.w && new_y >= 0 && new_y < r_canvas.h)
5656                     {
5657                       if (cur_tool == TOOL_STAMP)
5658                         {
5659                           stamp_xor(new_x, new_y);
5660 
5661                           update_screen(old_x - (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5662                                         old_y - (CUR_STAMP_H + 1) / 2 + r_canvas.y,
5663                                         old_x + (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5664                                         old_y + (CUR_STAMP_H + 1) / 2 + r_canvas.y);
5665                         }
5666                       else
5667                         {
5668                           rect_xor(new_x - w / 2, new_y - h / 2, new_x + w / 2, new_y + h / 2);
5669 
5670                           update_screen(new_x - w / 2 + r_canvas.x,
5671                                         new_y - h / 2 + r_canvas.y,
5672                                         new_x + w / 2 + r_canvas.x, new_y + h / 2 + r_canvas.y);
5673                         }
5674                     }
5675                   if (cur_tool == TOOL_STAMP && HIT(r_toolopt) && event.motion.y > r_toolopt.h
5676                       && event.motion.state == SDL_PRESSED && stamp_size_selector_clicked)
5677                     {
5678                       int control_sound = -1;
5679                       int w, h;
5680                       int old_size;
5681 
5682 #ifdef DEBUG
5683                       float choice;
5684 #endif
5685 
5686                       old_size = stamp_data[stamp_group][cur_stamp[stamp_group]]->size;
5687                       w = CUR_STAMP_W;
5688                       h = CUR_STAMP_H;
5689 
5690                       stamp_data[stamp_group][cur_stamp[stamp_group]]->size = (((MAX_STAMP_SIZE - MIN_STAMP_SIZE + 1
5691                                                                                  /* +1 to address lack of ability to get back to max default stamp size (SF Bug #1668235 -bjk 2011.01.08) */
5692                                                                                 ) * (event.button.x -
5693                                                                                      (WINDOW_WIDTH - r_ttoolopt.w))) / r_toolopt.w) +
5694                         MIN_STAMP_SIZE;
5695 
5696 #ifdef DEBUG
5697                       printf("Old size = %d, Chose %0.4f, New size =%d\n", old_size, choice,
5698                              stamp_data[stamp_group][cur_stamp[stamp_group]]->size);
5699 #endif
5700 
5701                       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size != old_size)
5702                         {
5703                           if (stamp_xored)
5704                             {
5705                               stamp_xor(canvas->w / 2, canvas->h / 2);
5706                               stamp_xored = 0;
5707 
5708                               update_screen(canvas->w / 2 - (w + 1) / 2 + r_canvas.x,
5709                                             canvas->h / 2 - (h + 1) / 2 + r_canvas.y,
5710                                             canvas->w / 2 + (w + 1) / 2 + r_canvas.x,
5711                                             canvas->h / 2 + (h + 1) / 2 + r_canvas.y);
5712                             }
5713 
5714                           update_stamp_xor();
5715                           stamp_xor(canvas->w / 2, canvas->h / 2);
5716                           stamp_xored = 1;
5717                           update_screen(canvas->w / 2 - (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5718                                         canvas->h / 2 - (CUR_STAMP_H + 1) / 2 + r_canvas.y,
5719                                         canvas->w / 2 + (CUR_STAMP_W + 1) / 2 + r_canvas.x,
5720                                         canvas->h / 2 + (CUR_STAMP_H + 1) / 2 + r_canvas.y);
5721                         }
5722 
5723                       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size < old_size)
5724                         control_sound = SND_SHRINK;
5725                       else if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size > old_size)
5726                         control_sound = SND_GROW;
5727 
5728                       if (control_sound)
5729                         {
5730                           playsound(screen, 0, control_sound, 0, SNDPOS_CENTER, SNDDIST_NEAR);
5731                           draw_stamps();
5732                           update_screen_rect(&r_toolopt);
5733                         }
5734                     }
5735                 }
5736               else if (cur_tool == TOOL_SHAPES && shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
5737                 {
5738                   do_shape(shape_start_x, shape_start_y,
5739                            shape_current_x, shape_current_y, shape_rotation(shape_start_x, shape_start_y, old_x, old_y), 0);
5740 
5741 
5742                   do_shape(shape_start_x, shape_start_y,
5743                            shape_current_x, shape_current_y, shape_rotation(shape_start_x, shape_start_y, new_x, new_y), 0);
5744 
5745 
5746                   /* FIXME: Do something less intensive! */
5747                   SDL_Flip(screen);
5748                 }
5749 
5750               old_x = new_x;
5751               old_y = new_y;
5752               oldpos_x = event.button.x;
5753               oldpos_y = event.button.y;
5754             }
5755         }
5756 
5757       if (cur_tool == TOOL_TEXT || (cur_tool == TOOL_LABEL && cur_label != LABEL_SELECT))
5758         {
5759           /* if (onscreen_keyboard) */
5760           /*   osk_clicked(kbd, old_x, old_y); */
5761           /* on_screen_keyboardd(); */
5762           cur_cursor_blink = SDL_GetTicks();
5763 
5764           if (cursor_x != -1 && cursor_y != -1 && cur_cursor_blink > last_cursor_blink + CURSOR_BLINK_SPEED)
5765             {
5766               last_cursor_blink = SDL_GetTicks();
5767               draw_blinking_cursor();
5768             }
5769         }
5770 
5771       if (motioner | hatmotioner)
5772         handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x, valhat_y);
5773 
5774 
5775       SDL_Delay(10);
5776     }
5777   while (!done);
5778 }
5779 
5780 /**
5781  * Hide the blinking text entry cursor (caret),
5782  * if it was visible.
5783  */
hide_blinking_cursor(void)5784 static void hide_blinking_cursor(void)
5785 {
5786   if (cur_toggle_count & 1)
5787     {
5788       draw_blinking_cursor();
5789     }
5790 }
5791 
5792 /**
5793  * Draw & hide the blinking text entry cursor (caret),
5794  * via XOR.  (Also keeps track of whether the cursor
5795  * is currently visible; used by hide_blinking_cursor(), above.)
5796  */
draw_blinking_cursor(void)5797 static void draw_blinking_cursor(void)
5798 {
5799   cur_toggle_count++;
5800 
5801   line_xor(cursor_x + cursor_textwidth, cursor_y,
5802            cursor_x + cursor_textwidth, cursor_y + TuxPaint_Font_FontHeight(getfonthandle(cur_font)));
5803 
5804   update_screen(cursor_x + r_canvas.x + cursor_textwidth,
5805                 cursor_y + r_canvas.y,
5806                 cursor_x + r_canvas.x + cursor_textwidth,
5807                 cursor_y + r_canvas.y + TuxPaint_Font_FontHeight(getfonthandle(cur_font)));
5808 }
5809 
5810 /**
5811  * Draw a line on the canvas using the current paint brush.
5812  *
5813  * @param x1 Starting X coordinate
5814  * @param y1 Starting Y coordinate
5815  * @param x2 Ending X coordinate
5816  * @param y2 Ending Y coordinate
5817  * @param update Update the screen afterwards?
5818  */
brush_draw(int x1,int y1,int x2,int y2,int update)5819 static void brush_draw(int x1, int y1, int x2, int y2, int update)
5820 {
5821   int dx, dy, y, frame_w, w, h;
5822   int orig_x1, orig_y1, orig_x2, orig_y2, tmp;
5823   int direction, r;
5824   float m, b;
5825 
5826   orig_x1 = x1;
5827   orig_y1 = y1;
5828 
5829   orig_x2 = x2;
5830   orig_y2 = y2;
5831 
5832 
5833   frame_w = img_brushes[cur_brush]->w / abs(brushes_frames[cur_brush]);
5834   w = frame_w / (brushes_directional[cur_brush] ? 3 : 1);
5835   h = img_brushes[cur_brush]->h / (brushes_directional[cur_brush] ? 3 : 1);
5836 
5837   x1 = x1 - (w >> 1);
5838   y1 = y1 - (h >> 1);
5839 
5840   x2 = x2 - (w >> 1);
5841   y2 = y2 - (h >> 1);
5842 
5843 
5844   direction = BRUSH_DIRECTION_NONE;
5845   if (brushes_directional[cur_brush])
5846     {
5847       r = brush_rotation(x1, y1, x2, y2) + 22;
5848       if (r < 0)
5849         r = r + 360;
5850 
5851       if (x1 != x2 || y1 != y2)
5852         direction = (r / 45);
5853     }
5854 
5855 
5856   dx = x2 - x1;
5857   dy = y2 - y1;
5858 
5859   if (dx != 0)
5860     {
5861       m = ((float)dy) / ((float)dx);
5862       b = y1 - m * x1;
5863 
5864       if (x2 >= x1)
5865         dx = 1;
5866       else
5867         dx = -1;
5868 
5869 
5870       while (x1 != x2)
5871         {
5872           y1 = m * x1 + b;
5873           y2 = m * (x1 + dx) + b;
5874 
5875           if (y1 > y2)
5876             {
5877               for (y = y1; y >= y2; y--)
5878                 blit_brush(x1, y, direction);
5879             }
5880           else
5881             {
5882               for (y = y1; y <= y2; y++)
5883                 blit_brush(x1, y, direction);
5884             }
5885 
5886           x1 = x1 + dx;
5887         }
5888     }
5889   else
5890     {
5891       if (y1 > y2)
5892         {
5893           y = y1;
5894           y1 = y2;
5895           y2 = y;
5896         }
5897 
5898       for (y = y1; y <= y2; y++)
5899         blit_brush(x1, y, direction);
5900     }
5901 
5902   if (orig_x1 > orig_x2)
5903     {
5904       tmp = orig_x1;
5905       orig_x1 = orig_x2;
5906       orig_x2 = tmp;
5907     }
5908 
5909   if (orig_y1 > orig_y2)
5910     {
5911       tmp = orig_y1;
5912       orig_y1 = orig_y2;
5913       orig_y2 = tmp;
5914     }
5915 
5916 
5917   if (update)
5918     {
5919       update_canvas(orig_x1 - (w >> 1), orig_y1 - (h >> 1), orig_x2 + (w >> 1), orig_y2 + (h >> 1));
5920     }
5921 }
5922 
5923 
5924 /**
5925  * Reset the brush counter, such that the next
5926  * attempt to draw something is guaranteed to
5927  * do so, regardless of the brushe's spacing.
5928  */
reset_brush_counter(void)5929 void reset_brush_counter(void)
5930 {
5931   brush_counter = 999;
5932 }
5933 
5934 
5935 /**
5936  * Draw into the canvas using the current paint brush.
5937  * Adheres to:
5938  *  - brush spacing (used by some brushes to avoid smearing)
5939  *  - brush animation
5940  *  - drawing direction
5941  *
5942  * @param x X coordinate
5943  * @param y Y coordinate
5944  * @param direction BRUSH_DIRECTION_... being drawn
5945  */
blit_brush(int x,int y,int direction)5946 static void blit_brush(int x, int y, int direction)
5947 {
5948   SDL_Rect src, dest;
5949 
5950   brush_counter++;
5951 
5952   if (brush_counter >= img_cur_brush_spacing)
5953     {
5954       brush_counter = 0;
5955 
5956       if (img_cur_brush_frames >= 0)
5957         {
5958           brush_frame++;
5959           if (brush_frame >= img_cur_brush_frames)
5960             brush_frame = 0;
5961         }
5962       else
5963         {
5964           int old_brush_frame = brush_frame;
5965 
5966           do
5967             {
5968               brush_frame = rand() % abs(img_cur_brush_frames);
5969             }
5970           while (brush_frame == old_brush_frame);
5971         }
5972 
5973       dest.x = x;
5974       dest.y = y;
5975 
5976       if (img_cur_brush_directional)
5977         {
5978           if (direction == BRUSH_DIRECTION_UP_LEFT ||
5979               direction == BRUSH_DIRECTION_UP || direction == BRUSH_DIRECTION_UP_RIGHT)
5980             {
5981               src.y = 0;
5982             }
5983           else if (direction == BRUSH_DIRECTION_LEFT ||
5984                    direction == BRUSH_DIRECTION_NONE || direction == BRUSH_DIRECTION_RIGHT)
5985             {
5986               src.y = img_cur_brush_h;
5987             }
5988           else if (direction == BRUSH_DIRECTION_DOWN_LEFT ||
5989                    direction == BRUSH_DIRECTION_DOWN || direction == BRUSH_DIRECTION_DOWN_RIGHT)
5990             {
5991               src.y = img_cur_brush_h << 1;
5992             }
5993 
5994           if (direction == BRUSH_DIRECTION_UP_LEFT ||
5995               direction == BRUSH_DIRECTION_LEFT || direction == BRUSH_DIRECTION_DOWN_LEFT)
5996             {
5997               src.x = brush_frame * img_cur_brush_frame_w;
5998             }
5999           else if (direction == BRUSH_DIRECTION_UP ||
6000                    direction == BRUSH_DIRECTION_NONE || direction == BRUSH_DIRECTION_DOWN)
6001             {
6002               src.x = brush_frame * img_cur_brush_frame_w + img_cur_brush_w;
6003             }
6004           else if (direction == BRUSH_DIRECTION_UP_RIGHT ||
6005                    direction == BRUSH_DIRECTION_RIGHT || direction == BRUSH_DIRECTION_DOWN_RIGHT)
6006             {
6007               src.x = brush_frame * img_cur_brush_frame_w + (img_cur_brush_w << 1);
6008             }
6009         }
6010       else
6011         {
6012           src.x = brush_frame * img_cur_brush_w;
6013           src.y = 0;
6014         }
6015 
6016       src.w = img_cur_brush_w;
6017       src.h = img_cur_brush_h;
6018 
6019       SDL_BlitSurface(img_cur_brush, &src, canvas, &dest);
6020     }
6021 }
6022 
6023 
6024 /* stamp tinter */
6025 
6026 #define TINTER_ANYHUE 0         /* like normal, but remaps all hues in the stamp */
6027 #define TINTER_NARROW 1         /* like normal, but narrow hue angle */
6028 #define TINTER_NORMAL 2         /* normal */
6029 #define TINTER_VECTOR 3         /* map black->white to black->destination */
6030 
6031 
6032 typedef struct multichan
6033 {
6034   double L, hue, sat;           /* L,a,b would be better -- 2-way formula unknown */
6035   unsigned char or, og, ob, alpha;      /* old 8-bit values */
6036 } multichan;
6037 
6038 #define X0 ((double)0.9505)
6039 #define Y0 ((double)1.0000)
6040 #define Z0 ((double)1.0890)
6041 #define u0_prime ( (4.0 * X0) / (X0 + 15.0*Y0 + 3.0*Z0) )
6042 #define v0_prime ( (9.0 * Y0) / (X0 + 15.0*Y0 + 3.0*Z0) )
6043 
6044 
6045 /**
6046  * FIXME
6047  */
fill_multichan(multichan * mc,double * up,double * vp)6048 static void fill_multichan(multichan * mc, double *up, double *vp)
6049 {
6050   double X, Y, Z, u, v;
6051   double u_prime, v_prime;      /* temp, part of official formula */
6052   double Y_norm, fract;         /* severely temp */
6053 
6054   double r = sRGB_to_linear_table[mc->or];
6055   double g = sRGB_to_linear_table[mc->og];
6056   double b = sRGB_to_linear_table[mc->ob];
6057 
6058   /* coordinate change, RGB --> XYZ */
6059   X = 0.4124 * r + 0.3576 * g + 0.1805 * b;
6060   Y = 0.2126 * r + 0.7152 * g + 0.0722 * b;
6061   Z = 0.0193 * r + 0.1192 * g + 0.9505 * b;
6062 
6063   /* XYZ --> Luv */
6064   Y_norm = Y / Y0;
6065   fract = 1.0 / (X + 15.0 * Y + 3.0 * Z);
6066   u_prime = 4.0 * X * fract;
6067   v_prime = 9.0 * Y * fract;
6068   mc->L = (Y_norm > 0.008856) ? 116.0 * pow(Y_norm, 1.0 / 3.0) - 16.0 : 903.3 * Y_norm;
6069   u = 13.0 * mc->L * (u_prime - u0_prime);
6070   v = 13.0 * mc->L * (v_prime - v0_prime);
6071 
6072   mc->sat = sqrt(u * u + v * v);
6073   mc->hue = atan2(u, v);
6074   if (up)
6075     *up = u;
6076   if (vp)
6077     *vp = v;
6078 }
6079 
6080 
6081 /**
6082  * FIXME
6083  */
tint_part_1(multichan * work,SDL_Surface * in)6084 static double tint_part_1(multichan * work, SDL_Surface * in)
6085 {
6086   int xx, yy;
6087   double u_total = 0;
6088   double v_total = 0;
6089   double u, v;
6090 
6091   Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[in->format->BytesPerPixel];
6092 
6093 
6094   SDL_LockSurface(in);
6095   for (yy = 0; yy < in->h; yy++)
6096     {
6097       for (xx = 0; xx < in->w; xx++)
6098         {
6099           multichan *mc = work + yy * in->w + xx;
6100 
6101           /* put pixels into a more tolerable form */
6102           SDL_GetRGBA(getpixel(in, xx, yy), in->format, &mc->or, &mc->og, &mc->ob, &mc->alpha);
6103 
6104           fill_multichan(mc, &u, &v);
6105 
6106           /* average out u and v, giving more weight to opaque
6107              high-saturation pixels
6108              (this is to take an initial guess at the primary hue) */
6109 
6110           u_total += mc->alpha * u * mc->sat;
6111           v_total += mc->alpha * v * mc->sat;
6112 
6113         }
6114     }
6115   SDL_UnlockSurface(in);
6116 
6117 #ifdef DEBUG
6118   fprintf(stderr, "u_total=%f\nv_total=%f\natan2()=%f\n", u_total, v_total, atan2(u_total, v_total));
6119 #endif
6120 
6121   return atan2(u_total, v_total);
6122 }
6123 
6124 
6125 /**
6126  * FIXME
6127  */
change_colors(SDL_Surface * out,multichan * work,double hue_range,multichan * key_color_ptr)6128 static void change_colors(SDL_Surface * out, multichan * work, double hue_range, multichan * key_color_ptr)
6129 {
6130   double lower_hue_1, upper_hue_1, lower_hue_2, upper_hue_2;
6131   int xx, yy;
6132   multichan dst;
6133   double satratio;
6134   double slope;
6135   void (*putpixel) (SDL_Surface *, int, int, Uint32);
6136   double old_sat;
6137   double newsat;
6138   double L;
6139   double X, Y, Z;
6140   double u_prime, v_prime;      /* temp, part of official formula */
6141   unsigned tries;
6142   double u;
6143   double v;
6144   double r, g, b;
6145 
6146 
6147   /* prepare source and destination color info
6148      should reset hue_range or not? won't bother for now */
6149   multichan key_color = *key_color_ptr; /* want to work from a copy, for safety */
6150 
6151   lower_hue_1 = key_color.hue - hue_range;
6152   upper_hue_1 = key_color.hue + hue_range;
6153   if (lower_hue_1 < -M_PI)
6154     {
6155       lower_hue_2 = lower_hue_1 + 2 * M_PI;
6156       upper_hue_2 = upper_hue_1 + 2 * M_PI;
6157     }
6158   else
6159     {
6160       lower_hue_2 = lower_hue_1 - 2 * M_PI;
6161       upper_hue_2 = upper_hue_1 - 2 * M_PI;
6162     }
6163 
6164   /* get the destination color set up */
6165   dst.or = color_hexes[cur_color][0];
6166   dst.og = color_hexes[cur_color][1];
6167   dst.ob = color_hexes[cur_color][2];
6168   fill_multichan(&dst, NULL, NULL);
6169 
6170   satratio = dst.sat / key_color.sat;
6171   slope = (dst.L - key_color.L) / dst.sat;
6172   putpixel = putpixels[out->format->BytesPerPixel];
6173 
6174   SDL_LockSurface(out);
6175   for (yy = 0; yy < out->h; yy++)
6176     {
6177       for (xx = 0; xx < out->w; xx++)
6178         {
6179           multichan *mc = work + yy * out->w + xx;
6180           double oldhue = mc->hue;
6181 
6182           /* if not in the first range, and not in the second range, skip this one
6183              (really should alpha-blend as a function of hue angle difference) */
6184           if ((oldhue < lower_hue_1 || oldhue > upper_hue_1) && (oldhue < lower_hue_2 || oldhue > upper_hue_2))
6185             {
6186               putpixel(out, xx, yy, SDL_MapRGBA(out->format, mc->or, mc->og, mc->ob, mc->alpha));
6187               continue;
6188             }
6189 
6190           /* Modify the pixel */
6191           old_sat = mc->sat;
6192           newsat = old_sat * satratio;
6193           L = mc->L;
6194           if (dst.sat > 0)
6195             L += newsat * slope;        /* not greyscale destination */
6196           else
6197             L += old_sat * (dst.L - key_color.L) / key_color.sat;
6198 
6199           /* convert from L,u,v all the way back to sRGB with 8-bit channels */
6200           tries = 3;
6201         trysat:;
6202           u = newsat * sin(dst.hue);
6203           v = newsat * cos(dst.hue);
6204 
6205           /* Luv to XYZ */
6206           u_prime = u / (13.0 * L) + u0_prime;
6207           v_prime = v / (13.0 * L) + v0_prime;
6208           Y = (L > 7.99959199307) ? Y0 * pow((L + 16.0) / 116.0, 3.0) : Y0 * L / 903.3;
6209           X = 2.25 * Y * u_prime / v_prime;
6210           Z = (3.0 * Y - 0.75 * Y * u_prime) / v_prime - 5.0 * Y;
6211 
6212           /* coordinate change: XYZ to RGB */
6213           r = 3.2410 * X + -1.5374 * Y + -0.4986 * Z;
6214           g = -0.9692 * X + 1.8760 * Y + 0.0416 * Z;
6215           b = 0.0556 * X + -0.2040 * Y + 1.0570 * Z;
6216 
6217           /* If it is out of gamut, try to de-saturate it a few times before truncating.
6218              (the linear_to_sRGB function will truncate) */
6219           if ((r <= -0.5 || g <= -0.5 || b <= -0.5 || r >= 255.5 || g >= 255.5 || b >= 255.5) && tries--)
6220             {
6221               newsat *= 0.8;
6222               goto trysat;
6223             }
6224 
6225           putpixel(out, xx, yy,
6226                    SDL_MapRGBA(out->format, linear_to_sRGB(r), linear_to_sRGB(g), linear_to_sRGB(b), mc->alpha));
6227         }
6228     }
6229   SDL_UnlockSurface(out);
6230 }
6231 
6232 
6233 /**
6234  * FIXME
6235  */
find_most_saturated(double initial_hue,multichan * work,unsigned num,double * hue_range_ptr)6236 static multichan *find_most_saturated(double initial_hue, multichan * work, unsigned num, double *hue_range_ptr)
6237 {
6238   /* find the most saturated pixel near the initial hue guess */
6239   multichan *key_color_ptr = NULL;
6240   double hue_range;
6241   unsigned i;
6242   double max_sat;
6243   double lower_hue_1;
6244   double upper_hue_1;
6245   double lower_hue_2;
6246   double upper_hue_2;
6247   multichan *mc;
6248 
6249   switch (stamp_data[stamp_group][cur_stamp[stamp_group]]->tinter)
6250     {
6251     default:
6252     case TINTER_NORMAL:
6253       hue_range = 18 * M_PI / 180.0;    /* plus or minus 18 degrees search, 27 replace */
6254       break;
6255     case TINTER_NARROW:
6256       hue_range = 6 * M_PI / 180.0;     /* plus or minus 6 degrees search, 9 replace */
6257       break;
6258     case TINTER_ANYHUE:
6259       hue_range = M_PI;         /* plus or minus 180 degrees */
6260       break;
6261     }
6262 
6263 hue_range_retry:;
6264 
6265   max_sat = 0;
6266   lower_hue_1 = initial_hue - hue_range;
6267   upper_hue_1 = initial_hue + hue_range;
6268 
6269   if (lower_hue_1 < -M_PI)
6270     {
6271       lower_hue_2 = lower_hue_1 + 2 * M_PI;
6272       upper_hue_2 = upper_hue_1 + 2 * M_PI;
6273     }
6274   else
6275     {
6276       lower_hue_2 = lower_hue_1 - 2 * M_PI;
6277       upper_hue_2 = upper_hue_1 - 2 * M_PI;
6278     }
6279 
6280   i = num;
6281   while (i--)
6282     {
6283       mc = work + i;
6284 
6285       /* if not in the first range, and not in the second range, skip this one */
6286       if ((mc->hue < lower_hue_1 || mc->hue > upper_hue_1) && (mc->hue < lower_hue_2 || mc->hue > upper_hue_2))
6287         continue;
6288 
6289       if (mc->sat > max_sat)
6290         {
6291           max_sat = mc->sat;
6292           key_color_ptr = mc;
6293         }
6294     }
6295 
6296   if (!key_color_ptr)
6297     {
6298       hue_range *= 1.5;
6299 
6300       if (hue_range < M_PI)
6301         goto hue_range_retry;
6302     }
6303 
6304   *hue_range_ptr = hue_range;
6305 
6306   return key_color_ptr;
6307 }
6308 
6309 
6310 /**
6311  * FIXME
6312  */
vector_tint_surface(SDL_Surface * out,SDL_Surface * in)6313 static void vector_tint_surface(SDL_Surface * out, SDL_Surface * in)
6314 {
6315   int xx, yy;
6316 
6317   Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[in->format->BytesPerPixel];
6318   void (*putpixel) (SDL_Surface *, int, int, Uint32) = putpixels[out->format->BytesPerPixel];
6319 
6320   double r = sRGB_to_linear_table[color_hexes[cur_color][0]];
6321   double g = sRGB_to_linear_table[color_hexes[cur_color][1]];
6322   double b = sRGB_to_linear_table[color_hexes[cur_color][2]];
6323 
6324   SDL_LockSurface(in);
6325   for (yy = 0; yy < in->h; yy++)
6326     {
6327       for (xx = 0; xx < in->w; xx++)
6328         {
6329           unsigned char r8, g8, b8, a8;
6330           double old;
6331 
6332           SDL_GetRGBA(getpixel(in, xx, yy), in->format, &r8, &g8, &b8, &a8);
6333           /* get the linear greyscale value */
6334           old =
6335             sRGB_to_linear_table[r8] * 0.2126 + sRGB_to_linear_table[g8] * 0.7152 + sRGB_to_linear_table[b8] * 0.0722;
6336 
6337           putpixel(out, xx, yy,
6338                    SDL_MapRGBA(out->format, linear_to_sRGB(r * old),
6339                                linear_to_sRGB(g * old), linear_to_sRGB(b * old), a8));
6340         }
6341     }
6342   SDL_UnlockSurface(in);
6343 }
6344 
6345 
6346 /**
6347  * Tint a surface (e.g., a stamp) using the currently-selected color.
6348  *
6349  * @param tmp_surf Destination surface
6350  * @param surf_ptr Source surface
6351  */
tint_surface(SDL_Surface * tmp_surf,SDL_Surface * surf_ptr)6352 static void tint_surface(SDL_Surface * tmp_surf, SDL_Surface * surf_ptr)
6353 {
6354   unsigned width = surf_ptr->w;
6355   unsigned height = surf_ptr->h;
6356   multichan *work;
6357   multichan *key_color_ptr;
6358   double initial_hue;
6359   double hue_range;
6360 
6361 
6362   work = malloc(sizeof(multichan) * width * height);
6363 
6364   if (work)
6365     {
6366       initial_hue = tint_part_1(work, surf_ptr);
6367 
6368 #ifdef DEBUG
6369       /* FIXME: To stderr, not stdout? */
6370       printf("initial_hue = %f\n", initial_hue);
6371 #endif
6372 
6373       key_color_ptr = find_most_saturated(initial_hue, work, width * height, &hue_range);
6374 
6375 #ifdef DEBUG
6376       /* FIXME: To stderr, not stdout? */
6377       printf("key_color_ptr = %d\n", (int)(intptr_t) key_color_ptr);    //EP added (intptr_t) to avoid warning on x64
6378 #endif
6379 
6380       if (key_color_ptr)
6381         {
6382           /* wider for processing than for searching */
6383           hue_range *= 1.5;
6384 
6385           change_colors(tmp_surf, work, hue_range, key_color_ptr);
6386 
6387           free(work);
6388           return;
6389         }
6390       else
6391         {
6392           fprintf(stderr, "find_most_saturated() failed\n");
6393         }
6394 
6395       free(work);
6396     }
6397 
6398   /* Failed!  Fall back: */
6399 
6400   fprintf(stderr, "Falling back to tinter=vector, this should be in the *.dat file\n");
6401 
6402   vector_tint_surface(tmp_surf, surf_ptr);
6403 }
6404 
6405 
6406 
6407 /**
6408  * Draw the current stamp onto the canvas.
6409  *
6410  * @param x X coordinate
6411  * @param y Y coordinate
6412  */
stamp_draw(int x,int y)6413 static void stamp_draw(int x, int y)
6414 {
6415   SDL_Rect dest;
6416   SDL_Surface *tmp_surf, *surf_ptr, *final_surf;
6417   Uint32 amask;
6418   Uint8 r, g, b, a;
6419   int xx, yy, dont_free_tmp_surf, base_x, base_y;
6420 
6421   Uint32(*getpixel) (SDL_Surface *, int, int);
6422   void (*putpixel) (SDL_Surface *, int, int, Uint32);
6423 
6424   surf_ptr = active_stamp;
6425 
6426   getpixel = getpixels[surf_ptr->format->BytesPerPixel];
6427 
6428 
6429   /* Create a temp surface to play with: */
6430 
6431   if (stamp_colorable(cur_stamp[stamp_group]) || stamp_tintable(cur_stamp[stamp_group]))
6432     {
6433       amask = ~(surf_ptr->format->Rmask | surf_ptr->format->Gmask | surf_ptr->format->Bmask);
6434 
6435       tmp_surf =
6436         SDL_CreateRGBSurface(SDL_SWSURFACE,
6437                              surf_ptr->w,
6438                              surf_ptr->h,
6439                              surf_ptr->format->BitsPerPixel,
6440                              surf_ptr->format->Rmask, surf_ptr->format->Gmask, surf_ptr->format->Bmask, amask);
6441 
6442       if (tmp_surf == NULL)
6443         {
6444           fprintf(stderr, "\nError: Can't render the colored stamp!\n"
6445                   "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
6446 
6447           cleanup();
6448           exit(1);
6449         }
6450 
6451       dont_free_tmp_surf = 0;
6452     }
6453   else
6454     {
6455       /* Not altering color; no need to create temp surf if we don't use it! */
6456 
6457       tmp_surf = NULL;
6458       dont_free_tmp_surf = 1;
6459     }
6460 
6461   if (tmp_surf != NULL)
6462     putpixel = putpixels[tmp_surf->format->BytesPerPixel];
6463   else
6464     putpixel = NULL;
6465 
6466 
6467   /* Alter the stamp's color, if needed: */
6468 
6469   if (stamp_colorable(cur_stamp[stamp_group]) && tmp_surf != NULL)
6470     {
6471       /* Render the stamp in the chosen color: */
6472 
6473       /* FIXME: It sucks to render this EVERY TIME.  Why not just when
6474          they pick the color, or pick the stamp, like with brushes? */
6475 
6476       /* Render the stamp: */
6477 
6478       SDL_LockSurface(surf_ptr);
6479       SDL_LockSurface(tmp_surf);
6480 
6481       for (yy = 0; yy < surf_ptr->h; yy++)
6482         {
6483           for (xx = 0; xx < surf_ptr->w; xx++)
6484             {
6485               SDL_GetRGBA(getpixel(surf_ptr, xx, yy), surf_ptr->format, &r, &g, &b, &a);
6486 
6487               putpixel(tmp_surf, xx, yy,
6488                        SDL_MapRGBA(tmp_surf->format,
6489                                    color_hexes[cur_color][0], color_hexes[cur_color][1], color_hexes[cur_color][2], a));
6490             }
6491         }
6492 
6493       SDL_UnlockSurface(tmp_surf);
6494       SDL_UnlockSurface(surf_ptr);
6495     }
6496   else if (stamp_tintable(cur_stamp[stamp_group]))
6497     {
6498       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->tinter == TINTER_VECTOR)
6499         vector_tint_surface(tmp_surf, surf_ptr);
6500       else
6501         tint_surface(tmp_surf, surf_ptr);
6502     }
6503   else
6504     {
6505       /* No color change, just use it! */
6506       tmp_surf = surf_ptr;
6507     }
6508 
6509   /* Shrink or grow it! */
6510   final_surf = thumbnail(tmp_surf, CUR_STAMP_W, CUR_STAMP_H, 0);
6511 
6512   /* Where it will go? */
6513   base_x = x - (CUR_STAMP_W + 1) / 2;
6514   base_y = y - (CUR_STAMP_H + 1) / 2;
6515 
6516   /* And blit it! */
6517   dest.x = base_x;
6518   dest.y = base_y;
6519   SDL_BlitSurface(final_surf, NULL, canvas, &dest);     /* FIXME: Conditional jump or move depends on uninitialised value(s) */
6520 
6521   update_canvas(x - (CUR_STAMP_W + 1) / 2,
6522                 y - (CUR_STAMP_H + 1) / 2, x + (CUR_STAMP_W + 1) / 2, y + (CUR_STAMP_H + 1) / 2);
6523 
6524   /* Free the temporary surfaces */
6525 
6526   if (!dont_free_tmp_surf)
6527     SDL_FreeSurface(tmp_surf);
6528 
6529   SDL_FreeSurface(final_surf);
6530 }
6531 
6532 
6533 /**
6534  * Store canvas or label into undo buffer
6535  */
rec_undo_buffer(void)6536 static void rec_undo_buffer(void)
6537 {
6538   int wanna_update_toolbar;
6539 
6540   wanna_update_toolbar = 0;
6541 
6542   rec_undo_label();
6543 
6544   SDL_BlitSurface(canvas, NULL, undo_bufs[cur_undo], NULL);
6545   undo_starters[cur_undo] = UNDO_STARTER_NONE;
6546 
6547   cur_undo = (cur_undo + 1) % NUM_UNDO_BUFS;
6548 
6549   if (cur_undo == oldest_undo)
6550     oldest_undo = (oldest_undo + 1) % NUM_UNDO_BUFS;
6551 
6552   newest_undo = cur_undo;
6553 
6554 #ifdef DEBUG
6555   /* FIXME: Stderr instead of stdout? */
6556   printf("DRAW: Current=%d  Oldest=%d  Newest=%d\n", cur_undo, oldest_undo, newest_undo);
6557 #endif
6558 
6559 
6560   /* Update toolbar buttons, if needed: */
6561 
6562   if (tool_avail[TOOL_UNDO] == 0)
6563     {
6564       tool_avail[TOOL_UNDO] = 1;
6565       wanna_update_toolbar = 1;
6566     }
6567 
6568   if (tool_avail[TOOL_REDO])
6569     {
6570       tool_avail[TOOL_REDO] = 0;
6571       wanna_update_toolbar = 1;
6572     }
6573 
6574   if (wanna_update_toolbar)
6575     {
6576       draw_toolbar();
6577       update_screen_rect(&r_tools);
6578     }
6579 }
6580 
6581 
6582 /**
6583  * Show program version, build date, and (optionally) details,
6584  * to stdout
6585  *
6586  * @param details Show details?
6587  */
show_version(int details)6588 void show_version(int details)
6589 {
6590   printf("\nTux Paint\n");
6591   printf("  Version " VER_VERSION " (" VER_DATE ")\n");
6592 
6593 
6594   if (details == 0)
6595     return;
6596 
6597 
6598   printf("\nBuilt with these options:\n");
6599 
6600 
6601   /* Quality reductions: */
6602 
6603 #ifdef LOW_QUALITY_THUMBNAILS
6604   printf("  Low Quality Thumbnails enabled  (LOW_QUALITY_THUMBNAILS)\n");
6605 #endif
6606 
6607 #ifdef LOW_QUALITY_COLOR_SELECTOR
6608   printf("  Low Quality Color Selector enabled  (LOW_QUALITY_COLOR_SELECTOR)\n");
6609 #endif
6610 
6611 #ifdef LOW_QUALITY_STAMP_OUTLINE
6612   printf("  Low Quality Stamp Outline enabled  (LOW_QUALITY_STAMP_OUTLINE)\n");
6613 #endif
6614 
6615 #ifdef NO_PROMPT_SHADOWS
6616   printf("  Prompt Shadows disabled  (NO_PROMPT_SHADOWS)\n");
6617 #endif
6618 
6619 #ifdef SMALL_CURSOR_SHAPES
6620   printf("  Small cursor shapes enabled  (SMALL_CURSOR_SHAPES)\n");
6621 #endif
6622 
6623 #ifdef NO_BILINEAR
6624   printf("  Bilinear scaling disabled  (NO_BILINEAR)\n");
6625 #endif
6626 
6627 #ifdef NOSVG
6628   printf("  SVG support disabled  (NOSVG)\n");
6629 #endif
6630 
6631   /* Sound: */
6632 
6633 #ifdef NOSOUND
6634   printf("  Sound disabled  (NOSOUND)\n");
6635 #endif
6636 
6637 
6638   /* Platform */
6639 
6640 #ifdef __APPLE__
6641   printf("  Built for Mac OS X  (__APPLE__)\n");
6642 #elif WIN32
6643   printf("  Built for Windows  (WIN32)\n");
6644 #elif __BEOS__
6645   printf("  Built for BeOS  (__BEOS__)\n");
6646 #elif __HAIKU__
6647   printf("  Built for Haiku (__HAIKU__)\n");
6648 #elif NOKIA_770
6649   printf("  Built for Maemo  (NOKIA_770)\n");
6650 #elif OLPC_XO
6651   printf("  Built for XO  (OLPC_XO)\n");
6652 #else
6653   printf("  Built for POSIX\n");
6654 #endif
6655 
6656 
6657   /* Video options */
6658 
6659 #ifdef USE_HWSURFACE
6660   printf("  Using hardware surface  (USE_HWSURFACE)\n");
6661 #else
6662   printf("  Using software surface  (no USE_HWSURFACE)\n");
6663 #endif
6664   printf("  Using %dbpp video  (VIDEO_BPP=%d)\n", VIDEO_BPP, VIDEO_BPP);
6665 
6666 
6667   /* Print method */
6668 
6669 #ifdef PRINTMETHOD_PNG_PNM_PS
6670   printf("  Prints as PNGs  (PRINTMETHOD_PNG_PNM_PS)\n");
6671 #endif
6672 
6673 #ifdef PRINTMETHOD_PS
6674   printf("  Prints as PostScript  (PRINTMETHOD_PS)\n");
6675 #endif
6676 
6677 
6678   /* Threading */
6679 
6680 #ifdef FORKED_FONTS
6681   printf("  Threaded font loader enabled  (FORKED_FONTS)\n");
6682 #else
6683   printf("  Threaded font loader disabled  (no FORKED_FONTS)\n");
6684 #endif
6685 
6686 
6687   /* Old code used */
6688 
6689 #ifdef OLD_STAMP_GROW_SHRINK
6690   printf("  Old-style stamp size UI  (OLD_STAMP_GROW_SHRINK)\n");
6691 #endif
6692 
6693   printf("  Data directory (DATA_PREFIX) = %s\n", DATA_PREFIX);
6694   printf("  Plugin directory (MAGIC_PREFIX) = %s\n", MAGIC_PREFIX);
6695   printf("  Doc directory (DOC_PREFIX) = %s\n", DOC_PREFIX);
6696   printf("  Locale directory (LOCALEDIR) = %s\n", LOCALEDIR);
6697   printf("  Input Method directory (IMDIR) = %s\n", IMDIR);
6698   printf("  System config directory (CONFDIR) = %s\n", CONFDIR);
6699 
6700 
6701   /* Debugging */
6702 
6703 #ifdef DEBUG
6704   printf("  Verbose debugging enabled  (DEBUG)\n");
6705 #endif
6706 
6707 #ifdef DEBUG_MALLOC
6708   printf("  Memory allocation debugging enabled  (DEBUG_MALLOC)\n");
6709 #endif
6710 
6711 
6712   printf("\n");
6713 }
6714 
6715 
6716 /**
6717  * Show usage display and exit.
6718  *
6719  * @param exitcode What exit() code to give;
6720  *   also determines stdout (0) vs stderr (non-zero) for output
6721  */
show_usage(int exitcode)6722 void show_usage(int exitcode)
6723 {
6724   FILE *f = exitcode ? stderr : stdout;
6725 
6726   /* *INDENT-OFF* */
6727   fprintf(f,
6728           "\n"
6729           "Usage: %s {--usage | --help | --version | --verbose-version | --copying}\n"
6730           "\n"
6731           " Config:\n"
6732           "  [--nosysconfig]\n"
6733           "\n"
6734           " Video/Sound:\n"
6735           "  [--windowed | --fullscreen]\n"
6736           "  [--WIDTHxHEIGHT | --native]\n"
6737           "  [--orient=landscape | --orient=portrait]\n"
6738           "  [--disablescreensaver | --allowscreensaver ]\n"
6739           "  [--sound | --nosound]\n"
6740           "  [--stereo | --nostereo]\n"
6741           "  [--colorfile FILE]\n"
6742           "\n"
6743           " Mouse/Keyboard:\n"
6744           "  [--fancycursors | --nofancycursors]\n"
6745           "  [--hidecursor | --showcursor]\n"
6746           "  [--noshortcuts | --shortcuts]\n"
6747           "  [--dontgrab | --grab]\n"
6748           "  [--wheelmouse | --nowheelmouse]\n"
6749           "  [--nobuttondistinction | --buttondistinction]\n"
6750           "\n"
6751           " Simplification:\n"
6752           "  [--complexshapes | --simpleshapes]\n"
6753           "  [--outlines | --nooutlines]\n"
6754           "  [--mixedcase | --uppercase]\n"
6755           "  [--stampsize=[0-10] | --stampsize=default]\n"
6756           "  [--quit | --noquit]\n"
6757           "  [--stamps | --nostamps]\n"
6758           "  [--nostampcontrols | --stampcontrols]\n"
6759           "  [--nomagiccontrols | --magiccontrols]\n"
6760           "  [--noshapecontrols | --shapecontrols]\n"
6761           "  [--nolabel | --label]\n"
6762           "  [--newcolorsfirst | --newcolorslast]\n"
6763           "\n"
6764           " Languages:\n"
6765           "  [--lang LANGUAGE | --locale LOCALE | --lang help]\n"
6766           "  [--mirrorstamps | --dontmirrorstamps]\n"
6767           "  [--sysfonts | --nosysfonts]\n"
6768           "  [--currentlocalefont | --alllocalefonts]\n"
6769           "\n"
6770           " Printing:\n"
6771           "  [--print | --noprint]\n"
6772           "  [--printdelay=SECONDS]\n"
6773           "  [--altprintmod | --altprintalways | --altprintnever]\n"
6774 #if defined(WIN32) || defined(__APPLE__)
6775           "  [--printcfg | --noprintcfg]\n"
6776 #endif
6777 #if !defined(WIN32) && !defined(__APPLE__) && !defined(__BEOS__) && !defined(__HAIKU__)
6778           "  [--printcommand=COMMAND]\n"
6779           "  [--altprintcommand=COMMAND]\n"
6780           "  [--papersize PAPERSIZE | --papersize help]\n"
6781 #endif
6782           "\n"
6783           " Saving:\n"
6784           "  [--saveoverask | --saveover | --saveovernew]\n"
6785           "  [--startblank | --startlast]\n"
6786           "  [--savedir DIRECTORY]\n"
6787           "  [--nosave | --save]\n"
6788           "  [--autosave | --noautosave]\n"
6789           "\n"
6790           " Data:\n"
6791           "  [--nolockfile]\n"
6792           "  [--datadir DIRECTORY]\n"
6793           "\n"
6794           " Exporting:\n"
6795           "  [--exportdir DIRECTORY]\n"
6796           "\n"
6797           " Accessibility:\n"
6798           "  [--mouse-accessibility]\n"
6799           "  [--mouse | --keyboard]\n"
6800           "  [--onscreen-keyboard]\n"
6801           "  [--onscreen-keyboard-layout=LAYOUT]\n"
6802           "  [--onscreen-keyboard-disable-change]\n"
6803           "\n"
6804           " Joystick:\n"
6805           "  [--joystick-dev N] (default=0)\n"
6806           "  [--joystick-slowness N] (0-500; default value is 15)\n"
6807           "  [--joystick-threshold N] (0-32766; default value is 3200)\n"
6808           "  [--joystick-maxsteps N] (1-7; default value is 7)\n"
6809           "  [--joystick-hat-slowness N] (0-500; default value is 15)\n"
6810           "  [--joystick-hat-timeout N] (0-3000; default value is 1000)\n"
6811           "  [--joystick-buttons-ignore=BUTTON1,BUTTON2,...]\n"
6812           "  [--joystick-btn-COMMAND=BUTTON]\n"
6813           /* FIXME: "--joystick-btn-help" to list available commands, like "--lang help" */
6814           "\n",
6815           progname);
6816   /* *INDENT-ON* */
6817 }
6818 
6819 
6820 /**
6821  * Compute default scale factor for stamps.
6822  *
6823  * The original Tux Paint canvas was 448x376. The canvas can be
6824  * other sizes now, but many old stamps are sized for the small
6825  * canvas. So, with larger canvases, we must choose a good scale
6826  * factor to compensate. As the canvas size grows, the user will
6827  * want a balance of "more stamps on the screen" and "stamps not
6828  * getting tiny". This will calculate the needed scale factor.
6829  *
6830  * @param ratio FIXME
6831  * @return FIXME
6832  */
compute_default_scale_factor(double ratio)6833 static unsigned compute_default_scale_factor(double ratio)
6834 {
6835   double old_diag = sqrt(448 * 448 + 376 * 376);
6836   double new_diag = sqrt(canvas->w * canvas->w + canvas->h * canvas->h);
6837   double good_def = ratio * sqrt(new_diag / old_diag);
6838   double good_log = log(good_def);
6839   unsigned defsize = HARD_MAX_STAMP_SIZE;
6840 
6841   while (defsize > 0)
6842     {
6843       double this_err = good_log - log(scaletable[defsize].numer / (double)scaletable[defsize].denom);
6844       double next_err = good_log - log(scaletable[defsize - 1].numer / (double)scaletable[defsize - 1].denom);
6845 
6846       if (fabs(next_err) > fabs(this_err))
6847         break;
6848       defsize--;
6849     }
6850   return defsize;
6851 }
6852 
6853 
6854 /**
6855  * Callback for directory walking while loading brushes
6856  *
6857  * @param screen Screen/window surface, for drawing progress bar animation
6858  * @param dir Directory path
6859  * @param dirlen Length of directory path string (ignored)
6860  * @param files List of files (being collected)
6861  * @param i Counter
6862  * @param locale UI's locale, for loading localized text (ignored)
6863  */
loadbrush_callback(SDL_Surface * screen,const char * restrict const dir,unsigned dirlen,tp_ftw_str * files,unsigned i,const char * restrict const locale)6864 static void loadbrush_callback(SDL_Surface * screen,
6865                                const char *restrict const dir,
6866                                unsigned dirlen, tp_ftw_str * files, unsigned i, const char *restrict const locale)
6867 {
6868   FILE *fi;
6869   char buf[64];
6870   int want_rand;
6871   int brush_w, brush_h;
6872   float scale;
6873 
6874   (void)dirlen;
6875   (void)locale;
6876 
6877 
6878   qsort(files, i, sizeof *files, compare_ftw_str);
6879   while (i--)
6880     {
6881       show_progress_bar(screen);
6882       if (strcasestr(files[i].str, ".png"))
6883         {
6884           char fname[512];
6885 
6886           if (strcasecmp(files[i].str, SHAPE_BRUSH_NAME) == 0)
6887             shape_brush = num_brushes;
6888 
6889           safe_snprintf(fname, sizeof fname, "%s/%s", dir, files[i].str);
6890           if (num_brushes == num_brushes_max)
6891             {
6892               num_brushes_max = num_brushes_max * 5 / 4 + 4;
6893               img_brushes = realloc(img_brushes, num_brushes_max * sizeof *img_brushes);
6894               img_brushes_thumbs = realloc(img_brushes_thumbs, num_brushes_max * sizeof *img_brushes_thumbs);
6895               brushes_frames = realloc(brushes_frames, num_brushes_max * sizeof(int));
6896               brushes_directional = realloc(brushes_directional, num_brushes_max * sizeof(short));
6897               brushes_spacing = realloc(brushes_spacing, num_brushes_max * sizeof(int));
6898             }
6899           img_brushes[num_brushes] = loadimage(fname);
6900 
6901           /* Load brush metadata, if any: */
6902 
6903           brushes_frames[num_brushes] = 1;
6904           brushes_directional[num_brushes] = 0;
6905           brushes_spacing[num_brushes] = img_brushes[num_brushes]->h / 4;
6906 
6907           strcpy(strcasestr(fname, ".png"), ".dat"); /* FIXME: Use strncpy (ugh, complicated) */
6908           fi = fopen(fname, "r");
6909 
6910           want_rand = 0;
6911 
6912           if (fi != NULL)
6913             {
6914               do
6915                 {
6916                   if (fgets(buf, sizeof(buf), fi))
6917                     {
6918                       if (strstr(buf, "frames=") != NULL)
6919                         {
6920                           brushes_frames[num_brushes] = atoi(strstr(buf, "frames=") + 7);
6921                         }
6922                       else if (strstr(buf, "spacing=") != NULL)
6923                         {
6924                           brushes_spacing[num_brushes] = atoi(strstr(buf, "spacing=") + 8);
6925                         }
6926                       else if (strstr(buf, "directional") != NULL)
6927                         {
6928                           brushes_directional[num_brushes] = 1;
6929                         }
6930                       else if (strstr(buf, "random") != NULL)
6931                         {
6932                           want_rand = 1;
6933                         }
6934                     }
6935                 }
6936               while (!feof(fi));
6937               fclose(fi);
6938 
6939               if (want_rand)
6940                 brushes_frames[num_brushes] *= -1;
6941             }
6942 
6943           /* Generate thumbnail */
6944           brush_w = ((img_brushes[num_brushes]->w / abs(brushes_frames[num_brushes])) / (brushes_directional[num_brushes] ? 3 : 1));
6945           brush_h = (img_brushes[num_brushes]->h / (brushes_directional[num_brushes] ? 3 : 1));
6946 
6947           if (brush_w <= button_w && brush_h <= button_h
6948           ) {
6949             img_brushes_thumbs[num_brushes] = duplicate_surface(img_brushes[num_brushes]);
6950           } else {
6951             if (brush_w > brush_h) {
6952               scale = (float) ((float)button_w / (float)brush_w);
6953             } else {
6954               scale = (float) ((float)button_h / (float)brush_h);
6955             }
6956 
6957             img_brushes_thumbs[num_brushes] =
6958               thumbnail2(
6959                 img_brushes[num_brushes],
6960                 img_brushes[num_brushes]->w * scale,
6961                 img_brushes[num_brushes]->h * scale,
6962                 0, /* no need to ask to keep aspect; already kept */
6963                 1  /* keep alpha */
6964               );
6965           }
6966 
6967           num_brushes++;
6968         }
6969       free(files[i].str);
6970     }
6971   free(files);
6972 }
6973 
6974 
6975 /**
6976  * FIXME
6977  */
load_brush_dir(SDL_Surface * screen,const char * restrict const dir)6978 static void load_brush_dir(SDL_Surface * screen, const char *restrict const dir)
6979 {
6980   char buf[TP_FTW_PATHSIZE];
6981   unsigned dirlen = strlen(dir);
6982 
6983   memcpy(buf, dir, dirlen);
6984   tp_ftw(screen, buf, dirlen, 0, loadbrush_callback, NULL);
6985 }
6986 
6987 /**
6988  * FIXME
6989  */
mirror_surface(SDL_Surface * s)6990 SDL_Surface *mirror_surface(SDL_Surface * s)
6991 {
6992   SDL_Surface *new_surf;
6993   int x;
6994   SDL_Rect src, dest;
6995 
6996 
6997   /* Mirror surface: */
6998 
6999   new_surf = duplicate_surface(s);
7000 
7001   if (new_surf != NULL)
7002     {
7003       for (x = 0; x < s->w; x++)
7004         {
7005           src.x = x;
7006           src.y = 0;
7007           src.w = 1;
7008           src.h = s->h;
7009 
7010           dest.x = s->w - x - 1;
7011           dest.y = 0;
7012 
7013           SDL_BlitSurface(s, &src, new_surf, &dest);
7014         }
7015 
7016       SDL_FreeSurface(s);
7017 
7018       return (new_surf);
7019     }
7020   else
7021     {
7022       return (s);
7023     }
7024 }
7025 
7026 /**
7027  * FIXME
7028  */
flip_surface(SDL_Surface * s)7029 SDL_Surface *flip_surface(SDL_Surface * s)
7030 {
7031   SDL_Surface *new_surf;
7032   int y;
7033   SDL_Rect src, dest;
7034 
7035 
7036   /* Flip surface: */
7037 
7038   new_surf = duplicate_surface(s);
7039 
7040   if (new_surf != NULL)
7041     {
7042       for (y = 0; y < s->h; y++)
7043         {
7044           src.x = 0;
7045           src.y = y;
7046           src.w = s->w;
7047           src.h = 1;
7048 
7049           dest.x = 0;
7050           dest.y = s->h - y - 1;
7051 
7052           SDL_BlitSurface(s, &src, new_surf, &dest);
7053         }
7054 
7055       SDL_FreeSurface(s);
7056 
7057       return (new_surf);
7058     }
7059   else
7060     {
7061       return (s);
7062     }
7063 }
7064 
7065 static unsigned default_stamp_size;
7066 
7067 /**
7068  * FIXME
7069  */
loadstamp_finisher(stamp_type * sd,unsigned w,unsigned h,double ratio)7070 static void loadstamp_finisher(stamp_type * sd, unsigned w, unsigned h, double ratio)
7071 {
7072   unsigned int upper = HARD_MAX_STAMP_SIZE;
7073   unsigned int underscanned_upper = HARD_MAX_STAMP_SIZE;
7074   unsigned int lower = 0;
7075   unsigned mid;
7076 
7077 #ifdef DEBUG
7078   printf("Finishing %s for %dx%d (ratio=%0.4f)\n", sd->stampname, w, h, ratio);
7079 #endif
7080 
7081   /* If Tux Paint is in mirror-image-by-default mode, mirror, if we can: */
7082   if (mirrorstamps && sd->mirrorable)
7083     sd->mirrored = 1;
7084 
7085   do
7086     {
7087       scaleparams *s = &scaletable[upper];
7088       int pw, ph;               /* proposed width and height */
7089 
7090       pw = (w * s->numer + s->denom - 1) / s->denom;
7091       ph = (h * s->numer + s->denom - 1) / s->denom;
7092 
7093 #ifdef ALLOW_STAMP_OVERSCAN
7094       /* OK to let a stamp stick off the sides in one direction, not two */
7095       /* By default, Tux Paint allowed stamps to be, at max, 2x as wide OR 2x as tall as canvas; scaled that back to 1.5 -bjk 2011.01.08 */
7096       if (pw < canvas->w * 1.5 && ph < canvas->h * 1)
7097         {
7098 #ifdef DEBUG
7099           printf("Upper at %d with proposed size %dx%d (wide)\n", upper, pw, ph);
7100 #endif
7101           if (pw > canvas->w)
7102             {
7103               underscanned_upper = upper - 1;
7104             }
7105           else
7106             {
7107               underscanned_upper = upper;
7108             }
7109           break;
7110         }
7111       if (pw < canvas->w * 1 && ph < canvas->h * 1.5)
7112         {
7113 #ifdef DEBUG
7114           printf("Upper at %d with proposed size %dx%d (tall)\n", upper, pw, ph);
7115 #endif
7116           if (ph > canvas->h)
7117             {
7118               underscanned_upper = upper - 1;
7119             }
7120           else
7121             {
7122               underscanned_upper = upper;
7123             }
7124           break;
7125         }
7126 #else
7127       if (pw <= canvas->w * 1 && ph <= canvas->h * 1)
7128         {
7129 #ifdef DEBUG
7130           printf("Upper at %d with proposed size %dx%d\n", upper, pw, ph);
7131 #endif
7132           underscanned_upper = upper;
7133           break;
7134         }
7135 #endif
7136     }
7137   while (--upper);
7138 
7139 
7140   do
7141     {
7142       scaleparams *s = &scaletable[lower];
7143       int pw, ph;               /* proposed width and height */
7144 
7145       pw = (w * s->numer + s->denom - 1) / s->denom;
7146       ph = (h * s->numer + s->denom - 1) / s->denom;
7147 
7148       if (pw * ph > 20)
7149         {
7150 #ifdef DEBUG
7151           printf("Lower at %d with proposed size %dx%d\n", lower, pw, ph);
7152 #endif
7153           break;
7154         }
7155     }
7156   while (++lower < HARD_MAX_STAMP_SIZE);
7157 
7158 
7159   if (upper < lower)
7160     {
7161       /* this, if it ever happens, is very bad */
7162       upper = (upper + lower) / 2;
7163       lower = upper;
7164     }
7165 
7166   mid = default_stamp_size;
7167   if (ratio != 1.0)
7168     mid = compute_default_scale_factor(ratio);
7169 
7170   /* Ratio override for SVGs! */
7171   if (ratio == 1.0 && sd->is_svg)
7172     {
7173       mid = compute_default_scale_factor(0.2);
7174     }
7175 
7176   if (mid > upper)
7177     mid = upper;
7178 
7179   if (mid > underscanned_upper)
7180     mid = underscanned_upper;
7181 
7182   if (mid < lower)
7183     mid = lower;
7184 
7185   sd->min = lower;
7186   sd->size = mid;
7187   sd->max = upper;
7188 
7189 #ifdef DEBUG
7190   printf("Final min=%d, size=%d, max=%d\n", lower, mid, upper);
7191 #endif
7192 
7193   if (stamp_size_override != -1)
7194     {
7195       sd->size = (((upper - lower) * stamp_size_override) / 10) + lower;
7196 #ifdef DEBUG
7197       printf("...but adjusting size to %d\n", sd->size);
7198 #endif
7199     }
7200 #ifdef DEBUG
7201   printf("\n");
7202 #endif
7203 }
7204 
7205 
7206 /**
7207  * FIXME
7208  */
7209 /* Note: must have read the *.dat file before calling this */
set_active_stamp(void)7210 static void set_active_stamp(void)
7211 {
7212   stamp_type *sd = stamp_data[stamp_group][cur_stamp[stamp_group]];
7213   unsigned len = strlen(sd->stampname);
7214   char *buf = alloca(len + strlen("_mirror_flip.EXT") + 1);
7215   int needs_mirror, needs_flip;
7216 
7217   if (active_stamp)
7218     SDL_FreeSurface(active_stamp);
7219   active_stamp = NULL;
7220 
7221   memcpy(buf, sd->stampname, len);
7222 
7223 #ifdef DEBUG
7224   printf("\nset_active_stamp()\n");
7225 #endif
7226 
7227   /* Look for pre-mirrored and pre-flipped version: */
7228 
7229   needs_mirror = sd->mirrored;
7230   needs_flip = sd->flipped;
7231 
7232   if (sd->mirrored && sd->flipped)
7233     {
7234       /* Want mirrored and flipped, both */
7235 
7236 #ifdef DEBUG
7237       printf("want both mirrored & flipped\n");
7238 #endif
7239 
7240       if (!sd->no_premirrorflip)
7241         {
7242 #ifndef NOSVG
7243           memcpy(buf + len, "_mirror_flip.svg", 17);
7244           active_stamp = do_loadimage(buf, 0);
7245 #endif
7246 
7247           if (active_stamp == NULL)
7248             {
7249               memcpy(buf + len, "_mirror_flip.png", 17);
7250               active_stamp = do_loadimage(buf, 0);
7251             }
7252         }
7253 
7254 
7255       if (active_stamp != NULL)
7256         {
7257 #ifdef DEBUG
7258           printf("found a _mirror_flip!\n");
7259 #endif
7260 
7261           needs_mirror = 0;
7262           needs_flip = 0;
7263         }
7264       else
7265         {
7266           /* Couldn't get one that was both, look for _mirror then _flip and
7267              flip or mirror it: */
7268 
7269 #ifdef DEBUG
7270           printf("didn't find a _mirror_flip\n");
7271 #endif
7272 
7273           if (!sd->no_premirror)
7274             {
7275 #ifndef NOSVG
7276               memcpy(buf + len, "_mirror.svg", 12);
7277               active_stamp = do_loadimage(buf, 0);
7278 #endif
7279 
7280               if (active_stamp == NULL)
7281                 {
7282                   memcpy(buf + len, "_mirror.png", 12);
7283                   active_stamp = do_loadimage(buf, 0);
7284                 }
7285             }
7286 
7287           if (active_stamp != NULL)
7288             {
7289 #ifdef DEBUG
7290               printf("found a _mirror!\n");
7291 #endif
7292               needs_mirror = 0;
7293             }
7294           else
7295             {
7296               /* Couldn't get one that was just pre-mirrored, look for a
7297                  pre-flipped */
7298 
7299 #ifdef DEBUG
7300               printf("didn't find a _mirror, either\n");
7301 #endif
7302 
7303               if (!sd->no_preflip)
7304                 {
7305 #ifndef NOSVG
7306                   memcpy(buf + len, "_flip.svg", 10);
7307                   active_stamp = do_loadimage(buf, 0);
7308 #endif
7309 
7310                   if (active_stamp == NULL)
7311                     {
7312                       memcpy(buf + len, "_flip.png", 10);
7313                       active_stamp = do_loadimage(buf, 0);
7314                     }
7315                 }
7316 
7317               if (active_stamp != NULL)
7318                 {
7319 #ifdef DEBUG
7320                   printf("found a _flip!\n");
7321 #endif
7322                   needs_flip = 0;
7323                 }
7324               else
7325                 {
7326 #ifdef DEBUG
7327                   printf("didn't find a _flip, either\n");
7328 #endif
7329                 }
7330             }
7331         }
7332     }
7333   else if (sd->flipped && !sd->no_preflip)
7334     {
7335       /* Want flipped only */
7336 
7337 #ifdef DEBUG
7338       printf("want flipped only\n");
7339 #endif
7340 
7341 #ifndef NOSVG
7342       memcpy(buf + len, "_flip.svg", 10);
7343       active_stamp = do_loadimage(buf, 0);
7344 #endif
7345 
7346       if (active_stamp == NULL)
7347         {
7348           memcpy(buf + len, "_flip.png", 10);
7349           active_stamp = do_loadimage(buf, 0);
7350         }
7351 
7352       if (active_stamp != NULL)
7353         {
7354 #ifdef DEBUG
7355           printf("found a _flip!\n");
7356 #endif
7357           needs_flip = 0;
7358         }
7359       else
7360         {
7361 #ifdef DEBUG
7362           printf("didn't find a _flip\n");
7363 #endif
7364         }
7365     }
7366   else if (sd->mirrored && !sd->no_premirror)
7367     {
7368       /* Want mirrored only */
7369 
7370 #ifdef DEBUG
7371       printf("want mirrored only\n");
7372 #endif
7373 
7374 #ifndef NOSVG
7375       memcpy(buf + len, "_mirror.svg", 12);
7376       active_stamp = do_loadimage(buf, 0);
7377 #endif
7378 
7379       if (active_stamp == NULL)
7380         {
7381           memcpy(buf + len, "_mirror.png", 12);
7382           active_stamp = do_loadimage(buf, 0);
7383         }
7384 
7385       if (active_stamp != NULL)
7386         {
7387 #ifdef DEBUG
7388           printf("found a _mirror!\n");
7389 #endif
7390           needs_mirror = 0;
7391         }
7392       else
7393         {
7394 #ifdef DEBUG
7395           printf("didn't find a _mirror\n");
7396 #endif
7397         }
7398     }
7399 
7400 
7401   /* Didn't want mirrored, or flipped, or couldn't load anything
7402      that was pre-rendered: */
7403 
7404   if (!active_stamp)
7405     {
7406 #ifdef DEBUG
7407       printf("loading normal\n");
7408 #endif
7409 
7410 #ifndef NOSVG
7411       memcpy(buf + len, ".svg", 5);
7412       active_stamp = do_loadimage(buf, 0);
7413 #endif
7414 
7415       if (active_stamp == NULL)
7416         {
7417           memcpy(buf + len, ".png", 5);
7418           active_stamp = do_loadimage(buf, 0);
7419         }
7420 
7421     }
7422 
7423   /* Never allow a NULL image! */
7424 
7425   if (!active_stamp)
7426     active_stamp = thumbnail(img_dead40x40, 40, 40, 1); /* copy it */
7427 
7428 
7429   /* If we wanted mirrored or flipped, and didn't get something pre-rendered,
7430      do it to the image we did load: */
7431 
7432   if (needs_mirror)
7433     {
7434 #ifdef DEBUG
7435       printf("mirroring\n");
7436 #endif
7437       active_stamp = mirror_surface(active_stamp);
7438     }
7439 
7440   if (needs_flip)
7441     {
7442 #ifdef DEBUG
7443       printf("flipping\n");
7444 #endif
7445       active_stamp = flip_surface(active_stamp);
7446     }
7447 
7448 #ifdef DEBUG
7449   printf("\n\n");
7450 #endif
7451 }
7452 
7453 /**
7454  * FIXME
7455  */
get_stamp_thumb(stamp_type * sd)7456 static void get_stamp_thumb(stamp_type * sd)
7457 {
7458   SDL_Surface *bigimg = NULL;
7459   unsigned len = strlen(sd->stampname);
7460   char *buf = alloca(len + strlen("_mirror_flip.EXT") + 1);
7461   int need_mirror, need_flip;
7462   double ratio;
7463   unsigned w;
7464   unsigned h;
7465 
7466 #ifdef DEBUG
7467   printf("\nget_stamp_thumb()\n");
7468 #endif
7469 
7470   memcpy(buf, sd->stampname, len);
7471 
7472   if (!sd->processed)
7473     {
7474       memcpy(buf + len, ".dat", 5);
7475       ratio = loadinfo(buf, sd);
7476     }
7477   else
7478     {
7479       /* So here, unless an SVG stamp has a .dat file with a 'scale',
7480          the Stamp ends up defaulting to 100% (ratio=1.0).
7481          Since we render the SVG as large as possible, for quality reasons,
7482          we almost never want the _default_ size to be 100%.
7483 
7484          So we need to either (a) keep track of the SVG's own pixel size
7485          and try to set the default size to something close to that,
7486          or (b) pick a universal initial size that we can apply to _all_ SVGs
7487          where the initial size is left unspecified (which means knowing when
7488          they're SVGs).
7489 
7490          So far, I'm doing (b), in loadstamp_finisher...
7491 
7492          -bjk 2009.09.29 */
7493 
7494       ratio = 1.0;
7495     }
7496 
7497   if (!sd->no_txt && !sd->stxt)
7498     {
7499       /* damn thing wants a .png extension; give it one */
7500       memcpy(buf + len, ".png", 5);
7501       sd->stxt = loaddesc(buf, &(sd->locale_text));
7502       sd->no_txt = !sd->stxt;
7503     }
7504 
7505 #ifndef NOSOUND
7506   /* good time to load the sound */
7507   if (!sd->no_sound && !sd->ssnd && use_sound)
7508     {
7509       /* damn thing wants a .png extension; give it one */
7510       memcpy(buf + len, ".png", 5);
7511       sd->ssnd = loadsound(buf);
7512       sd->no_sound = !sd->ssnd;
7513     }
7514 
7515   /* ...and the description */
7516   if (!sd->no_descsound && !sd->sdesc && use_sound)
7517     {
7518       /* damn thing wants a .png extension; give it one */
7519       memcpy(buf + len, ".png", 5);
7520       sd->sdesc = loaddescsound(buf);
7521       sd->no_descsound = !sd->sdesc;
7522     }
7523 #endif
7524 
7525 
7526   /* first see if we can re-use an existing thumbnail */
7527   if (sd->thumbnail)
7528     {
7529 #ifdef DEBUG
7530       printf("have an sd->thumbnail\n");
7531 #endif
7532 
7533       if (sd->thumb_mirrored_flipped == sd->flipped &&
7534           sd->thumb_mirrored_flipped == sd->mirrored &&
7535           sd->mirrored == sd->thumb_mirrored && sd->flipped == sd->thumb_flipped)
7536         {
7537           /* It's already the way we want */
7538 
7539 #ifdef DEBUG
7540           printf("mirrored == flipped == thumb_mirrored_flipped [bye]\n");
7541 #endif
7542 
7543           return;
7544         }
7545     }
7546 
7547 
7548   /* nope, see if there's a pre-rendered one we can use */
7549 
7550   show_progress_bar(screen);
7551 
7552   need_mirror = sd->mirrored;
7553   need_flip = sd->flipped;
7554   bigimg = NULL;
7555 
7556   if (sd->mirrored && sd->flipped)
7557     {
7558 #ifdef DEBUG
7559       printf("want mirrored & flipped\n");
7560 #endif
7561 
7562       if (!sd->no_premirrorflip)
7563         {
7564           memcpy(buf + len, "_mirror_flip.png", 17);
7565           bigimg = do_loadimage(buf, 0);
7566 
7567 #ifndef NOSVG
7568           if (bigimg == NULL)
7569             {
7570               memcpy(buf + len, "_mirror_flip.svg", 17);
7571               bigimg = do_loadimage(buf, 0);
7572             }
7573 #endif
7574         }
7575 
7576       if (bigimg)
7577         {
7578 #ifdef DEBUG
7579           printf("found a _mirror_flip!\n");
7580 #endif
7581 
7582           need_mirror = 0;
7583           need_flip = 0;
7584         }
7585       else
7586         {
7587 #ifdef DEBUG
7588           printf("didn't find a mirror_flip\n");
7589 #endif
7590           sd->no_premirrorflip = 1;
7591 
7592           if (!sd->no_premirror)
7593             {
7594               memcpy(buf + len, "_mirror.png", 12);
7595               bigimg = do_loadimage(buf, 0);
7596 
7597 #ifndef NOSVG
7598               if (bigimg == NULL)
7599                 {
7600                   memcpy(buf + len, "_mirror.svg", 12);
7601                   bigimg = do_loadimage(buf, 0);
7602                 }
7603 #endif
7604             }
7605 
7606           if (bigimg)
7607             {
7608 #ifdef DEBUG
7609               printf("found a _mirror\n");
7610 #endif
7611 
7612               need_mirror = 0;
7613             }
7614           else
7615             {
7616 #ifdef DEBUG
7617               printf("didn't find a mirror\n");
7618 #endif
7619 
7620               if (!sd->no_preflip)
7621                 {
7622                   memcpy(buf + len, "_flip.png", 10);
7623                   bigimg = do_loadimage(buf, 0);
7624 
7625 #ifndef NOSVG
7626                   if (bigimg == NULL)
7627                     {
7628                       memcpy(buf + len, "_flip.svg", 10);
7629                       bigimg = do_loadimage(buf, 0);
7630                     }
7631 #endif
7632                 }
7633 
7634               if (bigimg)
7635                 {
7636 #ifdef DEBUG
7637                   printf("found a _flip\n");
7638 #endif
7639 
7640                   need_flip = 0;
7641                 }
7642             }
7643         }
7644     }
7645   else if (sd->mirrored && !sd->no_premirror)
7646     {
7647 #ifdef DEBUG
7648       printf("want mirrored only\n");
7649 #endif
7650 
7651       memcpy(buf + len, "_mirror.png", 12);
7652       bigimg = do_loadimage(buf, 0);
7653 
7654 #ifndef NOSVG
7655       if (bigimg == NULL)
7656         {
7657           memcpy(buf + len, "_mirror.svg", 12);
7658           bigimg = do_loadimage(buf, 0);
7659         }
7660 #endif
7661 
7662       if (bigimg)
7663         {
7664 #ifdef DEBUG
7665           printf("found a _mirror!\n");
7666 #endif
7667           need_mirror = 0;
7668         }
7669       else
7670         {
7671 #ifdef DEBUG
7672           printf("didn't find a mirror\n");
7673 #endif
7674           sd->no_premirror = 1;
7675         }
7676     }
7677   else if (sd->flipped && !sd->no_preflip)
7678     {
7679 #ifdef DEBUG
7680       printf("want flipped only\n");
7681 #endif
7682 
7683       memcpy(buf + len, "_flip.png", 10);
7684       bigimg = do_loadimage(buf, 0);
7685 
7686 #ifndef NOSVG
7687       if (bigimg == NULL)
7688         {
7689           memcpy(buf + len, "_flip.svg", 10);
7690           bigimg = do_loadimage(buf, 0);
7691         }
7692 #endif
7693 
7694       if (bigimg)
7695         {
7696 #ifdef DEBUG
7697           printf("found a _flip!\n");
7698 #endif
7699           need_flip = 0;
7700         }
7701       else
7702         {
7703 #ifdef DEBUG
7704           printf("didn't find a flip\n");
7705 #endif
7706           sd->no_preflip = 1;
7707         }
7708     }
7709 
7710 
7711   /* If we didn't load a pre-rendered, load the normal one: */
7712 
7713   if (!bigimg)
7714     {
7715 #ifdef DEBUG
7716       printf("loading normal...\n");
7717 #endif
7718 
7719       memcpy(buf + len, ".png", 5);
7720       bigimg = do_loadimage(buf, 0);
7721 
7722 #ifndef NOSVG
7723       if (bigimg == NULL)
7724         {
7725           memcpy(buf + len, ".svg", 5);
7726           bigimg = do_loadimage(buf, 0);
7727         }
7728 #endif
7729     }
7730 
7731 
7732   /* Scale the stamp down to its thumbnail size: */
7733 
7734   w = 40;
7735   h = 40;
7736   int ww = (40 * button_h) / ORIGINAL_BUTTON_SIZE;
7737   int hh = (40 * button_h) / ORIGINAL_BUTTON_SIZE;
7738   if (bigimg)
7739     {
7740       w = bigimg->w;
7741       h = bigimg->h;
7742     }
7743 
7744   if (!bigimg)
7745     sd->thumbnail = thumbnail(img_dead40x40, ww, hh, 1);        /* copy */
7746   else if (bigimg->w > 40 || bigimg->h > 40)
7747     {
7748       sd->thumbnail = thumbnail(bigimg, ww, hh, 1);
7749       SDL_FreeSurface(bigimg);
7750     }
7751   else
7752     sd->thumbnail = bigimg;
7753 
7754 
7755   /* Mirror and/or flip the thumbnail, if we still need to do so: */
7756 
7757   if (need_mirror)
7758     {
7759 #ifdef DEBUG
7760       printf("mirroring\n");
7761 #endif
7762       sd->thumbnail = mirror_surface(sd->thumbnail);
7763     }
7764 
7765   if (need_flip)
7766     {
7767 #ifdef DEBUG
7768       printf("flipping\n");
7769 #endif
7770       sd->thumbnail = flip_surface(sd->thumbnail);
7771     }
7772 
7773 
7774   /* Note the fact that the thumbnail's mirror/flip is the same as the main
7775      stamp: */
7776 
7777   if (sd->mirrored && sd->flipped)
7778     sd->thumb_mirrored_flipped = 1;
7779   else
7780     sd->thumb_mirrored_flipped = 0;
7781 
7782   sd->thumb_mirrored = sd->mirrored;
7783   sd->thumb_flipped = sd->flipped;
7784 
7785 #ifdef DEBUG
7786   printf("\n\n");
7787 #endif
7788 
7789 
7790   /* Finish up, if we need to: */
7791 
7792   if (sd->processed)
7793     return;
7794 
7795   sd->processed = 1;            /* not really, but on the next line... */
7796   loadstamp_finisher(sd, w, h, ratio);
7797 }
7798 
7799 
7800 /**
7801  * Callback for directory walking while loading stamps
7802  *
7803  * @param screen Screen/window surface, for drawing progress bar animation
7804  * @param dir Directory path
7805  * @param dirlen Length of directory path string
7806  * @param files List of files (being collected)
7807  * @param i Counter
7808  * @param locale UI's locale, for loading localized text (ignored)
7809  */
loadstamp_callback(SDL_Surface * screen,const char * restrict const dir,unsigned dirlen,tp_ftw_str * files,unsigned i,const char * restrict const locale)7810 static void loadstamp_callback(SDL_Surface * screen,
7811                                const char *restrict const dir,
7812                                unsigned dirlen, tp_ftw_str * files, unsigned i, const char *restrict const locale)
7813 {
7814   (void)locale;
7815 #ifdef DEBUG
7816   /* FIXME: Stderr instead of stdout? */
7817   printf("loadstamp_callback (%d): %s\n", i, dir);
7818 #endif
7819 
7820   if (num_stamps[stamp_group] > 0)
7821     {
7822       /* If previous group had any stamps... */
7823 
7824       unsigned int i, slashcount;
7825 
7826 
7827       /* See if the current directory is shallow enough to be
7828          important for making a new stamp group: */
7829 
7830       slashcount = 0;
7831 
7832       for (i = strlen(load_stamp_basedir) + 1; i < strlen(dir); i++)
7833         {
7834           if (dir[i] == '/' || dir[i] == '\\')
7835             slashcount++;
7836         }
7837 
7838       if (slashcount <= stamp_group_dir_depth)
7839         {
7840           stamp_group++;
7841 #ifdef DEBUG
7842           /* FIXME: Stderr instead of stdout? */
7843           printf("\n...counts as a new group! now: %d\n", stamp_group);
7844 #endif
7845         }
7846       else
7847         {
7848 #ifdef DEBUG
7849           /* FIXME: Stderr instead of stdout? */
7850           printf("...is still part of group %d\n", stamp_group);
7851 #endif
7852         }
7853     }
7854 
7855 
7856   /* Sort and iterate the file list: */
7857   qsort(files, i, sizeof *files, compare_ftw_str);
7858   while (i--)
7859     {
7860       char fname[512];
7861       const char *dotext, *ext, *mirror_ext, *flip_ext, *mirrorflip_ext;
7862 
7863       ext = ".png";
7864       mirror_ext = "_mirror.png";
7865       flip_ext = "_flip.png";
7866       mirrorflip_ext = "_mirror_flip.png";
7867       dotext = (char *)strcasestr(files[i].str, ext);
7868 
7869 #ifndef NOSVG
7870       if (dotext == NULL)
7871         {
7872           ext = ".svg";
7873           mirror_ext = "_mirror.svg";
7874           flip_ext = "_flip.svg";
7875           mirrorflip_ext = "_mirror_flip.svg";
7876           dotext = (char *)strcasestr(files[i].str, ext);
7877         }
7878       else
7879         {
7880           /* Found PNG, but we support SVG; let's see if there's an SVG
7881            *       version too, before loading the PNG */
7882 
7883           char svgname[512];
7884           FILE *fi;
7885 
7886           safe_snprintf(svgname, sizeof(svgname), "%s/%s", dir, files[i].str);
7887           strcpy(strcasestr(svgname, ".png"), ".svg"); /* FIXME: Use strncpy (ugh, complicated) */
7888 
7889           fi = fopen(svgname, "r");
7890           if (fi != NULL)
7891             {
7892               debug("Found SVG version of ");
7893               debug(files[i].str);
7894               debug("\n");
7895 
7896               fclose(fi);
7897               continue;         /* ugh, i hate continues */
7898             }
7899         }
7900 #endif
7901 
7902       /*
7903        * Showing the progress bar across the screen can be CPU-intensive, so
7904        * update infrequently.
7905        */
7906       if ((i % 32) == 0)
7907         show_progress_bar(screen);
7908 
7909       if (dotext > files[i].str && !strcasecmp(dotext, ext)
7910           && (dotext - files[i].str + 1 + dirlen < (int)(sizeof fname))
7911           && !strcasestr(files[i].str, mirror_ext)
7912           && !strcasestr(files[i].str, flip_ext) && !strcasestr(files[i].str, mirrorflip_ext))
7913         {
7914           safe_snprintf(fname, sizeof fname, "%s/%s", dir, files[i].str);
7915           if (num_stamps[stamp_group] == max_stamps[stamp_group])
7916             {
7917               max_stamps[stamp_group] = max_stamps[stamp_group] * 5 / 4 + 15;
7918               stamp_data[stamp_group] = realloc(stamp_data[stamp_group],
7919                                                 max_stamps[stamp_group] * sizeof(*stamp_data[stamp_group]));
7920             }
7921           stamp_data[stamp_group][num_stamps[stamp_group]] =
7922             calloc(1, sizeof *stamp_data[stamp_group][num_stamps[stamp_group]]);
7923           stamp_data[stamp_group][num_stamps[stamp_group]]->stampname = malloc(dotext - files[i].str + 1 + dirlen + 1);
7924           memcpy(stamp_data[stamp_group][num_stamps[stamp_group]]->stampname, fname,
7925                  dotext - files[i].str + 1 + dirlen);
7926           stamp_data[stamp_group][num_stamps[stamp_group]]->stampname[dotext - files[i].str + 1 + dirlen] = '\0';
7927 
7928           if (strcmp(ext, ".svg") == 0)
7929             {
7930               stamp_data[stamp_group][num_stamps[stamp_group]]->is_svg = 1;
7931             }
7932           else
7933             {
7934               stamp_data[stamp_group][num_stamps[stamp_group]]->is_svg = 0;
7935             }
7936 
7937           num_stamps[stamp_group]++;
7938         }
7939       free(files[i].str);
7940     }
7941   free(files);
7942 }
7943 
7944 /**
7945  * FIXME
7946  */
load_stamp_dir(SDL_Surface * screen,const char * const dir)7947 static void load_stamp_dir(SDL_Surface * screen, const char *const dir)
7948 {
7949   char buf[TP_FTW_PATHSIZE];
7950   unsigned dirlen = strlen(dir);
7951 
7952   memcpy(buf, dir, dirlen);
7953   load_stamp_basedir = dir;
7954   tp_ftw(screen, buf, dirlen, 0, loadstamp_callback, NULL);
7955 }
7956 
7957 /**
7958  * FIXME
7959  */
load_stamps(SDL_Surface * screen)7960 static void load_stamps(SDL_Surface * screen)
7961 {
7962   char *homedirdir = get_fname("stamps", DIR_DATA);
7963 
7964   default_stamp_size = compute_default_scale_factor(1.0);
7965 
7966   load_stamp_dir(screen, homedirdir);
7967   load_stamp_dir(screen, DATA_PREFIX "stamps");
7968 #ifdef __MACOS__
7969   load_stamp_dir(screen, "Resources/stamps");
7970   load_stamp_dir(screen, "/Library/Application Support/TuxPaint/stamps");
7971 #endif
7972 #ifdef __IOS__
7973   load_stamp_dir(screen, "stamps");
7974 #endif
7975 #ifdef WIN32
7976   free(homedirdir);
7977   homedirdir = get_fname("data/stamps", DIR_DATA);
7978   load_stamp_dir(screen, homedirdir);
7979 #endif
7980 
7981   if (num_stamps[0] == 0)
7982     {
7983       fprintf(stderr, "\nWarning: No stamps found in " DATA_PREFIX "stamps/\n" "or %s\n\n", homedirdir);
7984     }
7985 
7986   num_stamp_groups = stamp_group + 1;
7987 
7988   free(homedirdir);
7989 }
7990 
7991 #ifndef FORKED_FONTS
7992 /**
7993  * FIXME
7994  */
load_user_fonts_stub(void * vp)7995 static int load_user_fonts_stub(void *vp)
7996 {
7997   return load_user_fonts(screen, vp, NULL);
7998 }
7999 #endif
8000 
8001 #ifndef NO_SDLPANGO
8002 volatile long fontconfig_thread_done = 0;
8003 
8004 /**
8005  * FIXME
8006  */
generate_fontconfig_cache_spinner(SDL_Surface * screen)8007 int generate_fontconfig_cache_spinner(SDL_Surface * screen)
8008 {
8009   SDL_Event event;
8010 
8011   while (fontconfig_thread_done == 0)
8012     {
8013       show_progress_bar(screen);
8014       SDL_Flip(screen);
8015       SDL_Delay(20);
8016 
8017       while (SDL_PollEvent(&event) > 0)
8018         {
8019           if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))
8020             {
8021               fprintf(stderr, "Aborting!\n");
8022               fflush(stdout);
8023               return (1);
8024             }
8025         }
8026     }
8027   return (0);
8028 }
8029 
8030 /**
8031  * FIXME
8032  */
generate_fontconfig_cache_real(void)8033 static int generate_fontconfig_cache_real(void)
8034 {
8035   TuxPaint_Font *tmp_font;
8036   SDL_Surface *tmp_surf;
8037   SDL_Color black = { 0, 0, 0, 0 };
8038 
8039 #ifdef DEBUG
8040   printf("-- Hello from generate_fontconfig_cache() (thread # %d)\n", SDL_ThreadID());
8041   fflush(stdout);
8042 #endif
8043 
8044   tmp_font = TuxPaint_Font_OpenFont(PANGO_DEFAULT_FONT, NULL, 12);
8045 
8046   if (tmp_font != NULL)
8047     {
8048 #ifdef DEBUG
8049       printf("-- Generated a font.\n");
8050       fflush(stdout);
8051 #endif
8052       tmp_surf = render_text(tmp_font, "Test", black);
8053       if (tmp_surf != NULL)
8054         {
8055 #ifdef DEBUG
8056           printf("-- Generated a surface\n");
8057           fflush(stdout);
8058 #endif
8059           SDL_FreeSurface(tmp_surf);
8060         }
8061       else
8062         {
8063 #ifdef DEBUG
8064           printf("-- Failed to make a surface!\n");
8065           fflush(stdout);
8066 #endif
8067         }
8068       TuxPaint_Font_CloseFont(tmp_font);
8069     }
8070   else
8071     {
8072 #ifdef DEBUG
8073       printf("-- Failed to generate a font!\n");
8074       fflush(stdout);
8075 #endif
8076     }
8077 
8078   fontconfig_thread_done = 1;
8079 
8080 #ifdef DEBUG
8081   printf("-- generate_fontconfig_cache() is done\n");
8082   fflush(stdout);
8083 #endif
8084   return (0);
8085 }
8086 
8087 /**
8088  * FIXME
8089  */
generate_fontconfig_cache(void * vp)8090 static int generate_fontconfig_cache(__attribute__((unused)) void *vp)
8091 {
8092   return generate_fontconfig_cache_real();
8093 }
8094 #endif
8095 
8096 #define hex2dec(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
8097   ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
8098   ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : 0)
8099 
8100 #ifndef WIN32
signal_handler(int sig)8101 static void signal_handler(int sig)
8102 {
8103   // It is not legal to call printf or most other functions here!
8104   if (sig == SIGUSR1 || sig == SIGUSR2)
8105     {
8106       autosave_on_quit = 1;
8107       no_prompt_on_quit = 1;
8108       if (sig == SIGUSR1)
8109         {
8110           promptless_save = SAVE_OVER_NO;
8111         }
8112       else
8113         {
8114           promptless_save = SAVE_OVER_ALWAYS;
8115         }
8116       raise(SIGTERM);
8117     }
8118 }
8119 #endif
8120 
8121 /**
8122  * FIXME
8123  */
8124 /* Render a button label using the appropriate string/font: */
do_render_button_label(const char * const label)8125 static SDL_Surface *do_render_button_label(const char *const label)
8126 {
8127   SDL_Surface *tmp_surf, *surf;
8128   SDL_Color black = { 0, 0, 0, 0 };
8129   TuxPaint_Font *myfont;
8130   char *td_str = textdir(gettext(label));
8131   char *upstr = uppercase(td_str);
8132 
8133   free(td_str);
8134 
8135   if (button_w <= ORIGINAL_BUTTON_SIZE)
8136     myfont = small_font;
8137   else if (button_w <= ORIGINAL_BUTTON_SIZE * 3)
8138     myfont = medium_font;
8139   else
8140     myfont = large_font;
8141 
8142   if (need_own_font && strcmp(gettext(label), label))
8143     myfont = locale_font;
8144   tmp_surf = render_text(myfont, upstr, black);
8145   free(upstr);
8146 
8147   surf = thumbnail(tmp_surf, min(button_w, tmp_surf->w), min(18 * button_scale + button_label_y_nudge, tmp_surf->h), 0);
8148   SDL_FreeSurface(tmp_surf);
8149 
8150   return surf;
8151 }
8152 
8153 /**
8154  * FIXME
8155  */
create_button_labels(void)8156 static void create_button_labels(void)
8157 {
8158   int i;
8159 
8160   /* Main tools */
8161   for (i = 0; i < NUM_TOOLS; i++)
8162     img_tool_names[i] = do_render_button_label(tool_names[i]);
8163 
8164   /* Magic Tools */
8165   for (i = 0; i < num_magics; i++)
8166     magics[i].img_name = do_render_button_label(magics[i].name);
8167 
8168   /* Shapes for Shape Tool */
8169   for (i = 0; i < NUM_SHAPES; i++)
8170     img_shape_names[i] = do_render_button_label(shape_names[i]);
8171 
8172   /* Fill methods for Fill Tool */
8173   for (i = 0; i < NUM_FILLS; i++)
8174     img_fill_names[i] = do_render_button_label(fill_names[i]);
8175 
8176   /* Buttons for the file open dialog */
8177 
8178   /* Open dialog: 'Open' button, to load the selected picture */
8179   img_openlabels_open = do_render_button_label(gettext_noop("Open"));
8180 
8181   /* Open dialog: 'Erase' button, to erase/deleted the selected picture */
8182   img_openlabels_erase = do_render_button_label(gettext_noop("Erase"));
8183 
8184   /* Open dialog: 'Slides' button, to switch to slide show mode */
8185   img_openlabels_slideshow = do_render_button_label(gettext_noop("Slides"));
8186 
8187   /* Open dialog: 'Export' button, to copy an image to an easily-accessible location */
8188   img_openlabels_pict_export = do_render_button_label(gettext_noop("Export"));
8189 
8190   /* Open dialog: 'Back' button, to dismiss Open dialog without opening a picture */
8191   img_openlabels_back = do_render_button_label(gettext_noop("Back"));
8192 
8193   /* Slideshow: 'Play' button, to begin a slideshow sequence */
8194   img_openlabels_play = do_render_button_label(gettext_noop("Play"));
8195 
8196   /* Slideshow: 'GIF Export' button, to create an animated GIF */
8197   img_openlabels_gif_export = do_render_button_label(gettext_noop("GIF Export"));
8198 
8199   /* Slideshow: 'Next' button, to load next slide (image) */
8200   img_openlabels_next = do_render_button_label(gettext_noop("Next"));
8201 }
8202 
8203 
8204 /**
8205  * FIXME
8206  */
seticon(void)8207 static void seticon(void)
8208 {
8209 #ifndef WIN32
8210   int masklen;
8211   Uint8 *mask;
8212 #endif
8213   SDL_Surface *icon;
8214 
8215   /* Load icon into a surface: */
8216 
8217 #ifndef WIN32
8218   icon = IMG_Load(DATA_PREFIX "images/icon.png");
8219 #else
8220   icon = IMG_Load(DATA_PREFIX "images/icon32x32.png");
8221 #endif
8222 
8223   if (icon == NULL)
8224     {
8225       fprintf(stderr,
8226               "\nWarning: I could not load the icon image: %s\n"
8227               "The Simple DirectMedia error that occurred was:\n"
8228               "%s\n\n", DATA_PREFIX "images/icon.png", SDL_GetError());
8229       return;
8230     }
8231 
8232 
8233 #ifndef WIN32
8234   /* Create mask: */
8235   masklen = (((icon->w) + 7) / 8) * (icon->h);
8236   mask = malloc(masklen * sizeof(Uint8));
8237   memset(mask, 0xFF, masklen);
8238 
8239   /* Set icon: */
8240   SDL_WM_SetIcon(icon, mask);
8241 
8242   /* Free icon surface & mask: */
8243   free(mask);
8244 #else
8245   /* Set icon: */
8246   SDL_WM_SetIcon(icon, NULL);
8247 #endif
8248   SDL_FreeSurface(icon);
8249 
8250 
8251   /* Grab keyboard and mouse, if requested: */
8252 
8253   if (grab_input)
8254     {
8255       debug("Grabbing input!");
8256       SDL_WM_GrabInput(SDL_GRAB_ON);
8257     }
8258 }
8259 
8260 
8261 /**
8262  * FIXME
8263  */
8264 /* Load a mouse pointer (cursor) shape: */
get_cursor(unsigned char * bits,unsigned char * mask_bits,unsigned int width,unsigned int height,unsigned int x,unsigned int y)8265 static SDL_Cursor *get_cursor(unsigned char *bits, unsigned char *mask_bits,
8266                               unsigned int width, unsigned int height, unsigned int x, unsigned int y)
8267 {
8268   Uint8 b;
8269   Uint8 temp_bitmap[128], temp_bitmask[128];
8270   unsigned int i;
8271 
8272 
8273   if (((width + 7) / 8) * height > 128)
8274     {
8275       fprintf(stderr, "Error: Cursor is too large!\n");
8276       cleanup();
8277       exit(1);
8278     }
8279 
8280   for (i = 0; i < ((width + 7) / 8) * height; i++)
8281     {
8282       b = bits[i];
8283 
8284       temp_bitmap[i] = (((b & 0x01) << 7) |
8285                         ((b & 0x02) << 5) |
8286                         ((b & 0x04) << 3) |
8287                         ((b & 0x08) << 1) |
8288                         ((b & 0x10) >> 1) | ((b & 0x20) >> 3) | ((b & 0x40) >> 5) | ((b & 0x80) >> 7));
8289 
8290       b = mask_bits[i];
8291 
8292       temp_bitmask[i] = (((b & 0x01) << 7) |
8293                          ((b & 0x02) << 5) |
8294                          ((b & 0x04) << 3) |
8295                          ((b & 0x08) << 1) |
8296                          ((b & 0x10) >> 1) | ((b & 0x20) >> 3) | ((b & 0x40) >> 5) | ((b & 0x80) >> 7));
8297     }
8298 
8299   return (SDL_CreateCursor(temp_bitmap, temp_bitmask, width, height, x, y));
8300 }
8301 
8302 /**
8303  * Load an image and Resize it according to the difference between the size of the Buttons original and current ones:
8304  */
loadimagerb(const char * const fname)8305 static SDL_Surface *loadimagerb(const char *const fname)
8306 {
8307   /* For the vaste majority of users return as soon as possible and touch the image as less as we can. */
8308   if (button_h == ORIGINAL_BUTTON_SIZE)
8309     return(loadimage(fname));
8310 
8311   /* Going to resize the button */
8312   int w,h;
8313   SDL_Surface *aux_surf;
8314   SDL_Surface *aux2_surf;
8315 
8316   aux_surf = loadimage(fname);
8317   if (aux_surf)
8318     {
8319       w = (aux_surf->w * button_w) / ORIGINAL_BUTTON_SIZE;
8320       h = (aux_surf->h * button_h) / ORIGINAL_BUTTON_SIZE;
8321       aux2_surf = thumbnail(aux_surf, w, h, 0);
8322     }
8323   else
8324     return (NULL);
8325 
8326   SDL_FreeSurface(aux_surf);
8327   return(aux2_surf);
8328 }
8329 
8330 /**
8331  * FIXME
8332  */
8333 /* Load an image (with errors): */
loadimage(const char * const fname)8334 static SDL_Surface *loadimage(const char *const fname)
8335 {
8336   return (do_loadimage(fname, 1));
8337 }
8338 
8339 
8340 /**
8341  * FIXME
8342  */
8343 /* Load an image: */
do_loadimage(const char * const fname,int abort_on_error)8344 static SDL_Surface *do_loadimage(const char *const fname, int abort_on_error)
8345 {
8346   SDL_Surface *s, *disp_fmt_s;
8347 
8348 
8349   /* Load the image file: */
8350 
8351   s = myIMG_Load((char *)fname);
8352   if (s == NULL)
8353     {
8354       if (abort_on_error)
8355         {
8356           fprintf(stderr,
8357                   "\nError: I couldn't load a graphics file:\n"
8358                   "%s\n" "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", fname, SDL_GetError());
8359 
8360           cleanup();
8361           exit(1);
8362         }
8363       else
8364         {
8365           return (NULL);
8366         }
8367     }
8368 
8369 
8370   /* Convert to the display format: */
8371 
8372   disp_fmt_s = SDL_DisplayFormatAlpha(s);
8373   if (disp_fmt_s == NULL)
8374     {
8375       if (abort_on_error)
8376         {
8377           fprintf(stderr,
8378                   "\nError: I couldn't convert a graphics file:\n"
8379                   "%s\n" "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", fname, SDL_GetError());
8380 
8381           SDL_FreeSurface(s);
8382           cleanup();
8383           exit(1);
8384         }
8385       else
8386         {
8387           SDL_FreeSurface(s);
8388           return (NULL);
8389         }
8390     }
8391 
8392 
8393   /* Free the temp. surface & return the converted one: */
8394 
8395   SDL_FreeSurface(s);
8396 
8397   return (disp_fmt_s);
8398 }
8399 
8400 
8401 /**
8402  * FIXME
8403  */
8404 /* Draw the toolbar: */
draw_toolbar(void)8405 static void draw_toolbar(void)
8406 {
8407   int i, off_y, max, most, tool;
8408   SDL_Rect dest;
8409 
8410   most = (buttons_tall * gd_toolopt.cols) - TOOLOFFSET;
8411   off_y = 0;
8412   /* FIXME: Only allow print if we have something to print! */
8413 
8414 
8415   draw_image_title(TITLE_TOOLS, r_ttools);
8416 
8417 
8418 
8419   /* Do we need scrollbars? */
8420   if (NUM_TOOLS > most + TOOLOFFSET)
8421     {
8422       off_y = img_scroll_up->h;
8423       max = most - gd_tools.cols + TOOLOFFSET;
8424       gd_tools.rows = max / gd_tools.cols;
8425 
8426       dest.x = 0;
8427       dest.y = r_ttools.h;
8428 
8429       if (tool_scroll > 0)
8430         {
8431           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
8432         }
8433       else
8434         {
8435           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
8436         }
8437 
8438       dest.x = 0;
8439       dest.y = r_ttools.h + off_y + ((most - gd_tools.cols + TOOLOFFSET) / gd_tools.cols * button_h);
8440 
8441 
8442 
8443       if (tool_scroll < NUM_TOOLS - (most - gd_tools.cols) - TOOLOFFSET)
8444         {
8445           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
8446         }
8447       else
8448         {
8449           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
8450         }
8451     }
8452   else
8453     {
8454       off_y = 0;
8455       max = most + TOOLOFFSET;
8456     }
8457 
8458 
8459 
8460 
8461   for (tool = tool_scroll; tool < tool_scroll + max; tool++)
8462     {
8463       i = tool - tool_scroll;
8464       dest.x = ((i % 2) * button_w);
8465       dest.y = ((i / 2) * button_h) + r_ttools.h + off_y;
8466 
8467 
8468       if (tool < NUM_TOOLS)
8469         {
8470           SDL_Surface *button_color;
8471           SDL_Surface *button_body;
8472 
8473           if (tool_scroll + i == cur_tool)
8474             {
8475               button_body = img_btn_down;
8476               button_color = img_black;
8477             }
8478           else if (tool_avail[tool])
8479             {
8480               button_body = img_btn_up;
8481               button_color = img_black;
8482             }
8483           else
8484             {
8485               button_body = img_btn_off;
8486               button_color = img_grey;
8487             }
8488           SDL_BlitSurface(button_body, NULL, screen, &dest);
8489           SDL_BlitSurface(button_color, NULL, img_tools[tool], NULL);
8490           SDL_BlitSurface(button_color, NULL, img_tool_names[tool], NULL);
8491 
8492           dest.x = ((i % 2) * button_w) + 4;
8493           dest.y = ((i / 2) * button_h) + r_ttools.h + 2 + off_y;
8494 
8495           SDL_BlitSurface(img_tools[tool], NULL, screen, &dest);
8496 
8497 
8498           dest.x = ((i % 2) * button_w) + (4 * button_w) / ORIGINAL_BUTTON_SIZE + ((40 * button_w) / ORIGINAL_BUTTON_SIZE - img_tool_names[tool]->w) / 2;
8499           dest.y = ((i / 2) * button_h) + r_ttools.h + (2 * button_w) / ORIGINAL_BUTTON_SIZE + (((44 + button_label_y_nudge) * button_w) / ORIGINAL_BUTTON_SIZE - img_tool_names[tool]->h) + off_y;
8500 
8501           SDL_BlitSurface(img_tool_names[tool], NULL, screen, &dest);
8502         }
8503       else
8504         {
8505           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
8506         }
8507     }
8508 }
8509 
8510 
8511 /**
8512  * FIXME
8513  */
8514 /* Draw magic controls: */
draw_magic(void)8515 static void draw_magic(void)
8516 {
8517   int magic, i, max, off_y;
8518   SDL_Rect dest;
8519   int most;
8520 
8521   draw_image_title(TITLE_MAGIC, r_ttoolopt);
8522 
8523   /* How many can we show? */
8524 
8525   most = (buttons_tall * gd_toolopt.cols) - gd_toolopt.cols - TOOLOFFSET; /* was 12 */
8526   if (disable_magic_controls)
8527     most = most + gd_toolopt.cols; /* was 14 */
8528 
8529   if (num_magics > most + TOOLOFFSET)
8530     {
8531       off_y = img_scroll_down->h;
8532       max = (most - 2) + TOOLOFFSET;
8533 
8534       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8535       dest.y = r_ttoolopt.h;
8536 
8537       if (magic_scroll > 0)
8538         {
8539           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
8540         }
8541       else
8542         {
8543           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
8544         }
8545 
8546       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8547       dest.y = r_ttoolopt.h + img_scroll_down->h + ((((most - 2) / 2) + TOOLOFFSET / 2) * button_h);
8548 
8549       if (magic_scroll < num_magics - (most - 2) - TOOLOFFSET)
8550         {
8551           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
8552         }
8553       else
8554         {
8555           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
8556         }
8557     }
8558   else
8559     {
8560       off_y = 0;
8561       max = most + TOOLOFFSET;
8562     }
8563 
8564 
8565   for (magic = magic_scroll; magic < magic_scroll + max; magic++)
8566     {
8567       i = magic - magic_scroll;
8568 
8569       dest.x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w);
8570       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
8571 
8572       if (magic < num_magics)
8573         {
8574           if (magic == cur_magic)
8575             {
8576               SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
8577             }
8578           else
8579             {
8580               SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
8581             }
8582 
8583           dest.x = WINDOW_WIDTH - r_ttoolopt.w + ((i % 2) * button_w) + 4;
8584           dest.y = ((i / 2) * button_h) + r_ttoolopt.h + 4 + off_y;
8585 
8586           SDL_BlitSurface(magics[magic].img_icon, NULL, screen, &dest);
8587 
8588 
8589           dest.x = WINDOW_WIDTH - r_ttoolopt.w + ((i % 2) * button_w) + (4 * button_w) / ORIGINAL_BUTTON_SIZE + ((40 * button_w) / ORIGINAL_BUTTON_SIZE - magics[magic].img_name->w) / 2;
8590           dest.y = (((i / 2) * button_h) + r_ttoolopt.h + (4 * button_h) / ORIGINAL_BUTTON_SIZE + ((44 * button_h) / ORIGINAL_BUTTON_SIZE - magics[magic].img_name->h) + off_y);
8591 
8592           SDL_BlitSurface(magics[magic].img_name, NULL, screen, &dest);
8593         }
8594       else
8595         {
8596           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
8597         }
8598     }
8599 
8600 
8601   /* Draw magic controls: */
8602 
8603   if (!disable_magic_controls)
8604     {
8605       SDL_Surface *button_color;
8606 
8607       /* Show paint button: */
8608 
8609       if (magics[cur_magic].mode == MODE_PAINT || magics[cur_magic].mode == MODE_ONECLICK
8610           || magics[cur_magic].mode == MODE_PAINT_WITH_PREVIEW)
8611         button_color = img_btn_down;    /* Active */
8612       else if (magics[cur_magic].avail_modes & MODE_PAINT || magics[cur_magic].avail_modes & MODE_ONECLICK
8613                || magics[cur_magic].avail_modes & MODE_PAINT_WITH_PREVIEW)
8614         button_color = img_btn_up;      /* Available, but not active */
8615       else
8616         button_color = img_btn_off;     /* Unavailable */
8617 
8618       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8619       dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
8620 
8621       SDL_BlitSurface(button_color, NULL, screen, &dest);
8622 
8623       dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_magic_paint->w) / 2;
8624       dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_magic_paint->h) / 2);
8625 
8626       SDL_BlitSurface(img_magic_paint, NULL, screen, &dest);
8627 
8628 
8629       /* Show fullscreen button: */
8630 
8631       if (magics[cur_magic].mode == MODE_FULLSCREEN)
8632         button_color = img_btn_down;    /* Active */
8633       else if (magics[cur_magic].avail_modes & MODE_FULLSCREEN)
8634         button_color = img_btn_up;      /* Available, but not active */
8635       else
8636         button_color = img_btn_off;     /* Unavailable */
8637 
8638       dest.x = WINDOW_WIDTH - button_w;
8639       dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
8640 
8641       SDL_BlitSurface(button_color, NULL, screen, &dest);
8642 
8643       dest.x = WINDOW_WIDTH - button_w + (button_w - img_magic_fullscreen->w) / 2;
8644       dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_magic_fullscreen->h) / 2);
8645 
8646       SDL_BlitSurface(img_magic_fullscreen, NULL, screen, &dest);
8647     }
8648 }
8649 
8650 
8651 static unsigned colors_state = COLORSEL_ENABLE | COLORSEL_CLOBBER;
8652 
8653 /**
8654  * FIXME
8655  */
8656 /* Draw color selector: */
draw_colors(unsigned action)8657 static unsigned draw_colors(unsigned action)
8658 {
8659   unsigned i;
8660   SDL_Rect dest;
8661   static unsigned old_color;
8662   unsigned old_colors_state;
8663 
8664   old_colors_state = colors_state;
8665 
8666   if (action == COLORSEL_CLOBBER || action == COLORSEL_CLOBBER_WIPE)
8667     colors_state |= COLORSEL_CLOBBER;
8668   else if (action == COLORSEL_REFRESH)
8669     colors_state &= ~COLORSEL_CLOBBER;
8670   else if (action == COLORSEL_DISABLE)
8671     colors_state = COLORSEL_DISABLE;
8672   else if (action == COLORSEL_ENABLE || action == COLORSEL_FORCE_REDRAW)
8673     colors_state = COLORSEL_ENABLE;
8674 
8675   colors_are_selectable = (colors_state == COLORSEL_ENABLE);
8676 
8677   if (colors_state & COLORSEL_CLOBBER && action != COLORSEL_CLOBBER_WIPE)
8678     return old_colors_state;
8679 
8680   if (cur_color == old_color && colors_state == old_colors_state &&
8681       action != COLORSEL_CLOBBER_WIPE && action != COLORSEL_FORCE_REDRAW)
8682     return old_colors_state;
8683 
8684   old_color = cur_color;
8685 
8686   for (i = 0; i < (unsigned int)NUM_COLORS; i++)
8687     {
8688       dest.x = r_colors.x + i % gd_colors.cols * color_button_w;
8689       dest.y = r_colors.y + i / gd_colors.cols * color_button_h;
8690 #ifndef LOW_QUALITY_COLOR_SELECTOR
8691       SDL_BlitSurface((colors_state == COLORSEL_ENABLE)
8692                       ? img_color_btns[i + (i == cur_color) * NUM_COLORS] : img_color_btn_off, NULL, screen, &dest);
8693 #else
8694       dest.w = color_button_w;
8695       dest.h = color_button_h;
8696 
8697       if (colors_state == COLORSEL_ENABLE)
8698         SDL_FillRect(screen, &dest,
8699                      SDL_MapRGB(screen->format, color_hexes[i][0], color_hexes[i][1], color_hexes[i][2]));
8700       else
8701         SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 240, 240, 240));
8702 
8703       if (i == cur_color && colors_state == COLORSEL_ENABLE)
8704         {
8705           dest.y += 4;
8706           SDL_BlitSurface(img_paintcan, NULL, screen, &dest);
8707         }
8708 #endif
8709 
8710     }
8711   update_screen_rect(&r_colors);
8712 
8713   /* if only the color changed, no need to draw the title */
8714   if (colors_state == old_colors_state)
8715     return old_colors_state;
8716 
8717   /* If more than one colors rows, fill the parts of the r_tcolors not covered by the title. */
8718   if (gd_colors.rows > 1)
8719     SDL_FillRect(screen, &r_tcolors, SDL_MapRGBA(screen->format, 255,255,255,255));
8720 
8721   if (colors_state == COLORSEL_ENABLE)
8722     {
8723       SDL_BlitSurface(img_title_large_on, NULL, screen, &r_tcolors);
8724 
8725       dest.x = r_tcolors.x + (r_tcolors.w - img_title_names[TITLE_COLORS]->w) / 2;
8726       dest.y = r_tcolors.y + (r_tcolors.h - img_title_names[TITLE_COLORS]->h) / 2;
8727       SDL_BlitSurface(img_title_names[TITLE_COLORS], NULL, screen, &dest);
8728     }
8729   else
8730     {
8731       SDL_BlitSurface(img_title_large_off, NULL, screen, &r_tcolors);
8732     }
8733 
8734   update_screen_rect(&r_tcolors);
8735 
8736   return old_colors_state;
8737 }
8738 
8739 
8740 /**
8741  * FIXME
8742  */
8743 /* Draw brushes: */
draw_brushes(void)8744 static void draw_brushes(void)
8745 {
8746   int i, off_y, max, brush;
8747   SDL_Rect src, dest;
8748   int most;
8749 
8750   /* Draw the title: */
8751   draw_image_title(TITLE_BRUSHES, r_ttoolopt);
8752 
8753   /* Space for buttons, was 14 */
8754   most = (buttons_tall * gd_toolopt.cols) - TOOLOFFSET;
8755 
8756   /* Do we need scrollbars? */
8757 
8758   if (num_brushes > most + TOOLOFFSET)
8759     {
8760       most = most - gd_toolopt.cols; /* was 12 */
8761       off_y = img_scroll_up->h;
8762       max = most + TOOLOFFSET;
8763 
8764       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8765       dest.y = r_ttoolopt.h;
8766 
8767       if (brush_scroll > 0)
8768         {
8769           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
8770         }
8771       else
8772         {
8773           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
8774         }
8775 
8776       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8777       dest.y = r_ttoolopt.h + img_scroll_up->h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
8778 
8779       if (brush_scroll < num_brushes - most - TOOLOFFSET)
8780         {
8781           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
8782         }
8783       else
8784         {
8785           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
8786         }
8787     }
8788   else
8789     {
8790       off_y = 0;
8791       max = most + TOOLOFFSET;
8792     }
8793 
8794 
8795   /* Draw each of the shown brushes: */
8796 
8797   for (brush = brush_scroll; brush < brush_scroll + max; brush++)
8798     {
8799       i = brush - brush_scroll;
8800 
8801 
8802       dest.x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w);
8803       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
8804 
8805       if (brush == cur_brush)
8806         {
8807           SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
8808         }
8809       else if (brush < num_brushes)
8810         {
8811           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
8812         }
8813       else
8814         {
8815           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
8816         }
8817 
8818       if (brush < num_brushes)
8819         {
8820           if (brushes_directional[brush])
8821             src.x = (img_brushes_thumbs[brush]->w / abs(brushes_frames[brush])) / 3;
8822           else
8823             src.x = 0;
8824 
8825           src.y = brushes_directional[brush] ? (img_brushes_thumbs[brush]->h / 3) : 0;
8826 
8827           src.w = (img_brushes_thumbs[brush]->w / abs(brushes_frames[brush])) / (brushes_directional[brush] ? 3 : 1);
8828           src.h = (img_brushes_thumbs[brush]->h / (brushes_directional[brush] ? 3 : 1));
8829 
8830           dest.x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w) + ((button_w - src.w) >> 1);
8831           dest.y = ((i / 2) * button_h) + r_ttoolopt.h + ((button_h - src.h) >> 1) + off_y;
8832 
8833           SDL_BlitSurface(img_brushes_thumbs[brush], &src, screen, &dest);
8834         }
8835     }
8836 }
8837 
8838 
8839 /**
8840  * FIXME
8841  */
8842 /* Draw fonts: */
draw_fonts(void)8843 static void draw_fonts(void)
8844 {
8845   int i, off_y, max, font, most;
8846   SDL_Rect dest, src;
8847   SDL_Surface *tmp_surf;
8848   SDL_Color black = { 0, 0, 0, 0 };
8849 
8850   /* Draw the title: */
8851   draw_image_title(TITLE_LETTERS, r_ttoolopt);
8852 
8853   /* Space for buttons, was 14 */
8854   most = (buttons_tall * gd_toolopt.cols) - TOOLOFFSET;
8855 
8856   /* How many can we show? */
8857 
8858   if (cur_tool == TOOL_LABEL)
8859     {
8860       most = most - gd_toolopt.cols - gd_toolopt.cols - gd_toolopt.cols;
8861       if (disable_stamp_controls)
8862         most = most + gd_toolopt.cols + gd_toolopt.cols;
8863     }
8864   else
8865     {
8866       most = most - gd_toolopt.cols - gd_toolopt.cols;
8867       if (disable_stamp_controls)
8868         most = most + gd_toolopt.cols + gd_toolopt.cols /* Ugly! */;
8869     }
8870 
8871 #ifdef DEBUG
8872   printf("there are %d font families\n", num_font_families);
8873 #endif
8874 
8875 
8876   /* Do we need scrollbars? */
8877 
8878   if (num_font_families > most + TOOLOFFSET)
8879     {
8880       off_y = img_scroll_up->h;
8881       max = most - gd_toolopt.cols + TOOLOFFSET;
8882 
8883       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8884       dest.y = r_ttoolopt.h;
8885 
8886       if (font_scroll > 0)
8887         {
8888           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
8889         }
8890       else
8891         {
8892           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
8893         }
8894 
8895       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
8896       dest.y = r_ttoolopt.h + off_y + (((most - gd_toolopt.cols) / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
8897 
8898       /*      if (!disable_stamp_controls)
8899 	      dest.y = dest.y - (button_h * 2); */
8900 
8901       if (font_scroll < num_font_families - (most - gd_toolopt.cols) - TOOLOFFSET)
8902         {
8903           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
8904         }
8905       else
8906         {
8907           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
8908         }
8909     }
8910   else
8911     {
8912       off_y = 0;
8913       max = most + TOOLOFFSET;
8914     }
8915 
8916   /* Draw each of the shown fonts: */
8917   if (!num_font_families % 2)
8918     font_scroll = min(font_scroll, max(0, num_font_families - max));    /*     FIXAM COMENTARI */
8919   else
8920     font_scroll = min(font_scroll, max(0, num_font_families + 1 - max));        /*     FIXAM COMENTARI */
8921 
8922   for (font = font_scroll; font < font_scroll + max; font++)
8923     {
8924       i = font - font_scroll;
8925 
8926 
8927       dest.x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w);
8928       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
8929 
8930       if (font == cur_font)
8931         {
8932           SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
8933         }
8934       else if (font < num_font_families)
8935         {
8936           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
8937         }
8938       else
8939         {
8940           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
8941         }
8942 
8943       if (font < num_font_families)
8944         {
8945           SDL_Surface *tmp_surf_1;
8946 
8947           /* Label for 'Letters' buttons (font selector, down the right when the Text tool is being used); used to show the difference between font faces */
8948           tmp_surf_1 = render_text(getfonthandle(font), gettext("Aa"), black);
8949 
8950           if (tmp_surf_1 == NULL)
8951             {
8952               fprintf(stderr, "render_text() returned NULL!\n");
8953               return;
8954             }
8955 
8956           if (tmp_surf_1->w > button_w || tmp_surf_1->h > button_h)
8957             {
8958               tmp_surf = thumbnail(tmp_surf_1, button_w, button_h, 1);
8959               SDL_FreeSurface(tmp_surf_1);
8960             }
8961           else
8962             tmp_surf = tmp_surf_1;
8963 
8964           src.x = (tmp_surf->w - button_w) / 2;
8965           src.y = (tmp_surf->h - button_h) / 2;
8966           src.w = button_w;
8967           src.h = button_h;
8968 
8969           if (src.x < 0)
8970             src.x = 0;
8971           if (src.y < 0)
8972             src.y = 0;
8973 
8974           dest.x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w);
8975           if (src.w > tmp_surf->w)
8976             {
8977               src.w = tmp_surf->w;
8978               dest.x = dest.x + ((button_w - (tmp_surf->w)) / 2);
8979             }
8980 
8981 
8982           dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
8983           if (src.h > tmp_surf->h)
8984             {
8985               src.h = tmp_surf->h;
8986               dest.y = dest.y + ((button_h - (tmp_surf->h)) / 2);
8987             }
8988 
8989           SDL_BlitSurface(tmp_surf, &src, screen, &dest);
8990 
8991           SDL_FreeSurface(tmp_surf);
8992         }
8993     }
8994 
8995 
8996   /* Draw text controls: */
8997 
8998   if (!disable_stamp_controls)
8999     {
9000       SDL_Surface *button_color;
9001       SDL_Surface *button_body;
9002 
9003       if (cur_tool == TOOL_LABEL)
9004         {
9005 
9006           /* disabling rotation as I am not sure how this should be implemented */
9007           dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9008           dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9009           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9010 
9011           /* if(cur_label == LABEL_ROTATE) */
9012           /*   SDL_BlitSurface(img_btn_down, NULL, screen, &dest); */
9013           /* else */
9014           /*   SDL_BlitSurface(img_btn_up, NULL, screen, &dest); */
9015 
9016           /* dest.x = WINDOW_WIDTH - r_ttoolopt.w + (48 - img_label->w) / 2; */
9017           /* dest.y = (40 + ((4 + TOOLOFFSET / 2) * 48) + (48 - img_label->h) / 2); */
9018 
9019           /* SDL_BlitSurface(img_label, NULL, screen, &dest); */
9020 
9021           dest.x = WINDOW_WIDTH - button_w;
9022           dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9023 
9024           if (cur_label == LABEL_SELECT)
9025             SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9026 
9027           else
9028             {
9029               if (are_labels())
9030                 SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9031               else
9032                 SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9033             }
9034 
9035 
9036           dest.x = WINDOW_WIDTH - button_w + (button_w - img_label_select->w) / 2;
9037           dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_label_select->h) / 2);
9038 
9039           SDL_BlitSurface(img_label_select, NULL, screen, &dest);
9040 	  most = most + gd_toolopt.cols;
9041         }
9042 
9043       /* Show bold button: */
9044 
9045       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9046       dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9047 
9048       if (text_state & TTF_STYLE_BOLD)
9049         SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9050       else
9051         SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9052 
9053       dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_bold->w) / 2;
9054       dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_bold->h) / 2);
9055 
9056       SDL_BlitSurface(img_bold, NULL, screen, &dest);
9057 
9058 
9059       /* Show italic button: */
9060 
9061       dest.x = WINDOW_WIDTH - button_w;
9062       dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9063 
9064       if (text_state & TTF_STYLE_ITALIC)
9065         SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9066       else
9067         SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9068 
9069       dest.x = WINDOW_WIDTH - button_w + (button_w - img_italic->w) / 2;
9070       dest.y = (r_ttoolopt.h + ((most /gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_italic->h) / 2);
9071 
9072       SDL_BlitSurface(img_italic, NULL, screen, &dest);
9073 
9074       most = most + gd_toolopt.cols;
9075       // printf("most %d\n", most);
9076 
9077       /* Show shrink button: */
9078 
9079       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9080       dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9081 
9082       if (text_size > MIN_TEXT_SIZE)
9083         {
9084           button_color = img_black;
9085           button_body = img_btn_up;
9086         }
9087       else
9088         {
9089           button_color = img_grey;
9090           button_body = img_btn_off;
9091         }
9092       SDL_BlitSurface(button_body, NULL, screen, &dest);
9093 
9094       dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_shrink->w) / 2;
9095       dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_shrink->h) / 2);
9096 
9097       SDL_BlitSurface(button_color, NULL, img_shrink, NULL);
9098       SDL_BlitSurface(img_shrink, NULL, screen, &dest);
9099 
9100 
9101       /* Show grow button: */
9102 
9103       dest.x = WINDOW_WIDTH - button_w;
9104       dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9105 
9106       if (text_size < MAX_TEXT_SIZE)
9107         {
9108           button_color = img_black;
9109           button_body = img_btn_up;
9110         }
9111       else
9112         {
9113           button_color = img_grey;
9114           button_body = img_btn_off;
9115         }
9116       SDL_BlitSurface(button_body, NULL, screen, &dest);
9117 
9118       dest.x = WINDOW_WIDTH - button_w + (button_w - img_grow->w) / 2;
9119       dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_grow->h) / 2);
9120 
9121       SDL_BlitSurface(button_color, NULL, img_grow, NULL);
9122       SDL_BlitSurface(img_grow, NULL, screen, &dest);
9123     }
9124   else
9125     {
9126       if (cur_tool == TOOL_LABEL)
9127         {
9128           dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9129           dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9130 
9131           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9132 
9133           dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_label->w) / 2;
9134           dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_label->h) / 2);
9135 
9136           SDL_BlitSurface(img_label, NULL, screen, &dest);
9137 
9138           dest.x = WINDOW_WIDTH - button_w;
9139           dest.y = r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9140 
9141           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9142 
9143           dest.x = WINDOW_WIDTH - button_w + (button_w - img_label_select->w) / 2;
9144           dest.y = (r_ttoolopt.h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h) + (button_h - img_label_select->h) / 2);
9145 
9146           SDL_BlitSurface(img_label_select, NULL, screen, &dest);
9147         }
9148     }
9149 }
9150 
9151 
9152 /**
9153  * FIXME
9154  */
9155 /* Draw stamps: */
draw_stamps(void)9156 static void draw_stamps(void)
9157 {
9158   int i, off_y, max, stamp, most;
9159   int base_x, base_y;
9160   SDL_Rect dest;
9161   SDL_Surface *img;
9162   int sizes, size_at;
9163   float x_per, y_per;
9164   int xx, yy;
9165   SDL_Surface *btn, *blnk;
9166   SDL_Surface *button_color;
9167   SDL_Surface *button_body;
9168 
9169 
9170   /* Draw the title: */
9171   draw_image_title(TITLE_STAMPS, r_ttoolopt);
9172 
9173 
9174   /* How many can we show? */
9175 
9176   most = (buttons_tall * gd_toolopt.cols) - gd_toolopt.cols - gd_toolopt.cols - gd_toolopt.cols - TOOLOFFSET;                     /* was 10 and 14, before left/right controls -bjk 2007.05.03 */
9177   if (disable_stamp_controls)
9178     most = (buttons_tall * gd_toolopt.cols) - gd_toolopt.cols - TOOLOFFSET;
9179 
9180 
9181   /* Do we need scrollbars? */
9182 
9183   if (num_stamps[stamp_group] > most + TOOLOFFSET)
9184     {
9185       off_y = img_scroll_up->h;
9186       max = (most - gd_toolopt.cols) + TOOLOFFSET;
9187 
9188       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9189       dest.y = r_ttoolopt.h;
9190 
9191       if (stamp_scroll[stamp_group] > 0)
9192         {
9193           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
9194         }
9195       else
9196         {
9197           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
9198         }
9199 
9200 
9201       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9202       dest.y = r_ttoolopt.h + off_y + (((most + 2) / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);   /* was 6, before left/right controls -bjk 2007.05.03 */
9203 
9204       if (!disable_stamp_controls)
9205         dest.y = dest.y - (button_h * 2);
9206 
9207       if (stamp_scroll[stamp_group] < num_stamps[stamp_group] - (most - 2) - TOOLOFFSET)
9208         {
9209           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
9210         }
9211       else
9212         {
9213           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
9214         }
9215     }
9216   else
9217     {
9218       off_y = 0;
9219       max = most + TOOLOFFSET;
9220     }
9221 
9222 
9223   /* Draw each of the shown stamps: */
9224 
9225   for (stamp = stamp_scroll[stamp_group]; stamp < stamp_scroll[stamp_group] + max; stamp++)
9226     {
9227       i = stamp - stamp_scroll[stamp_group];
9228 
9229       dest.x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w);
9230       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
9231 
9232       if (stamp == cur_stamp[stamp_group])
9233         {
9234           SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9235         }
9236       else if (stamp < num_stamps[stamp_group])
9237         {
9238           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9239         }
9240       else
9241         {
9242           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9243         }
9244 
9245       if (stamp < num_stamps[stamp_group])
9246         {
9247           get_stamp_thumb(stamp_data[stamp_group][stamp]);
9248           img = stamp_data[stamp_group][stamp]->thumbnail;
9249 
9250           base_x = ((i % 2) * button_w) + (WINDOW_WIDTH - r_ttoolopt.w) + ((button_w - (img->w)) / 2);
9251 
9252           base_y = ((i / 2) * button_h) + r_ttoolopt.h + ((button_h - (img->h)) / 2) + off_y;
9253 
9254           dest.x = base_x;
9255           dest.y = base_y;
9256 
9257           SDL_BlitSurface(img, NULL, screen, &dest);
9258         }
9259     }
9260 
9261 
9262   /* Draw stamp group buttons (prev/next): */
9263 
9264 
9265   /* Show prev button: */
9266 
9267   button_color = img_black;
9268   button_body = img_btn_nav;
9269 
9270   dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9271   dest.y = r_ttoolopt.h + (((most + TOOLOFFSET) / 2) * button_h);
9272 
9273   SDL_BlitSurface(button_body, NULL, screen, &dest);
9274 
9275   dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_prev->w) / 2;
9276   dest.y = (r_ttoolopt.h + (((most + TOOLOFFSET) / 2) * button_h) + (button_h - img_prev->h) / 2);
9277 
9278   SDL_BlitSurface(button_color, NULL, img_prev, NULL);
9279   SDL_BlitSurface(img_prev, NULL, screen, &dest);
9280 
9281   /* Show next button: */
9282 
9283   button_color = img_black;
9284   button_body = img_btn_nav;
9285 
9286   dest.x = WINDOW_WIDTH - button_w;
9287   dest.y = r_ttoolopt.h + (((most + TOOLOFFSET) / gd_toolopt.cols) * button_h);
9288 
9289   SDL_BlitSurface(button_body, NULL, screen, &dest);
9290 
9291   dest.x = WINDOW_WIDTH - button_w + (button_w - img_next->w) / 2;
9292   dest.y = (r_ttoolopt.h + (((most + TOOLOFFSET) / gd_toolopt.cols) * button_h) + (button_h - img_next->h) / 2);
9293 
9294   SDL_BlitSurface(button_color, NULL, img_next, NULL);
9295   SDL_BlitSurface(img_next, NULL, screen, &dest);
9296 
9297 
9298   /* Draw stamp controls: */
9299 
9300   if (!disable_stamp_controls)
9301     {
9302       /* Show mirror button: */
9303 
9304       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9305       dest.y = r_ttoolopt.h + ((most + gd_toolopt.cols+ TOOLOFFSET) / gd_toolopt.cols * button_h);
9306 
9307       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->mirrorable)
9308         {
9309           if (stamp_data[stamp_group][cur_stamp[stamp_group]]->mirrored)
9310             {
9311               button_color = img_black;
9312               button_body = img_btn_down;
9313             }
9314           else
9315             {
9316               button_color = img_black;
9317               button_body = img_btn_up;
9318             }
9319         }
9320       else
9321         {
9322           button_color = img_grey;
9323           button_body = img_btn_off;
9324         }
9325       SDL_BlitSurface(button_body, NULL, screen, &dest);
9326 
9327       dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_mirror->w) / 2;
9328       dest.y = (r_ttoolopt.h + ((most + gd_toolopt.cols + TOOLOFFSET) / gd_toolopt.cols * button_h) + (button_h - img_mirror->h) / 2);
9329 
9330       SDL_BlitSurface(button_color, NULL, img_mirror, NULL);
9331       SDL_BlitSurface(img_mirror, NULL, screen, &dest);
9332 
9333       /* Show flip button: */
9334 
9335       dest.x = WINDOW_WIDTH - button_w;
9336       dest.y = r_ttoolopt.h + ((most + gd_toolopt.cols + TOOLOFFSET) / gd_toolopt.cols * button_h);
9337 
9338       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->flipable)
9339         {
9340           if (stamp_data[stamp_group][cur_stamp[stamp_group]]->flipped)
9341             {
9342               button_color = img_black;
9343               button_body = img_btn_down;
9344             }
9345           else
9346             {
9347               button_color = img_black;
9348               button_body = img_btn_up;
9349             }
9350         }
9351       else
9352         {
9353           button_color = img_grey;
9354           button_body = img_btn_off;
9355         }
9356       SDL_BlitSurface(button_body, NULL, screen, &dest);
9357 
9358       dest.x = WINDOW_WIDTH - button_w + (button_w - img_flip->w) / 2;
9359       dest.y = (r_ttoolopt.h + ((most + gd_toolopt.cols + TOOLOFFSET) / gd_toolopt.cols * button_h) + (button_h - img_flip->h) / 2);
9360 
9361       SDL_BlitSurface(button_color, NULL, img_flip, NULL);
9362       SDL_BlitSurface(img_flip, NULL, screen, &dest);
9363 
9364 
9365 #ifdef OLD_STAMP_GROW_SHRINK
9366       /* Show shrink button: */
9367 
9368       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9369       dest.y = 40 + ((6 + TOOLOFFSET / 2) * button_h);
9370 
9371       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size > MIN_STAMP_SIZE)
9372         {
9373           button_color = img_black;
9374           button_body = img_btn_up;
9375         }
9376       else
9377         {
9378           button_color = img_grey;
9379           button_body = img_btn_off;
9380         }
9381       SDL_BlitSurface(button_body, NULL, screen, &dest);
9382 
9383       dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_shrink->w) / 2;
9384       dest.y = (40 + ((6 + TOOLOFFSET / 2) * button_h) + (button_h - img_shrink->h) / 2);
9385 
9386       SDL_BlitSurface(button_color, NULL, img_shrink, NULL);
9387       SDL_BlitSurface(img_shrink, NULL, screen, &dest);
9388 
9389 
9390       /* Show grow button: */
9391 
9392       dest.x = WINDOW_WIDTH - button_w;
9393       dest.y = 40 + ((6 + TOOLOFFSET / 2) * button_h);
9394 
9395       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->size < MAX_STAMP_SIZE)
9396         {
9397           button_color = img_black;
9398           button_body = img_btn_up;
9399         }
9400       else
9401         {
9402           button_color = img_grey;
9403           button_body = img_btn_off;
9404         }
9405       SDL_BlitSurface(button_body, NULL, screen, &dest);
9406 
9407       dest.x = WINDOW_WIDTH - button_w + (button_w - img_grow->w) / 2;
9408       dest.y = (40 + ((6 + TOOLOFFSET / 2) * button_h) + (button_h - img_grow->h) / 2);
9409 
9410       SDL_BlitSurface(button_color, NULL, img_grow, NULL);
9411       SDL_BlitSurface(img_grow, NULL, screen, &dest);
9412 
9413 #else
9414       sizes = MAX_STAMP_SIZE - MIN_STAMP_SIZE + 1;      /* +1 for SF Bug #1668235 -bjk 2011.01.08 */
9415       size_at = (stamp_data[stamp_group][cur_stamp[stamp_group]]->size - MIN_STAMP_SIZE);
9416 
9417       x_per = (float)r_ttoolopt.w / sizes;
9418       y_per = (float)button_h / sizes;
9419 
9420       for (i = 0; i < sizes; i++)
9421         {
9422           xx = ceil(x_per);
9423           yy = ceil(y_per * i);
9424 
9425           if (i <= size_at)
9426             btn = thumbnail(img_btn_down, xx, yy, 0);
9427           else
9428             btn = thumbnail(img_btn_up, xx, yy, 0);
9429 
9430           blnk = thumbnail(img_btn_off, xx, button_h - yy, 0);
9431 
9432           /* FIXME: Check for NULL! */
9433 
9434           dest.x = (WINDOW_WIDTH - r_ttoolopt.w) + (i * x_per);
9435           dest.y = (((most + gd_toolopt.cols + gd_toolopt.cols + gd_toolopt.cols + TOOLOFFSET) / gd_toolopt.cols * button_h)) - 8 * button_scale;
9436           SDL_BlitSurface(blnk, NULL, screen, &dest);
9437 
9438           dest.x = (WINDOW_WIDTH - r_ttoolopt.w) + (i * x_per);
9439           dest.y = (((most + gd_toolopt.cols + gd_toolopt.cols + gd_toolopt.cols + gd_toolopt.cols + TOOLOFFSET) / gd_toolopt.cols * button_h)) - 8 * button_scale - (y_per * i);
9440           SDL_BlitSurface(btn, NULL, screen, &dest);
9441 
9442           SDL_FreeSurface(btn);
9443           SDL_FreeSurface(blnk);
9444         }
9445 #endif
9446     }
9447 
9448     redraw_tux_text();
9449 }
9450 
9451 
9452 /**
9453  * FIXME
9454  */
9455 /* Draw the shape selector: */
draw_shapes(void)9456 static void draw_shapes(void)
9457 {
9458   int i, shape, max, off_y, most;
9459   SDL_Rect dest;
9460 
9461 
9462   draw_image_title(TITLE_SHAPES, r_ttoolopt);
9463 
9464   if (disable_shape_controls)
9465     most = (buttons_tall * gd_toolopt.cols) - TOOLOFFSET;
9466   else
9467     most = (buttons_tall * gd_toolopt.cols) - gd_toolopt.cols - TOOLOFFSET;
9468 
9469   if (NUM_SHAPES > most + TOOLOFFSET)
9470     {
9471       off_y = img_scroll_up->h;
9472       max = (most - 2) + TOOLOFFSET;
9473 
9474       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9475       dest.y = r_ttoolopt.h;
9476 
9477       if (shape_scroll > 0)
9478         {
9479           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
9480         }
9481       else
9482         {
9483           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
9484         }
9485 
9486       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9487       dest.y = r_ttoolopt.h + img_scroll_up->h + ((((most - 2) / 2) + TOOLOFFSET / 2) * button_h);
9488 
9489       if (shape_scroll < NUM_SHAPES - (most - 2) - TOOLOFFSET)
9490         {
9491           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
9492         }
9493       else
9494         {
9495           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
9496         }
9497     }
9498   else
9499     {
9500       off_y = 0;
9501       max = most + TOOLOFFSET;
9502     }
9503 
9504   for (shape = shape_scroll; shape < shape_scroll + max; shape++)
9505     {
9506       i = shape - shape_scroll;
9507 
9508       dest.x = ((i % 2) * button_w) + WINDOW_WIDTH - r_ttoolopt.w;
9509       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
9510 
9511       if (shape == cur_shape)
9512         {
9513           SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9514         }
9515       else if (shape < NUM_SHAPES)
9516         {
9517           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9518         }
9519       else
9520         {
9521           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9522         }
9523 
9524 
9525       if (shape < NUM_SHAPES)
9526         {
9527           dest.x = ((i % 2) * button_w) + (4 * button_w) / ORIGINAL_BUTTON_SIZE + WINDOW_WIDTH - r_ttoolopt.w;
9528           dest.y = ((i / 2) * button_h) + r_ttoolopt.h + (4 * button_h) / ORIGINAL_BUTTON_SIZE + off_y;
9529 
9530           SDL_BlitSurface(img_shapes[shape], NULL, screen, &dest);
9531 
9532           dest.x = ((i % 2) * button_w) + (4 * button_w) / ORIGINAL_BUTTON_SIZE + WINDOW_WIDTH - r_ttoolopt.w + ((40 * button_w) / ORIGINAL_BUTTON_SIZE - img_shape_names[shape]->w) / 2;
9533 	  dest.y = ((i / 2) * button_h) + r_ttoolopt.h + (4 * button_h) / ORIGINAL_BUTTON_SIZE + ((44 * button_h) / ORIGINAL_BUTTON_SIZE - img_shape_names[shape]->h) + off_y;
9534 
9535           SDL_BlitSurface(img_shape_names[shape], NULL, screen, &dest);
9536         }
9537     }
9538 
9539   /* Draw shape tool controls: */
9540 
9541   if (!disable_shape_controls)
9542     {
9543       SDL_Surface *button_color;
9544 
9545       /* Show shape-from-center button: */
9546 
9547       if (shape_mode == SHAPEMODE_CENTER)
9548         button_color = img_btn_down;
9549       else
9550         button_color = img_btn_up;
9551 
9552       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9553       dest.y = r_ttoolopt.h + ((most + TOOLOFFSET) / gd_toolopt.cols * button_h);
9554 
9555       SDL_BlitSurface(button_color, NULL, screen, &dest);
9556 
9557       dest.x = WINDOW_WIDTH - r_ttoolopt.w + (button_w - img_shapes_center->w) / 2;
9558       dest.y = (r_ttoolopt.h + ((most + TOOLOFFSET) / gd_toolopt.cols * button_h) + (button_h - img_shapes_center->h) / 2);
9559 
9560       SDL_BlitSurface(img_shapes_center, NULL, screen, &dest);
9561 
9562 
9563       /* Show shape-from-corner button: */
9564 
9565       if (shape_mode == SHAPEMODE_CORNER)
9566         button_color = img_btn_down;
9567       else
9568         button_color = img_btn_up;
9569 
9570       dest.x = WINDOW_WIDTH - button_w;
9571       dest.y = r_ttoolopt.h + ((most + TOOLOFFSET) / gd_toolopt.cols * button_h);
9572 
9573       SDL_BlitSurface(button_color, NULL, screen, &dest);
9574 
9575       dest.x = WINDOW_WIDTH - button_w + (button_w - img_shapes_corner->w) / 2;
9576       dest.y = (r_ttoolopt.h + ((most + TOOLOFFSET) / gd_toolopt.cols * button_h) + (button_h - img_shapes_corner->h) / 2);
9577 
9578       SDL_BlitSurface(img_shapes_corner, NULL, screen, &dest);
9579     }
9580 }
9581 
9582 
9583 /**
9584  * FIXME
9585  */
9586 /* Draw the eraser selector: */
draw_erasers(void)9587 static void draw_erasers(void)
9588 {
9589   int i, j, x, y, sz;
9590   int xx, yy, n;
9591   void (*putpixel) (SDL_Surface *, int, int, Uint32);
9592   SDL_Rect dest;
9593   int most, off_y, max;
9594 
9595   putpixel = putpixels[screen->format->BytesPerPixel];
9596 
9597   draw_image_title(TITLE_ERASERS, r_ttoolopt);
9598 
9599   /* Space for buttons, was 14 */
9600   most = (buttons_tall * gd_toolopt.cols) - TOOLOFFSET;
9601 
9602   /* Do we need scrollbars? */
9603 
9604   if (NUM_FILLS > most + TOOLOFFSET)
9605     {
9606       most = most - gd_toolopt.cols; /* was 12 */
9607       off_y = img_scroll_up->h;
9608       max = most + TOOLOFFSET;
9609 
9610       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9611       dest.y = r_ttoolopt.h;
9612 
9613       if (eraser_scroll > 0)
9614         {
9615           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
9616         }
9617       else
9618         {
9619           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
9620         }
9621 
9622       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9623       dest.y = r_ttoolopt.h + img_scroll_up->h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9624 
9625       if (eraser_scroll < NUM_ERASERS - most - TOOLOFFSET)
9626         {
9627           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
9628         }
9629       else
9630         {
9631           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
9632         }
9633     }
9634   else
9635     {
9636       off_y = 0;
9637       max = most + TOOLOFFSET;
9638     }
9639 
9640   for (j = 0; j < most + TOOLOFFSET; j++)
9641     {
9642       i = j;
9643       dest.x = ((i % 2) * button_w) + WINDOW_WIDTH - r_ttoolopt.w;
9644       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
9645 
9646       i = j + eraser_scroll;
9647 
9648       if (i == cur_eraser)
9649         {
9650           SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9651         }
9652       else if (i < NUM_ERASERS)
9653         {
9654           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9655         }
9656       else
9657         {
9658           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9659         }
9660 
9661       if (i < NUM_ERASERS)
9662         {
9663           if (i < NUM_ERASERS / 2)
9664             {
9665               /* Square */
9666 
9667               sz = (2 + (((NUM_ERASERS / 2) - 1 - i) * (38 / ((NUM_ERASERS / 2) - 1)))) * button_scale;
9668 
9669               x = ((i % 2) * button_w) + WINDOW_WIDTH - r_ttoolopt.w + 24 * button_scale - sz / 2;
9670               y = ((j / 2) * button_h) + r_ttoolopt.h + 24 * button_scale - sz / 2 + off_y;
9671 
9672               dest.x = x;
9673               dest.y = y;
9674               dest.w = sz;
9675               dest.h = 2;
9676 
9677               SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
9678 
9679               dest.x = x;
9680               dest.y = y + sz - 2;
9681               dest.w = sz;
9682               dest.h = 2;
9683 
9684               SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
9685 
9686               dest.x = x;
9687               dest.y = y;
9688               dest.w = 2;
9689               dest.h = sz;
9690 
9691               SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
9692 
9693               dest.x = x + sz - 2;
9694               dest.y = y;
9695               dest.w = 2;
9696               dest.h = sz;
9697 
9698               SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
9699             }
9700           else
9701             {
9702               /* Circle */
9703 
9704               sz = (2 + (((NUM_ERASERS / 2) - 1 - (i - NUM_ERASERS / 2)) * (38 / ((NUM_ERASERS / 2) - 1)))) * button_scale;
9705 
9706               x = ((i % 2) * button_w) + WINDOW_WIDTH - r_ttoolopt.w + 24 * button_scale- sz / 2;
9707               y = ((j / 2) * button_h) + 40 * button_scale + 24 * button_scale - sz / 2 + off_y;
9708 
9709               for (yy = 0; yy <= sz; yy++)
9710                 {
9711                   for (xx = 0; xx <= sz; xx++)
9712                     {
9713                       n = (xx * xx) + (yy * yy) - ((sz / 2) * (sz / 2));
9714 
9715                       if (n >= -sz && n <= sz)
9716                         {
9717                           putpixel(screen, (x + sz / 2) + xx, (y + sz / 2) + yy, SDL_MapRGB(screen->format, 0, 0, 0));
9718 
9719                           putpixel(screen, (x + sz / 2) - xx, (y + sz / 2) + yy, SDL_MapRGB(screen->format, 0, 0, 0));
9720 
9721                           putpixel(screen, (x + sz / 2) + xx, (y + sz / 2) - yy, SDL_MapRGB(screen->format, 0, 0, 0));
9722 
9723                           putpixel(screen, (x + sz / 2) - xx, (y + sz / 2) - yy, SDL_MapRGB(screen->format, 0, 0, 0));
9724 
9725                         }
9726                     }
9727                 }
9728             }
9729         }
9730     }
9731 }
9732 
9733 
9734 /**
9735  * FIXME
9736  */
9737 /* Draw no selectables: */
draw_none(void)9738 static void draw_none(void)
9739 {
9740   int i;
9741   SDL_Rect dest;
9742 
9743   dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9744   dest.y = 0;
9745   SDL_BlitSurface(img_title_off, NULL, screen, &dest);
9746 
9747   for (i = 0; i < buttons_tall * gd_toolopt.cols; i++)
9748     {
9749       dest.x = ((i % 2) * button_w) + WINDOW_WIDTH - r_ttoolopt.w;
9750       dest.y = ((i / 2) * button_h) + r_ttoolopt.h;
9751 
9752       SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9753     }
9754 }
9755 
9756 /* Draw Fill sub-tools */
draw_fills(void)9757 static void draw_fills(void)
9758 {
9759   int i, j, x, y, sz;
9760   int xx, yy, n;
9761   void (*putpixel) (SDL_Surface *, int, int, Uint32);
9762   SDL_Rect dest;
9763   int most, off_y, max;
9764 
9765   draw_image_title(TITLE_FILLS, r_ttoolopt);
9766 
9767   /* Space for buttons, was 14 */
9768   most = (buttons_tall * gd_toolopt.cols) - TOOLOFFSET;
9769 
9770 
9771   /* Do we need scrollbars? */
9772 
9773   if (NUM_FILLS > most + TOOLOFFSET)
9774     {
9775       most = most - gd_toolopt.cols; /* was 12 */
9776       off_y = img_scroll_up->h;
9777       max = most + TOOLOFFSET;
9778 
9779       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9780       dest.y = r_ttoolopt.h;
9781 
9782       if (fill_scroll > 0)
9783         {
9784           SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
9785         }
9786       else
9787         {
9788           SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
9789         }
9790 
9791       dest.x = WINDOW_WIDTH - r_ttoolopt.w;
9792       dest.y = r_ttoolopt.h + img_scroll_up->h + ((most / gd_toolopt.cols + TOOLOFFSET / gd_toolopt.cols) * button_h);
9793 
9794       if (fill_scroll < NUM_FILLS - most - TOOLOFFSET)
9795         {
9796           SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
9797         }
9798       else
9799         {
9800           SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
9801         }
9802     }
9803   else
9804     {
9805       off_y = 0;
9806       max = most + TOOLOFFSET;
9807     }
9808 
9809   for (j = 0; j < most + TOOLOFFSET; j++)
9810     {
9811       i = j;
9812       dest.x = ((i % 2) * button_w) + WINDOW_WIDTH - r_ttoolopt.w;
9813       dest.y = ((i / 2) * button_h) + r_ttoolopt.h + off_y;
9814 
9815       i = j + fill_scroll;
9816 
9817       if (i == cur_fill)
9818         {
9819           SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
9820         }
9821       else if (i < NUM_FILLS)
9822         {
9823           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
9824         }
9825       else
9826         {
9827           SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
9828         }
9829 
9830       if (i < NUM_FILLS)
9831         {
9832           dest.x = ((i % 2) * button_w) + (4 * button_w) / ORIGINAL_BUTTON_SIZE + WINDOW_WIDTH - r_ttoolopt.w;
9833           dest.y = ((i / 2) * button_h) + r_ttoolopt.h + (4 * button_h) / ORIGINAL_BUTTON_SIZE + off_y;
9834 
9835           SDL_BlitSurface(img_fills[i], NULL, screen, &dest);
9836 
9837           dest.x = ((i % 2) * button_w) + (4 * button_w) / ORIGINAL_BUTTON_SIZE + WINDOW_WIDTH - r_ttoolopt.w + ((40 * button_w) / ORIGINAL_BUTTON_SIZE - img_fill_names[i]->w) / 2;
9838 	  dest.y = ((i / 2) * button_h) + r_ttoolopt.h + (4 * button_h) / ORIGINAL_BUTTON_SIZE + ((44 * button_h) / ORIGINAL_BUTTON_SIZE - img_fill_names[i]->h) + off_y;
9839 
9840           SDL_BlitSurface(img_fill_names[i], NULL, screen, &dest);
9841         }
9842     }
9843 
9844 }
9845 
9846 /**
9847  * FIXME
9848  */
9849 /* Create a thumbnail: */
thumbnail(SDL_Surface * src,int max_x,int max_y,int keep_aspect)9850 static SDL_Surface *thumbnail(SDL_Surface * src, int max_x, int max_y, int keep_aspect)
9851 {
9852   return (thumbnail2(src, max_x, max_y, keep_aspect, 1));
9853 }
9854 
9855 /**
9856  * FIXME
9857  */
thumbnail2(SDL_Surface * src,int max_x,int max_y,int keep_aspect,int keep_alpha)9858 static SDL_Surface *thumbnail2(SDL_Surface * src, int max_x, int max_y, int keep_aspect, int keep_alpha)
9859 {
9860   int x, y;
9861   float src_x, src_y, off_x, off_y;
9862   SDL_Surface *s;
9863 
9864 #ifdef GAMMA_CORRECTED_THUMBNAILS
9865   float tr, tg, tb, ta;
9866 #else
9867   Uint32 tr, tg, tb, ta;
9868 #endif
9869   Uint8 r, g, b, a;
9870   float xscale, yscale;
9871   int tmp;
9872   void (*putpixel) (SDL_Surface *, int, int, Uint32);
9873 
9874   Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[src->format->BytesPerPixel];
9875 
9876   /* Determine scale and centering offsets: */
9877 
9878   if (!keep_aspect)
9879     {
9880       yscale = (float)((float)src->h / (float)max_y);
9881       xscale = (float)((float)src->w / (float)max_x);
9882 
9883       off_x = 0;
9884       off_y = 0;
9885     }
9886   else
9887     {
9888       if (src->h > src->w)
9889         {
9890           yscale = (float)((float)src->h / (float)max_y);
9891           xscale = yscale;
9892 
9893           off_x = ((src->h - src->w) / xscale) / 2;
9894           off_y = 0;
9895         }
9896       else
9897         {
9898           xscale = (float)((float)src->w / (float)max_x);
9899           yscale = xscale;
9900 
9901           off_x = 0;
9902           off_y = ((src->w - src->h) / xscale) / 2;
9903         }
9904     }
9905 
9906 
9907 #ifndef NO_BILINEAR
9908   if (max_x > src->w && max_y > src->h)
9909     return (zoom(src, max_x, max_y));
9910 #endif
9911 
9912 
9913   /* Create surface for thumbnail: */
9914 
9915   s = SDL_CreateRGBSurface(src->flags,  /* SDL_SWSURFACE, */
9916                            max_x, max_y, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask,
9917                            src->format->Bmask, src->format->Amask);
9918 
9919 
9920   if (s == NULL)
9921     {
9922       fprintf(stderr, "\nError: Can't build stamp thumbnails\n"
9923               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
9924 
9925       cleanup();
9926       exit(1);
9927     }
9928 
9929   putpixel = putpixels[s->format->BytesPerPixel];
9930 
9931   /* Create thumbnail itself: */
9932 
9933   SDL_LockSurface(src);
9934   SDL_LockSurface(s);
9935 
9936   for (y = 0; y < max_y; y++)
9937     {
9938       for (x = 0; x < max_x; x++)
9939         {
9940 #ifndef LOW_QUALITY_THUMBNAILS
9941           tr = 0;
9942           tg = 0;
9943           tb = 0;
9944           ta = 0;
9945 
9946           tmp = 0;
9947 
9948           for (src_y = y * yscale; src_y < y * yscale + yscale && src_y < src->h; src_y++)
9949             {
9950               for (src_x = x * xscale; src_x < x * xscale + xscale && src_x < src->w; src_x++)
9951                 {
9952                   SDL_GetRGBA(getpixel(src, src_x, src_y), src->format, &r, &g, &b, &a);
9953 
9954 #ifdef GAMMA_CORRECTED_THUMBNAILS
9955                   /* per: http://www.4p8.com/eric.brasseur/gamma.html */
9956 
9957                   tr = tr + sRGB_to_linear_table[r];
9958                   tg = tg + sRGB_to_linear_table[g];
9959                   tb = tb + sRGB_to_linear_table[b];
9960 #else
9961                   tr = tr + r;
9962                   tb = tb + b;
9963                   tg = tg + g;
9964 #endif
9965                   ta = ta + a;
9966 
9967                   tmp++;
9968                 }
9969             }
9970 
9971           if (tmp != 0)
9972             {
9973               tr = tr / tmp;
9974               tb = tb / tmp;
9975               tg = tg / tmp;
9976               ta = ta / tmp;
9977 
9978 #ifdef GAMMA_CORRECTED_THUMBNAILS
9979               tr = linear_to_sRGB(tr);
9980               tg = linear_to_sRGB(tg);
9981               tb = linear_to_sRGB(tb);
9982 #endif
9983 
9984               if (keep_alpha == 0 && s->format->Amask != 0)
9985                 {
9986                   tr = ((ta * tr) / 255) + (255 - ta);
9987                   tg = ((ta * tg) / 255) + (255 - ta);
9988                   tb = ((ta * tb) / 255) + (255 - ta);
9989 
9990                   putpixel(s, x + off_x, y + off_y, SDL_MapRGBA(s->format, (Uint8) tr, (Uint8) tg, (Uint8) tb, 0xff));
9991                 }
9992               else
9993                 {
9994                   putpixel(s, x + off_x, y + off_y, SDL_MapRGBA(s->format,
9995                                                                 (Uint8) tr, (Uint8) tg, (Uint8) tb, (Uint8) ta));
9996                 }
9997             }
9998 #else
9999           src_x = x * xscale;
10000           src_y = y * yscale;
10001 
10002           putpixel(s, x + off_x, y + off_y, getpixel(src, src_x, src_y));
10003 #endif
10004         }
10005     }
10006 
10007   SDL_UnlockSurface(s);
10008   SDL_UnlockSurface(src);
10009 
10010   return s;
10011 }
10012 
10013 
10014 #ifndef NO_BILINEAR
10015 
10016 /**
10017  * FIXME
10018  */
10019 /* Based on code from: http://www.codeproject.com/cs/media/imageprocessing4.asp
10020    copyright 2002 Christian Graus */
zoom(SDL_Surface * src,int new_w,int new_h)10021 static SDL_Surface *zoom(SDL_Surface * src, int new_w, int new_h)
10022 {
10023   SDL_Surface *s;
10024   void (*putpixel) (SDL_Surface *, int, int, Uint32);
10025 
10026   Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[src->format->BytesPerPixel];
10027   float xscale, yscale;
10028   int x, y;
10029   float floor_x, ceil_x, floor_y, ceil_y, fraction_x, fraction_y, one_minus_x, one_minus_y;
10030   float n1, n2;
10031   float r1, g1, b1, a1;
10032   float r2, g2, b2, a2;
10033   float r3, g3, b3, a3;
10034   float r4, g4, b4, a4;
10035   Uint8 r, g, b, a;
10036 
10037 
10038   /* Create surface for zoom: */
10039 
10040   s = SDL_CreateRGBSurface(src->flags,  /* SDL_SWSURFACE, */
10041                            new_w, new_h, src->format->BitsPerPixel,
10042                            src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
10043 
10044 
10045   if (s == NULL)
10046     {
10047       fprintf(stderr, "\nError: Can't build zoom surface\n"
10048               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
10049 
10050       cleanup();
10051       exit(1);
10052     }
10053 
10054   putpixel = putpixels[s->format->BytesPerPixel];
10055 
10056 
10057   SDL_LockSurface(src);
10058   SDL_LockSurface(s);
10059 
10060   xscale = (float)src->w / (float)new_w;
10061   yscale = (float)src->h / (float)new_h;
10062 
10063   for (x = 0; x < new_w; x++)
10064     {
10065       for (y = 0; y < new_h; y++)
10066         {
10067           floor_x = floor((float)x * xscale);
10068           ceil_x = floor_x + 1;
10069           if (ceil_x >= src->w)
10070             ceil_x = floor_x;
10071 
10072           floor_y = floor((float)y * yscale);
10073           ceil_y = floor_y + 1;
10074           if (ceil_y >= src->h)
10075             ceil_y = floor_y;
10076 
10077           fraction_x = x * xscale - floor_x;
10078           fraction_y = y * yscale - floor_y;
10079 
10080           one_minus_x = 1.0 - fraction_x;
10081           one_minus_y = 1.0 - fraction_y;
10082 
10083 #if VIDEO_BPP==32
10084           {                     //EP added local block to avoid warning "Passing arg 3 from incompatible pointer type" of section below block
10085             Uint8 r, g, b, a;
10086 
10087             SDL_GetRGBA(getpixel(src, floor_x, floor_y), src->format, &r, &g, &b, &a);
10088             r1 = (float)r;
10089             g1 = (float)g;
10090             b1 = (float)b;
10091             a1 = (float)a;
10092             SDL_GetRGBA(getpixel(src, ceil_x, floor_y), src->format, &r, &g, &b, &a);
10093             r2 = (float)r;
10094             g2 = (float)g;
10095             b2 = (float)b;
10096             a2 = (float)a;
10097             SDL_GetRGBA(getpixel(src, floor_x, ceil_y), src->format, &r, &g, &b, &a);
10098             r3 = (float)r;
10099             g3 = (float)g;
10100             b3 = (float)b;
10101             a3 = (float)a;
10102             SDL_GetRGBA(getpixel(src, ceil_x, ceil_y), src->format, &r, &g, &b, &a);
10103             r4 = (float)r;
10104             g4 = (float)g;
10105             b4 = (float)b;
10106             a4 = (float)a;
10107           }
10108           /*
10109              SDL_GetRGBA(getpixel(src, floor_x, floor_y), src->format,
10110              &r1, &g1, &b1, &a1);
10111              SDL_GetRGBA(getpixel(src, ceil_x,  floor_y), src->format,
10112              &r2, &g2, &b2, &a2);
10113              SDL_GetRGBA(getpixel(src, floor_x, ceil_y),  src->format,
10114              &r3, &g3, &b3, &a3);
10115              SDL_GetRGBA(getpixel(src, ceil_x,  ceil_y),  src->format,
10116              &r4, &g4, &b4, &a4);
10117            */
10118 #else
10119           {
10120             Uint8 r, g, b, a;
10121 
10122             r = g = b = a = 0;  /* Unused, bah! */
10123 
10124             SDL_GetRGBA(getpixel(src, floor_x, floor_y), src->format, &r, &g, &b, &a);
10125             r1 = (float)r;
10126             g1 = (float)g;
10127             b1 = (float)b;
10128             a1 = (float)a;
10129 
10130             SDL_GetRGBA(getpixel(src, ceil_x, floor_y), src->format, &r, &g, &b, &a);
10131             r2 = (float)r;
10132             g2 = (float)g;
10133             b2 = (float)b;
10134             a2 = (float)a;
10135 
10136             SDL_GetRGBA(getpixel(src, floor_x, ceil_y), src->format, &r, &g, &b, &a);
10137             r3 = (float)r;
10138             g3 = (float)g;
10139             b3 = (float)b;
10140             a3 = (float)a;
10141 
10142             SDL_GetRGBA(getpixel(src, ceil_x, ceil_y), src->format, &r, &g, &b, &a);
10143             r4 = (float)r;
10144             g4 = (float)g;
10145             b4 = (float)b;
10146             a4 = (float)a;
10147           }
10148 #endif
10149 
10150           n1 = (one_minus_x * r1 + fraction_x * r2);
10151           n2 = (one_minus_x * r3 + fraction_x * r4);
10152           r = (one_minus_y * n1 + fraction_y * n2);
10153 
10154           n1 = (one_minus_x * g1 + fraction_x * g2);
10155           n2 = (one_minus_x * g3 + fraction_x * g4);
10156           g = (one_minus_y * n1 + fraction_y * n2);
10157 
10158           n1 = (one_minus_x * b1 + fraction_x * b2);
10159           n2 = (one_minus_x * b3 + fraction_x * b4);
10160           b = (one_minus_y * n1 + fraction_y * n2);
10161 
10162           n1 = (one_minus_x * a1 + fraction_x * a2);
10163           n2 = (one_minus_x * a3 + fraction_x * a4);
10164           a = (one_minus_y * n1 + fraction_y * n2);
10165 
10166           putpixel(s, x, y, SDL_MapRGBA(s->format, r, g, b, a));
10167         }
10168     }
10169 
10170   SDL_UnlockSurface(s);
10171   SDL_UnlockSurface(src);
10172 
10173   return s;
10174 
10175 }
10176 #endif
10177 
10178 
10179 /* XOR must show up on black, white, 0x7f grey, and 0x80 grey.
10180   XOR must be exactly 100% perfectly reversable. */
_xorpixel(SDL_Surface * surf,int x,int y)10181 static void _xorpixel(SDL_Surface * surf, int x, int y)
10182 {
10183   Uint8 *p;
10184   int BytesPerPixel;
10185 
10186   /* if outside the canvas, return */
10187   if (x < 0 || x >= surf->w || y < 0 || y >= surf->h)
10188     return;
10189 
10190   /* Always 4, except 3 when loading a saved image. */
10191   BytesPerPixel = surf->format->BytesPerPixel;
10192 
10193   /* Set a pointer to the exact location in memory of the pixel */
10194   p = (Uint8 *) (((Uint8 *) surf->pixels) +   /* Start: beginning of RAM */
10195                  (y * surf->pitch) +  /* Go down Y lines */
10196                  (x * BytesPerPixel));  /* Go in X pixels */
10197 
10198   /* XOR the (correctly-sized) piece of data in the surface's RAM */
10199   if (likely(BytesPerPixel == 4))
10200     *(Uint32 *) p ^= 0x80808080u;       /* 32-bit display */
10201   else if (BytesPerPixel == 1)
10202     *p ^= 0x80;
10203   else if (BytesPerPixel == 2)
10204     *(Uint16 *) p ^= 0xd6d6;
10205   else if (BytesPerPixel == 3)
10206     {
10207       p[0] ^= 0x80;
10208       p[1] ^= 0x80;
10209       p[2] ^= 0x80;
10210     }
10211 }
10212 
10213 /**
10214  * FIXME
10215  */
xorpixel(int x,int y)10216 static void xorpixel(int x, int y) {
10217   /* if outside the canvas, return */
10218   if ((unsigned)x >= (unsigned)canvas->w || (unsigned)y >= (unsigned)canvas->h)
10219     return;
10220 
10221   /* now switch to screen coordinates */
10222   x += r_canvas.x;
10223   y += r_canvas.y;
10224 
10225   _xorpixel(screen, x, y);
10226 }
10227 
10228 
10229 /**
10230  * FIXME
10231  */
10232 /* Undo! */
do_undo(void)10233 static void do_undo(void)
10234 {
10235   int wanna_update_toolbar;
10236 
10237   wanna_update_toolbar = 0;
10238 
10239   if (cur_undo != oldest_undo)
10240     {
10241       cur_undo--;
10242 
10243       do_undo_label_node();
10244 
10245       if (cur_undo < 0)
10246         cur_undo = NUM_UNDO_BUFS - 1;
10247 
10248 #ifdef DEBUG
10249       printf("BLITTING: %d\n", cur_undo);
10250 #endif
10251       SDL_BlitSurface(undo_bufs[cur_undo], NULL, canvas, NULL);
10252 
10253 
10254       if (img_starter != NULL)
10255         {
10256           if (undo_starters[cur_undo] == UNDO_STARTER_MIRRORED)
10257             {
10258               starter_mirrored = !starter_mirrored;
10259               mirror_starter();
10260             }
10261           else if (undo_starters[cur_undo] == UNDO_STARTER_FLIPPED)
10262             {
10263               starter_flipped = !starter_flipped;
10264               flip_starter();
10265             }
10266         }
10267 
10268       update_canvas(0, 0, (WINDOW_WIDTH - r_ttoolopt.w), (button_h * 7) + 40 + HEIGHTOFFSET);
10269 
10270 
10271       if (cur_undo == oldest_undo)
10272         {
10273           tool_avail[TOOL_UNDO] = 0;
10274           wanna_update_toolbar = 1;
10275         }
10276 
10277       if (tool_avail[TOOL_REDO] == 0)
10278         {
10279           tool_avail[TOOL_REDO] = 1;
10280           wanna_update_toolbar = 1;
10281         }
10282 
10283       if (wanna_update_toolbar)
10284         {
10285           draw_toolbar();
10286           update_screen_rect(&r_tools);
10287         }
10288 
10289       been_saved = 0;
10290     }
10291 
10292 #ifdef DEBUG
10293   printf("UNDO: Current=%d  Oldest=%d  Newest=%d\n", cur_undo, oldest_undo, newest_undo);
10294 #endif
10295 }
10296 
10297 
10298 /**
10299  * FIXME
10300  */
10301 /* Redo! */
do_redo(void)10302 static void do_redo(void)
10303 {
10304   if (cur_undo != newest_undo)
10305     {
10306       if (img_starter != NULL)
10307         {
10308           if (undo_starters[cur_undo] == UNDO_STARTER_MIRRORED)
10309             {
10310               starter_mirrored = !starter_mirrored;
10311               mirror_starter();
10312             }
10313           else if (undo_starters[cur_undo] == UNDO_STARTER_FLIPPED)
10314             {
10315               starter_flipped = !starter_flipped;
10316               flip_starter();
10317             }
10318         }
10319 
10320       cur_undo = (cur_undo + 1) % NUM_UNDO_BUFS;
10321 
10322 #ifdef DEBUG
10323       printf("BLITTING: %d\n", cur_undo);
10324 #endif
10325       do_redo_label_node();
10326       SDL_BlitSurface(undo_bufs[cur_undo], NULL, canvas, NULL);
10327 
10328       update_canvas(0, 0, (WINDOW_WIDTH - r_ttoolopt.w), (button_h * 7) + 40 + HEIGHTOFFSET);
10329 
10330       been_saved = 0;
10331     }
10332 
10333 #ifdef DEBUG
10334   printf("REDO: Current=%d  Oldest=%d  Newest=%d\n", cur_undo, oldest_undo, newest_undo);
10335 #endif
10336 
10337 
10338   if (((cur_undo + 1) % NUM_UNDO_BUFS) == newest_undo)
10339     {
10340       tool_avail[TOOL_REDO] = 0;
10341     }
10342 
10343   tool_avail[TOOL_UNDO] = 1;
10344 
10345   draw_toolbar();
10346   update_screen_rect(&r_tools);
10347 }
10348 
10349 
10350 /**
10351  * FIXME
10352  */
10353 /* Create the current brush in the current color: */
render_brush(void)10354 static void render_brush(void)
10355 {
10356   Uint32 amask;
10357   int x, y;
10358   Uint8 r, g, b, a;
10359 
10360   Uint32(*getpixel_brush) (SDL_Surface *, int, int) = getpixels[img_brushes[cur_brush]->format->BytesPerPixel];
10361   void (*putpixel_brush) (SDL_Surface *, int, int, Uint32) = putpixels[img_brushes[cur_brush]->format->BytesPerPixel];
10362 
10363 
10364   /* Kludge; not sure why cur_brush would become greater! */
10365 
10366   if (cur_brush >= num_brushes)
10367     cur_brush = 0;
10368 
10369 
10370   /* Free the old rendered brush (if any): */
10371 
10372   if (img_cur_brush != NULL)
10373     {
10374       SDL_FreeSurface(img_cur_brush);
10375     }
10376 
10377 
10378   /* Create a surface to render into: */
10379 
10380   amask = ~(img_brushes[cur_brush]->format->Rmask |
10381             img_brushes[cur_brush]->format->Gmask | img_brushes[cur_brush]->format->Bmask);
10382 
10383   img_cur_brush =
10384     SDL_CreateRGBSurface(SDL_SWSURFACE,
10385                          img_brushes[cur_brush]->w,
10386                          img_brushes[cur_brush]->h,
10387                          img_brushes[cur_brush]->format->BitsPerPixel,
10388                          img_brushes[cur_brush]->format->Rmask,
10389                          img_brushes[cur_brush]->format->Gmask, img_brushes[cur_brush]->format->Bmask, amask);
10390 
10391   if (img_cur_brush == NULL)
10392     {
10393       fprintf(stderr, "\nError: Can't render a brush!\n"
10394               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
10395 
10396       cleanup();
10397       exit(1);
10398     }
10399 
10400 
10401   /* Render the new brush: */
10402 
10403   SDL_LockSurface(img_brushes[cur_brush]);
10404   SDL_LockSurface(img_cur_brush);
10405 
10406   for (y = 0; y < img_brushes[cur_brush]->h; y++)
10407     {
10408       for (x = 0; x < img_brushes[cur_brush]->w; x++)
10409         {
10410           SDL_GetRGBA(getpixel_brush(img_brushes[cur_brush], x, y), img_brushes[cur_brush]->format, &r, &g, &b, &a);
10411 
10412           if (r == g && g == b)
10413             {
10414               putpixel_brush(img_cur_brush, x, y,
10415                              SDL_MapRGBA(img_cur_brush->format,
10416                                          color_hexes[cur_color][0],
10417                                          color_hexes[cur_color][1], color_hexes[cur_color][2], a));
10418             }
10419           else
10420             {
10421               putpixel_brush(img_cur_brush, x, y,
10422                              SDL_MapRGBA(img_cur_brush->format,
10423                                          (r + color_hexes[cur_color][0]) >> 1,
10424                                          (g + color_hexes[cur_color][1]) >> 1,
10425                                          (b + color_hexes[cur_color][2]) >> 1, a));
10426             }
10427         }
10428     }
10429 
10430   SDL_UnlockSurface(img_cur_brush);
10431   SDL_UnlockSurface(img_brushes[cur_brush]);
10432 
10433   img_cur_brush_frame_w = img_cur_brush->w / abs(brushes_frames[cur_brush]);
10434   img_cur_brush_w = img_cur_brush_frame_w / (brushes_directional[cur_brush] ? 3 : 1);
10435   img_cur_brush_h = img_cur_brush->h / (brushes_directional[cur_brush] ? 3 : 1);
10436   img_cur_brush_frames = brushes_frames[cur_brush];
10437   img_cur_brush_directional = brushes_directional[cur_brush];
10438   img_cur_brush_spacing = brushes_spacing[cur_brush];
10439 
10440   brush_counter = 0;
10441 }
10442 
10443 
10444 /**
10445  * FIXME
10446  */
10447 /* Draw a XOR line: */
line_xor(int x1,int y1,int x2,int y2)10448 static void line_xor(int x1, int y1, int x2, int y2)
10449 {
10450   int dx, dy, y, num_drawn;
10451   float m, b;
10452 
10453 
10454   /* Kludgey, but it works: */
10455 
10456   /* SDL_LockSurface(screen); */
10457 
10458   dx = x2 - x1;
10459   dy = y2 - y1;
10460 
10461   num_drawn = 0;
10462 
10463   if (dx != 0)
10464     {
10465       m = ((float)dy) / ((float)dx);
10466       b = y1 - m * x1;
10467 
10468       if (x2 >= x1)
10469         dx = 1;
10470       else
10471         dx = -1;
10472 
10473 
10474       while (x1 != x2)
10475         {
10476           y1 = m * x1 + b;
10477           y2 = m * (x1 + dx) + b;
10478 
10479           if (y1 > y2)
10480             {
10481               y = y1;
10482               y1 = y2;
10483               y2 = y;
10484             }
10485 
10486           for (y = y1; y <= y2; y++)
10487             {
10488               num_drawn++;
10489               if (num_drawn < 10 || dont_do_xor == 0)
10490                 xorpixel(x1, y);
10491             }
10492 
10493           x1 = x1 + dx;
10494         }
10495     }
10496   else
10497     {
10498       if (y1 > y2)
10499         {
10500           for (y = y1; y >= y2; y--)
10501             {
10502               num_drawn++;
10503 
10504               if (num_drawn < 10 || dont_do_xor == 0)
10505                 xorpixel(x1, y);
10506             }
10507         }
10508       else
10509         {
10510           for (y = y1; y <= y2; y++)
10511             {
10512               num_drawn++;
10513 
10514               if (num_drawn < 10 || dont_do_xor == 0)
10515                 xorpixel(x1, y);
10516             }
10517         }
10518     }
10519 
10520   /* SDL_UnlockSurface(screen); */
10521 }
10522 
10523 
10524 /**
10525  * FIXME
10526  */
10527 /* Draw a XOR rectangle: */
rect_xor(int x1,int y1,int x2,int y2)10528 static void rect_xor(int x1, int y1, int x2, int y2)
10529 {
10530   if (x1 < 0)
10531     x1 = 0;
10532 
10533   if (x2 < 0)
10534     x2 = 0;
10535 
10536   if (y1 < 0)
10537     y1 = 0;
10538 
10539   if (y2 < 0)
10540     y2 = 0;
10541 
10542   if (x1 >= (WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w))
10543     x1 = (WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w) - 1;
10544 
10545   if (x2 >= (WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w))
10546     x2 = (WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w) - 1;
10547 
10548   if (y1 >= (button_h * 7) + 40 + HEIGHTOFFSET)
10549     y1 = (button_h * 7) + 40 + HEIGHTOFFSET - 1;
10550 
10551   if (y2 >= (button_h * 7) + 40 + HEIGHTOFFSET)
10552     y2 = (button_h * 7) + 40 + HEIGHTOFFSET - 1;
10553 
10554   line_xor(x1, y1, x2, y1);
10555   line_xor(x2, y1, x2, y2);
10556   line_xor(x2, y2, x1, y2);
10557   line_xor(x1, y2, x1, y1);
10558 }
10559 
10560 
calc_eraser_size(int which_eraser)10561 static int calc_eraser_size(int which_eraser)
10562 {
10563 #define NUM_SIZES (NUM_ERASERS / 2)
10564   if (which_eraser >= NUM_SIZES)
10565     which_eraser -= NUM_SIZES;
10566 
10567   return(((NUM_SIZES - 1 - which_eraser) * ((ERASER_MAX - ERASER_MIN) / (NUM_SIZES - 1))) + ERASER_MIN);
10568 }
10569 
10570 /**
10571  * FIXME
10572  */
10573 /* Erase at the cursor! */
do_eraser(int x,int y,int update)10574 static void do_eraser(int x, int y, int update)
10575 {
10576   SDL_Rect dest;
10577   int sz;
10578   int xx, yy, n, hit;
10579 
10580   sz = calc_eraser_size(cur_eraser);
10581 
10582   if (cur_eraser < NUM_SIZES)
10583     {
10584       /* Square eraser: */
10585 
10586       dest.x = x - (sz / 2);
10587       dest.y = y - (sz / 2);
10588       dest.w = sz;
10589       dest.h = sz;
10590 
10591       if (img_starter_bkgd == NULL)
10592         SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, canvas_color_r, canvas_color_g, canvas_color_b));
10593       else
10594         SDL_BlitSurface(img_starter_bkgd, &dest, canvas, &dest);
10595     }
10596   else
10597     {
10598       /* Round eraser: */
10599 
10600       for (yy = 0; yy <= sz; yy++)
10601         {
10602           hit = 0;
10603           for (xx = 0; xx <= sz && hit == 0; xx++)
10604             {
10605               n = (xx * xx) + (yy * yy) - ((sz * sz) / 4);
10606 
10607               if (n >= -sz && n <= sz)
10608                 hit = 1;
10609 
10610               if (hit)
10611                 {
10612                   dest.x = x - xx;
10613                   dest.y = y - yy;
10614                   dest.w = xx * 2;
10615                   dest.h = 1;
10616 
10617                   if (img_starter_bkgd == NULL)
10618                     SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format,
10619                                                            canvas_color_r, canvas_color_g, canvas_color_b));
10620                   else
10621                     SDL_BlitSurface(img_starter_bkgd, &dest, canvas, &dest);
10622 
10623 
10624                   dest.x = x - xx;
10625                   dest.y = y + yy;
10626                   dest.w = xx * 2;
10627                   dest.h = 1;
10628 
10629                   if (img_starter_bkgd == NULL)
10630                     SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format,
10631                                                            canvas_color_r, canvas_color_g, canvas_color_b));
10632                   else
10633                     SDL_BlitSurface(img_starter_bkgd, &dest, canvas, &dest);
10634                 }
10635             }
10636         }
10637     }
10638 
10639 
10640 #ifndef NOSOUND
10641   if (!mute && use_sound)
10642     {
10643       if (!Mix_Playing(0))
10644         {
10645           eraser_sound = (eraser_sound + 1) % 2;
10646 
10647           playsound(screen, 0, SND_ERASER1 + eraser_sound, 0, x, SNDDIST_NEAR);
10648         }
10649     }
10650 #endif
10651 
10652   if (update)
10653     {
10654       update_canvas(x - sz / 2, y - sz / 2, x + sz / 2, y + sz / 2);
10655 
10656       rect_xor(x - sz / 2, y - sz / 2, x + sz / 2, y + sz / 2);
10657 
10658 #ifdef __APPLE__
10659       /* Prevent ghosted eraser outlines from remaining on the screen in windowed mode */
10660       update_screen(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
10661 #endif
10662     }
10663 }
10664 
10665 /**
10666  * Draw a line on the canvas using the eraser.
10667  *
10668  * @param x1 Starting X coordinate
10669  * @param y1 Starting Y coordinate
10670  * @param x2 Ending X coordinate
10671  * @param y2 Ending Y coordinate
10672  */
eraser_draw(int x1,int y1,int x2,int y2)10673 static void eraser_draw(int x1, int y1, int x2, int y2)
10674 {
10675   int dx, dy, y;
10676   int orig_x1, orig_y1, orig_x2, orig_y2, tmp, length;
10677   float m, b;
10678 
10679   orig_x1 = x1;
10680   orig_y1 = y1;
10681 
10682   orig_x2 = x2;
10683   orig_y2 = y2;
10684 
10685 
10686   dx = x2 - x1;
10687   dy = y2 - y1;
10688 
10689   if (dx != 0)
10690     {
10691       m = ((float)dy) / ((float)dx);
10692       b = y1 - m * x1;
10693 
10694       if (x2 >= x1)
10695         dx = 1;
10696       else
10697         dx = -1;
10698 
10699 
10700       while (x1 != x2)
10701         {
10702           y1 = m * x1 + b;
10703           y2 = m * (x1 + dx) + b;
10704 
10705           if (y1 > y2)
10706             {
10707               for (y = y1; y >= y2; y--)
10708                 do_eraser(x1, y, 0);
10709             }
10710           else
10711             {
10712               for (y = y1; y <= y2; y++)
10713                 do_eraser(x1, y, 0);
10714             }
10715 
10716           x1 = x1 + dx;
10717         }
10718     }
10719   else
10720     {
10721       if (y1 > y2)
10722         {
10723           y = y1;
10724           y1 = y2;
10725           y2 = y;
10726         }
10727 
10728       for (y = y1; y <= y2; y++)
10729         do_eraser(x1, y, 0);
10730     }
10731 
10732 
10733   if (orig_x1 > orig_x2)
10734     {
10735       tmp = orig_x1;
10736       orig_x1 = orig_x2;
10737       orig_x2 = tmp;
10738     }
10739 
10740   if (orig_y1 > orig_y2)
10741     {
10742       tmp = orig_y1;
10743       orig_y1 = orig_y2;
10744       orig_y2 = tmp;
10745     }
10746 
10747   length = (calc_eraser_size(cur_eraser) >> 1) + 1;
10748   update_canvas(orig_x1 - length, orig_y1 - length, orig_x2 + length, orig_y2 + length);
10749 }
10750 
10751 /**
10752  * FIXME
10753  */
10754 /* Reset available tools (for new image / starting out): */
reset_avail_tools(void)10755 static void reset_avail_tools(void)
10756 {
10757   int i;
10758   int disallow_print = disable_print;   /* set to 1 later if printer unavail */
10759 
10760   for (i = 0; i < NUM_TOOLS; i++)
10761     {
10762       tool_avail[i] = 1;
10763     }
10764 
10765 
10766   /* Unavailable at the beginning of a new canvas: */
10767 
10768   tool_avail[TOOL_UNDO] = 0;
10769   tool_avail[TOOL_REDO] = 0;
10770 
10771   if (been_saved)
10772     tool_avail[TOOL_SAVE] = 0;
10773 
10774 
10775   /* Unavailable in rare circumstances: */
10776 
10777   if (num_stamps[0] == 0)
10778     tool_avail[TOOL_STAMP] = 0;
10779 
10780   if (num_magics == 0)
10781     tool_avail[TOOL_MAGIC] = 0;
10782 
10783 
10784   /* Disable quit? */
10785 
10786   if (disable_quit)
10787     tool_avail[TOOL_QUIT] = 0;
10788 
10789 
10790   /* Disable Label? */
10791 
10792   if (disable_label)
10793     tool_avail[TOOL_LABEL] = 0;
10794 
10795 
10796   /* Disable save? */
10797 
10798   if (disable_save)
10799     tool_avail[TOOL_SAVE] = 0;
10800 
10801 
10802 #ifdef WIN32
10803   if (!IsPrinterAvailable())
10804     disallow_print = 1;
10805 #endif
10806 
10807 #if defined __BEOS__
10808   if (!IsPrinterAvailable())
10809     disallow_print = disable_print = 1;
10810 #endif
10811 
10812 
10813   /* Disable print? */
10814 
10815   if (disallow_print)
10816     tool_avail[TOOL_PRINT] = 0;
10817 
10818 #ifdef NOKIA_770
10819   /* There is no way for the user to enter text, so just disable this. */
10820   /* FIXME: Some Maemo devices have built-in keyboards now, don't they!? -bjk 2009.10.09 */
10821   tool_avail[TOOL_TEXT] = 0;
10822   tool_avail[TOOL_LABEL] = 0;
10823 #endif
10824 }
10825 
10826 
10827 /**
10828  * FIXME
10829  */
10830 /* Save and disable available tools (for Open-Dialog) */
disable_avail_tools(void)10831 static void disable_avail_tools(void)
10832 {
10833   int i;
10834 
10835   hide_blinking_cursor();
10836   for (i = 0; i < NUM_TOOLS; i++)
10837     {
10838       tool_avail_bak[i] = tool_avail[i];
10839       tool_avail[i] = 0;
10840     }
10841 }
10842 
10843 /**
10844  * FIXME
10845  */
10846 /* Restore and enable available tools (for End-Of-Open-Dialog) */
enable_avail_tools(void)10847 static void enable_avail_tools(void)
10848 {
10849   int i;
10850 
10851   for (i = 0; i < NUM_TOOLS; i++)
10852     {
10853       tool_avail[i] = tool_avail_bak[i];
10854     }
10855 }
10856 
10857 
10858 /**
10859  * FIXME
10860  */
10861 /* For qsort() call in do_open()... */
compare_dirent2s(struct dirent2 * f1,struct dirent2 * f2)10862 static int compare_dirent2s(struct dirent2 *f1, struct dirent2 *f2)
10863 {
10864 #ifdef DEBUG
10865   printf("compare_dirents: %s\t%s\n", f1->f.d_name, f2->f.d_name);
10866 #endif
10867 
10868   if (f1->place == f2->place)
10869     return (strcmp(f1->f.d_name, f2->f.d_name));
10870   else
10871     return (f1->place - f2->place);
10872 }
10873 
10874 
10875 /**
10876  * FIXME
10877  */
10878 /* Draw tux's text on the screen: */
draw_tux_text(int which_tux,const char * const str,int want_right_to_left)10879 static void draw_tux_text(int which_tux, const char *const str, int want_right_to_left)
10880 {
10881   draw_tux_text_ex(which_tux, str, want_right_to_left, 0);
10882 }
10883 
10884 static int latest_tux;
10885 static const char *latest_tux_text;
10886 static int latest_r2l;
10887 static Uint8 latest_locale_text;
10888 
10889 /**
10890  * FIXME
10891  */
redraw_tux_text(void)10892 static void redraw_tux_text(void)
10893 {
10894   draw_tux_text_ex(latest_tux, latest_tux_text, latest_r2l, latest_locale_text);
10895 }
10896 
10897 /**
10898  * FIXME
10899  */
draw_tux_text_ex(int which_tux,const char * const str,int want_right_to_left,Uint8 locale_text)10900 static void draw_tux_text_ex(int which_tux, const char *const str, int want_right_to_left, Uint8 locale_text)
10901 {
10902   SDL_Rect dest;
10903   SDL_Color black = { 0, 0, 0, 0 };
10904   int w;
10905   SDL_Surface *btn;
10906 
10907   latest_tux = which_tux;
10908   latest_tux_text = str;
10909   latest_r2l = want_right_to_left;
10910   latest_locale_text = locale_text;
10911 
10912 
10913   /* Remove any text-changing timer if one is running: */
10914   control_drawtext_timer(0, "", 0);
10915 
10916   /* Clear first: */
10917   SDL_FillRect(screen, &r_tuxarea, SDL_MapRGB(screen->format, 255, 255, 255));
10918 
10919   /* Draw tux: */
10920   dest.x = r_tuxarea.x;
10921   dest.y = r_tuxarea.y + r_tuxarea.h - img_tux[which_tux]->h;
10922 
10923   /* if he's too tall to fit, go off the bottom (not top) edge */
10924   if (dest.y < r_tuxarea.y)
10925     dest.y = r_tuxarea.y;
10926 
10927   /* Don't let sfx and speak buttons cover the top of Tux, either: */
10928   if (cur_tool == TOOL_STAMP && use_sound && !mute)
10929     {
10930       if (dest.y < r_sfx.y + r_sfx.h)
10931         dest.y = r_sfx.y + r_sfx.h;
10932     }
10933 
10934   SDL_BlitSurface(img_tux[which_tux], NULL, screen, &dest);
10935 
10936   /* Wide enough for Tux, or two stamp sound buttons (whichever's wider) */
10937   w = max(img_tux[which_tux]->w, img_btnsm_up->w * 2) + 5;
10938 
10939   wordwrap_text_ex(str, black, w, r_tuxarea.y, r_tuxarea.w, want_right_to_left, locale_text);
10940 
10941 
10942   /* Draw 'sound effect' and 'speak' buttons, if we're in the Stamp tool */
10943 
10944   if (cur_tool == TOOL_STAMP && use_sound && !mute)
10945     {
10946       /* Sound effect: */
10947 
10948       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->no_sound)
10949         btn = img_btnsm_off;
10950       else
10951         btn = img_btnsm_up;
10952 
10953       dest.x = 0;
10954       dest.y = r_tuxarea.y;
10955 
10956       SDL_BlitSurface(btn, NULL, screen, &dest);
10957 
10958       dest.x = (img_btnsm_up->w - img_sfx->w) / 2;
10959       dest.y = r_tuxarea.y + ((img_btnsm_up->h - img_sfx->h) / 2);
10960 
10961       SDL_BlitSurface(img_sfx, NULL, screen, &dest);
10962 
10963 
10964       /* Speak: */
10965 
10966       if (stamp_data[stamp_group][cur_stamp[stamp_group]]->no_descsound)
10967         btn = img_btnsm_off;
10968       else
10969         btn = img_btnsm_up;
10970 
10971       dest.x = img_btnsm_up->w;
10972       dest.y = r_tuxarea.y;
10973 
10974       SDL_BlitSurface(btn, NULL, screen, &dest);
10975 
10976       dest.x = img_btnsm_up->w + ((img_btnsm_up->w - img_speak->w) / 2);
10977       dest.y = r_tuxarea.y + ((img_btnsm_up->h - img_speak->h) / 2);
10978 
10979       SDL_BlitSurface(img_speak, NULL, screen, &dest);
10980     }
10981 
10982   update_screen_rect(&r_tuxarea);
10983 }
10984 
10985 
10986 /**
10987  * FIXME
10988  */
wordwrap_text(const char * const str,SDL_Color color,int left,int top,int right,int want_right_to_left)10989 static void wordwrap_text(const char *const str, SDL_Color color, int left, int top, int right, int want_right_to_left)
10990 {
10991   wordwrap_text_ex(str, color, left, top, right, want_right_to_left, 0);
10992 }
10993 
10994 /**
10995  * FIXME
10996  */
wordwrap_text_ex(const char * const str,SDL_Color color,int left,int top,int right,int want_right_to_left,Uint8 locale_text)10997 static void wordwrap_text_ex(const char *const str, SDL_Color color,
10998                              int left, int top, int right, int want_right_to_left, Uint8 locale_text)
10999 {
11000   SDL_Surface *text;
11001   TuxPaint_Font *myfont = medium_font;
11002   SDL_Rect dest;
11003 
11004 #ifdef NO_SDLPANGO
11005   int len;
11006   int x, y, j;
11007   unsigned int i;
11008   char substr[512];
11009   unsigned char *locale_str;
11010   char *tstr;
11011   unsigned char utf8_char[5];
11012   SDL_Rect src;
11013   int utf8_str_len, last_text_height;
11014   unsigned char utf8_str[512];
11015 #else
11016   SDLPango_Matrix pango_color;
11017 #endif
11018 
11019 
11020   if (str == NULL || str[0] == '\0')
11021     return;                     /* No-op! */
11022 
11023   if (need_own_font && (strcmp(gettext(str), str) || locale_text))
11024     myfont = locale_font;
11025 
11026   if (strcmp(str, gettext(str)) == 0)
11027     {
11028       /* String isn't translated!  Don't write right-to-left, even if our locale is an RTOL language: */
11029       want_right_to_left = 0;
11030     }
11031 
11032 
11033 #ifndef NO_SDLPANGO
11034   /* Letting SDL_Pango do all this stuff! */
11035 
11036   sdl_color_to_pango_color(color, &pango_color);
11037 
11038   SDLPango_SetDefaultColor(myfont->pango_context, &pango_color);
11039   SDLPango_SetMinimumSize(myfont->pango_context, right - left, canvas->h - top);
11040   if (want_right_to_left && need_right_to_left)
11041     {
11042       SDLPango_SetBaseDirection(locale_font->pango_context, SDLPANGO_DIRECTION_RTL);
11043       if (only_uppercase)
11044         {
11045           char *upper_str = uppercase(gettext(str));
11046 
11047           SDLPango_SetText_GivenAlignment(myfont->pango_context, upper_str, -1, SDLPANGO_ALIGN_RIGHT);
11048           free(upper_str);
11049         }
11050       else
11051         SDLPango_SetText_GivenAlignment(myfont->pango_context, gettext(str), -1, SDLPANGO_ALIGN_RIGHT);
11052     }
11053   else
11054     {
11055       SDLPango_SetBaseDirection(locale_font->pango_context, SDLPANGO_DIRECTION_LTR);
11056       if (only_uppercase)
11057         {
11058           char *upper_str = uppercase(gettext(str));
11059 
11060           SDLPango_SetText_GivenAlignment(myfont->pango_context, upper_str, -1, SDLPANGO_ALIGN_LEFT);
11061           free(upper_str);
11062         }
11063       else
11064         SDLPango_SetText_GivenAlignment(myfont->pango_context, gettext(str), -1, SDLPANGO_ALIGN_LEFT);
11065     }
11066 
11067   text = SDLPango_CreateSurfaceDraw(myfont->pango_context);
11068 
11069   dest.x = left;
11070   dest.y = top;
11071   if (text != NULL)
11072     {
11073       SDL_BlitSurface(text, NULL, screen, &dest);
11074       SDL_FreeSurface(text);
11075     }
11076 #else
11077 
11078   /* Cursor starting position: */
11079 
11080   x = left;
11081   y = top;
11082 
11083   last_text_height = 0;
11084 
11085   debug(str);
11086   debug(gettext(str));
11087   debug("...");
11088 
11089   if (strcmp(str, "") != 0)
11090     {
11091       if (want_right_to_left == 0)
11092         locale_str = (unsigned char *)strdup(gettext(str));
11093       else
11094         locale_str = (unsigned char *)textdir(gettext(str));
11095 
11096 
11097       /* For each UTF8 character: */
11098 
11099       utf8_str_len = 0;
11100       utf8_str[0] = '\0';
11101 
11102       for (i = 0; i <= strlen((char *)locale_str); i++)
11103         {
11104           if (locale_str[i] < 128)
11105             {
11106               utf8_str[utf8_str_len++] = locale_str[i];
11107               utf8_str[utf8_str_len] = '\0';
11108 
11109 
11110               /* Space?  Blit the word! (Word-wrap if necessary) */
11111 
11112               if (locale_str[i] == ' ' || locale_str[i] == '\0')
11113                 {
11114                   if (only_uppercase)
11115                     {
11116                       char *upper_utf8_str = uppercase((char *)utf8_str);
11117 
11118                       text = render_text(myfont, (char *)upper_utf8_str, color);
11119                       free(upper_utf8_str);
11120                     }
11121                   else
11122                     text = render_text(myfont, (char *)utf8_str, color);
11123 
11124                   if (!text)
11125                     continue;   /* Didn't render anything... */
11126 
11127                   /* ----------- */
11128                   if (text->w > right - left)
11129                     {
11130                       /* Move left and down (if not already at left!) */
11131 
11132                       if (x > left)
11133                         {
11134                           if (need_right_to_left && want_right_to_left)
11135                             anti_carriage_return(left, right, top, top + text->h, y + text->h, x - left);
11136 
11137                           x = left;
11138                           y = y + text->h;
11139                         }
11140 
11141 
11142                       /* Junk the blitted word; it's too long! */
11143 
11144                       last_text_height = text->h;
11145                       SDL_FreeSurface(text);
11146 
11147 
11148                       /* For each UTF8 character: */
11149 
11150                       for (j = 0; j < utf8_str_len; j++)
11151                         {
11152                           /* How many bytes does this character need? */
11153 
11154                           if (utf8_str[j] < 128)        /* 0xxx xxxx - 1 byte */
11155                             {
11156                               utf8_char[0] = utf8_str[j];
11157                               utf8_char[1] = '\0';
11158                             }
11159                           else if ((utf8_str[j] & 0xE0) == 0xC0)        /* 110x xxxx - 2 bytes */
11160                             {
11161                               utf8_char[0] = utf8_str[j];
11162                               utf8_char[1] = utf8_str[j + 1];
11163                               utf8_char[2] = '\0';
11164                               j = j + 1;
11165                             }
11166                           else if ((utf8_str[j] & 0xF0) == 0xE0)        /* 1110 xxxx - 3 bytes */
11167                             {
11168                               utf8_char[0] = utf8_str[j];
11169                               utf8_char[1] = utf8_str[j + 1];
11170                               utf8_char[2] = utf8_str[j + 2];
11171                               utf8_char[3] = '\0';
11172                               j = j + 2;
11173                             }
11174                           else  /* 1111 0xxx - 4 bytes */
11175                             {
11176                               utf8_char[0] = utf8_str[j];
11177                               utf8_char[1] = utf8_str[j + 1];
11178                               utf8_char[2] = utf8_str[j + 2];
11179                               utf8_char[3] = utf8_str[j + 3];
11180                               utf8_char[4] = '\0';
11181                               j = j + 3;
11182                             }
11183 
11184 
11185                           if (utf8_char[0] != '\0')
11186                             {
11187                               text = render_text(myfont, (char *)utf8_char, color);
11188                               if (text != NULL)
11189                                 {
11190                                   if (x + text->w > right)
11191                                     {
11192                                       if (need_right_to_left && want_right_to_left)
11193                                         anti_carriage_return(left, right, top, top + text->h, y + text->h, x - left);
11194 
11195                                       x = left;
11196                                       y = y + text->h;
11197                                     }
11198 
11199                                   dest.x = x;
11200 
11201                                   if (need_right_to_left && want_right_to_left)
11202                                     dest.y = top;
11203                                   else
11204                                     dest.y = y;
11205 
11206                                   SDL_BlitSurface(text, NULL, screen, &dest);
11207 
11208                                   last_text_height = text->h;
11209 
11210                                   x = x + text->w;
11211 
11212                                   SDL_FreeSurface(text);
11213                                 }
11214                             }
11215                         }
11216                     }
11217                   else
11218                     {
11219                       /* Not too wide for one line... */
11220 
11221                       if (x + text->w > right)
11222                         {
11223                           /* This word needs to move down? */
11224 
11225                           if (need_right_to_left && want_right_to_left)
11226                             anti_carriage_return(left, right, top, top + text->h, y + text->h, x - left);
11227 
11228                           x = left;
11229                           y = y + text->h;
11230                         }
11231 
11232                       dest.x = x;
11233 
11234                       if (need_right_to_left && want_right_to_left)
11235                         dest.y = top;
11236                       else
11237                         dest.y = y;
11238 
11239                       SDL_BlitSurface(text, NULL, screen, &dest);
11240 
11241                       last_text_height = text->h;
11242                       x = x + text->w;
11243 
11244                       SDL_FreeSurface(text);
11245                     }
11246 
11247 
11248                   utf8_str_len = 0;
11249                   utf8_str[0] = '\0';
11250                 }
11251             }
11252           else if ((locale_str[i] & 0xE0) == 0xC0)
11253             {
11254               utf8_str[utf8_str_len++] = locale_str[i];
11255               utf8_str[utf8_str_len++] = locale_str[i + 1];
11256               utf8_str[utf8_str_len] = '\0';
11257               i++;
11258             }
11259           else if ((locale_str[i] & 0xF0) == 0xE0)
11260             {
11261               utf8_str[utf8_str_len++] = locale_str[i];
11262               utf8_str[utf8_str_len++] = locale_str[i + 1];
11263               utf8_str[utf8_str_len++] = locale_str[i + 2];
11264               utf8_str[utf8_str_len] = '\0';
11265               i = i + 2;
11266             }
11267           else
11268             {
11269               utf8_str[utf8_str_len++] = locale_str[i];
11270               utf8_str[utf8_str_len++] = locale_str[i + 1];
11271               utf8_str[utf8_str_len++] = locale_str[i + 2];
11272               utf8_str[utf8_str_len++] = locale_str[i + 3];
11273               utf8_str[utf8_str_len] = '\0';
11274               i = i + 3;
11275             }
11276         }
11277 
11278       free(locale_str);
11279     }
11280   else if (strlen(str) != 0)
11281     {
11282       /* Truncate if too big! (sorry!) */
11283 
11284       {
11285         char *s1 = gettext(str);
11286 
11287         if (want_right_to_left)
11288           {
11289             char *freeme = s1;
11290 
11291             s1 = textdir(s1);
11292             free(freeme);
11293           }
11294         tstr = uppercase(s1);
11295         free(s1);
11296       }
11297 
11298       if (strlen(tstr) > sizeof(substr) - 1)
11299         tstr[sizeof(substr) - 1] = '\0';
11300 
11301 
11302       /* For each word... */
11303 
11304       for (i = 0; i < strlen(tstr); i++)
11305         {
11306           /* Figure out the word... */
11307 
11308           len = 0;
11309 
11310           for (j = i; tstr[j] != ' ' && tstr[j] != '\0'; j++)
11311             {
11312               substr[len++] = tstr[j];
11313             }
11314 
11315           substr[len++] = ' ';
11316           substr[len] = '\0';
11317 
11318 
11319           /* Render the word for display... */
11320 
11321 
11322           if (only_uppercase)
11323             {
11324               char *uppercase_substr = uppercase(substr);
11325 
11326               text = render_text(myfont, uppercase_substr, color);
11327               free(uppercase_substr);
11328             }
11329           else
11330             text = render_text(myfont, substr, color);
11331 
11332 
11333           /* If it won't fit on this line, move to the next! */
11334 
11335           if (x + text->w > right)      /* Correct? */
11336             {
11337               if (need_right_to_left && want_right_to_left)
11338                 anti_carriage_return(left, right, top, top + text->h, y + text->h, x - left);
11339 
11340               x = left;
11341               y = y + text->h;
11342             }
11343 
11344 
11345           /* Draw the word: */
11346 
11347           dest.x = x;
11348 
11349           if (need_right_to_left && want_right_to_left)
11350             dest.y = top;
11351           else
11352             dest.y = y;
11353 
11354           SDL_BlitSurface(text, NULL, screen, &dest);
11355 
11356 
11357           /* Move the cursor one word's worth: */
11358 
11359           x = x + text->w;
11360 
11361 
11362           /* Free the temp. surface: */
11363 
11364           last_text_height = text->h;
11365           SDL_FreeSurface(text);
11366 
11367 
11368           /* Now on to the next word... */
11369 
11370           i = j;
11371         }
11372 
11373       free(tstr);
11374     }
11375 
11376 
11377   /* Right-justify the final line of text, in right-to-left mode: */
11378 
11379   if (need_right_to_left && want_right_to_left && last_text_height > 0)
11380     {
11381       src.x = left;
11382       src.y = top;
11383       src.w = x - left;
11384       src.h = last_text_height;
11385 
11386       dest.x = right - src.w;
11387       dest.y = top;
11388 
11389       SDL_BlitSurface(screen, &src, screen, &dest);
11390 
11391       dest.x = left;
11392       dest.y = top;
11393       dest.w = (right - left - src.w);
11394       dest.h = last_text_height;
11395 
11396       SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
11397     }
11398 #endif
11399 }
11400 
11401 
11402 #ifndef NOSOUND
11403 
11404 /**
11405  * FIXME
11406  */
playstampdesc(int chan)11407 static void playstampdesc(int chan)
11408 {
11409   static SDL_Event playsound_event;
11410 
11411   if (chan == 2)                /* Only do this when the channel playing the stamp sfx has ended! */
11412     {
11413       debug("Stamp SFX ended. Pushing event to play description sound...");
11414 
11415       playsound_event.type = SDL_USEREVENT;
11416       playsound_event.user.code = USEREVENT_PLAYDESCSOUND;
11417       playsound_event.user.data1 = (void *)(intptr_t) cur_stamp[stamp_group];   //EP added (intptr_t) to avoid warning on x64
11418 
11419       SDL_PushEvent(&playsound_event);
11420     }
11421 }
11422 
11423 #endif
11424 
11425 
11426 /* Load a file's sound: */
11427 
11428 #ifndef NOSOUND
11429 
11430 /**
11431  * FIXME
11432  */
loadsound_extra(const char * const fname,const char * extra)11433 static Mix_Chunk *loadsound_extra(const char *const fname, const char *extra)
11434 {
11435   char *snd_fname;
11436   char tmp_str[MAX_PATH], ext[5];
11437   Mix_Chunk *tmp_snd;
11438 
11439 
11440   if (strcasestr(fname, ".png") != NULL)
11441     {
11442       strcpy(ext, ".png"); /* char[5] size is sufficient */
11443     }
11444   else
11445     {
11446       /* Sorry, we only handle images */
11447 
11448       return (NULL);
11449     }
11450 
11451   /* First, check for localized version of sound: */
11452 
11453   snd_fname = malloc(strlen(fname) + strlen(lang_prefix) + 16);
11454 
11455   strcpy(snd_fname, fname); /* malloc'd size should be sufficient */
11456   safe_snprintf(tmp_str, sizeof(tmp_str), "%s_%s.ogg", extra, lang_prefix);
11457   strcpy((char *)strcasestr(snd_fname, ext), tmp_str); /* FIXME: Use strncpy() (ugh, complicated) */
11458   debug(snd_fname);
11459   tmp_snd = Mix_LoadWAV(snd_fname);
11460 
11461   if (tmp_snd == NULL)
11462     {
11463       debug("...No local version of sound (OGG)!");
11464 
11465       strcpy(snd_fname, fname); /* malloc'd size should be sufficient */
11466       safe_snprintf(tmp_str, sizeof(tmp_str), "%s_%s.wav", extra, lang_prefix);
11467       strcpy((char *)strcasestr(snd_fname, ext), tmp_str); /* FIXME: Use strncpy() (ugh, complicated) */
11468       debug(snd_fname);
11469       tmp_snd = Mix_LoadWAV(snd_fname);
11470 
11471       if (tmp_snd == NULL)
11472         {
11473           debug("...No local version of sound (WAV)!");
11474 
11475           /* Check for non-country-code locale */
11476 
11477           strcpy(snd_fname, fname); /* malloc'd size should be sufficient */
11478           safe_snprintf(tmp_str, sizeof(tmp_str), "%s_%s.ogg", extra, short_lang_prefix);
11479           strcpy((char *)strcasestr(snd_fname, ext), tmp_str); /* FIXME: Use strncpy() (ugh, complicated) */
11480           debug(snd_fname);
11481           tmp_snd = Mix_LoadWAV(snd_fname);
11482 
11483           if (tmp_snd == NULL)
11484             {
11485               debug("...No short local version of sound (OGG)!");
11486 
11487               strcpy(snd_fname, fname); /* malloc'd size should be sufficient */
11488               safe_snprintf(tmp_str, sizeof(tmp_str), "%s_%s.wav", extra, short_lang_prefix);
11489               strcpy((char *)strcasestr(snd_fname, ext), tmp_str); /* FIXME: Use strncpy() (ugh, complicated) */
11490               debug(snd_fname);
11491               tmp_snd = Mix_LoadWAV(snd_fname);
11492 
11493               if (tmp_snd == NULL)
11494                 {
11495                   /* Now, check for default sound: */
11496 
11497                   debug("...No short local version of sound (WAV)!");
11498 
11499                   strcpy(snd_fname, fname); /* malloc'd size should be sufficient */
11500                   safe_snprintf(tmp_str, sizeof(tmp_str), "%s.ogg", extra);
11501                   strcpy((char *)strcasestr(snd_fname, ext), tmp_str); /* FIXME: Use strncpy() (ugh, complicated) */
11502                   debug(snd_fname);
11503                   tmp_snd = Mix_LoadWAV(snd_fname);
11504 
11505                   if (tmp_snd == NULL)
11506                     {
11507                       debug("...No default version of sound (OGG)!");
11508 
11509                       strcpy(snd_fname, fname); /* malloc'd size should be sufficient */
11510                       safe_snprintf(tmp_str, sizeof(tmp_str), "%s.wav", extra);
11511                       strcpy((char *)strcasestr(snd_fname, ext), tmp_str); /* FIXME: Use strncpy() (ugh, complicated) */
11512                       debug(snd_fname);
11513                       tmp_snd = Mix_LoadWAV(snd_fname);
11514 
11515                       if (tmp_snd == NULL)
11516                         debug("...No default version of sound (WAV)!");
11517                     }
11518                 }
11519             }
11520         }
11521     }
11522 
11523   free(snd_fname);
11524   return (tmp_snd);
11525 }
11526 
11527 
11528 /**
11529  * FIXME
11530  */
loadsound(const char * const fname)11531 static Mix_Chunk *loadsound(const char *const fname)
11532 {
11533   return (loadsound_extra(fname, ""));
11534 }
11535 
11536 /**
11537  * FIXME
11538  */
loaddescsound(const char * const fname)11539 static Mix_Chunk *loaddescsound(const char *const fname)
11540 {
11541   return (loadsound_extra(fname, "_desc"));
11542 }
11543 
11544 #endif
11545 
11546 
11547 /* Strip any trailing spaces: */
11548 
11549 /**
11550  * FIXME
11551  */
strip_trailing_whitespace(char * buf)11552 static void strip_trailing_whitespace(char *buf)
11553 {
11554   unsigned i = strlen(buf);
11555 
11556   while (i--)
11557     {
11558       if (!isspace(buf[i]))
11559         break;
11560       buf[i] = '\0';
11561     }
11562 }
11563 
11564 
11565 /* Load a file's description: */
11566 
11567 /**
11568  * FIXME
11569  */
loaddesc(const char * const fname,Uint8 * locale_text)11570 static char *loaddesc(const char *const fname, Uint8 * locale_text)
11571 {
11572   char *txt_fname, *extptr;
11573   char buf[512], def_buf[512];  /* doubled to 512 per TOYAMA Shin-Ichi's requested; -bjk 2007.05.10 */
11574   int found, got_first;
11575   FILE *fi;
11576   int i;
11577 
11578   txt_fname = strdup(fname);
11579   *locale_text = 0;
11580 
11581   extptr = strcasestr(txt_fname, ".png");
11582 
11583 #ifndef NOSVG
11584   if (extptr == NULL)
11585     extptr = strcasestr(txt_fname, ".svg");
11586 #endif
11587 
11588   if (extptr != NULL)
11589     {
11590       found = 0;
11591       strcpy(def_buf, ""); /* In case of total inability to find anything */
11592 
11593       /* Set the first available language */
11594       for (i = 0; i < num_wished_langs && !found; i++)
11595         {
11596           strcpy((char *)extptr, ".txt"); /* safe; pointing into a safe spot within an existing string (txt_fname) */
11597           fi = fopen(txt_fname, "r");
11598           if (!fi)
11599             return NULL;
11600 
11601           got_first = 0;
11602           strcpy(def_buf, "");
11603 
11604           do
11605             {
11606               if (fgets(buf, sizeof(buf), fi))
11607                 {
11608                   if (!feof(fi))
11609                     {
11610                       strip_trailing_whitespace(buf);
11611 
11612                       if (!got_first)
11613                         {
11614                           /* First one is the default: */
11615 
11616                           strcpy(def_buf, buf); /* safe; both the same size */
11617                           got_first = 1;
11618                         }
11619 
11620                       debug(buf);
11621 
11622 
11623                       //      lang_prefix = lang_prefixes[langint];
11624                       /* See if it's the one for this locale... */
11625 
11626                       if ((char *)strcasestr(buf, wished_langs[i].lang_prefix) == buf)
11627                         {
11628 
11629                           debug(buf + strlen(wished_langs[i].lang_prefix));
11630                           if ((char *)strcasestr(buf + strlen(wished_langs[i].lang_prefix), ".utf8=") ==
11631                               buf + strlen(wished_langs[i].lang_prefix))
11632                             {
11633                               lang_prefix = wished_langs[i].lang_prefix;
11634                               short_lang_prefix = strdup(lang_prefix);
11635                               /* When in doubt, cut off country code */
11636                               if (strchr(short_lang_prefix, '_'))
11637                                 *strchr(short_lang_prefix, '_') = '\0';
11638 
11639                               need_own_font = wished_langs[i].need_own_font;
11640                               need_right_to_left = wished_langs[i].need_right_to_left;
11641                               need_right_to_left_word = wished_langs[i].need_right_to_left_word;
11642 
11643                               found = 1;
11644 
11645                               debug("...FOUND!");
11646                             }
11647                         }
11648                     }
11649                 }
11650             }
11651           while (!feof(fi) && !found);
11652 
11653           fclose(fi);
11654 
11655         }
11656 
11657       free(txt_fname);
11658 
11659 
11660       /* Return the string: */
11661 
11662       if (found)
11663         {
11664           *locale_text = 1;
11665           return (strdup(buf + (strlen(lang_prefix)) + 6));
11666         }
11667       else
11668         {
11669           /* No locale-specific translation; use the default (English) */
11670           return (strdup(def_buf));
11671         }
11672     }
11673   else
11674     {
11675       fprintf(stderr, "Somehow, '%s' doesn't have a filename extension!?\n", fname);
11676       return NULL;
11677     }
11678 }
11679 
11680 
11681 /**
11682  * FIXME
11683  */
11684 /* Load a *.dat file */
loadinfo(const char * const fname,stamp_type * inf)11685 static double loadinfo(const char *const fname, stamp_type * inf)
11686 {
11687   char buf[256];
11688   FILE *fi;
11689   double ratio = 1.0;
11690 
11691   inf->colorable = 0;
11692   inf->tintable = 0;
11693   inf->mirrorable = 1;
11694   inf->flipable = 1;
11695   inf->tinter = TINTER_NORMAL;
11696 
11697   fi = fopen(fname, "r");
11698   if (!fi)
11699     return ratio;
11700 
11701   do
11702     {
11703       if (fgets(buf, sizeof buf, fi))
11704         {
11705           if (!feof(fi))
11706             {
11707               strip_trailing_whitespace(buf);
11708 
11709               if (strcmp(buf, "colorable") == 0)
11710                 inf->colorable = 1;
11711               else if (strcmp(buf, "tintable") == 0)
11712                 inf->tintable = 1;
11713               else if (!memcmp(buf, "scale", 5) && (isspace(buf[5]) || buf[5] == '='))
11714                 {
11715                   double tmp, tmp2;
11716                   char *cp = buf + 6;
11717 
11718                   while (isspace(*cp) || *cp == '=')
11719                     cp++;
11720                   if (strchr(cp, '%'))
11721                     {
11722                       tmp = strtod(cp, NULL) / 100.0;
11723                       if (tmp > 0.0001 && tmp < 10000.0)
11724                         ratio = tmp;
11725                     }
11726                   else if (strchr(cp, '/'))
11727                     {
11728                       tmp = strtod(cp, &cp);
11729                       while (*cp && !isdigit(*cp))
11730                         cp++;
11731                       tmp2 = strtod(cp, NULL);
11732                       if (tmp > 0.0001 && tmp < 10000.0 && tmp2 > 0.0001 && tmp2 < 10000.0
11733                           && tmp / tmp2 > 0.0001 && tmp / tmp2 < 10000.0)
11734                         ratio = tmp / tmp2;
11735                     }
11736                   else if (strchr(cp, ':'))
11737                     {
11738                       tmp = strtod(cp, &cp);
11739                       while (*cp && !isdigit(*cp))
11740                         cp++;
11741                       tmp2 = strtod(cp, NULL);
11742                       if (tmp > 0.0001 && tmp < 10000.0 &&
11743                           tmp2 > 0.0001 && tmp2 < 10000.0 && tmp2 / tmp > 0.0001 && tmp2 / tmp < 10000.0)
11744                         ratio = tmp2 / tmp;
11745                     }
11746                   else
11747                     {
11748                       tmp = strtod(cp, NULL);
11749                       if (tmp > 0.0001 && tmp < 10000.0)
11750                         ratio = 1.0 / tmp;
11751                     }
11752                 }
11753               else if (!memcmp(buf, "tinter", 6) && (isspace(buf[6]) || buf[6] == '='))
11754                 {
11755                   char *cp = buf + 7;
11756 
11757                   while (isspace(*cp) || *cp == '=')
11758                     cp++;
11759                   if (!strcmp(cp, "anyhue"))
11760                     {
11761                       inf->tinter = TINTER_ANYHUE;
11762                     }
11763                   else if (!strcmp(cp, "narrow"))
11764                     {
11765                       inf->tinter = TINTER_NARROW;
11766                     }
11767                   else if (!strcmp(cp, "normal"))
11768                     {
11769                       inf->tinter = TINTER_NORMAL;
11770                     }
11771                   else if (!strcmp(cp, "vector"))
11772                     {
11773                       inf->tinter = TINTER_VECTOR;
11774                     }
11775                   else
11776                     {
11777                       debug(cp);
11778                     }
11779                 }
11780               else if (strcmp(buf, "nomirror") == 0)
11781                 inf->mirrorable = 0;
11782               else if (strcmp(buf, "noflip") == 0)
11783                 inf->flipable = 0;
11784             }
11785         }
11786     }
11787   while (!feof(fi));
11788 
11789   fclose(fi);
11790   return ratio;
11791 }
11792 
11793 
11794 /**
11795  * FIXME
11796  */
NondefectiveBlit(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)11797 static int SDLCALL NondefectiveBlit(SDL_Surface * src, SDL_Rect * srcrect, SDL_Surface * dst, SDL_Rect * dstrect)
11798 {
11799   int dstx = 0;
11800   int dsty = 0;
11801   int srcx = 0;
11802   int srcy = 0;
11803   int srcw = src->w;
11804   int srch = src->h;
11805 
11806   Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[src->format->BytesPerPixel];
11807   void (*putpixel) (SDL_Surface *, int, int, Uint32) = putpixels[dst->format->BytesPerPixel];
11808 
11809 
11810   if (srcrect)
11811     {
11812       srcx = srcrect->x;
11813       srcy = srcrect->y;
11814       srcw = srcrect->w;
11815       srch = srcrect->h;
11816     }
11817   if (dstrect)
11818     {
11819       dstx = dstrect->x;
11820       dsty = dstrect->y;
11821     }
11822   if (dsty < 0)
11823     {
11824       srcy += -dsty;
11825       srch -= -dsty;
11826       dsty = 0;
11827     }
11828   if (dstx < 0)
11829     {
11830       srcx += -dstx;
11831       srcw -= -dstx;
11832       dstx = 0;
11833     }
11834   if (dstx + srcw > dst->w - 1)
11835     {
11836       srcw -= (dstx + srcw) - (dst->w - 1);
11837     }
11838   if (dsty + srch > dst->h - 1)
11839     {
11840       srch -= (dsty + srch) - (dst->h - 1);
11841     }
11842   if (srcw < 1 || srch < 1)
11843     return -1;                  /* no idea what to return if nothing done */
11844   srch++;                       /* "++" is a tweak to get to edges -bjk 2009.10.11 */
11845   while (srch--)
11846     {
11847       int i = srcw + 1;         /* "+ 1" is a tweak to get to edges -bjk 2009.10.11 */
11848 
11849       while (i--)
11850         {
11851           putpixel(dst, i + dstx, srch + dsty, getpixel(src, i + srcx, srch + srcy));
11852         }
11853     }
11854 
11855   return (0);
11856 }
11857 
11858 
11859 /**
11860  * Copy an image, scaling and smearing, as needed, into a new surface.
11861  * Free the original surface.
11862  *
11863  * @param SDL_Surface * src -- source surface (will be freed by this function!)
11864  * @param SDL_Surface * dst -- destination surface
11865  * @param int SDCALL(*blit) -- function for blitting; "NondefectiveBlit" or "SDL_BlitSurface"
11866  */
autoscale_copy_smear_free(SDL_Surface * src,SDL_Surface * dst,int SDLCALL (* blit)(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect))11867 static void autoscale_copy_smear_free(SDL_Surface * src, SDL_Surface * dst,
11868                                       int SDLCALL(*blit) (SDL_Surface * src,
11869                                                           SDL_Rect * srcrect, SDL_Surface * dst, SDL_Rect * dstrect))
11870 {
11871   SDL_Surface *src1;
11872   SDL_Rect dest;
11873 
11874   /* What to do when in 640x480 mode, and loading an
11875      800x600 (or larger) image!? Scale it. Starters must
11876      be scaled too. Keep the pixels square though, filling
11877      in the gaps via a smear. */
11878   if (src->w != dst->w || src->h != dst->h)
11879     {
11880       if (src->w / (float)dst->w > src->h / (float)dst->h)
11881         src1 = thumbnail(src, dst->w, src->h * dst->w / src->w, 0);
11882       else
11883         src1 = thumbnail(src, src->w * dst->h / src->h, dst->h, 0);
11884       SDL_FreeSurface(src);
11885       src = src1;
11886     }
11887 
11888   dest.x = (dst->w - src->w) / 2;
11889   dest.y = (dst->h - src->h) / 2;
11890   blit(src, NULL, dst, &dest);
11891 
11892   if (src->w != dst->w)
11893     {
11894       /* we know that the heights match, and src is narrower */
11895       SDL_Rect sour;
11896       int i = (dst->w - src->w) / 2;
11897 
11898       sour.w = 1;
11899       sour.x = 0;
11900       sour.h = src->h;
11901       sour.y = 0;
11902       while (i-- > 0)
11903         {
11904           dest.x = i;
11905           blit(src, &sour, dst, &dest);
11906         }
11907       sour.x = src->w - 1;
11908       i = (dst->w - src->w) / 2 + src->w - 1;
11909       while (++i < dst->w)
11910         {
11911           dest.x = i;
11912           blit(src, &sour, dst, &dest);
11913         }
11914     }
11915 
11916   if (src->h != dst->h)
11917     {
11918       /* we know that the widths match, and src is shorter */
11919       SDL_Rect sour;
11920       int i = (dst->h - src->h) / 2;
11921 
11922       sour.w = src->w;
11923       sour.x = 0;
11924       sour.h = 1;
11925       sour.y = 0;
11926       while (i-- > 0)
11927         {
11928           dest.y = i;
11929           blit(src, &sour, dst, &dest);
11930         }
11931       sour.y = src->h - 1;
11932       i = (dst->h - src->h) / 2 + src->h - 1;
11933       while (++i < dst->h)
11934         {
11935           dest.y = i;
11936           blit(src, &sour, dst, &dest);
11937         }
11938     }
11939 
11940   SDL_FreeSurface(src);
11941 }
11942 
11943 
11944 /**
11945  * FIXME
11946  */
load_starter_id(char * saved_id,FILE * fil)11947 static void load_starter_id(char *saved_id, FILE * fil)
11948 {
11949   char *rname;
11950   char fname[FILENAME_MAX];
11951   FILE *fi;
11952   char color_tag;
11953   int r, g, b, __attribute__((unused))tmp;
11954   char * __attribute__((unused)) tmp_ptr;
11955 
11956   rname = NULL;
11957 
11958   if (saved_id != NULL)
11959     {
11960       safe_snprintf(fname, sizeof(fname), "saved/%s.dat", saved_id);
11961       rname = get_fname(fname, DIR_SAVE);
11962 
11963       fi = fopen(rname, "r");
11964     }
11965   else
11966     fi = fil;
11967 
11968   starter_id[0] = '\0';
11969   template_id[0] = '\0';
11970 
11971   if (fi != NULL)
11972     {
11973       if (fgets(starter_id, sizeof(starter_id), fi))
11974         {
11975           starter_id[strlen(starter_id) - 1] = '\0';
11976 
11977           tmp = fscanf(fi, "%d", &starter_mirrored);
11978           tmp = fscanf(fi, "%d", &starter_flipped);
11979           tmp = fscanf(fi, "%d", &starter_personal);
11980 
11981           do
11982             {
11983               color_tag = fgetc(fi);
11984             }
11985           while ((color_tag == '\n' || color_tag == '\r') && !feof(fi));
11986 
11987           if (!feof(fi) && color_tag == 'c')
11988             {
11989               tmp = fscanf(fi, "%d", &r);
11990               tmp = fscanf(fi, "%d", &g);
11991               tmp = fscanf(fi, "%d", &b);
11992 
11993               canvas_color_r = (Uint8) r;
11994               canvas_color_g = (Uint8) g;
11995               canvas_color_b = (Uint8) b;
11996             }
11997           else
11998             {
11999               canvas_color_r = 255;
12000               canvas_color_g = 255;
12001               canvas_color_b = 255;
12002             }
12003 
12004           do
12005             {
12006               color_tag = fgetc(fi);
12007             }
12008           while ((color_tag == '\n' || color_tag == '\r') && !feof(fi));
12009 
12010           if (!feof(fi) && color_tag == 'T')
12011             {
12012               tmp_ptr = fgets(template_id, sizeof(template_id), fi);
12013               template_id[strlen(template_id) - 1] = '\0';
12014               tmp = fscanf(fi, "%d", &template_personal);
12015 #ifdef DEBUG
12016               printf("template = %s\n (Personal=%d)", template_id, template_personal);
12017 #endif
12018             }
12019           if (!feof(fi) && color_tag == 'M')
12020             {
12021               starter_modified = fgetc(fi);
12022             }
12023         }
12024       fclose(fi);
12025     }
12026   else
12027     {
12028       canvas_color_r = 255;
12029       canvas_color_g = 255;
12030       canvas_color_b = 255;
12031     }
12032 
12033   if (saved_id != NULL)
12034     free(rname);
12035 }
12036 
12037 
12038 /**
12039  * FIXME
12040  */
load_starter_helper(char * path_and_basename,const char * extension,SDL_Surface * (* load_func)(const char *))12041 static SDL_Surface *load_starter_helper(char *path_and_basename, const char *extension, SDL_Surface * (*load_func) (const char *))
12042 {
12043   char *ext;
12044   char fname[256];
12045   SDL_Surface *surf;
12046   unsigned int i;
12047 
12048   ext = strdup(extension);
12049   safe_snprintf(fname, sizeof(fname), "%s.%s", path_and_basename, ext);
12050 
12051   surf = (*load_func) (fname);
12052 
12053   if (surf == NULL)
12054     {
12055       for (i = 0; i < strlen(ext); i++)
12056         {
12057           ext[i] = toupper(ext[i]);
12058         }
12059       safe_snprintf(fname, sizeof(fname), "%s.%s", path_and_basename, ext);
12060       surf = (*load_func) (fname);
12061     }
12062 
12063   free(ext);
12064 
12065   return (surf);
12066 }
12067 
12068 
12069 /**
12070  * FIXME
12071  */
load_starter(char * img_id)12072 static void load_starter(char *img_id)
12073 {
12074   char *dirname;
12075   char fname[256];
12076   SDL_Surface *tmp_surf;
12077 
12078   /* Determine path to starter files: */
12079 
12080   if (starter_personal == 0)
12081     dirname = strdup(DATA_PREFIX "starters");
12082   else
12083     dirname = get_fname("starters", DIR_DATA);
12084 
12085   /* Clear them to NULL first: */
12086   img_starter = NULL;
12087   img_starter_bkgd = NULL;
12088 
12089   /* Load the core image: */
12090   tmp_surf = NULL;
12091 
12092 #ifndef NOSVG
12093   if (tmp_surf == NULL)
12094     {
12095       /* Try loading an SVG */
12096 
12097       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12098       tmp_surf = load_starter_helper(fname, "svg", &load_svg);
12099     }
12100 #endif
12101 
12102   if (tmp_surf == NULL)
12103     {
12104       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12105       tmp_surf = load_starter_helper(fname, "png", &IMG_Load);
12106     }
12107 
12108   if (tmp_surf == NULL)
12109     {
12110       /* Try loading a KPX */
12111       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12112       tmp_surf = load_starter_helper(fname, "kpx", &myIMG_Load);
12113     }
12114 
12115 
12116   if (tmp_surf != NULL)
12117     {
12118       img_starter = SDL_DisplayFormatAlpha(tmp_surf);
12119       SDL_FreeSurface(tmp_surf);
12120     }
12121 
12122   /* Try to load the a background image: */
12123 
12124   tmp_surf = NULL;
12125 
12126 #ifndef NOSVG
12127   /* (Try SVG) */
12128   if (tmp_surf == NULL)
12129     {
12130       safe_snprintf(fname, sizeof(fname), "%s/%s-back", dirname, img_id);
12131       tmp_surf = load_starter_helper(fname, "svg", &load_svg);
12132     }
12133 #endif
12134 
12135   /* (JPEG) */
12136   if (tmp_surf == NULL)
12137     {
12138       safe_snprintf(fname, sizeof(fname), "%s/%s-back", dirname, img_id);
12139       tmp_surf = load_starter_helper(fname, "jpeg", &IMG_Load);
12140     }
12141 
12142   if (tmp_surf == NULL)
12143     {
12144       /* (Then just JPG) */
12145       safe_snprintf(fname, sizeof(fname), "%s/%s-back", dirname, img_id);
12146       tmp_surf = load_starter_helper(fname, "jpg", &IMG_Load);
12147     }
12148 
12149   /* (Failed? Try PNG next) */
12150   if (tmp_surf == NULL)
12151     {
12152       safe_snprintf(fname, sizeof(fname), "%s/%s-back", dirname, img_id);
12153       tmp_surf = load_starter_helper(fname, "png", &IMG_Load);
12154     }
12155 
12156   if (tmp_surf != NULL)
12157     {
12158       img_starter_bkgd = SDL_DisplayFormat(tmp_surf);
12159       SDL_FreeSurface(tmp_surf);
12160     }
12161 
12162 
12163   /* If no background, let's try to remove all white
12164      (so we don't have to _REQUIRE_ users create Starters with
12165      transparency, if they're simple black-and-white outlines */
12166 
12167   if (img_starter != NULL && img_starter_bkgd == NULL)
12168     {
12169       int x, y;
12170 
12171       Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[img_starter->format->BytesPerPixel];
12172       void (*putpixel) (SDL_Surface *, int, int, Uint32) = putpixels[img_starter->format->BytesPerPixel];
12173       Uint32 p;
12174       Uint8 r, g, b, a;
12175       int any_transparency;
12176 
12177       any_transparency = 0;
12178 
12179       for (y = 0; y < img_starter->h && !any_transparency; y++)
12180         {
12181           for (x = 0; x < img_starter->w && !any_transparency; x++)
12182             {
12183               p = getpixel(img_starter, x, y);
12184               SDL_GetRGBA(p, img_starter->format, &r, &g, &b, &a);
12185 
12186               if (a < 255)
12187                 any_transparency = 1;
12188             }
12189         }
12190 
12191       if (!any_transparency)
12192         {
12193           /* No transparency found!  We MUST check for white pixels to save
12194              the day! */
12195 
12196           for (y = 0; y < img_starter->h; y++)
12197             {
12198               for (x = 0; x < img_starter->w; x++)
12199                 {
12200                   p = getpixel(img_starter, x, y);
12201                   SDL_GetRGBA(p, img_starter->format, &r, &g, &b, &a);
12202 
12203                   if (abs(r - g) < 16 && abs(r - b) < 16 && abs(b - g) < 16)
12204                     {
12205                       a = 255 - ((r + g + b) / 3);
12206                     }
12207 
12208                   p = SDL_MapRGBA(img_starter->format, r, g, b, a);
12209                   putpixel(img_starter, x, y, p);
12210                 }
12211             }
12212         }
12213     }
12214 
12215 
12216   /* Scale if needed... */
12217 
12218   if (img_starter != NULL && (img_starter->w != canvas->w || img_starter->h != canvas->h))
12219     {
12220       tmp_surf = img_starter;
12221 
12222       img_starter = SDL_CreateRGBSurface(canvas->flags,
12223                                          canvas->w, canvas->h,
12224                                          tmp_surf->format->BitsPerPixel,
12225                                          tmp_surf->format->Rmask,
12226                                          tmp_surf->format->Gmask, tmp_surf->format->Bmask, tmp_surf->format->Amask);
12227 
12228       /* 3rd arg ignored for RGBA surfaces */
12229       SDL_SetAlpha(tmp_surf, SDL_RLEACCEL, SDL_ALPHA_OPAQUE);
12230       autoscale_copy_smear_free(tmp_surf, img_starter, NondefectiveBlit);
12231       SDL_SetAlpha(img_starter, SDL_RLEACCEL | SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
12232     }
12233 
12234 
12235   if (img_starter_bkgd != NULL && (img_starter_bkgd->w != canvas->w || img_starter_bkgd->h != canvas->h))
12236     {
12237       tmp_surf = img_starter_bkgd;
12238 
12239       img_starter_bkgd = SDL_CreateRGBSurface(SDL_SWSURFACE,
12240                                               canvas->w, canvas->h,
12241                                               canvas->format->BitsPerPixel,
12242                                               canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, 0);
12243 
12244       autoscale_copy_smear_free(tmp_surf, img_starter_bkgd, SDL_BlitSurface);
12245     }
12246 
12247   free(dirname);
12248 }
12249 
12250 
12251 /**
12252  * FIXME
12253  */
load_template(char * img_id)12254 static void load_template(char *img_id)
12255 {
12256   char *dirname;
12257   char fname[256];
12258   SDL_Surface *tmp_surf;
12259 
12260   /* Determine path to starter files: */
12261 
12262   if (template_personal == 0)
12263     dirname = strdup(DATA_PREFIX "templates");
12264   else
12265     dirname = get_fname("templates", DIR_SAVE);
12266 
12267   /* Clear them to NULL first: */
12268   img_starter = NULL;
12269   img_starter_bkgd = NULL;
12270 
12271   /* (Try loading a KPX) */
12272   safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12273   tmp_surf = load_starter_helper(fname, "kpx", &myIMG_Load);
12274 
12275 #ifndef NOSVG
12276   /* (Failed? Try SVG next) */
12277   if (tmp_surf == NULL)
12278     {
12279       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12280       tmp_surf = load_starter_helper(fname, "svg", &load_svg);
12281     }
12282 #endif
12283 
12284   /* (JPEG) */
12285   if (tmp_surf == NULL)
12286     {
12287       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12288       tmp_surf = load_starter_helper(fname, "jpeg", &IMG_Load);
12289     }
12290   if (tmp_surf == NULL)
12291     {
12292       /* (Then just JPG) */
12293       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12294       tmp_surf = load_starter_helper(fname, "jpg", &IMG_Load);
12295     }
12296 
12297   /* (Failed? Try PNG next) */
12298   if (tmp_surf == NULL)
12299     {
12300       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, img_id);
12301       tmp_surf = load_starter_helper(fname, "png", &IMG_Load);
12302     }
12303 
12304   if (tmp_surf != NULL)
12305     {
12306       img_starter_bkgd = SDL_DisplayFormat(tmp_surf);
12307       SDL_FreeSurface(tmp_surf);
12308     }
12309 
12310 
12311   /* Scale if needed... */
12312 
12313   if (img_starter_bkgd != NULL && (img_starter_bkgd->w != canvas->w || img_starter_bkgd->h != canvas->h))
12314     {
12315       tmp_surf = img_starter_bkgd;
12316 
12317       img_starter_bkgd = SDL_CreateRGBSurface(SDL_SWSURFACE,
12318                                               canvas->w, canvas->h,
12319                                               canvas->format->BitsPerPixel,
12320                                               canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, 0);
12321 
12322       autoscale_copy_smear_free(tmp_surf, img_starter_bkgd, SDL_BlitSurface);
12323     }
12324 
12325   free(dirname);
12326 }
12327 
12328 
12329 /**
12330  * FIXME
12331  */
12332 /* Load current (if any) image: */
load_current(void)12333 static void load_current(void)
12334 {
12335   SDL_Surface *tmp, *org_surf;
12336   char *fname;
12337   char ftmp[1024];
12338   FILE *fi;
12339 
12340   /* Determine the current picture's ID: */
12341 
12342   fname = get_fname("current_id.txt", DIR_SAVE);
12343 
12344   fi = fopen(fname, "r");
12345   if (fi == NULL)
12346     {
12347       fprintf(stderr,
12348               "\nWarning: Couldn't determine the current image's ID\n"
12349               "%s\n" "The system error that occurred was:\n" "%s\n\n", fname, strerror(errno));
12350       file_id[0] = '\0';
12351       starter_id[0] = '\0';
12352       template_id[0] = '\0';
12353     }
12354   else
12355     {
12356       if (fgets(file_id, sizeof(file_id), fi))
12357         {
12358           if (strlen(file_id) > 0)
12359             {
12360               file_id[strlen(file_id) - 1] = '\0';
12361             }
12362         }
12363       fclose(fi);
12364     }
12365 
12366   free(fname);
12367 
12368 
12369   /* Load that image: */
12370 
12371   if (file_id[0] != '\0')
12372     {
12373 
12374       start_label_node = NULL;
12375       current_label_node = NULL;
12376       first_label_node_in_redo_stack = NULL;
12377       highlighted_label_node = NULL;
12378       label_node_to_edit = NULL;
12379       have_to_rec_label_node = FALSE;
12380 
12381       safe_snprintf(ftmp, sizeof(ftmp), "saved/%s%s", file_id, FNAME_EXTENSION);
12382       fname = get_fname(ftmp, DIR_SAVE);
12383 
12384       tmp = myIMG_Load_RWops(fname);
12385 
12386       if (tmp == NULL)
12387         {
12388           fprintf(stderr,
12389                   "\nWarning: Couldn't load any current image.\n"
12390                   "%s\n" "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", fname, SDL_GetError());
12391 
12392           file_id[0] = '\0';
12393           starter_id[0] = '\0';
12394           template_id[0] = '\0';
12395         }
12396       else
12397         {
12398           org_surf = SDL_DisplayFormat(tmp);
12399           autoscale_copy_smear_free(tmp, canvas, SDL_BlitSurface);
12400 
12401           /* First we run this for compatibility, then we will chek if
12402              there are data embedded in the png file */
12403           load_starter_id(file_id, NULL);
12404           if (starter_id[0] != '\0')
12405             {
12406               load_starter(starter_id);
12407 
12408               if (starter_mirrored)
12409                 mirror_starter();
12410 
12411               if (starter_flipped)
12412                 flip_starter();
12413             }
12414           else if (template_id[0] != '\0')
12415             {
12416               load_template(template_id);
12417             }
12418 
12419           load_embedded_data(fname, org_surf);
12420         }
12421 
12422       free(fname);
12423     }
12424 }
12425 
12426 
12427 /**
12428  * FIXME
12429  */
12430 /* Make sure we have a 'path' directory */
make_directory(int dir_type,const char * path,const char * errmsg)12431 static int make_directory(int dir_type, const char *path, const char *errmsg)
12432 {
12433   char *fname;
12434   int res;
12435 
12436   fname = get_fname(path, dir_type);
12437   res = mkdir(fname, 0755);
12438   if (res != 0 && errno != EEXIST)
12439     {
12440       fprintf(stderr,
12441               "\nError: %s:\n" "%s\n" "The error that occurred was:\n" "%s\n\n", errmsg, fname, strerror(errno));
12442       free(fname);
12443       return 0;
12444     }
12445   free(fname);
12446   return 1;
12447 }
12448 
12449 /**
12450  * FIXME
12451  */
12452 /* Save the current image to disk: */
save_current(void)12453 static void save_current(void)
12454 {
12455   char *fname;
12456   FILE *fi;
12457 
12458   if (!make_directory(DIR_SAVE, "", "Can't create user data directory (E001)"))
12459     {
12460       draw_tux_text(TUX_OOPS, strerror(errno), 0);
12461       return;
12462     }
12463 
12464   fname = get_fname("current_id.txt", DIR_SAVE);
12465 
12466   fi = fopen(fname, "w");
12467   if (fi == NULL)
12468     {
12469       fprintf(stderr,
12470               "\nError: Can't keep track of current image.\n"
12471               "%s\n" "The error that occurred was:\n" "%s\n\n", fname, strerror(errno));
12472 
12473       draw_tux_text(TUX_OOPS, strerror(errno), 0);
12474     }
12475   else
12476     {
12477       fprintf(fi, "%s\n", file_id);
12478       fclose(fi);
12479     }
12480 
12481   free(fname);
12482 }
12483 
12484 
12485 /**
12486  * FIXME
12487  */
12488 /* Prompt the user with a yes/no question: */
do_prompt(const char * const text,const char * const btn_yes,const char * const btn_no,int ox,int oy)12489 static int do_prompt(const char *const text, const char *const btn_yes, const char *const btn_no, int ox, int oy)
12490 {
12491   return (do_prompt_image(text, btn_yes, btn_no, NULL, NULL, NULL, ox, oy));
12492 }
12493 
12494 /**
12495  * FIXME
12496  */
do_prompt_snd(const char * const text,const char * const btn_yes,const char * const btn_no,int snd,int ox,int oy)12497 static int do_prompt_snd(const char *const text, const char *const btn_yes,
12498                          const char *const btn_no, int snd, int ox, int oy)
12499 {
12500   return (do_prompt_image_flash_snd(text, btn_yes, btn_no, NULL, NULL, NULL, 0, snd, ox, oy));
12501 }
12502 
12503 /**
12504  * FIXME
12505  */
do_prompt_image(const char * const text,const char * const btn_yes,const char * const btn_no,SDL_Surface * img1,SDL_Surface * img2,SDL_Surface * img3,int ox,int oy)12506 static int do_prompt_image(const char *const text, const char *const btn_yes,
12507                            const char *const btn_no, SDL_Surface * img1,
12508                            SDL_Surface * img2, SDL_Surface * img3, int ox, int oy)
12509 {
12510   return (do_prompt_image_snd(text, btn_yes, btn_no, img1, img2, img3, SND_NONE, ox, oy));
12511 }
12512 
12513 /**
12514  * FIXME
12515  */
do_prompt_image_snd(const char * const text,const char * const btn_yes,const char * const btn_no,SDL_Surface * img1,SDL_Surface * img2,SDL_Surface * img3,int snd,int ox,int oy)12516 static int do_prompt_image_snd(const char *const text,
12517                                const char *const btn_yes,
12518                                const char *const btn_no, SDL_Surface * img1,
12519                                SDL_Surface * img2, SDL_Surface * img3, int snd, int ox, int oy)
12520 {
12521   return (do_prompt_image_flash_snd(text, btn_yes, btn_no, img1, img2, img3, 0, snd, ox, oy));
12522 }
12523 
12524 /**
12525  * FIXME
12526  */
do_prompt_image_flash(const char * const text,const char * const btn_yes,const char * const btn_no,SDL_Surface * img1,SDL_Surface * img2,SDL_Surface * img3,int animate,int ox,int oy)12527 static int do_prompt_image_flash(const char *const text,
12528                                  const char *const btn_yes,
12529                                  const char *const btn_no, SDL_Surface * img1,
12530                                  SDL_Surface * img2, SDL_Surface * img3, int animate, int ox, int oy)
12531 {
12532   return (do_prompt_image_flash_snd(text, btn_yes, btn_no, img1, img2, img3, animate, SND_NONE, ox, oy));
12533 }
12534 
12535 #define PROMPT_W (min(canvas->w, 440 * button_scale))
12536 #define PROMPT_LEFT (r_tools.w - PROMPTOFFSETX + canvas->w / 2 - PROMPT_W / 2 - 4)
12537 
12538 /**
12539  * FIXME
12540  */
do_prompt_image_flash_snd(const char * const text,const char * const btn_yes,const char * const btn_no,SDL_Surface * img1,SDL_Surface * img2,SDL_Surface * img3,int animate,int snd,int ox,int oy)12541 static int do_prompt_image_flash_snd(const char *const text,
12542                                      const char *const btn_yes,
12543                                      const char *const btn_no,
12544                                      SDL_Surface * img1, SDL_Surface * img2,
12545                                      SDL_Surface * img3, int animate, int snd, int ox, int oy)
12546 {
12547   int oox, ooy, nx, ny;
12548   SDL_Event event;
12549   SDL_Rect dest, dest_back;
12550   int done, ans, w, counter;
12551   SDL_Color black = { 0, 0, 0, 0 };
12552   SDLKey key;
12553   SDLKey key_y, key_n;
12554   char *keystr;
12555   SDL_Surface *backup;
12556 
12557 #ifndef NO_PROMPT_SHADOWS
12558   int i;
12559   SDL_Surface *alpha_surf;
12560 #endif
12561   int img1_w, img2_w, img3_w, max_img_w, img_y, offset;
12562   SDL_Surface *img1b;
12563   int free_img1b;
12564   int txt_left, txt_right, img_left, btn_left, txt_btn_left, txt_btn_right;
12565   int val_x, val_y, motioner;
12566   int valhat_x, valhat_y, hatmotioner;
12567 
12568 #ifdef DEBUG
12569   if (snd >= 0)
12570     {
12571       printf("Prompt and play sound #%d: %s\n", snd, sound_fnames[snd]);
12572       fflush(stdout);
12573     }
12574   else
12575     {
12576       printf("Prompt without sound\n");
12577       fflush(stdout);
12578     }
12579 #endif
12580 
12581   val_x = val_y = motioner = 0;
12582   valhat_x = valhat_y = hatmotioner = 0;
12583   emulate_button_pressed = 0;
12584 
12585   hide_blinking_cursor();
12586 
12587   /* Admittedly stupid way of determining which keys can be used for
12588      positive and negative responses in dialogs (e.g., [Y] (for 'yes') in English) */
12589   keystr = textdir(gettext("Yes"));
12590   key_y = tolower(keystr[0]);
12591   free(keystr);
12592 
12593   keystr = textdir(gettext("No"));
12594   key_n = tolower(keystr[0]);
12595   free(keystr);
12596 
12597 
12598   do_setcursor(cursor_arrow);
12599 
12600 
12601   /* Draw button box: */
12602 
12603   playsound(screen, 0, SND_PROMPT, 1, SNDPOS_CENTER, SNDDIST_NEAR);
12604 
12605   backup = SDL_CreateRGBSurface(screen->flags, screen->w, screen->h,
12606                                 screen->format->BitsPerPixel,
12607                                 screen->format->Rmask,
12608                                 screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
12609 
12610   SDL_BlitSurface(screen, NULL, backup, NULL);
12611 
12612   for (w = 0; w <= r_ttools.w; w = w + 2)
12613     {
12614       oox = ox - w;
12615       ooy = oy - w;
12616 
12617       nx = PROMPT_LEFT + r_ttools.w - w + PROMPTOFFSETX;
12618       ny = 2 + canvas->h / 2 - w;
12619 
12620       dest.x = ((nx * w) + (oox * (r_ttools.w - w))) / r_ttools.w;
12621       dest.y = ((ny * w) + (ooy * (r_ttools.w - w))) / r_ttools.w;
12622       dest.w = (PROMPT_W - r_ttools.w * 2) + w * 2;
12623       dest.h = w * 2;
12624       SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 224 - (int)(w / button_scale), 224 - (int)(w / button_scale), 244 - (int)(w / button_scale)));
12625 
12626       SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
12627 
12628       if ((w % 8) == 0)
12629         SDL_Delay(1);
12630 
12631       if (w == r_ttools.w - 2)
12632 	{
12633         SDL_BlitSurface(backup, NULL, screen, NULL);
12634 	}
12635     }
12636 
12637   SDL_FreeSurface(backup);
12638 
12639 
12640   playsound(screen, 1, snd, 1, SNDPOS_LEFT, SNDDIST_NEAR);
12641 
12642 #ifndef NO_PROMPT_SHADOWS
12643   alpha_surf = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
12644                                     (PROMPT_W - r_ttools.w * 2) + (w - 4) * 2,
12645                                     (w - 4) * 2,
12646                                     screen->format->BitsPerPixel,
12647                                     screen->format->Rmask,
12648                                     screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
12649 
12650   if (alpha_surf != NULL)
12651     {
12652       SDL_FillRect(alpha_surf, NULL, SDL_MapRGB(alpha_surf->format, 0, 0, 0));
12653       SDL_SetAlpha(alpha_surf, SDL_SRCALPHA, 64);
12654 
12655       for (i = 8; i > 0; i = i - 2)
12656         {
12657           dest.x = PROMPT_LEFT + r_ttools.w - (w - 4) + i + PROMPTOFFSETX;
12658           dest.y = 94 / button_scale + r_ttools.w / button_scale - (w - 4) + i + PROMPTOFFSETY;
12659           dest.w = (PROMPT_W - r_ttools.w * 2) + (w - 4) * 2;
12660           dest.h = (w - 4) * 2;
12661 	  dest.y = canvas->h / 2 - dest.h / 2 + i + 2;
12662           SDL_BlitSurface(alpha_surf, NULL, screen, &dest);
12663         }
12664 
12665       SDL_FreeSurface(alpha_surf);
12666     }
12667 #endif
12668 
12669 
12670   w = w - 6;
12671 
12672   dest_back.x =  dest.x = PROMPT_LEFT + r_ttools.w - w + PROMPTOFFSETX;
12673   dest_back.w = dest.w = (PROMPT_W - r_ttools.w * 2) + w * 2;
12674   dest_back.h = dest.h = w * 2;
12675   dest_back.y = dest.y = canvas->h / 2 - dest.h / 2 + 2;
12676   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
12677 
12678   /* Make sure image on the right isn't too tall!
12679      (Thumbnails in Open dialog are larger on larger displays, and can
12680      cause arrow and trashcan icons to be pushed out of the dialog window!) */
12681 
12682   free_img1b = 0;
12683   img1b = NULL;
12684 
12685   if (img1 != NULL)
12686     {
12687       if (img1->h > 64 * button_scale && img2 != NULL /* Only scale if it matters */ )
12688         {
12689           img1b = thumbnail(img1, 80 * button_scale, 64 * button_scale, 1);
12690           free_img1b = 1;
12691         }
12692       else
12693         {
12694           img1b = img1;
12695         }
12696     }
12697 
12698 
12699   /* If we're showing any images on the right, determine the widest width
12700      for them: */
12701 
12702   offset = img1_w = img2_w = img3_w = 0;
12703 
12704   if (img1b != NULL)
12705     img1_w = img1b->w;
12706   if (img2 != NULL)
12707     img2_w = img2->w;
12708   if (img3 != NULL)
12709     img3_w = img3->w;
12710 
12711   max_img_w = max(img1_w, max(img2_w, img3_w));
12712 
12713   if (max_img_w > 0)
12714     offset = max_img_w + 8;
12715 
12716 
12717   /* Draw the question: */
12718 
12719   if (need_right_to_left == 0)
12720     {
12721       txt_left = (PROMPT_LEFT + 6) + PROMPTOFFSETX;
12722       txt_right = (PROMPT_LEFT + PROMPT_W - 5) + PROMPTOFFSETX - offset;
12723       img_left = (PROMPT_LEFT + PROMPT_W - 5) + PROMPTOFFSETX - max_img_w - 4;
12724       btn_left = (PROMPT_LEFT + 6) + PROMPTOFFSETX;
12725       txt_btn_left = txt_left + img_yes->w + 4;
12726       txt_btn_right = txt_right;
12727     }
12728   else
12729     {
12730       txt_left = (PROMPT_LEFT + 6) + PROMPTOFFSETX + offset;
12731       txt_right = (PROMPT_LEFT + PROMPT_W - 5) + PROMPTOFFSETX;
12732       img_left = (PROMPT_LEFT + 6) + PROMPTOFFSETX + 4;
12733       btn_left = (PROMPT_LEFT + PROMPT_W - 5) + PROMPTOFFSETX - img_yes->w - 4;
12734       txt_btn_left = txt_left;
12735       txt_btn_right = btn_left;
12736     }
12737 
12738   wordwrap_text(text, black, txt_left, dest.y + 2 , txt_right, 1);
12739 
12740 
12741   /* Draw the images (if any, and if not animated): */
12742 
12743    img_y = dest_back.y + 6;
12744 
12745   if (img1b != NULL)
12746     {
12747       dest.x = img_left + (max_img_w - img1b->w) / 2;
12748       dest.y = img_y;
12749 
12750       SDL_BlitSurface(img1b, NULL, screen, &dest);
12751 
12752       if (!animate)
12753         img_y = img_y + img1b->h + 4;
12754     }
12755 
12756   if (!animate)
12757     {
12758       if (img2 != NULL)
12759         {
12760           dest.x = img_left + (max_img_w - img2->w) / 2;
12761           dest.y = img_y;
12762 
12763           SDL_BlitSurface(img2, NULL, screen, &dest);
12764 
12765           img_y = img_y + img2->h + 4;
12766         }
12767 
12768       if (img3 != NULL)
12769         {
12770           dest.x = img_left + (max_img_w - img3->w) / 2;
12771           dest.y = img_y;
12772 
12773           SDL_BlitSurface(img3, NULL, screen, &dest);
12774 
12775           img_y = img_y + img3->h + 4;  /* unnecessary */
12776         }
12777     }
12778 
12779 
12780   /* Draw yes button: */
12781 
12782   dest.x = btn_left;
12783   dest.y = dest_back.y + dest_back.h - 4 - button_h - 4 - button_h;
12784   SDL_BlitSurface(img_yes, NULL, screen, &dest);
12785 
12786 
12787   /* (Bound to UTF8 domain, so always ask for UTF8 rendering!) */
12788 
12789   wordwrap_text(btn_yes, black, txt_btn_left, dest.y + 5, txt_btn_right, 1);
12790 
12791 
12792   /* Draw no button: */
12793 
12794   if (strlen(btn_no) != 0)
12795     {
12796       dest.x = btn_left;
12797       dest.y = dest.y + button_h + 4;
12798       SDL_BlitSurface(img_no, NULL, screen, &dest);
12799 
12800       wordwrap_text(btn_no, black, txt_btn_left, dest.y + 5, txt_btn_right, 1);
12801     }
12802 
12803 
12804   /* Draw Tux, waiting... */
12805 
12806   draw_tux_text(TUX_BORED, "", 0);
12807 
12808   SDL_Flip(screen);
12809 
12810   done = 0;
12811   counter = 0;
12812   ans = 0;
12813 
12814   do
12815     {
12816       while (SDL_PollEvent(&event))
12817         {
12818           if (event.type == SDL_QUIT)
12819             {
12820               ans = 0;
12821               done = 1;
12822             }
12823           else if (event.type == SDL_ACTIVEEVENT)
12824             {
12825               handle_active(&event);
12826             }
12827           else if (event.type == SDL_KEYUP)
12828             {
12829               key = event.key.keysym.sym;
12830 
12831               handle_keymouse(key, SDL_KEYUP, 24, NULL, NULL);
12832             }
12833           else if (event.type == SDL_KEYDOWN)
12834             {
12835               key = event.key.keysym.sym;
12836 
12837               handle_keymouse(key, SDL_KEYDOWN, 24, NULL, NULL);
12838 
12839 
12840               /* FIXME: Should use SDLK_{c} instead of '{c}'?  How? */
12841 
12842               if (key == key_y || key == SDLK_RETURN)
12843                 {
12844                   /* Y or ENTER - Yes! */
12845 
12846                   ans = 1;
12847                   done = 1;
12848                 }
12849               else if (key == key_n || key == SDLK_ESCAPE)
12850                 {
12851                   /* N or ESCAPE - No! */
12852 
12853                   if (strlen(btn_no) != 0)
12854                     {
12855                       ans = 0;
12856                       done = 1;
12857                     }
12858                   else
12859                     {
12860                       if (key == SDLK_ESCAPE)
12861                         {
12862                           /* ESCAPE also simply dismisses if there's no Yes/No
12863                              choice: */
12864 
12865                           ans = 1;
12866                           done = 1;
12867                         }
12868                     }
12869                 }
12870             }
12871           else if (event.type == SDL_MOUSEBUTTONDOWN && valid_click(event.button.button))
12872             {
12873               if (event.button.x >= btn_left && event.button.x < btn_left + img_yes->w)
12874                 {
12875                   if (event.button.y >= dest_back.y + dest_back.h - 4 - button_h - 4 - button_h &&
12876 		      event.button.y < dest_back.y + dest_back.h - 4 - button_h - 4 - button_h + img_yes->h)
12877                     {
12878                       ans = 1;
12879                       done = 1;
12880                     }
12881                   else if (strlen(btn_no) != 0 &&
12882                            event.button.y >= dest_back.y + dest_back.h - 4 - button_h &&
12883 			   event.button.y < dest_back.y + dest_back.h - 4 - button_h + img_no->h)
12884                     {
12885                       ans = 0;
12886                       done = 1;
12887                     }
12888                 }
12889             }
12890           else if (event.type == SDL_MOUSEMOTION)
12891             {
12892               if (event.button.x >= btn_left &&
12893                   event.button.x < btn_left + img_yes->w &&
12894                   ((event.button.y >= dest_back.y + dest_back.h - 4 - button_h - 4 - button_h &&
12895                     event.button.y < dest_back.y + dest_back.h - 4 - button_h - 4 - button_h + img_yes->h) ||
12896                    (strlen(btn_no) != 0 &&
12897                     event.button.y >=  dest_back.y + dest_back.h - 4 - button_h &&
12898 		    event.button.y < dest_back.y + dest_back.h - 4 - button_h + img_no->h)))
12899                 {
12900                   do_setcursor(cursor_hand);
12901                 }
12902               else
12903                 {
12904                   do_setcursor(cursor_arrow);
12905                 }
12906               oldpos_x = event.button.x;
12907               oldpos_y = event.button.y;
12908             }
12909 
12910           else if (event.type == SDL_JOYAXISMOTION)
12911             handle_joyaxismotion(event, &motioner, &val_x, &val_y);
12912 
12913           else if (event.type == SDL_JOYHATMOTION)
12914             handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
12915 
12916 
12917           else if (event.type == SDL_JOYBALLMOTION)
12918             handle_joyballmotion(event, oldpos_x, oldpos_y);
12919 
12920           else if (event.type == SDL_JOYBUTTONDOWN)
12921             handle_joybuttonupdown(event, oldpos_x, oldpos_y);
12922         }
12923 
12924       if (motioner | hatmotioner)
12925         handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x, valhat_y);
12926 
12927 
12928       SDL_Delay(10);
12929 
12930       if (animate)
12931         {
12932           counter++;
12933 
12934           if (counter == 5)
12935             {
12936               dest.x = img_left + (max_img_w - img2->w) / 2;
12937               dest.y = img_y;
12938 
12939               SDL_BlitSurface(img2, NULL, screen, &dest);
12940               SDL_Flip(screen);
12941             }
12942           else if (counter == 10)
12943             {
12944               if (img3 != NULL)
12945                 {
12946                   dest.x = img_left + (max_img_w - img3->w) / 2;
12947                   dest.y = img_y;
12948 
12949                   SDL_BlitSurface(img3, NULL, screen, &dest);
12950                   SDL_Flip(screen);
12951                 }
12952               else
12953                 counter = 15;
12954             }
12955 
12956           if (counter == 15)
12957             {
12958               dest.x = img_left + (max_img_w - img1b->w) / 2;
12959               dest.y = img_y;
12960 
12961               SDL_BlitSurface(img1b, NULL, screen, &dest);
12962               SDL_Flip(screen);
12963 
12964               counter = 0;
12965             }
12966         }
12967     }
12968   while (!done);
12969 
12970 
12971   /* FIXME: Sound effect! */
12972   /* ... */
12973 
12974 
12975   /* Erase question prompt: */
12976 
12977   update_canvas(0, 0, canvas->w, canvas->h);
12978 
12979   if (free_img1b)
12980     SDL_FreeSurface(img1b);
12981 
12982   return ans;
12983 }
12984 
12985 
12986 /**
12987  * FIXME
12988  */
12989 /* Free memory and prepare to quit: */
cleanup(void)12990 static void cleanup(void)
12991 {
12992   int i, j;
12993 
12994   for (j = 0; j < num_stamp_groups; j++)
12995     {
12996       for (i = 0; i < num_stamps[j]; i++)
12997         {
12998 #ifndef NOSOUND
12999           if (stamp_data[j][i]->ssnd)
13000             {
13001               Mix_FreeChunk(stamp_data[j][i]->ssnd);
13002               stamp_data[j][i]->ssnd = NULL;
13003             }
13004           if (stamp_data[j][i]->sdesc)
13005             {
13006               Mix_FreeChunk(stamp_data[j][i]->sdesc);
13007               stamp_data[j][i]->sdesc = NULL;
13008             }
13009 #endif
13010           if (stamp_data[j][i]->stxt)
13011             {
13012               free(stamp_data[j][i]->stxt);
13013               stamp_data[j][i]->stxt = NULL;
13014             }
13015           free_surface(&stamp_data[j][i]->thumbnail);
13016 
13017           free(stamp_data[j][i]->stampname);
13018           free(stamp_data[j][i]);
13019           stamp_data[j][i] = NULL;
13020         }
13021       free(stamp_data[j]);
13022     }
13023   free_surface(&active_stamp);
13024 
13025   free_surface_array(img_brushes, num_brushes);
13026   free_surface_array(img_brushes_thumbs, num_brushes);
13027   free(brushes_frames);
13028   free(brushes_directional);
13029   free(brushes_spacing);
13030   free_surface_array(img_tools, NUM_TOOLS);
13031   free_surface_array(img_tool_names, NUM_TOOLS);
13032   free_surface_array(img_title_names, NUM_TITLES);
13033   for (i = 0; i < num_magics; i++)
13034     {
13035       free_surface(&(magics[i].img_icon));
13036       free_surface(&(magics[i].img_name));
13037     }
13038   free_surface_array(img_shapes, NUM_SHAPES);
13039   free_surface_array(img_shape_names, NUM_SHAPES);
13040   free_surface_array(img_fills, NUM_FILLS);
13041   free_surface_array(img_fill_names, NUM_FILLS);
13042   free_surface_array(img_tux, NUM_TIP_TUX);
13043 
13044   free_surface(&img_openlabels_open);
13045   free_surface(&img_openlabels_slideshow);
13046   free_surface(&img_openlabels_erase);
13047   free_surface(&img_openlabels_pict_export);
13048   free_surface(&img_openlabels_back);
13049   free_surface(&img_openlabels_next);
13050   free_surface(&img_openlabels_play);
13051   free_surface(&img_openlabels_gif_export);
13052 
13053   free_surface(&img_progress);
13054 
13055   free_surface(&img_yes);
13056   free_surface(&img_no);
13057 
13058   free_surface(&img_prev);
13059   free_surface(&img_next);
13060 
13061   free_surface(&img_mirror);
13062   free_surface(&img_flip);
13063 
13064   free_surface(&img_title_on);
13065   free_surface(&img_title_off);
13066   free_surface(&img_title_large_on);
13067   free_surface(&img_title_large_off);
13068 
13069   free_surface(&img_open);
13070   free_surface(&img_erase);
13071   free_surface(&img_pict_export);
13072   free_surface(&img_back);
13073   free_surface(&img_trash);
13074 
13075   free_surface(&img_slideshow);
13076   free_surface(&img_play);
13077   free_surface(&img_gif_export);
13078   free_surface(&img_select_digits);
13079 
13080   free_surface(&img_dead40x40);
13081 
13082   free_surface(&img_printer);
13083   free_surface(&img_printer_wait);
13084   free_surface(&img_save_over);
13085 
13086   free_surface(&img_btn_up);
13087   free_surface(&img_btn_down);
13088   free_surface(&img_btn_off);
13089   free_surface(&img_btn_hold);
13090 
13091   free_surface(&img_btnsm_up);
13092   free_surface(&img_btnsm_off);
13093   free_surface(&img_btnsm_down);
13094   free_surface(&img_btnsm_hold);
13095 
13096   free_surface(&img_btn_nav);
13097   free_surface(&img_btnsm_nav);
13098 
13099   free_surface(&img_sfx);
13100   free_surface(&img_speak);
13101 
13102   free_surface(&img_cursor_up);
13103   free_surface(&img_cursor_down);
13104 
13105   free_surface(&img_cursor_starter_up);
13106   free_surface(&img_cursor_starter_down);
13107 
13108   free_surface(&img_scroll_up);
13109   free_surface(&img_scroll_down);
13110   free_surface(&img_scroll_up_off);
13111   free_surface(&img_scroll_down_off);
13112 
13113   free_surface(&img_grow);
13114   free_surface(&img_shrink);
13115 
13116   free_surface(&img_magic_paint);
13117   free_surface(&img_magic_fullscreen);
13118 
13119   free_surface(&img_shapes_center);
13120   free_surface(&img_shapes_corner);
13121 
13122   free_surface(&img_bold);
13123   free_surface(&img_italic);
13124 
13125   free_surface_array(undo_bufs, NUM_UNDO_BUFS);
13126 
13127 #ifdef LOW_QUALITY_COLOR_SELECTOR
13128   free_surface(&img_paintcan);
13129 #else
13130   free_surface_array(img_color_btns, NUM_COLORS * 2);
13131   free(img_color_btns);
13132 #endif
13133 
13134   if (onscreen_keyboard)
13135     {
13136       free_surface(&img_oskdel);
13137       free_surface(&img_osktab);
13138       free_surface(&img_oskenter);
13139       free_surface(&img_oskcapslock);
13140       free_surface(&img_oskshift);
13141     }
13142 
13143   free_surface(&screen);
13144   free_surface(&img_starter);
13145   free_surface(&img_starter_bkgd);
13146   free_surface(&canvas);
13147   free_surface(&save_canvas);
13148   free_surface(&img_cur_brush);
13149 
13150   if (touched != NULL)
13151     {
13152       free(touched);
13153       touched = NULL;
13154     }
13155 
13156   if (sim_flood_touched != NULL)
13157     {
13158       free(sim_flood_touched);
13159       sim_flood_touched = NULL;
13160     }
13161 
13162   if (medium_font != NULL)
13163     {
13164 #ifdef DEBUG
13165       printf("cleanup: medium font\n"); //EP
13166 #endif
13167       TuxPaint_Font_CloseFont(medium_font);
13168       medium_font = NULL;
13169     }
13170 
13171   if (small_font != NULL)
13172     {
13173 #ifdef DEBUG
13174       printf("cleanup: small font\n");  //EP
13175 #endif
13176       TuxPaint_Font_CloseFont(small_font);
13177       small_font = NULL;
13178     }
13179 
13180   if (large_font != NULL)
13181     {
13182 #ifdef DEBUG
13183       printf("cleanup: large font\n");  //EP
13184 #endif
13185       TuxPaint_Font_CloseFont(large_font);
13186       large_font = NULL;
13187     }
13188 
13189 #ifdef FORKED_FONTS
13190   free(user_font_families);     /* we'll leak the bodies... oh well */
13191 #else
13192   for (i = 0; i < num_font_families; i++)
13193     {
13194       if (user_font_families[i])
13195         {
13196           char **cpp = user_font_families[i]->filename - 1;
13197 
13198           if (*++cpp)
13199             free(*cpp);
13200           if (*++cpp)
13201             free(*cpp);
13202           if (*++cpp)
13203             free(*cpp);
13204           if (*++cpp)
13205             free(*cpp);
13206           if (user_font_families[i]->handle)
13207             TuxPaint_Font_CloseFont(user_font_families[i]->handle);
13208           free(user_font_families[i]->directory);
13209           free(user_font_families[i]->family);
13210           free(user_font_families[i]);
13211           user_font_families[i] = NULL;
13212         }
13213     }
13214 #endif
13215 
13216 #ifndef NOSOUND
13217   if (use_sound)
13218     {
13219       for (i = 0; i < NUM_SOUNDS; i++)
13220         {
13221           if (sounds[i])
13222             {
13223               Mix_FreeChunk(sounds[i]);
13224               sounds[i] = NULL;
13225             }
13226         }
13227 
13228       Mix_CloseAudio();
13229     }
13230 #endif
13231 
13232   for (i = 0; i < num_plugin_files; i++)
13233     magic_funcs[i].shutdown(magic_api_struct);
13234 
13235   free_cursor(&cursor_hand);
13236   free_cursor(&cursor_arrow);
13237   free_cursor(&cursor_watch);
13238   free_cursor(&cursor_up);
13239   free_cursor(&cursor_down);
13240   free_cursor(&cursor_tiny);
13241   free_cursor(&cursor_crosshair);
13242   free_cursor(&cursor_brush);
13243   free_cursor(&cursor_wand);
13244   free_cursor(&cursor_insertion);
13245   free_cursor(&cursor_rotate);
13246 
13247   for (i = 0; i < NUM_COLORS; i++)
13248     {
13249       free(color_hexes[i]);
13250       free(color_names[i]);
13251     }
13252   free(color_hexes);
13253   free(color_names);
13254 
13255 
13256   /* (Just in case...) */
13257 
13258   SDL_WM_GrabInput(SDL_GRAB_OFF);
13259 
13260 
13261   /* If we're using a lockfile, we can 'clear' it when we quit
13262      (so Tux Paint can be launched again soon, if the user wants to!) */
13263 
13264   if (ok_to_use_lockfile)
13265     {
13266       char *lock_fname;
13267       time_t zero_time;
13268       FILE *fi;
13269 
13270 #ifndef WIN32
13271       lock_fname = get_fname("lockfile.dat", DIR_SAVE);
13272 #else
13273       lock_fname = get_temp_fname("lockfile.dat");
13274 #endif
13275 
13276       zero_time = (time_t) 0;
13277 
13278       fi = fopen(lock_fname, "w");
13279       if (fi != NULL)
13280         {
13281           /* If we can write to it, do so! */
13282 
13283           fwrite(&zero_time, sizeof(time_t), 1, fi);
13284           fclose(fi);
13285         }
13286       else
13287         {
13288           fprintf(stderr,
13289                   "\nWarning: I couldn't create the lockfile (%s)\n"
13290                   "The error that occurred was:\n" "%s\n\n", lock_fname, strerror(errno));
13291         }
13292 
13293       free(lock_fname);
13294     }
13295 
13296   if (kbd)
13297     osk_free(kbd);
13298 
13299 #if !defined(WIN32) && !defined(__APPLE__) && !defined(__BEOS__)
13300 //  if (papersize != NULL)
13301 //    free(papersize);
13302 #endif
13303 
13304 
13305   /* Close up! */
13306 
13307   /* FIXME: Pango contexts lying around? -bjk 2007.07.24 */
13308 
13309   TTF_Quit();
13310   SDL_Quit();
13311 
13312   /* Call this once only, at exit */
13313 //EP now deprecated
13314 /*
13315 #if !defined(NOSVG) && !defined(OLD_SVG)
13316 #ifdef DEBUG
13317   printf("rsvg_term()\n"); fflush(stdout);
13318 #endif
13319   rsvg_term();
13320 #endif
13321 */
13322 }
13323 
13324 
13325 /**
13326  * FIXME
13327  */
free_surface(SDL_Surface ** surface_array)13328 static void free_surface(SDL_Surface ** surface_array)
13329 {
13330   if (surface_array)            //EP added this line to avoid app crash
13331     if (*surface_array)
13332       {
13333         SDL_FreeSurface(*surface_array);
13334         *surface_array = NULL;
13335       }
13336 }
13337 
13338 
13339 /**
13340  * FIXME
13341  */
free_surface_array(SDL_Surface * surface_array[],int count)13342 static void free_surface_array(SDL_Surface * surface_array[], int count)
13343 {
13344   int i;
13345 
13346   if (surface_array)            //EP added this line to avoid app crash
13347     for (i = 0; i < count; ++i)
13348       {
13349         free_surface(&surface_array[i]);
13350       }
13351 }
13352 
13353 
13354 /**
13355  * FIXME
13356  */
13357 /* Draw a shape! */
do_shape(int sx,int sy,int nx,int ny,int rotn,int use_brush)13358 static void do_shape(int sx, int sy, int nx, int ny, int rotn, int use_brush)
13359 {
13360   int side, angle_skip, init_ang, rx, ry, rmax, x1, y1, x2, y2, xp, yp, xv, yv, old_brush, step;
13361   float a1, a2, rotn_rad;
13362   int xx, yy, offx, offy, max_x, max_y;
13363 
13364 
13365   /* Determine radius/shape of the shape to draw: */
13366 
13367   old_brush = 0;
13368 
13369   rx = abs(nx - sx);
13370   ry = abs(ny - sy);
13371   if (shape_mode == SHAPEMODE_CORNER)
13372     {
13373       rx = sqrt(rx * rx) / 2;
13374       ry = sqrt(ry * ry) / 2;
13375     }
13376 
13377   /* If the shape has a 1:1 ("locked") aspect ratio, use the larger radius: */
13378 
13379   if (shape_locked[cur_shape])
13380     {
13381       if (rx > ry)
13382         ry = rx;
13383       else
13384         rx = ry;
13385     }
13386 
13387 
13388   /* Is the shape tiny?  Make it SOME size, first! */
13389 
13390   if (rx < 15 && ry < 15)
13391     {
13392       rx = 15;
13393       ry = 15;
13394     }
13395 
13396   if (rx < 2)
13397     rx = 2;
13398   if (ry < 2)
13399     ry = 2;
13400 
13401 
13402   /* Render a default brush: */
13403 
13404   if (use_brush)
13405     {
13406       old_brush = cur_brush;
13407       cur_brush = shape_brush;  /* Now only semi-ludgy! */
13408       render_brush();
13409     }
13410 
13411 
13412   /* Draw the shape: */
13413 
13414   angle_skip = 360 / shape_sides[cur_shape];
13415 
13416   init_ang = shape_init_ang[cur_shape];
13417 
13418 
13419   if (shape_mode == SHAPEMODE_CORNER)
13420     {
13421       /* Get extent of shape based on it's vertices,
13422          and scale up if we need to
13423          (e.g., square's points are at 45, 135, 225 & 315 degrees,
13424          which do not extend to the full radius).
13425 
13426          This works well for square and rectangle; it mostly
13427          works for triangle and 5-pointed star, but it seems
13428          sufficient. -bjk 2020.08.15 */
13429       max_x = 0;
13430       max_y = 0;
13431       for (side = 0; side < shape_sides[cur_shape]; side++)
13432         {
13433           a1 = (angle_skip * side + init_ang) * M_PI / 180;
13434           a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
13435           x1 = (int)(cos(a1) * rx);
13436           y1 = (int)(-sin(a1) * ry);
13437 
13438           if (abs(x1) > max_x)
13439             max_x = abs(x1);
13440           if (abs(y1) > max_y)
13441             max_y = abs(y1);
13442         }
13443 
13444       if (max_x < rx)
13445         rx = (rx * rx) / max_x;
13446       if (max_y < ry)
13447         ry = (ry * ry) / max_y;
13448     }
13449 
13450 
13451   step = 1;
13452 
13453   if (dont_do_xor && !use_brush)
13454     {
13455       /* If we're in light outline mode & not drawing the shape with the brush,
13456          if it has lots of sides (like a circle), reduce the number of sides: */
13457 
13458       if (shape_sides[cur_shape] > 5)
13459         step = (shape_sides[cur_shape] / 8);
13460     }
13461 
13462 
13463   /* Where is the object? */
13464   if (shape_mode == SHAPEMODE_CENTER) {
13465     offx = 0;
13466     offy = 0;
13467   } else {
13468     offx = (nx - sx) / 2;
13469     offy = (ny - sy) / 2;
13470 
13471     if (shape_locked[cur_shape])
13472       {
13473         if (abs(offx) > abs(offy))
13474           {
13475             if (offy > 0)
13476               offy = abs(offx);
13477             else
13478               offy = -abs(offx);
13479           }
13480         else
13481           {
13482             if (offx > 0)
13483               offx = abs(offy);
13484             else
13485               offx = -abs(offy);
13486           }
13487       }
13488   }
13489 
13490   for (side = 0; side < shape_sides[cur_shape]; side = side + step)
13491     {
13492       a1 = (angle_skip * side + init_ang) * M_PI / 180;
13493       a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
13494 
13495       x1 = (int)(cos(a1) * rx);
13496       y1 = (int)(-sin(a1) * ry);
13497 
13498       x2 = (int)(cos(a2) * rx);
13499       y2 = (int)(-sin(a2) * ry);
13500 
13501       xv = (int)(cos((a1 + a2) / 2) * rx * shape_valley[cur_shape] / 100);
13502       yv = (int)(-sin((a1 + a2) / 2) * ry * shape_valley[cur_shape] / 100);
13503 
13504       /* Rotate the line: */
13505 
13506       if (rotn != 0)
13507         {
13508           rotn_rad = rotn * M_PI / 180;
13509 
13510           xp = (x1 + offx) * cos(rotn_rad) - (y1 + offy) * sin(rotn_rad);
13511           yp = (x1 + offx) * sin(rotn_rad) + (y1 + offy) * cos(rotn_rad);
13512 
13513           x1 = xp - offx;
13514           y1 = yp - offy;
13515 
13516           xp = (x2 + offx) * cos(rotn_rad) - (y2 + offy) * sin(rotn_rad);
13517           yp = (x2 + offx) * sin(rotn_rad) + (y2 + offy) * cos(rotn_rad);
13518 
13519           x2 = xp - offx;
13520           y2 = yp - offy;
13521 
13522           xp = (xv + offx) * cos(rotn_rad) - (yv + offy) * sin(rotn_rad);
13523           yp = (xv + offx) * sin(rotn_rad) + (yv + offy) * cos(rotn_rad);
13524 
13525           xv = xp - offx;
13526           yv = yp - offy;
13527         }
13528 
13529 
13530       /* Center the line around the center of the shape: */
13531 
13532       x1 = x1 + sx + offx;
13533       y1 = y1 + sy + offy;
13534       x2 = x2 + sx + offx;
13535       y2 = y2 + sy + offy;
13536       xv = xv + sx + offx;
13537       yv = yv + sy + offy;
13538 
13539 
13540       /* Draw: */
13541 
13542       if (!use_brush)
13543         {
13544           /* (XOR) */
13545           if (shape_valley[cur_shape] == 100)
13546             line_xor(x1, y1, x2, y2);
13547           else
13548             {
13549               line_xor(x1, y1, xv, yv);
13550               line_xor(xv, yv, x2, y2);
13551             }
13552         }
13553       else
13554         {
13555           if (shape_valley[cur_shape] == 100)
13556             /* Brush */
13557 
13558             brush_draw(x1, y1, x2, y2, 0);
13559           else
13560             /* Stars */
13561             {
13562               brush_draw(x1, y1, xv, yv, 0);
13563               brush_draw(xv, yv, x2, y2, 0);
13564             }
13565         }
13566     }
13567 
13568 
13569   if (use_brush && shape_filled[cur_shape] && rx > 0 && ry > 0)
13570     {
13571       /* FIXME: In the meantime, we'll do this lame radius-based fill: */
13572 
13573       for (xx = max(abs(rx), abs(ry)); xx > 0; xx--)
13574         {
13575           yy = min(xx * rx / ry, xx * ry / rx);
13576 
13577           for (side = 0; side < shape_sides[cur_shape]; side++)
13578             {
13579               a1 = (angle_skip * side + init_ang) * M_PI / 180;
13580               a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
13581 
13582               if (yy == xx * ry / rx)
13583                 {
13584                   x1 = (int)(cos(a1) * xx);
13585                   y1 = (int)(-sin(a1) * yy);
13586 
13587                   x2 = (int)(cos(a2) * xx);
13588                   y2 = (int)(-sin(a2) * yy);
13589 
13590                   xv = (int)(cos((a1 + a2) / 2) * xx * shape_valley[cur_shape] / 100);
13591                   yv = (int)(-sin((a1 + a2) / 2) * yy * shape_valley[cur_shape] / 100);
13592                 }
13593               else
13594                 {
13595                   x1 = (int)(cos(a1) * yy);
13596                   y1 = (int)(-sin(a1) * xx);
13597 
13598                   x2 = (int)(cos(a2) * yy);
13599                   y2 = (int)(-sin(a2) * xx);
13600 
13601                   xv = (int)(cos((a1 + a2) / 2) * yy * shape_valley[cur_shape] / 100);
13602                   yv = (int)(-sin((a1 + a2) / 2) * xx * shape_valley[cur_shape] / 100);
13603                 }
13604 
13605               /* Rotate the line: */
13606 
13607               if (rotn != 0)
13608                 {
13609                   rotn_rad = rotn * M_PI / 180;
13610 
13611                   xp = (x1 + offx) * cos(rotn_rad) - (y1 + offy) * sin(rotn_rad);
13612                   yp = (x1 + offx) * sin(rotn_rad) + (y1 + offy) * cos(rotn_rad);
13613 
13614                   x1 = xp - offx;
13615                   y1 = yp - offy;
13616 
13617                   xp = (x2 + offx) * cos(rotn_rad) - (y2 + offy) * sin(rotn_rad);
13618                   yp = (x2 + offx) * sin(rotn_rad) + (y2 + offy) * cos(rotn_rad);
13619 
13620                   x2 = xp - offx;
13621                   y2 = yp - offy;
13622 
13623                   xp = (xv + offx) * cos(rotn_rad) - (yv + offy) * sin(rotn_rad);
13624                   yp = (xv + offx) * sin(rotn_rad) + (yv + offy) * cos(rotn_rad);
13625 
13626                   xv = xp - offx;
13627                   yv = yp - offy;
13628                 }
13629 
13630 
13631               /* Center the line around the center of the shape: */
13632 
13633               x1 = x1 + sx + offx;
13634               y1 = y1 + sy + offy;
13635               x2 = x2 + sx + offx;
13636               y2 = y2 + sy + offy;
13637               xv = xv + sx + offx;
13638               yv = yv + sy + offy;
13639 
13640 
13641               /* Draw: */
13642               if (shape_valley[cur_shape] == 100)
13643                 brush_draw(x1, y1, x2, y2, 0);
13644               else
13645                 /* Stars */
13646                 {
13647                   brush_draw(x1, y1, xv, yv, 0);
13648                   brush_draw(xv, yv, x2, y2, 0);
13649                 }
13650             }
13651 
13652           if (xx % 10 == 0)
13653             update_canvas(0, 0, WINDOW_WIDTH - r_ttoolopt.w, (button_h * 7) + 40 + HEIGHTOFFSET);
13654         }
13655     }
13656 
13657 
13658   /* Update it! */
13659 
13660   if (use_brush)
13661     {
13662       if (abs(rx) > abs(ry))
13663         rmax = abs(rx) + 20;
13664       else
13665         rmax = abs(ry) + 20;
13666 
13667       update_canvas(sx - rmax + offx, sy - rmax + offy, sx + rmax + offx, sy + rmax + offy);
13668     }
13669 
13670 
13671   /* Return to normal brush (for paint brush and line tools): */
13672 
13673   if (use_brush)
13674     {
13675       cur_brush = old_brush;
13676       render_brush();
13677     }
13678 }
13679 
13680 
13681 /**
13682  * FIXME
13683  */
13684 /* What angle is the mouse away from the center of a shape? */
shape_rotation(int ctr_x,int ctr_y,int ox,int oy)13685 static int shape_rotation(int ctr_x, int ctr_y, int ox, int oy)
13686 {
13687   int deg;
13688 
13689   deg = (atan2(oy - ctr_y, ox - ctr_x) * 180 / M_PI);
13690 
13691   if (shape_reverse) {
13692     deg = (deg + 180) % 360;
13693   }
13694 
13695   if (shape_radius < 50)
13696     deg = ((deg - 15) / 30) * 30;
13697   else if (shape_radius < 100)
13698     deg = ((deg - 7) / 15) * 15;
13699 
13700   /* Disabled b/c it adversely affected shape_locked shapes (square & octagon)
13701      with corner-dragged shapes; disabling doesn't seem to cause problems
13702      with any shape, in either drag mode... -bjk 2020-11-10 */
13703 /*
13704   if (shape_locked[cur_shape])
13705     {
13706       int angle_skip;
13707 
13708       angle_skip = 360 / shape_sides[cur_shape];
13709       deg = deg % angle_skip;
13710     }
13711 */
13712 
13713   return (deg);
13714 }
13715 
13716 
13717 /**
13718  * FIXME
13719  */
13720 /* What angle is the mouse away from a brush drag or line draw? */
brush_rotation(int ctr_x,int ctr_y,int ox,int oy)13721 static int brush_rotation(int ctr_x, int ctr_y, int ox, int oy)
13722 {
13723   int deg;
13724 
13725   deg = (atan2(oy - ctr_y, ox - ctr_x) * 180 / M_PI);
13726 
13727   return (deg);
13728 }
13729 
13730 
13731 /* Prompt to ask whether user wishes to save over old version of their file */
13732 #define PROMPT_SAVE_OVER_TXT gettext_noop("Replace the picture with your changes?")
13733 
13734 /* Positive response to saving over old version
13735    (like a 'File:Save' action in other applications) */
13736 #define PROMPT_SAVE_OVER_YES gettext_noop("Yes, replace the old one!")
13737 
13738 /* Negative response to saving over old version (saves a new image)
13739    (like a 'File:Save As...' action in other applications) */
13740 #define PROMPT_SAVE_OVER_NO  gettext_noop("No, save a new file!")
13741 
13742 
13743 /**
13744  * FIXME
13745  */
13746 /* Save the current image: */
do_save(int tool,int dont_show_success_results)13747 static int do_save(int tool, int dont_show_success_results)
13748 {
13749   int scroll;
13750   char *fname;
13751   char tmp[1024];
13752   SDL_Surface *thm;
13753   FILE *fi;
13754 
13755   /* Was saving completely disabled? */
13756 
13757   if (disable_save)
13758     return 0;
13759 
13760   scroll = (NUM_TOOLS > buttons_tall * gd_tools.cols) ? img_scroll_down->h : 0;
13761   tmp_apply_uncommited_text();
13762 
13763   SDL_BlitSurface(canvas, NULL, save_canvas, NULL);
13764   SDL_BlitSurface(label, NULL, save_canvas, NULL);
13765 
13766   if (promptless_save == SAVE_OVER_NO)
13767     {
13768       /* Never save over - _always_ save a new file! */
13769 
13770       get_new_file_id();
13771     }
13772   else if (promptless_save == SAVE_OVER_PROMPT)
13773     {
13774       /* Saving the same picture? */
13775 
13776       if (file_id[0] != '\0')
13777         {
13778           /* We sure we want to do that? */
13779 
13780           if (do_prompt_image_snd(PROMPT_SAVE_OVER_TXT,
13781                                   PROMPT_SAVE_OVER_YES,
13782                                   PROMPT_SAVE_OVER_NO,
13783                                   img_save_over, NULL, NULL, SND_AREYOUSURE,
13784                                   (TOOL_SAVE % 2) * button_w + button_w / 2,
13785 				  (TOOL_SAVE / 2) * button_h + r_ttools.h + button_h / 2 - tool_scroll * button_h / gd_tools.cols + scroll) == 0)
13786             {
13787               /* No - Let's save a new picture! */
13788 
13789               get_new_file_id();
13790             }
13791           if (tool == TOOL_TEXT || tool == TOOL_LABEL)
13792             do_render_cur_text(0);
13793         }
13794       else
13795         {
13796           /* Saving a new picture: */
13797 
13798           get_new_file_id();
13799         }
13800     }
13801   else if (promptless_save == SAVE_OVER_ALWAYS)
13802     {
13803       if (file_id[0] == '\0')
13804         get_new_file_id();
13805     }
13806 
13807 
13808   /* Make sure we have a ~/.tuxpaint directory: */
13809 
13810   show_progress_bar(screen);
13811   do_setcursor(cursor_watch);
13812 
13813   if (!make_directory(DIR_SAVE, "", "Can't create user data directory (E002)"))
13814     {
13815       fprintf(stderr, "Cannot save the any pictures! SORRY!\n\n");
13816       draw_tux_text(TUX_OOPS, strerror(errno), 0);
13817       return 0;
13818     }
13819 
13820   show_progress_bar(screen);
13821 
13822 
13823   /* Make sure we have a ~/.tuxpaint/saved directory: */
13824 
13825   if (!make_directory(DIR_SAVE, "saved", "Can't create user data directory (for saved drawings) (E003)"))
13826     {
13827       fprintf(stderr, "Cannot save any pictures! SORRY!\n\n");
13828       draw_tux_text(TUX_OOPS, strerror(errno), 0);
13829       return 0;
13830     }
13831 
13832   show_progress_bar(screen);
13833 
13834 
13835   /* Make sure we have a ~/.tuxpaint/saved/.thumbs/ directory: */
13836 
13837   if (!make_directory(DIR_SAVE, "saved/.thumbs", "Can't create user data thumbnail directory (for saved drawings' thumbnails) (E004)"))
13838     {
13839       fprintf(stderr, "Cannot save any pictures! SORRY!\n\n");
13840       draw_tux_text(TUX_OOPS, strerror(errno), 0);
13841       return 0;
13842     }
13843 
13844   show_progress_bar(screen);
13845 
13846   /* Make sure we have a ~/.tuxpaint/saved/.label/ directory: */
13847 
13848   if (!make_directory(DIR_SAVE, "saved/.label", "Can't create label information directory (E005)"))
13849     {
13850       fprintf(stderr, "Cannot save label information! SORRY!\n\n");
13851       draw_tux_text(TUX_OOPS, strerror(errno), 0);
13852       return 0;
13853     }
13854 
13855 
13856   /* Save the file: */
13857 
13858   safe_snprintf(tmp, sizeof(tmp), "saved/%s%s", file_id, FNAME_EXTENSION);
13859   fname = get_fname(tmp, DIR_SAVE);
13860   debug(fname);
13861 
13862   fi = fopen(fname, "wb");
13863   if (fi == NULL)
13864     {
13865       fprintf(stderr,
13866               "\nError: Couldn't save the current image!\n"
13867               "%s\n" "The system error that occurred was:\n" "%s\n\n", fname, strerror(errno));
13868 
13869       draw_tux_text(TUX_OOPS, strerror(errno), 0);
13870     }
13871   else
13872     {
13873       if (!do_png_save(fi, fname, save_canvas, 1))
13874         {
13875           free(fname);
13876           return 0;
13877         }
13878     }
13879 
13880   free(fname);
13881 
13882 
13883   show_progress_bar(screen);
13884 
13885 
13886   /* Save thumbnail, too: */
13887 
13888   /* (Was thumbnail in old directory, rather than under .thumbs?) */
13889 
13890   safe_snprintf(tmp, sizeof(tmp), "saved/%s-t%s", file_id, FNAME_EXTENSION);
13891   fname = get_fname(tmp, DIR_SAVE);
13892   fi = fopen(fname, "r");
13893   if (fi != NULL)
13894     {
13895       fclose(fi);
13896     }
13897   else
13898     {
13899       /* No old thumbnail!  Save this image's thumbnail in the new place,
13900          under ".thumbs" */
13901 
13902       safe_snprintf(tmp, sizeof(tmp), "saved/.thumbs/%s-t%s", file_id, FNAME_EXTENSION);
13903       fname = get_fname(tmp, DIR_SAVE);
13904     }
13905 
13906   debug(fname);
13907 
13908   thm = thumbnail(save_canvas, THUMB_W - 20, THUMB_H - 20, 0);
13909 
13910   fi = fopen(fname, "wb");
13911   if (fi == NULL)
13912     {
13913       fprintf(stderr, "\nError: Couldn't save thumbnail of image!\n"
13914               "%s\n" "The system error that occurred was:\n" "%s\n\n", fname, strerror(errno));
13915     }
13916   else
13917     {
13918       do_png_save(fi, fname, thm, 0);
13919     }
13920   SDL_FreeSurface(thm);
13921 
13922   free(fname);
13923 
13924 #if 0                           /* No more writing the .dat file */
13925   /* Write 'starter' and/or canvas color info, if it's useful to: */
13926 
13927   if (starter_id[0] != '\0' ||
13928       template_id[0] != '\0' || canvas_color_r != 255 || canvas_color_g != 255 || canvas_color_b != 255)
13929     {
13930       safe_snprintf(tmp, sizeof(tmp), "saved/%s.dat", file_id);
13931       fname = get_fname(tmp, DIR_SAVE);
13932       fi = fopen(fname, "w");
13933       if (fi != NULL)
13934         {
13935           fprintf(fi, "%s\n", starter_id);
13936           fprintf(fi, "%d %d %d\n", starter_mirrored, starter_flipped, starter_personal);
13937           fprintf(fi, "c%d %d %d\n", canvas_color_r, canvas_color_g, canvas_color_b);
13938           fprintf(fi, "T%s\n", template_id);
13939           fprintf(fi, "%d\n", template_personal);
13940           fclose(fi);
13941         }
13942 
13943       free(fname);
13944     }
13945 #endif
13946 
13947   /* All happy! */
13948 
13949   playsound(screen, 0, SND_SAVE, 1, SNDPOS_CENTER, SNDDIST_NEAR);
13950 
13951   if (!dont_show_success_results)
13952     {
13953       draw_tux_text(TUX_DEFAULT, tool_tips[TOOL_SAVE], 1);
13954       do_setcursor(cursor_arrow);
13955     }
13956 
13957   undo_tmp_applied_text();
13958 
13959   return 1;
13960 }
13961 
13962 /**
13963  * FIXME
13964  */
set_chunk_data(unsigned char ** chunk_data,size_t * chunk_data_len,size_t uncompressed_size,Bytef * data,size_t dataLen)13965 static void set_chunk_data(unsigned char **chunk_data, size_t * chunk_data_len, size_t uncompressed_size, Bytef * data,
13966                            size_t dataLen)
13967 {
13968   int headersLen;
13969   unsigned int i;
13970   char *line, *headers, *cdata;
13971   size_t line_sz, headers_sz;
13972 
13973   headersLen = 0;
13974   headers_sz = 256;
13975   headers = calloc(headers_sz, 1);
13976   line_sz = 256;
13977   line = calloc(line_sz, 1);
13978 
13979   safe_strncat(headers, "Tuxpaint\n", headers_sz);
13980   safe_strncat(headers, "Tuxpaint_" VER_VERSION "\n", headers_sz);
13981   safe_snprintf(line, line_sz, "%lu%s", uncompressed_size, "\n");
13982   safe_strncat(headers, line, headers_sz);
13983   safe_snprintf(line, line_sz, "%lu%s", dataLen, "\n");
13984   safe_strncat(headers, line, headers_sz);
13985 
13986   headersLen = strlen(headers);
13987   *chunk_data_len = headersLen + dataLen;
13988 
13989   cdata = calloc(*chunk_data_len, sizeof(unsigned char *));
13990   strcat(cdata, headers); /* FIXME: Use strncat() */
13991 
13992   for (i = 0; i < dataLen; i++)
13993     cdata[headersLen + i] = data[i];
13994   *chunk_data = (unsigned char *)cdata;
13995 
13996   free(line);
13997   free(headers);
13998 }
13999 
14000 /**
14001  * FIXME
14002  */
do_png_embed_data(png_structp png_ptr)14003 static void do_png_embed_data(png_structp png_ptr)
14004 {
14005 
14006   /* Embedding data and labels in the png file */
14007 
14008   /*
14009      Tuxpaint chunks:
14010 
14011      bKGD background color
14012 
14013      Custom chunks:
14014 
14015      tpDT -> 0 the traditional .dat file
14016      tpFG -> 1 the starter foreground surface with the transparent pixels cleaned up
14017      tpBG -> 2 the starter background surface cleared from what is covered by the foreground to compress better
14018      tpLD -> 3 the label diff
14019      tpLL -> 4 the label data
14020 
14021      Except in tpDT, the data of all other custom chunks will be compressed
14022 
14023      Chunk data must have a header to avoid conflicts with other software that may use similar names
14024      Headers are composed by four fields delimited with "\n" :
14025      The string "Tuxpaint" to easy identify them as Tuxpaint chunks.
14026      A string identifying the sofware who created it, in our case "Tuxpaint_"VER_VERSION No spaces allowed
14027      The size of the uncompressed data.
14028      The sizeof the compressed data following. These two are only relevant for compressed chunks
14029      After the fourth "\n" comes the data itself
14030    */
14031   int x, y;
14032   Uint8 r, g, b, a;
14033 
14034   png_unknown_chunk tuxpaint_chunks[5];
14035   size_t size_of_uncompressed_label_data, chunk_data_len;
14036   unsigned char *sbk_pixs;
14037   uLongf compressedLen;
14038   unsigned char *chunk_data;
14039   Bytef *compressed_data;
14040 
14041 #ifdef fmemopen_alternative
14042   char *fname;
14043 #endif
14044   char *ldata;
14045   FILE *lfi;
14046   int list_ctr = 0;
14047   Uint32 pix;
14048   int alpha_size;
14049   Uint32 i;
14050   struct label_node *current_node;
14051   char *char_stream, *line;
14052   size_t dat_size, char_stream_sz, line_sz;
14053 
14054 
14055   /* Starter foreground */
14056   if (img_starter)
14057     {
14058 #ifdef DEBUG
14059       printf("Saving starter... %d\n", (int)(intptr_t) img_starter);    //EP added (intptr_t) to avoid warning on x64
14060 #endif
14061       sbk_pixs = malloc(img_starter->h * img_starter->w * 4);
14062       compressedLen = compressBound(img_starter->h * img_starter->w * 4);
14063 
14064       compressed_data = malloc(compressedLen * sizeof(Bytef *));
14065 
14066       if (SDL_MUSTLOCK(img_starter))
14067         SDL_LockSurface(img_starter);
14068 
14069       for (y = 0; y < img_starter->h; y++)
14070         for (x = 0; x < img_starter->w; x++)
14071           {
14072             SDL_GetRGBA(getpixels[img_starter->format->BytesPerPixel] (img_starter, x, y), img_starter->format, &r, &g,
14073                         &b, &a);
14074 
14075 /* clear the transparent pixels assigning the same r g and b values */
14076             if (a == SDL_ALPHA_TRANSPARENT)
14077               {
14078                 sbk_pixs[4 * (y * img_starter->w + x)] = SDL_ALPHA_TRANSPARENT;
14079                 sbk_pixs[4 * (y * img_starter->w + x) + 1] = SDL_ALPHA_TRANSPARENT;
14080                 sbk_pixs[4 * (y * img_starter->w + x) + 2] = SDL_ALPHA_TRANSPARENT;
14081                 sbk_pixs[4 * (y * img_starter->w + x) + 3] = SDL_ALPHA_TRANSPARENT;
14082               }
14083             else
14084               {
14085                 sbk_pixs[4 * (y * img_starter->w + x)] = r;
14086                 sbk_pixs[4 * (y * img_starter->w + x) + 1] = g;
14087                 sbk_pixs[4 * (y * img_starter->w + x) + 2] = b;
14088                 sbk_pixs[4 * (y * img_starter->w + x) + 3] = a;
14089               }
14090           }
14091 
14092       if (SDL_MUSTLOCK(img_starter))
14093         SDL_UnlockSurface(img_starter);
14094 
14095       compress(compressed_data, &compressedLen, sbk_pixs, img_starter->h * img_starter->w * 4);
14096       set_chunk_data(&chunk_data, &chunk_data_len, img_starter->w * img_starter->h * 4, compressed_data, compressedLen);
14097 
14098       tuxpaint_chunks[1].data = (png_byte *) chunk_data;
14099       tuxpaint_chunks[1].size = chunk_data_len;
14100       tuxpaint_chunks[1].location = PNG_HAVE_IHDR;
14101       tuxpaint_chunks[1].name[0] = 't';
14102       tuxpaint_chunks[1].name[1] = 'p';
14103       tuxpaint_chunks[1].name[2] = 'F';
14104       tuxpaint_chunks[1].name[3] = 'G';
14105       tuxpaint_chunks[1].name[4] = '\0';
14106       png_write_chunk(png_ptr, tuxpaint_chunks[1].name, tuxpaint_chunks[1].data, tuxpaint_chunks[1].size);
14107 
14108       free(compressed_data);
14109       free(chunk_data);
14110       free(sbk_pixs);
14111     }
14112 
14113   /* Starter background */
14114   if (img_starter_bkgd)
14115     {
14116       sbk_pixs = malloc(img_starter_bkgd->w * img_starter_bkgd->h * 3);
14117       compressedLen = compressBound(img_starter_bkgd->h * img_starter_bkgd->w * 3);
14118 
14119       compressed_data = malloc(compressedLen * sizeof(Bytef *));
14120 
14121       if (SDL_MUSTLOCK(img_starter_bkgd))
14122         SDL_LockSurface(img_starter_bkgd);
14123 
14124       for (y = 0; y < img_starter_bkgd->h; y++)
14125         for (x = 0; x < img_starter_bkgd->w; x++)
14126           {
14127             SDL_GetRGB(getpixels[img_starter_bkgd->format->BytesPerPixel] (img_starter_bkgd, x, y),
14128                        img_starter_bkgd->format, &r, &g, &b);
14129 
14130             sbk_pixs[3 * (y * img_starter_bkgd->w + x)] = r;
14131             sbk_pixs[3 * (y * img_starter_bkgd->w + x) + 1] = g;
14132             sbk_pixs[3 * (y * img_starter_bkgd->w + x) + 2] = b;
14133           }
14134 
14135       /* Clear the parts covered by the foreground */
14136       if (img_starter)
14137         {
14138           if (SDL_MUSTLOCK(img_starter))
14139             SDL_LockSurface(img_starter);
14140           for (y = 0; y < img_starter_bkgd->h; y++)
14141             for (x = 0; x < img_starter_bkgd->w; x++)
14142               {
14143                 SDL_GetRGBA(getpixels[img_starter->format->BytesPerPixel] (img_starter, x, y), img_starter->format, &r,
14144                             &g, &b, &a);
14145 
14146                 if (a == SDL_ALPHA_OPAQUE)
14147                   {
14148                     sbk_pixs[3 * (y * img_starter_bkgd->w + x)] = SDL_ALPHA_TRANSPARENT;
14149                     sbk_pixs[3 * (y * img_starter_bkgd->w + x) + 1] = SDL_ALPHA_TRANSPARENT;
14150                     sbk_pixs[3 * (y * img_starter_bkgd->w + x) + 2] = SDL_ALPHA_TRANSPARENT;
14151                   }
14152               }
14153           if (SDL_MUSTLOCK(img_starter))
14154             SDL_UnlockSurface(img_starter);
14155         }
14156 
14157       if (SDL_MUSTLOCK(img_starter_bkgd))
14158         SDL_UnlockSurface(img_starter_bkgd);
14159 
14160 #ifdef DEBUG
14161       printf("%d \n", (int)compressedLen);
14162 #endif
14163 
14164       compress(compressed_data, &compressedLen, sbk_pixs, img_starter_bkgd->h * img_starter_bkgd->w * 3);
14165 
14166       set_chunk_data(&chunk_data, &chunk_data_len, img_starter_bkgd->w * img_starter_bkgd->h * 3, compressed_data,
14167                      compressedLen);
14168 #ifdef DEBUG
14169       printf("%d \n", (int)compressedLen);
14170 #endif
14171 
14172 
14173       tuxpaint_chunks[2].data = (png_byte *) chunk_data;
14174       tuxpaint_chunks[2].size = chunk_data_len;
14175       tuxpaint_chunks[2].location = PNG_HAVE_IHDR;
14176       tuxpaint_chunks[2].name[0] = 't';
14177       tuxpaint_chunks[2].name[1] = 'p';
14178       tuxpaint_chunks[2].name[2] = 'B';
14179       tuxpaint_chunks[2].name[3] = 'G';
14180       tuxpaint_chunks[2].name[4] = '\0';
14181       png_write_chunk(png_ptr, tuxpaint_chunks[2].name, tuxpaint_chunks[2].data, tuxpaint_chunks[2].size);
14182 
14183       free(compressed_data);
14184       free(chunk_data);
14185       free(sbk_pixs);
14186     }
14187 
14188   /* Label:  diff from label surface to canvas surface */
14189   if (label && are_labels())
14190     {
14191       sbk_pixs = malloc(label->h * label->w * 4);
14192       compressedLen = (uLongf) compressBound(label->h * label->w * 4);
14193       compressed_data = malloc(compressedLen * sizeof(Bytef *));
14194 
14195       if (SDL_MUSTLOCK(label))
14196         SDL_LockSurface(label);
14197       if (SDL_MUSTLOCK(canvas))
14198         SDL_LockSurface(canvas);
14199 
14200       for (y = 0; y < label->h; y++)
14201         {
14202           for (x = 0; x < label->w; x++)
14203             {
14204               SDL_GetRGBA(getpixels[label->format->BytesPerPixel] (label, x, y), label->format, &r, &g, &b, &a);
14205               if (a != SDL_ALPHA_TRANSPARENT)
14206                 {
14207                   SDL_GetRGB(getpixels[canvas->format->BytesPerPixel] (canvas, x, y), canvas->format, &r, &g, &b);
14208 
14209                   sbk_pixs[4 * (y * label->w + x)] = r;
14210                   sbk_pixs[4 * (y * label->w + x) + 1] = g;
14211                   sbk_pixs[4 * (y * label->w + x) + 2] = b;
14212                   sbk_pixs[4 * (y * label->w + x) + 3] = SDL_ALPHA_OPAQUE;
14213                 }
14214               else
14215                 {
14216                   sbk_pixs[4 * (y * label->w + x)] = SDL_ALPHA_TRANSPARENT;
14217                   sbk_pixs[4 * (y * label->w + x) + 1] = SDL_ALPHA_TRANSPARENT;
14218                   sbk_pixs[4 * (y * label->w + x) + 2] = SDL_ALPHA_TRANSPARENT;
14219                   sbk_pixs[4 * (y * label->w + x) + 3] = SDL_ALPHA_TRANSPARENT;
14220                 }
14221             }
14222         }
14223 
14224       if (SDL_MUSTLOCK(label))
14225         SDL_UnlockSurface(label);
14226       if (SDL_MUSTLOCK(canvas))
14227         SDL_UnlockSurface(canvas);
14228 
14229       compress(compressed_data, &compressedLen, sbk_pixs, canvas->h * canvas->w * 4);
14230       set_chunk_data(&chunk_data, &chunk_data_len, canvas->w * canvas->h * 4, compressed_data, compressedLen);
14231 
14232       tuxpaint_chunks[3].data = chunk_data;
14233       tuxpaint_chunks[3].size = chunk_data_len;
14234       tuxpaint_chunks[3].location = PNG_HAVE_IHDR;
14235       tuxpaint_chunks[3].name[0] = 't';
14236       tuxpaint_chunks[3].name[1] = 'p';
14237       tuxpaint_chunks[3].name[2] = 'L';
14238       tuxpaint_chunks[3].name[3] = 'D';
14239       tuxpaint_chunks[3].name[4] = '\0';
14240 
14241       png_write_chunk(png_ptr, tuxpaint_chunks[3].name, tuxpaint_chunks[3].data, tuxpaint_chunks[3].size);
14242       free(compressed_data);
14243       free(chunk_data);
14244       free(sbk_pixs);
14245 
14246       /* Label data */
14247 
14248 #ifndef fmemopen_alternative
14249 
14250       lfi = open_memstream(&ldata, &size_of_uncompressed_label_data);
14251 
14252 #else
14253 #ifndef WIN32
14254       fname = get_fname("tmpfile", DIR_SAVE);
14255 #else
14256       fname = get_temp_fname("tmpfile");
14257 #endif
14258 
14259       lfi = fopen(fname, "wb+");
14260 
14261 #endif
14262 
14263       current_node = current_label_node;
14264       while (current_node != NULL)
14265         {
14266           if (current_node->is_enabled && current_node->save_texttool_len > 0)
14267             list_ctr = list_ctr + 1;
14268           current_node = current_node->next_to_down_label_node;
14269         }
14270 
14271       fprintf(lfi, "%d\n", list_ctr);
14272       fprintf(lfi, "%d\n", r_canvas.w);
14273       fprintf(lfi, "%d\n\n", r_canvas.h);
14274 
14275       current_node = start_label_node;
14276       while (current_node && current_node != first_label_node_in_redo_stack)
14277         {
14278           if (current_node->is_enabled == TRUE && current_node->save_texttool_len > 0)
14279             {
14280 
14281 #ifdef WIN32
14282               iconv_t trans;
14283               wchar_t *wch;
14284               char *conv, *conv2;
14285               size_t in, out;
14286 
14287               in = out = 1;
14288               conv = malloc(255);
14289               trans = iconv_open("UTF-8", "WCHAR_T");
14290 
14291               fprintf(lfi, "%u\n", current_node->save_texttool_len);
14292               for (i = 0; i < current_node->save_texttool_len; i++)
14293                 {
14294                   conv2 = conv;
14295                   in = 2;
14296                   out = 10;
14297                   wch = &current_node->save_texttool_str[i];
14298                   iconv(trans, (char **)&wch, &in, &conv, &out);
14299                   conv[0] = '\0';
14300                   fprintf(lfi, "%s", conv2);
14301                 }
14302 #else
14303               fprintf(lfi, "%u\n", current_node->save_texttool_len);
14304 
14305               for (i = 0; i < current_node->save_texttool_len; i++)
14306                 {
14307                   fprintf(lfi, "%lc", (wint_t) current_node->save_texttool_str[i]);
14308                 }
14309 #endif
14310 
14311               fprintf(lfi, "\n");
14312 
14313 
14314               fprintf(lfi, "%u\n", current_node->save_color.r);
14315               fprintf(lfi, "%u\n", current_node->save_color.g);
14316               fprintf(lfi, "%u\n", current_node->save_color.b);
14317               fprintf(lfi, "%d\n", current_node->save_width);
14318               fprintf(lfi, "%d\n", current_node->save_height);
14319               fprintf(lfi, "%u\n", current_node->save_x);
14320               fprintf(lfi, "%u\n", current_node->save_y);
14321 
14322               if (current_node->save_font_type == NULL) /* Fonts yet setted */
14323                 {
14324                   fprintf(lfi, "%d\n", current_node->save_cur_font);
14325                   fprintf(lfi, "%s\n", TTF_FontFaceFamilyName(getfonthandle(current_node->save_cur_font)->ttf_font));
14326                 }
14327               else
14328                 {
14329                   fprintf(lfi, "%d\n", 0);
14330                   fprintf(lfi, "%s\n", current_node->save_font_type);
14331                 }
14332 
14333               fprintf(lfi, "%d\n", current_node->save_text_state);
14334               fprintf(lfi, "%u\n", current_node->save_text_size);
14335 
14336               SDL_LockSurface(current_node->label_node_surface);
14337               alpha_size = sizeof(Uint8);
14338               for (x = 0; x < current_node->save_width; x++)
14339                 for (y = 0; y < current_node->save_height; y++)
14340                   {
14341                     /* *INDENT-OFF* */
14342                     pix = getpixels[current_node->label_node_surface->format->BytesPerPixel](current_node->label_node_surface, x, y);
14343                     /* *INDENT-ON* */
14344                     SDL_GetRGBA(pix, current_label_node->label_node_surface->format, &r, &g, &b, &a);
14345                     fwrite(&a, alpha_size, 1, lfi);
14346                   }
14347               SDL_UnlockSurface(current_node->label_node_surface);
14348               fprintf(lfi, "\n\n");
14349             }
14350           current_node = current_node->next_to_up_label_node;
14351 #ifdef DEBUG
14352           printf("cur %p, red %p\n", current_node, first_label_node_in_redo_stack);
14353 #endif
14354         }
14355 
14356 #ifdef fmemopen_alternative
14357       size_of_uncompressed_label_data = ftell(lfi);
14358       rewind(lfi);
14359       ldata = malloc(size_of_uncompressed_label_data);
14360       for (i = 0; i < size_of_uncompressed_label_data; i++)
14361         fread(&ldata[i], 1, 1, lfi);
14362 #endif
14363 
14364       fclose(lfi);
14365 
14366       compressedLen = compressBound(size_of_uncompressed_label_data);
14367       compressed_data = malloc(compressedLen * sizeof(Bytef *));
14368       compress((Bytef *) compressed_data, &compressedLen, (unsigned char *)ldata, size_of_uncompressed_label_data);
14369       set_chunk_data(&chunk_data, &chunk_data_len, size_of_uncompressed_label_data, compressed_data, compressedLen);
14370 
14371       tuxpaint_chunks[4].data = chunk_data;
14372       tuxpaint_chunks[4].size = chunk_data_len;
14373       tuxpaint_chunks[4].location = PNG_HAVE_IHDR;
14374       tuxpaint_chunks[4].name[0] = 't';
14375       tuxpaint_chunks[4].name[1] = 'p';
14376       tuxpaint_chunks[4].name[2] = 'L';
14377       tuxpaint_chunks[4].name[3] = 'L';
14378       tuxpaint_chunks[4].name[4] = '\0';
14379 
14380       png_write_chunk(png_ptr, tuxpaint_chunks[4].name, tuxpaint_chunks[4].data, tuxpaint_chunks[4].size);
14381 
14382       free(compressed_data);
14383       free(chunk_data);
14384     }
14385 
14386 
14387   /* Write 'starter' and/or canvas color info, if it's useful to: */
14388 
14389   if (starter_id[0] != '\0' ||
14390       template_id[0] != '\0' || canvas_color_r != 255 || canvas_color_g != 255 || canvas_color_b != 255)
14391     {
14392       /* Usually the .dat data are less than 100 bytes, hope this keeps line and char_stream in the safe side */
14393       line_sz = 256;
14394       line = calloc(line_sz, 1);
14395       char_stream_sz = 256 + sizeof(starter_id) + sizeof(template_id),
14396       char_stream = calloc(char_stream_sz, 1);
14397 
14398       safe_snprintf(char_stream, char_stream_sz, "%s\n", starter_id);
14399 
14400       safe_snprintf(line, line_sz, "%d %d %d\n", starter_mirrored, starter_flipped, starter_personal);
14401       safe_strncat(char_stream, line, char_stream_sz);
14402 
14403       safe_snprintf(line, line_sz, "c%d %d %d\n", canvas_color_r, canvas_color_g, canvas_color_b);
14404       safe_strncat(char_stream, line, char_stream_sz);
14405 
14406       safe_snprintf(line, line_sz, "T%s\n", template_id);
14407       safe_strncat(char_stream, line, char_stream_sz);
14408 
14409       safe_snprintf(line, line_sz, "%d\n", template_personal);
14410       safe_strncat(char_stream, line, char_stream_sz);
14411 
14412       safe_snprintf(line, line_sz, "M%d\n", starter_modified);
14413       safe_strncat(char_stream, line, char_stream_sz);
14414 
14415       dat_size = strlen(char_stream);
14416 
14417       set_chunk_data(&chunk_data, &chunk_data_len, dat_size, (Bytef *) char_stream, dat_size);
14418 
14419       tuxpaint_chunks[4].data = chunk_data;
14420       tuxpaint_chunks[4].size = chunk_data_len;
14421       tuxpaint_chunks[4].location = PNG_HAVE_IHDR;
14422       tuxpaint_chunks[4].name[0] = 't';
14423       tuxpaint_chunks[4].name[1] = 'p';
14424       tuxpaint_chunks[4].name[2] = 'D';
14425       tuxpaint_chunks[4].name[3] = 'T';
14426       tuxpaint_chunks[4].name[4] = '\0';
14427 
14428       png_write_chunk(png_ptr, tuxpaint_chunks[4].name, tuxpaint_chunks[4].data, tuxpaint_chunks[4].size);
14429 
14430       free(char_stream);
14431       free(line);
14432       free(chunk_data);
14433     }
14434 }
14435 
14436 /**
14437  * FIXME
14438  */
14439 /* Actually save the PNG data to the file stream: */
do_png_save(FILE * fi,const char * const fname,SDL_Surface * surf,int embed)14440 static int do_png_save(FILE * fi, const char *const fname, SDL_Surface * surf, int embed)
14441 {
14442   png_structp png_ptr;
14443   png_infop info_ptr;
14444   png_text text_ptr[4];
14445   unsigned char **png_rows;
14446   Uint8 r, g, b;
14447   int x, y, count;
14448 
14449   Uint32(*getpixel) (SDL_Surface *, int, int) = getpixels[surf->format->BytesPerPixel];
14450 
14451 
14452   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
14453   if (png_ptr == NULL)
14454     {
14455       fclose(fi);
14456       png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
14457 
14458       fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
14459       draw_tux_text(TUX_OOPS, strerror(errno), 0);
14460     }
14461   else
14462     {
14463       info_ptr = png_create_info_struct(png_ptr);
14464       if (info_ptr == NULL)
14465         {
14466           fclose(fi);
14467           png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
14468 
14469           fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
14470           draw_tux_text(TUX_OOPS, strerror(errno), 0);
14471         }
14472       else
14473         {
14474           if (setjmp(png_jmpbuf(png_ptr)))
14475             {
14476               fclose(fi);
14477               png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
14478 
14479               fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
14480               draw_tux_text(TUX_OOPS, strerror(errno), 0);
14481 
14482               return 0;
14483             }
14484           else
14485             {
14486               png_init_io(png_ptr, fi);
14487 
14488               png_set_IHDR(png_ptr, info_ptr, surf->w, surf->h, 8, PNG_COLOR_TYPE_RGB, 1, PNG_COMPRESSION_TYPE_BASE,
14489                            PNG_FILTER_TYPE_BASE);
14490 
14491               png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
14492 
14493               /* Set headers */
14494 
14495               count = 0;
14496 
14497               /*
14498                  if (title != NULL && strlen(title) > 0)
14499                  {
14500                  text_ptr[count].key = "Title";
14501                  text_ptr[count].text = title;
14502                  text_ptr[count].compression = PNG_TEXT_COMPRESSION_NONE;
14503                  count++;
14504                  }
14505                */
14506 
14507               text_ptr[count].key = (png_charp) "Software";
14508               text_ptr[count].text = (png_charp) "Tux Paint " VER_VERSION " (" VER_DATE ")";
14509               text_ptr[count].compression = PNG_TEXT_COMPRESSION_NONE;
14510               count++;
14511 
14512 
14513               png_set_text(png_ptr, info_ptr, text_ptr, count);
14514 
14515               png_write_info(png_ptr, info_ptr);
14516 
14517               if (embed)
14518                 do_png_embed_data(png_ptr);
14519 
14520               /* Save the picture: */
14521 
14522               png_rows = malloc(sizeof(char *) * surf->h);
14523 
14524               for (y = 0; y < surf->h; y++)
14525                 {
14526                   png_rows[y] = malloc(sizeof(char) * 3 * surf->w);
14527 
14528                   for (x = 0; x < surf->w; x++)
14529                     {
14530                       SDL_GetRGB(getpixel(surf, x, y), surf->format, &r, &g, &b);
14531 
14532                       png_rows[y][x * 3 + 0] = r;
14533                       png_rows[y][x * 3 + 1] = g;
14534                       png_rows[y][x * 3 + 2] = b;
14535                     }
14536                 }
14537 
14538               png_write_image(png_ptr, png_rows);
14539 
14540               for (y = 0; y < surf->h; y++)
14541                 free(png_rows[y]);
14542 
14543               free(png_rows);
14544 
14545 
14546               png_write_end(png_ptr, NULL);
14547               png_destroy_write_struct(&png_ptr, &info_ptr);
14548               fclose(fi);
14549 
14550               return 1;
14551             }
14552         }
14553     }
14554 
14555   return 0;
14556 }
14557 
14558 /**
14559  * Generate a new file ID.  Used when saving a picture for
14560  * the first time (i.e., very first save, or saving as a new
14561  * file, rather than overwriting/replacing the existing version).
14562  *
14563  * Affects "file_id" string.
14564  */
get_new_file_id(void)14565 static void get_new_file_id(void)
14566 {
14567   time_t t;
14568 
14569   t = time(NULL);
14570 
14571   strftime(file_id, sizeof(file_id), "%Y%m%d%H%M%S", localtime(&t));
14572   debug(file_id);
14573 }
14574 
14575 
14576 /**
14577  * FIXME
14578  */
14579 /* Handle quitting (and prompting to save, if necessary!) */
do_quit(int tool)14580 static int do_quit(int tool)
14581 {
14582   int done, tmp_tool, scroll;
14583 
14584   if (!no_prompt_on_quit)
14585     {
14586       scroll = (NUM_TOOLS > buttons_tall * gd_tools.cols) ? img_scroll_down->h : 0;
14587       done = do_prompt_snd(PROMPT_QUIT_TXT,
14588                            PROMPT_QUIT_YES, PROMPT_QUIT_NO, SND_AREYOUSURE,
14589                            (TOOL_QUIT % 2) * button_w + button_w / 2,
14590 			   (TOOL_QUIT / 2) * button_h + r_ttools.h + button_h / 2 - tool_scroll * button_h / gd_tools.cols + scroll);
14591     }
14592   else
14593     {
14594       done = 1;
14595     }
14596 
14597   if (done && !been_saved && !disable_save)
14598     {
14599       if (autosave_on_quit ||
14600           do_prompt(PROMPT_QUIT_SAVE_TXT, PROMPT_QUIT_SAVE_YES, PROMPT_QUIT_SAVE_NO, screen->w / 2, screen->h / 2))
14601         {
14602           if (do_save(tool, 1))
14603             {
14604               /* Don't bug user about successful save when quitting -bjk 2007.05.15 */
14605               /* do_prompt(tool_tips[TOOL_SAVE], "OK", ""); */
14606             }
14607           else
14608             {
14609               /* Couldn't save!  Abort quit! */
14610 
14611               done = 0;
14612             }
14613         }
14614     }
14615   else
14616     {
14617       if (tool == TOOL_TEXT || tool == TOOL_LABEL)
14618         do_render_cur_text(0);
14619 
14620       /* Bring back stamp sound effects and speak buttons, if we were in
14621          Stamps tool: */
14622 
14623       tmp_tool = cur_tool;
14624       cur_tool = tool;
14625       draw_tux_text(TUX_BORED, "", 0);
14626       cur_tool = tmp_tool;
14627     }
14628   if (done)
14629     SDL_JoystickClose(joystick);
14630   return (done);
14631 }
14632 
14633 
14634 
14635 /* Open a saved image: */
14636 
14637 #define PLACE_COLOR_PALETTE (-1)
14638 #define PLACE_SAVED_DIR 0
14639 #define PLACE_PERSONAL_STARTERS_DIR 1
14640 #define PLACE_STARTERS_DIR 2
14641 #define PLACE_PERSONAL_TEMPLATES_DIR 3
14642 #define PLACE_TEMPLATES_DIR 4
14643 #define NUM_PLACES_TO_LOOK 5
14644 
14645 
14646 /* FIXME: This, do_slideshow() and do_new_dialog() should be combined
14647    and modularized! */
14648 /**
14649  * FIXME
14650  */
do_open(void)14651 static int do_open(void)
14652 {
14653   SDL_Surface *img, *img1, *img2, *org_surf;
14654   int things_alloced;
14655   SDL_Surface **thumbs = NULL;
14656   DIR *d;
14657   struct dirent *f;
14658   struct dirent2 *fs;
14659   int place;
14660   char *dirname[NUM_PLACES_TO_LOOK];
14661   char *rfname;
14662   char **d_names = NULL, **d_exts = NULL;
14663   int *d_places;
14664   FILE *fi;
14665   char fname[MAX_PATH];
14666   int num_files, i, done, slideshow, update_list, want_erase, want_export;
14667   int cur, which, num_files_in_dirs, j, any_saved_files;
14668   SDL_Rect dest;
14669   SDL_Event event;
14670   SDLKey key;
14671   Uint32 last_click_time;
14672   int last_click_which, last_click_button;
14673   int places_to_look;
14674   int opened_something;
14675   int val_x, val_y, motioner;
14676   int valhat_x, valhat_y, hatmotioner;
14677 
14678   val_x = val_y = motioner = 0;
14679   valhat_x = valhat_y = hatmotioner = 0;
14680   opened_something = 0;
14681 
14682   do
14683     {
14684       do_setcursor(cursor_watch);
14685 
14686       /* Allocate some space: */
14687 
14688       things_alloced = 32;
14689 
14690       fs = (struct dirent2 *)malloc(sizeof(struct dirent2) * things_alloced);
14691 
14692       num_files = 0;
14693       cur = 0;
14694       which = 0;
14695       slideshow = 0;
14696       num_files_in_dirs = 0;
14697       any_saved_files = 0;
14698 
14699 
14700       /* Open directories of images: */
14701 
14702       for (places_to_look = 0; places_to_look < NUM_PLACES_TO_LOOK; places_to_look++)
14703         {
14704           if (places_to_look == PLACE_SAVED_DIR)
14705             {
14706               /* Saved-images: */
14707 
14708               dirname[places_to_look] = get_fname("saved", DIR_SAVE);
14709             }
14710           else if (places_to_look == PLACE_PERSONAL_STARTERS_DIR)
14711             {
14712               /* Starters handled by New dialog... */
14713               dirname[places_to_look] = NULL;
14714               continue;
14715             }
14716           else if (places_to_look == PLACE_STARTERS_DIR)
14717             {
14718               /* Starters handled by New dialog... */
14719               dirname[places_to_look] = NULL;
14720               continue;
14721             }
14722           else if (places_to_look == PLACE_PERSONAL_TEMPLATES_DIR)
14723             {
14724               /* Templates handled by New dialog... */
14725               dirname[places_to_look] = NULL;
14726               continue;
14727             }
14728           else if (places_to_look == PLACE_TEMPLATES_DIR)
14729             {
14730               /* Templates handled by New dialog... */
14731               dirname[places_to_look] = NULL;
14732               continue;
14733             }
14734 
14735 
14736           /* Read directory of images and build thumbnails: */
14737 
14738           d = opendir(dirname[places_to_look]);
14739 
14740           if (d != NULL)
14741             {
14742               /* Gather list of files (for sorting): */
14743 
14744               do
14745                 {
14746                   f = readdir(d);
14747 
14748                   if (f != NULL)
14749                     {
14750                       memcpy(&(fs[num_files_in_dirs].f), f, sizeof(struct dirent));
14751                       fs[num_files_in_dirs].place = places_to_look;
14752 
14753                       num_files_in_dirs++;
14754 
14755                       if (places_to_look == PLACE_SAVED_DIR)
14756                         any_saved_files = 1;
14757 
14758                       if (num_files_in_dirs >= things_alloced)
14759                         {
14760                           things_alloced = things_alloced + 32;
14761 
14762                           /* FIXME: Valgrind says this is leaked -bjk 2007.07.19 */
14763                           fs = (struct dirent2 *)realloc(fs, sizeof(struct dirent2) * things_alloced);
14764                         }
14765                     }
14766                 }
14767               while (f != NULL);
14768 
14769               closedir(d);
14770             }
14771         }
14772 
14773 
14774       /* (Re)allocate space for the information about these files: */
14775 
14776       thumbs = (SDL_Surface * *)malloc(sizeof(SDL_Surface *) * num_files_in_dirs);
14777       d_places = (int *)malloc(sizeof(int) * num_files_in_dirs);
14778       d_names = (char **)malloc(sizeof(char *) * num_files_in_dirs);
14779       d_exts = (char **)malloc(sizeof(char *) * num_files_in_dirs);
14780 
14781 
14782       /* Sort: */
14783 
14784       qsort(fs, num_files_in_dirs, sizeof(struct dirent2), (int (*)(const void *, const void *))compare_dirent2s);
14785 
14786 
14787       /* Read directory of images and build thumbnails: */
14788 
14789       for (j = 0; j < num_files_in_dirs; j++)
14790         {
14791           f = &(fs[j].f);
14792           place = fs[j].place;
14793 
14794           show_progress_bar(screen);
14795 
14796           if (f != NULL)
14797             {
14798               debug(f->d_name);
14799 
14800               if (strcasestr(f->d_name, "-t.") == NULL && strcasestr(f->d_name, "-back.") == NULL)
14801                 {
14802                   if (strcasestr(f->d_name, FNAME_EXTENSION) != NULL
14803                       /* Support legacy BMP files for load: */
14804                       || strcasestr(f->d_name, ".bmp") != NULL)
14805                     {
14806                       safe_strncpy(fname, f->d_name, sizeof(fname));
14807                       if (strcasestr(fname, FNAME_EXTENSION) != NULL)
14808                         {
14809                           d_exts[num_files] = strdup(strcasestr(fname, FNAME_EXTENSION));
14810                           strcpy((char *)strcasestr(fname, FNAME_EXTENSION), ""); /* Safe; truncating */
14811                         }
14812 
14813                       if (strcasestr(fname, ".bmp") != NULL)
14814                         {
14815                           d_exts[num_files] = strdup(strcasestr(fname, ".bmp"));
14816                           strcpy((char *)strcasestr(fname, ".bmp"), ""); /* Safe; truncating */
14817                         }
14818 
14819                       d_names[num_files] = strdup(fname);
14820                       d_places[num_files] = place;
14821 
14822 
14823                       /* Is it the 'current' file we just loaded?
14824                          We'll make it the current selection! */
14825 
14826                       if (strcmp(d_names[num_files], file_id) == 0)
14827                         {
14828                           which = num_files;
14829                           cur = (which / 4) * 4;
14830 
14831                           /* Center the cursor (useful for when the last item is
14832                              selected first!) */
14833 
14834                           if (cur - 8 >= 0)
14835                             cur = cur - 8;
14836                           else if (cur - 4 >= 0)
14837                             cur = cur - 4;
14838                         }
14839 
14840 
14841                       /* Try to load thumbnail first: */
14842 
14843                       safe_snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
14844                                dirname[d_places[num_files]], d_names[num_files]);
14845                       debug(fname);
14846                       img = IMG_Load(fname);
14847 
14848                       if (img == NULL)
14849                         {
14850                           /* No thumbnail in the new location ("saved/.thumbs"),
14851                              try the old locatin ("saved/"): */
14852 
14853                           safe_snprintf(fname, sizeof(fname), "%s/%s-t.png",
14854                                    dirname[d_places[num_files]], d_names[num_files]);
14855                           debug(fname);
14856 
14857                           img = IMG_Load(fname);
14858                         }
14859 
14860                       if (img != NULL)
14861                         {
14862                           /* Loaded the thumbnail from one or the other location */
14863                           show_progress_bar(screen);
14864 
14865                           img1 = SDL_DisplayFormat(img);
14866                           SDL_FreeSurface(img);
14867 
14868                           /* if too big, or too small in both dimensions, rescale it
14869                              (for now: using old thumbnail as source for high speed, low quality) */
14870                           if (img1->w > THUMB_W - 20 || img1->h > THUMB_H - 20
14871                               || (img1->w < THUMB_W - 20 && img1->h < THUMB_H - 20))
14872                             {
14873                               img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
14874                               SDL_FreeSurface(img1);
14875                               img1 = img2;
14876                             }
14877 
14878                           thumbs[num_files] = img1;
14879 
14880                           if (thumbs[num_files] == NULL)
14881                             {
14882                               fprintf(stderr,
14883                                       "\nError: Couldn't create a thumbnail of " "saved image!\n" "%s\n", fname);
14884                             }
14885 
14886                           num_files++;
14887                         }
14888                       else
14889                         {
14890                           /* No thumbnail - load original: */
14891 
14892                           /* Make sure we have a ~/.tuxpaint/saved directory: */
14893                           if (make_directory(DIR_SAVE, "saved", "Can't create user data directory (for saved drawings) (E006)"))
14894                             {
14895                               /* (Make sure we have a .../saved/.thumbs/ directory:) */
14896                               make_directory(DIR_SAVE, "saved/.thumbs", "Can't create user data thumbnail directory (for saved drawings' thumbnails) (E007)");
14897                             }
14898 
14899 
14900                           if (img == NULL)
14901                             {
14902                               safe_snprintf(fname, sizeof(fname), "%s/%s", dirname[d_places[num_files]], f->d_name);
14903                               debug(fname);
14904                               img = myIMG_Load(fname);
14905                             }
14906 
14907 
14908                           show_progress_bar(screen);
14909 
14910                           if (img == NULL)
14911                             {
14912                               fprintf(stderr,
14913                                       "\nWarning: I can't open one of the saved files!\n"
14914                                       "%s\n"
14915                                       "The Simple DirectMedia Layer error that "
14916                                       "occurred was:\n" "%s\n\n", fname, SDL_GetError());
14917 
14918                               free(d_names[num_files]);
14919                               free(d_exts[num_files]);
14920                             }
14921                           else
14922                             {
14923                               /* Turn it into a thumbnail: */
14924 
14925                               img1 = SDL_DisplayFormatAlpha(img);
14926                               img2 = thumbnail2(img1, THUMB_W - 20, THUMB_H - 20, 0, 0);
14927                               SDL_FreeSurface(img1);
14928 
14929                               show_progress_bar(screen);
14930 
14931                               thumbs[num_files] = SDL_DisplayFormat(img2);
14932                               SDL_FreeSurface(img2);
14933                               if (thumbs[num_files] == NULL)
14934                                 {
14935                                   fprintf(stderr,
14936                                           "\nError: Couldn't create a thumbnail of " "saved image!\n" "%s\n", fname);
14937                                 }
14938 
14939                               SDL_FreeSurface(img);
14940 
14941                               show_progress_bar(screen);
14942 
14943 
14944                               /* Let's save this thumbnail, so we don't have to
14945                                  create it again next time 'Open' is called: */
14946 
14947                               if (d_places[num_files] == PLACE_SAVED_DIR)
14948                                 {
14949                                   debug("Saving thumbnail for this one!");
14950 
14951                                   safe_snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
14952                                            dirname[d_places[num_files]], d_names[num_files]);
14953 
14954                                   fi = fopen(fname, "wb");
14955                                   if (fi == NULL)
14956                                     {
14957                                       fprintf(stderr,
14958                                               "\nError: Couldn't save thumbnail of "
14959                                               "saved image!\n"
14960                                               "%s\n" "The error that occurred was:\n" "%s\n\n", fname, strerror(errno));
14961                                     }
14962                                   else
14963                                     {
14964                                       do_png_save(fi, fname, thumbs[num_files], 0);
14965                                     }
14966 
14967                                   show_progress_bar(screen);
14968                                 }
14969 
14970 
14971                               num_files++;
14972                             }
14973                         }
14974                     }
14975                 }
14976               else
14977                 {
14978                   /* It was a thumbnail file ("...-t.png") */
14979                 }
14980             }
14981         }
14982 
14983 
14984 
14985 #ifdef DEBUG
14986       printf("%d saved files were found!\n", num_files);
14987 #endif
14988 
14989 
14990 
14991       if (num_files == 0)
14992         {
14993           do_prompt_snd(PROMPT_OPEN_NOFILES_TXT, PROMPT_OPEN_NOFILES_YES, "",
14994                         SND_YOUCANNOT,
14995 			(TOOL_OPEN % 2) * button_w + button_w / 2,
14996 			(TOOL_OPEN / 2) * button_h + r_ttools.h + button_h / 2 - tool_scroll * button_h / gd_tools.cols);
14997         }
14998       else
14999         {
15000           /* Let user choose an image: */
15001 
15002           /* Instructions for 'Open' file dialog */
15003           char *instructions = textdir(gettext_noop("Choose the picture you want, then click “Open”."));
15004           draw_tux_text(TUX_BORED, instructions, 1);
15005 
15006           /* NOTE: cur is now set above; if file_id'th file is found, it's
15007              set to that file's index; otherwise, we default to '0' */
15008 
15009           update_list = 1;
15010           want_erase = 0;
15011           want_export = 0;
15012 
15013           done = 0;
15014           slideshow = 0;
15015 
15016           last_click_which = -1;
15017           last_click_time = 0;
15018           last_click_button = -1;
15019 
15020 
15021           do_setcursor(cursor_arrow);
15022 
15023 
15024           do
15025             {
15026               /* Update screen: */
15027 
15028               if (update_list)
15029                 {
15030                   /* Erase screen: */
15031 
15032                   dest.x = r_ttools.w;
15033                   dest.y = 0;
15034                   dest.w = WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w;
15035                   dest.h = button_h * buttons_tall + r_ttools.h;
15036 
15037                   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
15038 
15039 
15040                   /* Draw icons: */
15041 
15042                   for (i = cur; i < cur + 16 && i < num_files; i++)
15043                     {
15044                       /* Draw cursor: */
15045 
15046                       dest.x = THUMB_W * ((i - cur) % 4) + r_ttools.w;
15047                       dest.y = THUMB_H * ((i - cur) / 4) + img_scroll_up->h;
15048 
15049                       if (i == which)
15050                         {
15051                           SDL_BlitSurface(img_cursor_down, NULL, screen, &dest);
15052                           debug(d_names[i]);
15053                         }
15054                       else
15055                         SDL_BlitSurface(img_cursor_up, NULL, screen, &dest);
15056 
15057 
15058 
15059                       dest.x = THUMB_W * ((i - cur) % 4) + r_ttools.w + 10 + (THUMB_W - 20 - thumbs[i]->w) / 2;
15060                       dest.y = THUMB_H * ((i - cur) / 4) + img_scroll_up->h + 10 + (THUMB_H - 20 - thumbs[i]->h) / 2;
15061 
15062                       if (thumbs[i] != NULL)
15063                         SDL_BlitSurface(thumbs[i], NULL, screen, &dest);
15064                     }
15065 
15066 
15067                   /* Draw arrows: */
15068 
15069                   dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
15070                   dest.y = 0;
15071 
15072                   if (cur > 0)
15073                     SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
15074                   else
15075                     SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
15076 
15077                   dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
15078                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15079 
15080                   if (cur < num_files - 16)
15081                     SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
15082                   else
15083                     SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
15084 
15085 
15086                   /* "Open" button: */
15087 
15088                   dest.x = r_ttools.w;
15089                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15090                   SDL_BlitSurface(img_open, NULL, screen, &dest);
15091 
15092                   dest.x = r_ttools.w + (button_w - img_openlabels_open->w) / 2;
15093                   dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_open->h;
15094                   SDL_BlitSurface(img_openlabels_open, NULL, screen, &dest);
15095 
15096 
15097                   /* "Slideshow" button: */
15098 
15099                   dest.x = r_ttools.w + button_w;
15100                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15101                   if (any_saved_files)
15102                     SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
15103                   else
15104                     SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
15105 
15106                   dest.x = r_ttools.w + button_w;
15107                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15108                   SDL_BlitSurface(img_slideshow, NULL, screen, &dest);
15109 
15110                   dest.x = r_ttools.w + button_w + (button_w - img_openlabels_slideshow->w) / 2;
15111                   dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_slideshow->h;
15112                   SDL_BlitSurface(img_openlabels_slideshow, NULL, screen, &dest);
15113 
15114 
15115                   /* "Back" button: */
15116 
15117                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w;
15118                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15119                   SDL_BlitSurface(img_back, NULL, screen, &dest);
15120 
15121                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w + (button_w - img_openlabels_back->w) / 2;
15122                   dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_back->h;
15123                   SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
15124 
15125 
15126                   /* "Export" button: */
15127 
15128                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w - button_w;
15129                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15130 
15131                   if (d_places[which] != PLACE_STARTERS_DIR && d_places[which] != PLACE_PERSONAL_STARTERS_DIR)
15132                     SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
15133                   else
15134                     SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
15135 
15136                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w - button_w + (button_w - img_pict_export->w) / 2;
15137                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15138                   SDL_BlitSurface(img_pict_export, NULL, screen, &dest);
15139 
15140                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w - button_w + (button_w - img_openlabels_pict_export->w) / 2;
15141                   dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_pict_export->h;
15142                   SDL_BlitSurface(img_openlabels_pict_export, NULL, screen, &dest);
15143 
15144 
15145                   /* "Erase" button: */
15146 
15147                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w;
15148                   dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
15149 
15150                   if (d_places[which] != PLACE_STARTERS_DIR && d_places[which] != PLACE_PERSONAL_STARTERS_DIR)
15151                     SDL_BlitSurface(img_erase, NULL, screen, &dest);
15152                   else
15153                     SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
15154 
15155                   dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w + (button_w - img_openlabels_erase->w) / 2;
15156                   dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_erase->h;
15157                   SDL_BlitSurface(img_openlabels_erase, NULL, screen, &dest);
15158 
15159 
15160                   SDL_Flip(screen);
15161 
15162                   update_list = 0;
15163                 }
15164 
15165               while (SDL_PollEvent(&event))
15166                 {
15167                   if (event.type == SDL_QUIT)
15168                     {
15169                       done = 1;
15170 
15171                       /* FIXME: Handle SDL_Quit better */
15172                     }
15173                   else if (event.type == SDL_ACTIVEEVENT)
15174                     {
15175                       handle_active(&event);
15176                     }
15177                   else if (event.type == SDL_KEYUP)
15178                     {
15179                       key = event.key.keysym.sym;
15180 
15181                       handle_keymouse(key, SDL_KEYUP, 24, NULL, NULL);
15182                     }
15183                   else if (event.type == SDL_KEYDOWN)
15184                     {
15185                       key = event.key.keysym.sym;
15186 
15187                       handle_keymouse(key, SDL_KEYDOWN, 24, NULL, NULL);
15188 
15189                       /* This was interfering with handle_keymouse above,
15190                          remapping from LEFT RIGHT UP DOWN to F11 F12 F8 F7 */
15191                       if (key == SDLK_F11)
15192                         {
15193                           if (which > 0)
15194                             {
15195                               which--;
15196 
15197                               if (which < cur)
15198                                 cur = cur - 4;
15199 
15200                               update_list = 1;
15201                             }
15202                         }
15203                       else if (key == SDLK_F12)
15204                         {
15205                           if (which < num_files - 1)
15206                             {
15207                               which++;
15208 
15209                               if (which >= cur + 16)
15210                                 cur = cur + 4;
15211 
15212                               update_list = 1;
15213                             }
15214                         }
15215                       else if (key == SDLK_F8)
15216                         {
15217                           if (which >= 0)
15218                             {
15219                               which = which - 4;
15220 
15221                               if (which < 0)
15222                                 which = 0;
15223 
15224                               if (which < cur)
15225                                 cur = cur - 4;
15226 
15227                               update_list = 1;
15228                             }
15229                         }
15230                       else if (key == SDLK_F7)
15231                         {
15232                           if (which < num_files)
15233                             {
15234                               which = which + 4;
15235 
15236                               if (which >= num_files)
15237                                 which = num_files - 1;
15238 
15239                               if (which >= cur + 16)
15240                                 cur = cur + 4;
15241 
15242                               update_list = 1;
15243                             }
15244                         }
15245                       else if (key == SDLK_RETURN)      /* space also conflicts with handle_keymouse || key == SDLK_SPACE) */
15246                         {
15247                           /* Open */
15248 
15249                           done = 1;
15250                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
15251                         }
15252                       else if (key == SDLK_ESCAPE)
15253                         {
15254                           /* Go back: */
15255 
15256                           which = -1;
15257                           done = 1;
15258                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
15259                         }
15260                       else if (key == SDLK_d &&
15261                                (event.key.keysym.mod & KMOD_CTRL) &&
15262                                d_places[which] != PLACE_STARTERS_DIR &&
15263                                d_places[which] != PLACE_PERSONAL_STARTERS_DIR && !noshortcuts)
15264                         {
15265                           /* Delete! */
15266 
15267                           want_erase = 1;
15268                         }
15269                     }
15270                   else if (event.type == SDL_MOUSEBUTTONDOWN && valid_click(event.button.button))
15271                     {
15272                       if (event.button.x >= r_ttools.w && event.button.x < WINDOW_WIDTH - r_ttoolopt.w &&
15273                           event.button.y >= img_scroll_up->h && event.button.y < (button_h * buttons_tall + r_ttools.h) - button_h)
15274                         {
15275                           /* Picked an icon! */
15276 
15277                           int old_which = which;
15278 
15279                           which = ((event.button.x - r_ttools.w) / (THUMB_W) + (((event.button.y - img_scroll_up->h) / THUMB_H) * 4)) + cur;
15280 
15281                           if (which < num_files)
15282                             {
15283                               playsound(screen, 1, SND_BLEEP, 1, event.button.x, SNDDIST_NEAR);
15284                               update_list = 1;
15285 
15286 
15287                               if (which == last_click_which &&
15288                                   SDL_GetTicks() < last_click_time + 1000 && event.button.button == last_click_button)
15289                                 {
15290                                   /* Double-click! */
15291 
15292                                   done = 1;
15293                                 }
15294 
15295                               last_click_which = which;
15296                               last_click_time = SDL_GetTicks();
15297                               last_click_button = event.button.button;
15298                             }
15299                           else
15300                             which = old_which;
15301                         }
15302                       else if (event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
15303                                event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2)
15304                         {
15305                           if (event.button.y < img_scroll_up->h)
15306                             {
15307                               /* Up scroll button: */
15308 
15309                               if (cur > 0)
15310                                 {
15311                                   cur = cur - 4;
15312                                   update_list = 1;
15313                                   playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
15314 
15315                                   if (cur == 0)
15316                                     do_setcursor(cursor_arrow);
15317                                 }
15318 
15319                               if (which >= cur + 16)
15320                                 which = which - 4;
15321                             }
15322                           else if (event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15323                                    event.button.y < (button_h * buttons_tall + r_ttools.h) - img_scroll_up->h)
15324                             {
15325                               /* Down scroll button: */
15326 
15327                               if (cur < num_files - 16)
15328                                 {
15329                                   cur = cur + 4;
15330                                   update_list = 1;
15331                                   playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
15332 
15333                                   if (cur >= num_files - 16)
15334                                     do_setcursor(cursor_arrow);
15335                                 }
15336 
15337                               if (which < cur)
15338                                 which = which + 4;
15339                             }
15340                         }
15341                       else if (event.button.x >= r_ttools.w && event.button.x < r_ttools.w + button_w &&
15342                                event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15343                                event.button.y < (button_h * buttons_tall + r_ttools.h))
15344                         {
15345                           /* Open */
15346 
15347                           done = 1;
15348                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
15349                         }
15350                       else if (event.button.x >= r_ttools.w + button_w && event.button.x < r_ttools.w + button_w + button_w &&
15351                                event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15352                                event.button.y < (button_h * buttons_tall + r_ttools.h) && any_saved_files == 1)
15353                         {
15354                           /* Slideshow */
15355 
15356                           done = 1;
15357                           slideshow = 1;
15358                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
15359                         }
15360                       else if (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w) &&
15361                                event.button.x < (WINDOW_WIDTH - r_ttoolopt.w) &&
15362                                event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15363                                event.button.y < (button_h * buttons_tall + r_ttools.h))
15364                         {
15365                           /* Back */
15366 
15367                           which = -1;
15368                           done = 1;
15369                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
15370                         }
15371                       else if (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w) &&
15372                                event.button.x < (WINDOW_WIDTH - button_w - r_ttoolopt.w) &&
15373                                event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15374                                event.button.y < (button_h * buttons_tall + r_ttools.h) &&
15375                                d_places[which] != PLACE_STARTERS_DIR && d_places[which] != PLACE_PERSONAL_STARTERS_DIR)
15376                         {
15377                           /* Erase */
15378 
15379                           want_erase = 1;
15380                         }
15381                       else if (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w - button_w) &&
15382                                event.button.x < (WINDOW_WIDTH - button_w - button_w - r_ttoolopt.w) &&
15383                                event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15384                                event.button.y < (button_h * buttons_tall + r_ttools.h) &&
15385                                d_places[which] != PLACE_STARTERS_DIR && d_places[which] != PLACE_PERSONAL_STARTERS_DIR)
15386                         {
15387                           /* Export */
15388 
15389                           want_export = 1;
15390                         }
15391                     }
15392                   else if (event.type == SDL_MOUSEBUTTONDOWN &&
15393                            event.button.button >= 4 && event.button.button <= 5 && wheely)
15394                     {
15395                       /* Scroll wheel! */
15396 
15397                       if (event.button.button == 4 && cur > 0)
15398                         {
15399                           cur = cur - 4;
15400                           update_list = 1;
15401                           playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
15402 
15403                           if (cur == 0)
15404                             do_setcursor(cursor_arrow);
15405 
15406                           if (which >= cur + 16)
15407                             which = which - 4;
15408                         }
15409                       else if (event.button.button == 5 && cur < num_files - 16)
15410                         {
15411                           cur = cur + 4;
15412                           update_list = 1;
15413                           playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
15414 
15415                           if (cur >= num_files - 16)
15416                             do_setcursor(cursor_arrow);
15417 
15418                           if (which < cur)
15419                             which = which + 4;
15420                         }
15421                     }
15422                   else if (event.type == SDL_MOUSEMOTION)
15423                     {
15424                       /* Deal with mouse pointer shape! */
15425 
15426                       if (event.button.y < img_scroll_up->h &&
15427                           event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
15428                           event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 && cur > 0)
15429                         {
15430                           /* Scroll up button: */
15431 
15432                           do_setcursor(cursor_up);
15433                         }
15434                       else if (event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15435                                event.button.y < (button_h * buttons_tall + r_ttools.h - img_scroll_up->h) &&
15436                                event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
15437                                event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 && cur < num_files - 16)
15438                         {
15439                           /* Scroll down button: */
15440 
15441                           do_setcursor(cursor_down);
15442                         }
15443                       else if (((event.button.x >= r_ttools.w && event.button.x < r_ttools.w + button_w + button_w) ||
15444                                 (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w) &&
15445                                  event.button.x < (WINDOW_WIDTH - r_ttoolopt.w)) ||
15446                                 (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w - button_w) &&
15447                                  event.button.x < (WINDOW_WIDTH - button_w - r_ttoolopt.w) &&
15448 				 /* Both "Erase" and "Export" only work on our own files... */
15449                                  d_places[which] != PLACE_STARTERS_DIR &&
15450                                  d_places[which] != PLACE_PERSONAL_STARTERS_DIR)) &&
15451                                event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
15452                                event.button.y < (button_h * buttons_tall + r_ttools.h))
15453                         {
15454                           /* One of the command buttons: */
15455 
15456                           do_setcursor(cursor_hand);
15457                         }
15458                       else if (event.button.x >= r_ttools.w && event.button.x < WINDOW_WIDTH - r_ttoolopt.w &&
15459                                event.button.y > img_scroll_up->h &&
15460                                event.button.y < (button_h * buttons_tall + r_ttools.h) - button_h &&
15461                                ((((event.button.x - r_ttools.w) / (THUMB_W) +
15462                                   (((event.button.y - img_scroll_up->h) / THUMB_H) * 4)) + cur) < num_files))
15463                         {
15464                           /* One of the thumbnails: */
15465 
15466                           do_setcursor(cursor_hand);
15467                         }
15468                       else
15469                         {
15470                           /* Unclickable... */
15471 
15472                           do_setcursor(cursor_arrow);
15473                         }
15474                       oldpos_x = event.button.x;
15475                       oldpos_y = event.button.y;
15476                     }
15477 
15478                   else if (event.type == SDL_JOYAXISMOTION)
15479                     handle_joyaxismotion(event, &motioner, &val_x, &val_y);
15480 
15481                   else if (event.type == SDL_JOYHATMOTION)
15482                     handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
15483 
15484                   else if (event.type == SDL_JOYBALLMOTION)
15485                     handle_joyballmotion(event, oldpos_x, oldpos_y);
15486 
15487                   else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
15488                     handle_joybuttonupdown(event, oldpos_x, oldpos_y);
15489                 }
15490 
15491               if (motioner | hatmotioner)
15492                 handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x,
15493                                  valhat_y);
15494 
15495 
15496               SDL_Delay(10);
15497 
15498               if (want_erase)
15499                 {
15500                   want_erase = 0;
15501 
15502                   if (do_prompt_image_snd(PROMPT_ERASE_TXT,
15503                                           PROMPT_ERASE_YES, PROMPT_ERASE_NO,
15504                                           thumbs[which],
15505                                           img_popup_arrow, img_trash, SND_AREYOUSURE,
15506                                           WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w + 24, button_h * buttons_tall + r_ttools.h - button_h + img_scroll_up->h))
15507                     {
15508                       safe_snprintf(fname, sizeof(fname), "saved/%s%s", d_names[which], d_exts[which]);
15509 
15510                       rfname = get_fname(fname, DIR_SAVE);
15511 
15512                       if (trash(rfname) == 0)
15513                         {
15514                           update_list = 1;
15515 
15516 
15517                           /* Delete the thumbnail, too: */
15518 
15519                           safe_snprintf(fname, sizeof(fname), "saved/.thumbs/%s-t.png", d_names[which]);
15520 
15521                           free(rfname);
15522                           rfname = get_fname(fname, DIR_SAVE);
15523 
15524                           unlink(rfname);
15525 
15526 
15527                           /* Try deleting old-style thumbnail, too: */
15528 
15529                           safe_snprintf(fname, sizeof(fname), "saved/%s-t.png", d_names[which]);
15530 
15531                           free(rfname);
15532                           rfname = get_fname(fname, DIR_SAVE);
15533 
15534                           unlink(rfname);
15535 
15536 
15537                           /* Delete .dat file, if any: */
15538 
15539                           safe_snprintf(fname, sizeof(fname), "saved/%s.dat", d_names[which]);
15540 
15541                           free(rfname);
15542                           rfname = get_fname(fname, DIR_SAVE);
15543 
15544                           trash(rfname);
15545 
15546 
15547 
15548                           /* Move all other files up a notch: */
15549 
15550                           free(d_names[which]);
15551                           free(d_exts[which]);
15552                           free_surface(&thumbs[which]);
15553 
15554                           thumbs[which] = NULL;
15555 
15556                           for (i = which; i < num_files - 1; i++)
15557                             {
15558                               d_names[i] = d_names[i + 1];
15559                               d_exts[i] = d_exts[i + 1];
15560                               thumbs[i] = thumbs[i + 1];
15561                               d_places[i] = d_places[i + 1];
15562                             }
15563 
15564                           num_files--;
15565 
15566 
15567                           /* Make sure the cursor doesn't go off the end! */
15568 
15569                           if (which >= num_files)
15570                             which = num_files - 1;
15571 
15572 
15573                           /* Scroll up if the cursor goes off top of screen! */
15574 
15575                           if (which < cur && cur >= 4)
15576                             {
15577                               cur = cur - 4;
15578                               update_list = 1;
15579                             }
15580 
15581 
15582                           /* No files to open now? */
15583 
15584                           if (which < 0)
15585                             {
15586                               do_prompt_snd(PROMPT_OPEN_NOFILES_TXT,
15587                                             PROMPT_OPEN_NOFILES_YES, "", SND_YOUCANNOT, screen->w / 2, screen->h / 2);
15588                               done = 1;
15589                             }
15590                         }
15591                       else
15592                         {
15593                           perror(rfname);
15594 
15595                           do_prompt_snd("CAN'T", "OK", "", SND_YOUCANNOT, 0, 0);
15596                           update_list = 1;
15597                         }
15598 
15599                       free(rfname);
15600                     }
15601                   else
15602                     {
15603                       update_list = 1;
15604                     }
15605                 }
15606 
15607               if (want_export)
15608                 {
15609                   want_export = 0;
15610 
15611                   safe_snprintf(fname, sizeof(fname), "saved/%s%s", d_names[which], d_exts[which]);
15612                   rfname = get_fname(fname, DIR_SAVE);
15613                   if (export_pict(rfname))
15614                     do_prompt_snd(PROMPT_PICT_EXPORT_TXT, PROMPT_EXPORT_YES, "", SND_TUXOK, screen->w / 2, screen->h / 2);
15615                   else
15616                     do_prompt_snd(PROMPT_PICT_EXPORT_FAILED_TXT, PROMPT_EXPORT_YES, "", SND_YOUCANNOT, screen->w / 2, screen->h / 2);
15617 
15618                   draw_tux_text(TUX_BORED, instructions, 1);
15619 		  update_list = 1;
15620                 }
15621             }
15622           while (!done);
15623 
15624 
15625           if (!slideshow)
15626             {
15627               /* Load the chosen picture: */
15628 
15629               if (which != -1)
15630                 {
15631                   /* Save old one first? */
15632 
15633                   if (!been_saved && !disable_save)
15634                     {
15635                       if (do_prompt_image_snd(PROMPT_OPEN_SAVE_TXT,
15636                                               PROMPT_OPEN_SAVE_YES,
15637                                               PROMPT_OPEN_SAVE_NO,
15638                                               img_tools[TOOL_SAVE], NULL, NULL,
15639                                               SND_AREYOUSURE, screen->w / 2, screen->h / 2))
15640                         {
15641                           do_save(TOOL_OPEN, 1);
15642                         }
15643                     }
15644 
15645                   /* Clean the label stuff */
15646                   delete_label_list(&start_label_node);
15647                   start_label_node = current_label_node = first_label_node_in_redo_stack = highlighted_label_node =
15648                     label_node_to_edit = NULL;
15649                   have_to_rec_label_node = FALSE;
15650 
15651                   SDL_FillRect(label, NULL, SDL_MapRGBA(label->format, 0, 0, 0, 0));
15652 
15653                   /* Figure out filename: */
15654 
15655                   safe_snprintf(fname, sizeof(fname), "%s/%s%s", dirname[d_places[which]], d_names[which], d_exts[which]);
15656                   fi = fopen(fname, "r");
15657                   if (fi == NULL)
15658                     {
15659                       fprintf(stderr,
15660                               "\nWarning: Couldn't load the saved image! (1)\n"
15661                               "%s\n" "The file is missing.\n\n\n", fname);
15662                       do_prompt(PROMPT_OPEN_UNOPENABLE_TXT, PROMPT_OPEN_UNOPENABLE_YES, "", 0, 0);
15663                     }
15664                   fclose(fi);
15665 
15666                   img = myIMG_Load(fname);
15667 
15668                   if (img == NULL)
15669                     {
15670                       fprintf(stderr,
15671                               "\nWarning: Couldn't load the saved image! (2)\n"
15672                               "%s\n"
15673                               "The Simple DirectMedia Layer error that occurred "
15674                               "was:\n" "%s\n\n", fname, SDL_GetError());
15675 
15676                       do_prompt(PROMPT_OPEN_UNOPENABLE_TXT, PROMPT_OPEN_UNOPENABLE_YES, "", 0, 0);
15677                     }
15678                   else
15679                     {
15680                       free_surface(&img_starter);
15681                       free_surface(&img_starter_bkgd);
15682                       starter_mirrored = 0;
15683                       starter_flipped = 0;
15684                       starter_personal = 0;
15685 
15686                       org_surf = SDL_DisplayFormat(img);        /* Keep a copy of the original image
15687                                                                    unscaled to send to load_embedded_data */
15688                       autoscale_copy_smear_free(img, canvas, SDL_BlitSurface);
15689 
15690                       cur_undo = 0;
15691                       oldest_undo = 0;
15692                       newest_undo = 0;
15693 
15694                       /* Saved image: */
15695 
15696                       been_saved = 1;
15697 
15698                       safe_strncpy(file_id, d_names[which], sizeof(file_id));
15699                       starter_id[0] = '\0';
15700                       template_id[0] = '\0';
15701 
15702 
15703                       /* Keep this for compatibility */
15704                       /* See if this saved image was based on a 'starter' */
15705 
15706                       load_starter_id(d_names[which], NULL);
15707 
15708                       if (starter_id[0] != '\0')
15709                         {
15710                           load_starter(starter_id);
15711 
15712                           if (starter_mirrored)
15713                             mirror_starter();
15714 
15715                           if (starter_flipped)
15716                             flip_starter();
15717                         }
15718                       else if (template_id[0] != '\0')
15719                         load_template(template_id);
15720 
15721                       load_embedded_data(fname, org_surf);
15722 
15723                       reset_avail_tools();
15724 
15725                       tool_avail_bak[TOOL_UNDO] = 0;
15726                       tool_avail_bak[TOOL_REDO] = 0;
15727 
15728                       opened_something = 1;
15729                     }
15730                 }
15731             }
15732 
15733 
15734           update_canvas(0, 0, WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w, button_h * buttons_tall + r_ttools.h);
15735 
15736           free(instructions);
15737         }
15738 
15739 
15740       /* Clean up: */
15741 
15742       free_surface_array(thumbs, num_files);
15743 
15744       free(thumbs);
15745 
15746       for (i = 0; i < num_files; i++)
15747         {
15748           free(d_names[i]);
15749           free(d_exts[i]);
15750         }
15751 
15752       for (i = 0; i < NUM_PLACES_TO_LOOK; i++)
15753         if (dirname[i] != NULL)
15754           free(dirname[i]);
15755 
15756       free(d_names);
15757       free(d_exts);
15758       free(d_places);
15759 
15760 
15761       if (slideshow)
15762         {
15763           slideshow = do_slideshow();
15764         }
15765     }
15766   while (slideshow);
15767 
15768   return (opened_something);
15769 }
15770 
15771 
15772 /* FIXME: This, do_open() and do_new_dialog() should be combined and modularized! */
15773 
15774 /**
15775  * FIXME
15776  */
15777 /* Slide Show Selection Screen: */
do_slideshow(void)15778 static int do_slideshow(void)
15779 {
15780   SDL_Surface *img, *img1, *img2;
15781   int things_alloced;
15782   SDL_Surface **thumbs = NULL;
15783   DIR *d;
15784   struct dirent *f;
15785   struct dirent2 *fs;
15786   char *dirname;
15787   char **d_names = NULL, **d_exts = NULL;
15788   int *selected;
15789   int num_selected;
15790   FILE *fi;
15791   char fname[1024];
15792   int num_files, num_files_in_dir, i, done, update_list, cur, which, j, go_back, found, speed;
15793   SDL_Rect dest;
15794   SDL_Event event;
15795   SDLKey key;
15796   char *freeme, *instructions;
15797   int speeds;
15798   float x_per, y_per;
15799   int xx, yy;
15800   SDL_Surface *btn, *blnk;
15801   int val_x, val_y, motioner;
15802   int valhat_x, valhat_y, hatmotioner;
15803   int export_successful;
15804 
15805   val_x = val_y = motioner = 0;
15806   valhat_x = valhat_y = hatmotioner = 0;
15807   do_setcursor(cursor_watch);
15808 
15809   /* Allocate some space: */
15810 
15811   things_alloced = 32;
15812 
15813   fs = (struct dirent2 *)malloc(sizeof(struct dirent2) * things_alloced);
15814 
15815   num_files_in_dir = 0;
15816   num_files = 0;
15817   cur = 0;
15818   which = 0;
15819 
15820 
15821   /* Load list of saved-images: */
15822 
15823   dirname = get_fname("saved", DIR_SAVE);
15824 
15825 
15826   /* Read directory of images and build thumbnails: */
15827 
15828   d = opendir(dirname);
15829 
15830   if (d != NULL)
15831     {
15832       /* Gather list of files (for sorting): */
15833 
15834       do
15835         {
15836           f = readdir(d);
15837 
15838           if (f != NULL)
15839             {
15840               memcpy(&(fs[num_files_in_dir].f), f, sizeof(struct dirent));
15841               fs[num_files_in_dir].place = PLACE_SAVED_DIR;
15842 
15843               num_files_in_dir++;
15844 
15845               if (num_files_in_dir >= things_alloced)
15846                 {
15847                   things_alloced = things_alloced + 32;
15848                   fs = (struct dirent2 *)realloc(fs, sizeof(struct dirent2) * things_alloced);
15849                 }
15850             }
15851         }
15852       while (f != NULL);
15853 
15854       closedir(d);
15855     }
15856 
15857 
15858   /* (Re)allocate space for the information about these files: */
15859 
15860   thumbs = (SDL_Surface * *)malloc(sizeof(SDL_Surface *) * num_files_in_dir);
15861   d_names = (char **)malloc(sizeof(char *) * num_files_in_dir);
15862   d_exts = (char **)malloc(sizeof(char *) * num_files_in_dir);
15863   selected = (int *)malloc(sizeof(int) * num_files_in_dir);
15864 
15865 
15866   /* Sort: */
15867 
15868   qsort(fs, num_files_in_dir, sizeof(struct dirent2), (int (*)(const void *, const void *))compare_dirent2s);
15869 
15870 
15871   /* Read directory of images and build thumbnails: */
15872 
15873   for (j = 0; j < num_files_in_dir; j++)
15874     {
15875       f = &(fs[j].f);
15876 
15877       show_progress_bar(screen);
15878 
15879       if (f != NULL)
15880         {
15881           debug(f->d_name);
15882 
15883           if (strcasestr(f->d_name, "-t.") == NULL && strcasestr(f->d_name, "-back.") == NULL)
15884             {
15885               if (strcasestr(f->d_name, FNAME_EXTENSION) != NULL
15886                   /* Support legacy BMP files for load: */
15887                   || strcasestr(f->d_name, ".bmp") != NULL)
15888                 {
15889                   safe_strncpy(fname, f->d_name, sizeof(fname));
15890                   if (strcasestr(fname, FNAME_EXTENSION) != NULL)
15891                     {
15892                       d_exts[num_files] = strdup(strcasestr(fname, FNAME_EXTENSION));
15893                       strcpy((char *)strcasestr(fname, FNAME_EXTENSION), ""); /* FIXME: Use strncpy() (ugh, complicated) */
15894                     }
15895 
15896                   if (strcasestr(fname, ".bmp") != NULL)
15897                     {
15898                       d_exts[num_files] = strdup(strcasestr(fname, ".bmp"));
15899                       strcpy((char *)strcasestr(fname, ".bmp"), ""); /* Safe; truncating */
15900                     }
15901 
15902                   d_names[num_files] = strdup(fname);
15903 
15904 
15905                   /* FIXME: Try to center list on whatever was selected
15906                      in do_open() when the slideshow button was clicked. */
15907 
15908                   /* Try to load thumbnail first: */
15909 
15910                   safe_snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png", dirname, d_names[num_files]);
15911                   debug("Loading thumbnail...");
15912                   debug(fname);
15913                   img = IMG_Load(fname);
15914                   if (img == NULL)
15915                     {
15916                       /* No thumbnail in the new location ("saved/.thumbs"),
15917                          try the old locatin ("saved/"): */
15918 
15919                       safe_snprintf(fname, sizeof(fname), "%s/%s-t.png", dirname, d_names[num_files]);
15920                       debug(fname);
15921 
15922                       img = IMG_Load(fname);
15923                     }
15924 
15925 
15926                   if (img != NULL)
15927                     {
15928                       /* Loaded the thumbnail from one or the other location */
15929 
15930                       debug("Thumbnail loaded, scaling");
15931                       show_progress_bar(screen);
15932 
15933                       img1 = SDL_DisplayFormat(img);
15934                       SDL_FreeSurface(img);
15935 
15936                       if (img1 != NULL)
15937                         {
15938                           /* if too big, or too small in both dimensions, rescale it
15939                              (for now: using old thumbnail as source for high speed, low quality) */
15940                           if (img1->w > THUMB_W - 20 || img1->h > THUMB_H - 20
15941                               || (img1->w < THUMB_W - 20 && img1->h < THUMB_H - 20))
15942                             {
15943                               img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
15944                               SDL_FreeSurface(img1);
15945                               img1 = img2;
15946                             }
15947 
15948                           thumbs[num_files] = img1;
15949 
15950                           if (thumbs[num_files] == NULL)
15951                             {
15952                               fprintf(stderr, "\nError: Couldn't create a thumbnail of saved image!\n" "%s\n", fname);
15953                             }
15954                           else
15955                             num_files++;
15956                         }
15957                     }
15958                   else
15959                     {
15960                       /* No thumbnail - load original: */
15961 
15962                       /* Make sure we have a ~/.tuxpaint/saved directory: */
15963                       if (make_directory(DIR_SAVE, "saved", "Can't create user data directory (for saved drawings) (E008)"))
15964                         {
15965                           /* (Make sure we have a .../saved/.thumbs/ directory:) */
15966                           make_directory(DIR_SAVE, "saved/.thumbs", "Can't create user data thumbnail directory (for saved drawings' thumbnails) (E009)");
15967                         }
15968 
15969                       safe_snprintf(fname, sizeof(fname), "%s/%s", dirname, f->d_name);
15970 
15971                       debug("Loading original, to make thumbnail");
15972                       debug(fname);
15973                       img = myIMG_Load(fname);
15974 
15975 
15976                       show_progress_bar(screen);
15977 
15978 
15979                       if (img == NULL)
15980                         {
15981                           fprintf(stderr,
15982                                   "\nWarning: I can't open one of the saved files!\n"
15983                                   "%s\n"
15984                                   "The Simple DirectMedia Layer error that "
15985                                   "occurred was:\n" "%s\n\n", fname, SDL_GetError());
15986                         }
15987                       else
15988                         {
15989                           /* Turn it into a thumbnail: */
15990 
15991                           img1 = SDL_DisplayFormatAlpha(img);
15992                           img2 = thumbnail2(img1, THUMB_W - 20, THUMB_H - 20, 0, 0);
15993                           SDL_FreeSurface(img1);
15994 
15995                           show_progress_bar(screen);
15996 
15997                           thumbs[num_files] = SDL_DisplayFormat(img2);
15998                           SDL_FreeSurface(img2);
15999 
16000                           SDL_FreeSurface(img);
16001 
16002                           if (thumbs[num_files] == NULL)
16003                             {
16004                               fprintf(stderr, "\nError: Couldn't create a thumbnail of saved image!\n" "%s\n", fname);
16005                             }
16006                           else
16007                             {
16008                               show_progress_bar(screen);
16009 
16010                               /* Let's save this thumbnail, so we don't have to
16011                                  create it again next time 'Open' is called: */
16012 
16013                               debug("Saving thumbnail for this one!");
16014 
16015                               safe_snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png", dirname, d_names[num_files]);
16016 
16017                               fi = fopen(fname, "wb");
16018                               if (fi == NULL)
16019                                 {
16020                                   fprintf(stderr,
16021                                           "\nError: Couldn't save thumbnail of saved image!\n"
16022                                           "%s\n" "The error that occurred was:\n" "%s\n\n", fname, strerror(errno));
16023                                 }
16024                               else
16025                                 {
16026                                   do_png_save(fi, fname, thumbs[num_files], 0);
16027                                 }
16028 
16029                               show_progress_bar(screen);
16030 
16031                               num_files++;
16032                             }
16033                         }
16034                     }
16035                 }
16036             }
16037         }
16038     }
16039 
16040 #ifdef DEBUG
16041   printf("%d saved files were found!\n", num_files);
16042 #endif
16043   /* Let user choose images: */
16044 
16045   /* Instructions for Slideshow file dialog */
16046   instructions = textdir(TUX_TIP_SLIDESHOW);
16047   draw_tux_text(TUX_BORED, instructions, 1);
16048 
16049   /* NOTE: cur is now set above; if file_id'th file is found, it's
16050      set to that file's index; otherwise, we default to '0' */
16051 
16052   update_list = 1;
16053 
16054   go_back = 0;
16055   done = 0;
16056 
16057   /* FIXME: Make these global, so it sticks between views? */
16058   num_selected = 0;
16059   speed = 5;
16060 
16061   do_setcursor(cursor_arrow);
16062 
16063 
16064   do
16065     {
16066       /* Update screen: */
16067 
16068       if (update_list)
16069         {
16070           /* Erase screen: */
16071 
16072           dest.x = r_ttools.w;
16073           dest.y = 0;
16074           dest.w = WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w;
16075           dest.h = button_h * buttons_tall + r_ttools.h;
16076 
16077           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
16078 
16079 
16080           /* Draw icons: */
16081 
16082           for (i = cur; i < cur + 16 && i < num_files; i++)
16083             {
16084               /* Draw cursor: */
16085 
16086               dest.x = THUMB_W * ((i - cur) % 4) + r_ttools.w;
16087               dest.y = THUMB_H * ((i - cur) / 4) + img_scroll_up->h;
16088 
16089               if (i == which)
16090                 {
16091                   SDL_BlitSurface(img_cursor_down, NULL, screen, &dest);
16092                   debug(d_names[i]);
16093                 }
16094               else
16095                 SDL_BlitSurface(img_cursor_up, NULL, screen, &dest);
16096 
16097               if (thumbs[i] != NULL)
16098                 {
16099                   dest.x = THUMB_W * ((i - cur) % 4) + r_ttools.w + 10 + (THUMB_W - 20 - thumbs[i]->w) / 2;
16100                   dest.y = THUMB_H * ((i - cur) / 4) + img_scroll_up->h + 10 + (THUMB_H - 20 - thumbs[i]->h) / 2;
16101 
16102                   SDL_BlitSurface(thumbs[i], NULL, screen, &dest);
16103                 }
16104 
16105               found = -1;
16106 
16107               for (j = 0; j < num_selected && found == -1; j++)
16108                 {
16109                   if (selected[j] == i)
16110                     found = j;
16111                 }
16112 
16113               if (found != -1)
16114                 {
16115                   dest.x = (THUMB_W * ((i - cur) % 4) + r_ttools.h + 10 + (THUMB_W - 20 - thumbs[i]->w) / 2) + thumbs[i]->w;
16116                   dest.y = (THUMB_H * ((i - cur) / 4) + img_scroll_up->h + 10 + (THUMB_H - 20 - thumbs[i]->h) / 2) + thumbs[i]->h;
16117 
16118                   draw_selection_digits(dest.x, dest.y, found + 1);
16119                 }
16120             }
16121 
16122 
16123           /* Draw arrows: */
16124 
16125           dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
16126           dest.y = 0;
16127 
16128           if (cur > 0)
16129             SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
16130           else
16131             SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
16132 
16133           dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
16134           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16135 
16136           if (cur < num_files - 16)
16137             SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
16138           else
16139             SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
16140 
16141 
16142           /* "Play" button: */
16143 
16144           dest.x = r_ttools.w;
16145           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16146           SDL_BlitSurface(img_play, NULL, screen, &dest);
16147 
16148           dest.x = r_ttools.w + (button_w - img_openlabels_play->w) / 2;
16149           dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_play->h;
16150           SDL_BlitSurface(img_openlabels_play, NULL, screen, &dest);
16151 
16152 
16153           /* "GIF Export" button: */
16154 
16155           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w * 2;
16156           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16157           SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
16158 
16159           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w * 2 + (button_w - img_gif_export->w) / 2;
16160           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16161           SDL_BlitSurface(img_gif_export, NULL, screen, &dest);
16162 
16163           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w * 2 + (button_w - img_openlabels_gif_export->w) / 2;
16164           dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_gif_export->h;
16165           SDL_BlitSurface(img_openlabels_gif_export, NULL, screen, &dest);
16166 
16167 
16168           /* "Back" button: */
16169 
16170           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w;
16171           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16172           SDL_BlitSurface(img_back, NULL, screen, &dest);
16173 
16174           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w + (button_w - img_openlabels_back->w) / 2;
16175           dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_back->h;
16176           SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
16177 
16178 
16179           /* Speed control: */
16180 
16181           speeds = 10;
16182           x_per = (float)r_ttools.w / speeds;
16183           y_per = (float)button_h / speeds;
16184 
16185           for (i = 0; i < speeds; i++)
16186             {
16187               xx = ceil(x_per);
16188               yy = ceil(y_per * i);
16189 
16190               if (i <= speed)
16191                 btn = thumbnail(img_btn_down, xx, yy, 0);
16192               else
16193                 btn = thumbnail(img_btn_up, xx, yy, 0);
16194 
16195               blnk = thumbnail(img_btn_off, xx, button_h - yy, 0);
16196 
16197               /* FIXME: Check for NULL! */
16198 
16199               dest.x = r_ttools.w + button_w + (i * x_per);
16200               dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16201               SDL_BlitSurface(blnk, NULL, screen, &dest);
16202 
16203               dest.x = r_ttools.w + button_w + (i * x_per);
16204               dest.y = (button_h * buttons_tall + r_ttools.h) - (y_per * i);
16205               SDL_BlitSurface(btn, NULL, screen, &dest);
16206 
16207               SDL_FreeSurface(btn);
16208               SDL_FreeSurface(blnk);
16209             }
16210 
16211           SDL_Flip(screen);
16212 
16213           update_list = 0;
16214         }
16215 
16216       /* Was a call to SDL_WaitEvent(&event); before,
16217          changed to this while loop in order to get joystick working */
16218       while (SDL_PollEvent(&event))
16219         {
16220           if (event.type == SDL_QUIT)
16221             {
16222               done = 1;
16223 
16224               /* FIXME: Handle SDL_Quit better */
16225             }
16226           else if (event.type == SDL_ACTIVEEVENT)
16227             {
16228               handle_active(&event);
16229             }
16230           else if (event.type == SDL_KEYUP)
16231             {
16232               key = event.key.keysym.sym;
16233 
16234               handle_keymouse(key, SDL_KEYUP, 24, NULL, NULL);
16235             }
16236           else if (event.type == SDL_KEYDOWN)
16237             {
16238               key = event.key.keysym.sym;
16239 
16240 
16241               dest.x = button_w * 3;
16242               dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
16243               dest.w = button_w * 2;
16244               dest.h = button_h;
16245 
16246               handle_keymouse(key, SDL_KEYDOWN, 24, &dest, NULL);
16247 
16248               if (key == SDLK_RETURN)
16249                 {
16250                   /* Play */
16251 
16252                   //      done = 1;
16253                   //      playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
16254                   event.type = SDL_MOUSEBUTTONDOWN;
16255                   event.button.x = button_w * 2 + 5;
16256                   event.button.y = (button_h * buttons_tall + r_ttools.h) - button_h + 5;
16257                   event.button.button = 1;
16258                   SDL_PushEvent(&event);
16259 
16260 
16261                 }
16262               else if (key == SDLK_ESCAPE)
16263                 {
16264                   /* Go back: */
16265 
16266                   go_back = 1;
16267                   done = 1;
16268                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
16269                 }
16270             }
16271           else if (event.type == SDL_MOUSEBUTTONDOWN && valid_click(event.button.button))
16272             {
16273               if (event.button.x >= r_ttools.w && event.button.x < WINDOW_WIDTH - r_ttoolopt.w &&
16274                   event.button.y >= img_scroll_up->h && event.button.y < (button_h * buttons_tall + r_ttools.h - button_h))
16275                 {
16276                   /* Picked an icon! */
16277 
16278                   which = ((event.button.x - r_ttools.w) / (THUMB_W) + (((event.button.y - img_scroll_up->h) / THUMB_H) * 4)) + cur;
16279 
16280                   if (which < num_files)
16281                     {
16282                       playsound(screen, 1, SND_BLEEP, 1, event.button.x, SNDDIST_NEAR);
16283 
16284                       /* Is it selected already? */
16285 
16286                       found = -1;
16287                       for (i = 0; i < num_selected && found == -1; i++)
16288                         {
16289                           if (selected[i] == which)
16290                             found = i;
16291                         }
16292 
16293                       if (found == -1)
16294                         {
16295                           /* No!  Select it! */
16296 
16297                           selected[num_selected++] = which;
16298                         }
16299                       else
16300                         {
16301                           /* Yes!  Unselect it! */
16302 
16303                           for (i = found; i < num_selected - 1; i++)
16304                             selected[i] = selected[i + 1];
16305 
16306                           num_selected--;
16307                         }
16308 
16309                       update_list = 1;
16310                     }
16311                 }
16312               else if (event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
16313                        event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2)
16314                 {
16315                   if (event.button.y < img_scroll_up->h)
16316                     {
16317                       /* Up scroll button: */
16318 
16319                       if (cur > 0)
16320                         {
16321                           cur = cur - 4;
16322                           update_list = 1;
16323                           playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
16324 
16325                           if (cur == 0)
16326                             do_setcursor(cursor_arrow);
16327                         }
16328 
16329                       if (which >= cur + 16)
16330                         which = which - 4;
16331                     }
16332                   else if (event.button.y >= (button_h * buttons_tall + r_ttools.h - button_h) &&
16333                            event.button.y < (button_h * buttons_tall + r_ttools.h - img_scroll_down->h))
16334                     {
16335                       /* Down scroll button: */
16336 
16337                       if (cur < num_files - 16)
16338                         {
16339                           cur = cur + 4;
16340                           update_list = 1;
16341                           playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
16342 
16343                           if (cur >= num_files - 16)
16344                             do_setcursor(cursor_arrow);
16345                         }
16346 
16347                       if (which < cur)
16348                         which = which + 4;
16349                     }
16350                 }
16351               else if (event.button.x >= r_ttools.w && event.button.x < r_ttools.w + button_w &&
16352                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
16353                        event.button.y < (button_h * buttons_tall + r_ttools.h))
16354                 {
16355                   /* Play */
16356 
16357                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
16358 
16359 
16360                   /* If none selected, select all, in order! */
16361 
16362                   if (num_selected == 0)
16363                     {
16364                       for (i = 0; i < num_files; i++)
16365                         selected[i] = i;
16366                       num_selected = num_files;
16367                     }
16368 
16369                   play_slideshow(selected, num_selected, dirname, d_names, d_exts, speed);
16370 
16371 
16372                   /* Redraw entire screen, after playback: */
16373 
16374                   SDL_FillRect(screen, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
16375                   draw_toolbar();
16376                   draw_colors(COLORSEL_CLOBBER_WIPE);
16377                   draw_none();
16378 
16379                   /* Instructions for Slideshow file dialog */
16380                   freeme = textdir(TUX_TIP_SLIDESHOW);
16381                   draw_tux_text(TUX_BORED, freeme, 1);
16382                   free(freeme);
16383 
16384                   SDL_Flip(screen);
16385 
16386                   update_list = 1;
16387                 }
16388               else if (event.button.x >= r_ttools.w + button_w && event.button.x < r_ttools.w + button_w + r_ttools.w &&
16389                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
16390                        event.button.y < (button_h * buttons_tall + r_ttools.h))
16391                 {
16392                   /* Speed slider */
16393 
16394                   int old_speed, control_sound, click_x;
16395 
16396                   old_speed = speed;
16397 
16398                   click_x = event.button.x - r_ttools.w - button_w;
16399                   speed = ((10 * click_x) / r_ttools.w);
16400 
16401                   control_sound = -1;
16402 
16403                   if (speed < old_speed)
16404                     control_sound = SND_SHRINK;
16405                   else if (speed > old_speed)
16406                     control_sound = SND_GROW;
16407 
16408                   if (control_sound != -1)
16409                     {
16410                       playsound(screen, 0, control_sound, 0, SNDPOS_CENTER, SNDDIST_NEAR);
16411 
16412                       update_list = 1;
16413                     }
16414                 }
16415               else if (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w - button_w) &&
16416                        event.button.x < (WINDOW_WIDTH - r_ttoolopt.w - button_w) &&
16417                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
16418                        event.button.y < (button_h * buttons_tall + r_ttools.h))
16419                 {
16420                   /* GIF Export */
16421 
16422                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
16423 
16424                   if (num_selected < 2)
16425 		    {
16426 	              /* None selected? Too dangerous to automatically select all (like we do for slideshow playback).
16427 		         Only 1 selected?  No point in saving as GIF.
16428                       */
16429                       freeme = textdir(gettext_noop("Select 2 or more drawings to turn into an animated GIF."));
16430                       draw_tux_text(TUX_BORED, freeme, 1);
16431                       free(freeme);
16432 
16433                       control_drawtext_timer(2000, instructions, 0); /* N.B. It will draw instructions, regardless */
16434 		    }
16435 		  else
16436 		    {
16437                       export_successful = export_gif(selected, num_selected, dirname, d_names, d_exts, speed);
16438 
16439                       /* Redraw entire screen, after export: */
16440                       SDL_FillRect(screen, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
16441                       draw_toolbar();
16442                       draw_colors(COLORSEL_CLOBBER_WIPE);
16443                       draw_none();
16444 
16445                       /* Show a message depending on success */
16446                       if (export_successful)
16447                         do_prompt_snd(PROMPT_GIF_EXPORT_TXT, PROMPT_EXPORT_YES, "", SND_TUXOK, screen->w / 2, screen->h / 2);
16448                       else
16449                         do_prompt_snd(PROMPT_GIF_EXPORT_FAILED_TXT, PROMPT_EXPORT_YES, "", SND_YOUCANNOT, screen->w / 2, screen->h / 2);
16450 
16451                       freeme = textdir(TUX_TIP_SLIDESHOW);
16452                       draw_tux_text(TUX_BORED, freeme, 1);
16453                       free(freeme);
16454 
16455                       SDL_Flip(screen);
16456 
16457                       update_list = 1;
16458 		    }
16459                 }
16460               else if (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w) &&
16461                        event.button.x < (WINDOW_WIDTH - r_ttoolopt.w) &&
16462                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
16463                        event.button.y < (button_h * buttons_tall + r_ttools.h))
16464                 {
16465                   /* Back */
16466 
16467                   go_back = 1;
16468                   done = 1;
16469                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
16470                 }
16471             }
16472           else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button >= 4 && event.button.button <= 5 && wheely)
16473             {
16474               /* Scroll wheel! */
16475 
16476               if (event.button.button == 4 && cur > 0)
16477                 {
16478                   cur = cur - 4;
16479                   update_list = 1;
16480                   playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
16481 
16482                   if (cur == 0)
16483                     do_setcursor(cursor_arrow);
16484 
16485                   if (which >= cur + 16)
16486                     which = which - 4;
16487                 }
16488               else if (event.button.button == 5 && cur < num_files - 16)
16489                 {
16490                   cur = cur + 4;
16491                   update_list = 1;
16492                   playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
16493 
16494                   if (cur >= num_files - 16)
16495                     do_setcursor(cursor_arrow);
16496 
16497                   if (which < cur)
16498                     which = which + 4;
16499                 }
16500             }
16501           else if (event.type == SDL_MOUSEMOTION)
16502             {
16503               /* Deal with mouse pointer shape! */
16504 
16505               if (event.button.y < img_scroll_up->h &&
16506                   event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
16507                   event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 && cur > 0)
16508                 {
16509                   /* Scroll up button: */
16510 
16511                   do_setcursor(cursor_up);
16512                 }
16513               else if (event.button.y >= (button_h * buttons_tall + r_ttools.h - button_h) &&
16514                        event.button.y < (button_h * buttons_tall + r_ttools.h - img_scroll_up->h) &&
16515                        event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
16516                        event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 && cur < num_files - 16)
16517                 {
16518                   /* Scroll down button: */
16519 
16520                   do_setcursor(cursor_down);
16521                 }
16522               else if (((event.button.x >= r_ttools.w && event.button.x < r_ttools.w + button_w + r_ttools.w) ||
16523                         (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w * 2) &&
16524                          event.button.x < (WINDOW_WIDTH - r_ttoolopt.w))) &&
16525                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
16526                        event.button.y < (button_h * buttons_tall + r_ttools.h))
16527                 {
16528                   /* One of the command buttons: */
16529 
16530                   do_setcursor(cursor_hand);
16531                 }
16532               else if (event.button.x >= r_ttools.w && event.button.x < WINDOW_WIDTH - r_ttoolopt.w
16533                        && event.button.y > img_scroll_up->h
16534                        && event.button.y < (button_h * buttons_tall + r_ttools.h) - button_h
16535                        &&
16536                        ((((event.button.x - r_ttools.w) / (THUMB_W) +
16537                           (((event.button.y - img_scroll_up->h) / THUMB_H) * 4)) + cur) < num_files))
16538                 {
16539                   /* One of the thumbnails: */
16540 
16541                   do_setcursor(cursor_hand);
16542                 }
16543               else
16544                 {
16545                   /* Unclickable... */
16546 
16547                   do_setcursor(cursor_arrow);
16548                 }
16549               oldpos_x = event.button.x;
16550               oldpos_y = event.button.y;
16551             }
16552           else if (event.type == SDL_JOYAXISMOTION)
16553             handle_joyaxismotion(event, &motioner, &val_x, &val_y);
16554 
16555           else if (event.type == SDL_JOYHATMOTION)
16556             handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
16557 
16558           else if (event.type == SDL_JOYBALLMOTION)
16559             handle_joyballmotion(event, oldpos_x, oldpos_y);
16560 
16561           else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
16562             handle_joybuttonupdown(event, oldpos_x, oldpos_y);
16563 
16564           else if (event.type == SDL_USEREVENT)
16565             {
16566               if (event.user.code == USEREVENT_TEXT_UPDATE)
16567                 {
16568                   if (event.user.data1 != NULL)
16569                     {
16570                       draw_tux_text(TUX_BORED, instructions, 1);
16571                     }
16572 		}
16573 	    }
16574         }
16575 
16576       if (motioner | hatmotioner)
16577         handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x, valhat_y);
16578 
16579       SDL_Delay(10);
16580     }
16581   while (!done);
16582 
16583 
16584   /* Clean up: */
16585 
16586   free_surface_array(thumbs, num_files);
16587 
16588   free(thumbs);
16589 
16590   for (i = 0; i < num_files; i++)
16591     {
16592       free(d_names[i]);
16593       free(d_exts[i]);
16594     }
16595 
16596   free(dirname);
16597 
16598   free(d_names);
16599   free(d_exts);
16600   free(selected);
16601 
16602   control_drawtext_timer(0, "", 0);
16603   free(instructions);
16604 
16605   return go_back;
16606 }
16607 
16608 
16609 /**
16610  * Play an animated slideshow within Tux Paint.
16611  * Called by the Open->Slideshow dialog, once a set of images
16612  * have been selected/chosen, and "Play" is clicked.
16613  *
16614  * @param int * selected -- array of selected images, in the order they should be played
16615  * @param int num_selected -- count of how many images were selected
16616  * @char * dirname -- path to the directory of saved images to be played
16617  * @char ** d_names -- array of file basenames of the images to be played
16618  * @char ** d_ext -- array of file exentions of the images to be played
16619  * @int speed -- how fast to play the slideshow (0 = no automatic advance, 1 = slowest, 10 = as fast as possible)
16620  */
play_slideshow(int * selected,int num_selected,char * dirname,char ** d_names,char ** d_exts,int speed)16621 static void play_slideshow(int *selected, int num_selected, char *dirname, char **d_names, char **d_exts, int speed)
16622 {
16623   int i, which, next, done;
16624   int val_x, val_y, motioner;
16625   int valhat_x, valhat_y, hatmotioner;
16626 
16627   SDL_Surface *img;
16628   char *tmp_starter_id, *tmp_template_id, *tmp_file_id;
16629   int tmp_starter_mirrored, tmp_starter_flipped, tmp_starter_personal;
16630   /* FIXME: Do we want to keep `template_personal` safe, too? */
16631   char fname[1024];
16632   SDL_Event event;
16633   SDLKey key;
16634   SDL_Rect dest;
16635   Uint32 last_ticks;
16636 
16637   val_x = val_y = motioner = 0;
16638   valhat_x = valhat_y = hatmotioner = 0;
16639 
16640   /* Back up the current image's IDs, because they will get
16641      clobbered below! */
16642   tmp_starter_id = strdup(starter_id);
16643   tmp_template_id = strdup(template_id);
16644   tmp_file_id = strdup(file_id);
16645   tmp_starter_mirrored = starter_mirrored;
16646   tmp_starter_flipped = starter_flipped;
16647   tmp_starter_personal = starter_personal;
16648   /* FIXME: Do we want to keep `template_personal` safe, too? */
16649 
16650   do_setcursor(cursor_tiny);
16651 
16652   done = 0;
16653 
16654   do
16655     {
16656       for (i = 0; i < num_selected && !done; i++)
16657         {
16658           which = selected[i];
16659           show_progress_bar(screen);
16660 
16661 
16662           /* Figure out filename: */
16663 
16664           safe_snprintf(fname, sizeof(fname), "%s/%s%s", dirname, d_names[which], d_exts[which]);
16665 
16666 
16667           img = myIMG_Load(fname);
16668 
16669           if (img != NULL)
16670             {
16671               autoscale_copy_smear_free(img, screen, SDL_BlitSurface);
16672 
16673               safe_strncpy(file_id, d_names[which], sizeof(file_id));
16674 
16675 
16676 /* FIXME: is the starter even used??? -bjk 2009.10.16 */
16677 
16678               /* See if this saved image was based on a 'starter' */
16679 
16680               load_starter_id(d_names[which], NULL);
16681 
16682               if (starter_id[0] != '\0')
16683                 {
16684                   load_starter(starter_id);
16685 
16686                   if (starter_mirrored)
16687                     mirror_starter();
16688 
16689                   if (starter_flipped)
16690                     flip_starter();
16691                 }
16692               else
16693                 load_template(template_id);
16694             }
16695 
16696           /* "Back" button: */
16697 
16698           dest.x = screen->w - button_w;
16699           dest.y = screen->h - button_h;
16700           SDL_BlitSurface(img_back, NULL, screen, &dest);
16701 
16702           dest.x = screen->w - button_w + (button_w - img_openlabels_back->w) / 2;
16703           dest.y = screen->h - img_openlabels_back->h;
16704           SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
16705 
16706           /* "Next" button: */
16707 
16708           dest.x = 0;
16709           dest.y = screen->h - button_h;
16710           SDL_BlitSurface(img_play, NULL, screen, &dest);
16711 
16712           dest.x = (button_w - img_openlabels_next->w) / 2;
16713           dest.y = screen->h - img_openlabels_next->h;
16714           SDL_BlitSurface(img_openlabels_next, NULL, screen, &dest);
16715 
16716 
16717           SDL_Flip(screen);
16718 
16719 
16720           /* Handle any events, and otherwise wait for time to count down: */
16721 
16722           next = 0;
16723           last_ticks = SDL_GetTicks();
16724 
16725           do
16726             {
16727               while (SDL_PollEvent(&event))
16728                 {
16729                   if (event.type == SDL_QUIT)
16730                     {
16731                       /* FIXME: Handle SDL_QUIT better! */
16732 
16733                       next = 1;
16734                       done = 1;
16735                     }
16736                   else if (event.type == SDL_ACTIVEEVENT)
16737                     {
16738                       handle_active(&event);
16739                     }
16740                   else if (event.type == SDL_KEYDOWN)
16741                     {
16742                       key = event.key.keysym.sym;
16743 
16744                       handle_keymouse(key, SDL_KEYDOWN, 24, NULL, NULL);
16745 
16746                       if (key == SDLK_RETURN || key == SDLK_SPACE || key == SDLK_PAGEDOWN)
16747                         {
16748                           /* RETURN, SPACE or PAGEDOWN: Skip to next right away! */
16749 
16750                           next = 1;
16751                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
16752                         }
16753                       else if (key == SDLK_PAGEUP)
16754                         {
16755                           /* LEFT: Go back one! */
16756 
16757                           i = i - 2;
16758 
16759                           if (i < -1)
16760                             i = num_selected - 2;
16761 
16762                           next = 1;
16763                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
16764                         }
16765                       else if (key == SDLK_ESCAPE)
16766                         {
16767                           /* Go back: */
16768 
16769                           next = 1;
16770                           done = 1;
16771                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
16772                         }
16773                     }
16774                   else if (event.type == SDL_MOUSEBUTTONDOWN)
16775                     {
16776                       /* Mouse click! */
16777 
16778                       if (event.button.x >= screen->w - button_w && event.button.y >= screen->h - button_h)
16779                         {
16780                           /* Back button */
16781 
16782                           next = 1;
16783                           done = 1;
16784                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
16785                         }
16786                       else
16787                         {
16788                           /* Otherwise, skip to next image right away! */
16789 
16790                           next = 1;
16791                           playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
16792                         }
16793                     }
16794                   else if (event.type == SDL_MOUSEMOTION)
16795                     {
16796                       /* Deal with mouse pointer shape! */
16797 
16798                       if ((event.button.x >= screen->w - button_w || event.button.x < button_w) && event.button.y >= screen->h - button_h)
16799                         {
16800                           /* Back or Next buttons */
16801 
16802                           do_setcursor(cursor_hand);
16803                         }
16804                       else
16805                         {
16806                           /* Otherwise, minimal cursor... */
16807 
16808                           do_setcursor(cursor_tiny);
16809                         }
16810                       oldpos_x = event.button.x;
16811                       oldpos_y = event.button.y;
16812                     }
16813 
16814                   else if (event.type == SDL_JOYAXISMOTION)
16815                     handle_joyaxismotion(event, &motioner, &val_x, &val_y);
16816 
16817                   else if (event.type == SDL_JOYHATMOTION)
16818                     handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
16819 
16820                   else if (event.type == SDL_JOYBALLMOTION)
16821                     handle_joyballmotion(event, oldpos_x, oldpos_y);
16822 
16823                   else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
16824                     handle_joybuttonupdown(event, oldpos_x, oldpos_y);
16825 
16826                 }
16827 
16828               if (motioner | hatmotioner)
16829                 handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x,
16830                                  valhat_y);
16831 
16832               SDL_Delay(10);
16833 
16834 
16835               /* Automatically skip to the next one after time expires: */
16836 
16837               if (speed != 0)
16838                 {
16839                   if (SDL_GetTicks() >= last_ticks + (10 - speed) * 500)
16840                     next = 1;
16841                 }
16842             }
16843           while (!next);
16844         }
16845     }
16846   while (!done);
16847 
16848   /* Restore everything about the currently-active image
16849      that got clobbered above */
16850   strcpy(starter_id, tmp_starter_id); /* safe; originally strdup()'d from the dest. */
16851   free(tmp_starter_id);
16852 
16853   strcpy(template_id, tmp_template_id); /* safe; originally strdup()'d from the dest. */
16854   free(tmp_template_id);
16855 
16856   strcpy(file_id, tmp_file_id); /* safe; originally strdup()'d from the dest. */
16857   free(tmp_file_id);
16858 
16859   starter_mirrored = tmp_starter_mirrored;
16860   starter_flipped = tmp_starter_flipped;
16861   starter_personal = tmp_starter_personal;
16862 }
16863 
16864 
16865 
16866 /**
16867  * FIXME
16868  */
16869 /* Draws large, bitmap digits over thumbnails in slideshow selection screen: */
draw_selection_digits(int right,int bottom,int n)16870 static void draw_selection_digits(int right, int bottom, int n)
16871 {
16872   SDL_Rect src, dest;
16873   int i, v, len, place;
16874   int digit_w, digit_h, x, y;
16875 
16876   digit_w = img_select_digits->w / 10;
16877   digit_h = img_select_digits->h;
16878 
16879   if (n > 99)
16880     {
16881       len = 3;
16882       place = 100;
16883     }
16884   else if (n > 9)
16885     {
16886       len = 2;
16887       place = 10;
16888     }
16889   else
16890     {
16891       len = 1;
16892       place = 1;
16893     }
16894 
16895   x = right - digit_w * len;
16896   y = bottom - digit_h;
16897 
16898   for (i = 0; i < len; i++)
16899     {
16900       v = (n / place) % (place * 10);
16901 
16902       src.x = digit_w * v;
16903       src.y = 0;
16904       src.w = digit_w;
16905       src.h = digit_h;
16906 
16907       dest.x = x;
16908       dest.y = y;
16909 
16910       SDL_BlitSurface(img_select_digits, &src, screen, &dest);
16911 
16912       x = x + digit_w;
16913       place = place / 10;
16914     }
16915 }
16916 
16917 
16918 /**
16919  * FIXME
16920  */
16921 /* Let sound effects (e.g., "Save" sfx) play out before quitting... */
wait_for_sfx(void)16922 static void wait_for_sfx(void)
16923 {
16924 #ifndef NOSOUND
16925   if (use_sound)
16926     {
16927       while (Mix_Playing(-1))
16928         SDL_Delay(10);
16929     }
16930 #endif
16931 }
16932 
16933 
16934 /* stamp outline */
16935 #ifndef LOW_QUALITY_STAMP_OUTLINE
16936 /* XOR-based outline of rubber stamp shapes
16937    (unused if LOW_QUALITY_STAMP_OUTLINE is #defined) */
16938 
16939 #if 1
16940 #define STIPLE_W 5
16941 #define STIPLE_H 5
16942 static char stiple[] = "84210" "10842" "42108" "08421" "21084";
16943 #endif
16944 
16945 #if 0
16946 #define STIPLE_W 4
16947 #define STIPLE_H 4
16948 static char stiple[] = "8000" "0800" "0008" "0080";
16949 #endif
16950 
16951 #if 0
16952 #define STIPLE_W 12
16953 #define STIPLE_H 12
16954 static char stiple[] =
16955   "808808000000"
16956   "800000080880"
16957   "008088080000"
16958   "808000000808"
16959   "000080880800"
16960   "088080000008" "000000808808" "080880800000" "080000008088" "000808808000" "880800000080" "000008088080";
16961 #endif
16962 
16963 static unsigned char *stamp_outline_data;
16964 static int stamp_outline_w, stamp_outline_h;
16965 
16966 /**
16967  * FIXME
16968  */
update_stamp_xor(void)16969 static void update_stamp_xor(void)
16970 {
16971   int xx, yy, rx, ry;
16972   Uint8 dummy;
16973   SDL_Surface *src;
16974 
16975   Uint32(*getpixel) (SDL_Surface *, int, int);
16976   unsigned char *alphabits;
16977   int new_w;
16978   int new_h;
16979   unsigned char *outline;
16980   unsigned char *old_outline_data;
16981 
16982   src = active_stamp;
16983 
16984   /* start by scaling */
16985   src = thumbnail(src, CUR_STAMP_W, CUR_STAMP_H, 0);
16986 
16987   getpixel = getpixels[src->format->BytesPerPixel];
16988   alphabits = calloc(src->w + 4, src->h + 4);
16989 
16990   SDL_LockSurface(src);
16991   for (yy = 0; yy < src->h; yy++)
16992     {
16993       ry = yy;
16994       for (xx = 0; xx < src->w; xx++)
16995         {
16996           rx = xx;
16997           SDL_GetRGBA(getpixel(src, rx, ry),
16998                       src->format, &dummy, &dummy, &dummy, alphabits + xx + 2 + (yy + 2) * (src->w + 4));
16999         }
17000     }
17001   SDL_UnlockSurface(src);
17002 
17003   new_w = src->w + 4;
17004   new_h = src->h + 4;
17005   SDL_FreeSurface(src);
17006   outline = calloc(new_w, new_h);
17007 
17008   for (yy = 1; yy < new_h - 1; yy++)
17009     {
17010       for (xx = 1; xx < new_w - 1; xx++)
17011         {
17012           unsigned char above = 0;
17013           unsigned char below = 0xff;
17014           unsigned char tmp;
17015 
17016           tmp = alphabits[(xx - 1) + (yy - 1) * new_w];
17017           above |= tmp;
17018           below &= tmp;
17019           tmp = alphabits[(xx + 1) + (yy - 1) * new_w];
17020           above |= tmp;
17021           below &= tmp;
17022 
17023           tmp = alphabits[(xx + 0) + (yy - 1) * new_w];
17024           above |= tmp;
17025           below &= tmp;
17026           tmp = alphabits[(xx + 0) + (yy + 0) * new_w];
17027           above |= tmp;
17028           below &= tmp;
17029           tmp = alphabits[(xx + 1) + (yy + 0) * new_w];
17030           above |= tmp;
17031           below &= tmp;
17032           tmp = alphabits[(xx - 1) + (yy + 0) * new_w];
17033           above |= tmp;
17034           below &= tmp;
17035           tmp = alphabits[(xx + 0) + (yy + 1) * new_w];
17036           above |= tmp;
17037           below &= tmp;
17038 
17039           tmp = alphabits[(xx - 1) + (yy + 1) * new_w];
17040           above |= tmp;
17041           below &= tmp;
17042           tmp = alphabits[(xx + 1) + (yy + 1) * new_w];
17043           above |= tmp;
17044           below &= tmp;
17045 
17046           outline[xx + yy * new_w] = (above ^ below) >> 7;
17047         }
17048     }
17049 
17050   old_outline_data = stamp_outline_data;
17051   stamp_outline_data = outline;
17052   stamp_outline_w = new_w;
17053   stamp_outline_h = new_h;
17054   if (old_outline_data)
17055     free(old_outline_data);
17056   free(alphabits);
17057 }
17058 
17059 /**
17060  * FIXME
17061  */
stamp_xor(int x,int y)17062 static void stamp_xor(int x, int y)
17063 {
17064   int xx, yy, sx, sy;
17065 
17066   SDL_LockSurface(screen);
17067   for (yy = 0; yy < stamp_outline_h; yy++)
17068     {
17069       for (xx = 0; xx < stamp_outline_w; xx++)
17070         {
17071           if (!stamp_outline_data[xx + yy * stamp_outline_w])   /* FIXME: Conditional jump or move depends on uninitialised value(s) */
17072             continue;
17073           sx = x + xx - stamp_outline_w / 2;
17074           sy = y + yy - stamp_outline_h / 2;
17075           if (stiple[sx % STIPLE_W + sy % STIPLE_H * STIPLE_W] != '8')
17076             continue;
17077           xorpixel(sx, sy);
17078         }
17079     }
17080   SDL_UnlockSurface(screen);
17081 }
17082 
17083 #endif
17084 
17085 /**
17086  * FIXME
17087  */
rgbtohsv(Uint8 r8,Uint8 g8,Uint8 b8,float * h,float * s,float * v)17088 static void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v)
17089 {
17090   float rgb_min, rgb_max, delta, r, g, b;
17091 
17092   r = (r8 / 255.0);
17093   g = (g8 / 255.0);
17094   b = (b8 / 255.0);
17095 
17096   rgb_min = min(r, min(g, b));
17097   rgb_max = max(r, max(g, b));
17098   *v = rgb_max;
17099 
17100   delta = rgb_max - rgb_min;
17101 
17102   if (rgb_max == 0)
17103     {
17104       /* Black */
17105 
17106       *s = 0;
17107       *h = -1;
17108     }
17109   else
17110     {
17111       *s = delta / rgb_max;
17112 
17113       if (r == rgb_max)
17114         *h = (g - b) / delta;
17115       else if (g == rgb_max)
17116         *h = 2 + (b - r) / delta;       /* between cyan & yellow */
17117       else
17118         *h = 4 + (r - g) / delta;       /* between magenta & cyan */
17119 
17120       *h = (*h * 60);           /* degrees */
17121 
17122       if (*h < 0)
17123         *h = (*h + 360);
17124     }
17125 }
17126 
17127 
17128 /**
17129  * FIXME
17130  */
hsvtorgb(float h,float s,float v,Uint8 * r8,Uint8 * g8,Uint8 * b8)17131 static void hsvtorgb(float h, float s, float v, Uint8 * r8, Uint8 * g8, Uint8 * b8)
17132 {
17133   int i;
17134   float f, p, q, t, r, g, b;
17135 
17136   if (s == 0)
17137     {
17138       /* Achromatic (grey) */
17139 
17140       r = v;
17141       g = v;
17142       b = v;
17143     }
17144   else
17145     {
17146       h = h / 60;
17147       i = floor(h);
17148       f = h - i;
17149       p = v * (1 - s);
17150       q = v * (1 - s * f);
17151       t = v * (1 - s * (1 - f));
17152 
17153       if (i == 0)
17154         {
17155           r = v;
17156           g = t;
17157           b = p;
17158         }
17159       else if (i == 1)
17160         {
17161           r = q;
17162           g = v;
17163           b = p;
17164         }
17165       else if (i == 2)
17166         {
17167           r = p;
17168           g = v;
17169           b = t;
17170         }
17171       else if (i == 3)
17172         {
17173           r = p;
17174           g = q;
17175           b = v;
17176         }
17177       else if (i == 4)
17178         {
17179           r = t;
17180           g = p;
17181           b = v;
17182         }
17183       else
17184         {
17185           r = v;
17186           g = p;
17187           b = q;
17188         }
17189     }
17190 
17191 
17192   *r8 = (Uint8) (r * 255);
17193   *g8 = (Uint8) (g * 255);
17194   *b8 = (Uint8) (b * 255);
17195 }
17196 
17197 /**
17198  * FIXME
17199  */
print_image(void)17200 static void print_image(void)
17201 {
17202   int cur_time, scroll;
17203 
17204   cur_time = SDL_GetTicks() / 1000;
17205   scroll = (NUM_TOOLS > buttons_tall * gd_tools.cols) ? img_scroll_down->h : 0;
17206 
17207 #ifdef DEBUG
17208   printf("Current time = %d\n", cur_time);
17209 #endif
17210 
17211   if (cur_time >= last_print_time + print_delay)
17212     {
17213       if (alt_print_command_default == ALTPRINT_ALWAYS)
17214         want_alt_printcommand = 1;
17215       else if (alt_print_command_default == ALTPRINT_NEVER)
17216         want_alt_printcommand = 0;
17217       else                      /* ALTPRINT_MOD */
17218         want_alt_printcommand = (SDL_GetModState() & KMOD_ALT);
17219 
17220       if (do_prompt_image_snd(PROMPT_PRINT_NOW_TXT,
17221                               PROMPT_PRINT_NOW_YES,
17222                               PROMPT_PRINT_NOW_NO,
17223                               img_printer, NULL, NULL, SND_AREYOUSURE,
17224                               (TOOL_PRINT % 2) * button_w + button_w / 2,
17225 			      (TOOL_PRINT / 2) * button_h + r_ttools.h + button_h / 2 - tool_scroll * button_h / gd_tools.cols + scroll))
17226         {
17227           do_print();
17228 
17229           last_print_time = cur_time;
17230         }
17231     }
17232   else
17233     {
17234       do_prompt_image_snd(PROMPT_PRINT_TOO_SOON_TXT,
17235                           PROMPT_PRINT_TOO_SOON_YES, "", img_printer_wait, NULL, NULL, SND_YOUCANNOT, 0, screen->h);
17236     }
17237 }
17238 
17239 /**
17240  * FIXME
17241  */
do_print(void)17242 void do_print(void)
17243 {
17244   /* Assemble drawing plus any labels: */
17245   SDL_BlitSurface(canvas, NULL, save_canvas, NULL);
17246   SDL_BlitSurface(label, NULL, save_canvas, NULL);
17247 
17248 #if !defined(WIN32) && !defined(__BEOS__) && !defined(__APPLE__) && !defined(__HAIKU__)
17249   const char *pcmd;
17250   FILE *pi;
17251 
17252   /* Linux, Unix, etc. */
17253 
17254   if (want_alt_printcommand && !fullscreen)
17255     pcmd = altprintcommand;
17256   else
17257     pcmd = printcommand;
17258 
17259   pi = popen(pcmd, "w");
17260 
17261   if (pi == NULL)
17262     {
17263       perror(pcmd);
17264     }
17265   else
17266     {
17267 #ifdef PRINTMETHOD_PNG_PNM_PS
17268       if (do_png_save(pi, pcmd, save_canvas, 0))
17269         do_prompt_snd(PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "", SND_TUXOK, screen->w / 2, screen->h / 2);
17270 #elif defined(PRINTMETHOD_PNM_PS)
17271       /* nothing here */
17272 #elif defined(PRINTMETHOD_PS)
17273       if (do_ps_save(pi, pcmd, save_canvas, papersize, 1))
17274         do_prompt_snd(PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "", SND_TUXOK, screen->w / 2, screen->h / 2);
17275       else
17276         do_prompt_snd(PROMPT_PRINT_FAILED_TXT, PROMPT_PRINT_YES, "", SND_YOUCANNOT, screen->w / 2, screen->h / 2);
17277 #else
17278 #error No print method defined!
17279 #endif
17280     }
17281 #else
17282 #ifdef WIN32
17283   /* Win32 */
17284 
17285   char f[512];
17286   int show = want_alt_printcommand;
17287 
17288   safe_snprintf(f, sizeof(f), "%s/%s", savedir, "print.cfg");        /* FIXME */
17289 
17290   {
17291     const char *error = SurfacePrint(save_canvas, use_print_config ? f : NULL, show);
17292 
17293     if (error)
17294       fprintf(stderr, "%s\n", error);
17295   }
17296 #elif defined(__BEOS__)
17297   /* BeOS */
17298 
17299   SurfacePrint(save_canvas);
17300 #elif defined(__APPLE__)
17301   /* macOS, iOS */
17302   int show = (want_alt_printcommand && !fullscreen);
17303 
17304   const char *error = SurfacePrint(save_canvas, show);
17305 
17306   if (error)
17307     {
17308       fprintf(stderr, "Cannot print: %s\n", error);
17309       do_prompt_snd(error, PROMPT_PRINT_YES, "", SND_TUXOK, 0, 0);
17310     }
17311 
17312 #endif
17313 
17314 #endif
17315 }
17316 
17317 /**
17318  * FIXME
17319  */
do_render_cur_text(int do_blit)17320 static void do_render_cur_text(int do_blit)
17321 {
17322   int w, h;
17323 
17324   SDL_Color color = { color_hexes[cur_color][0],
17325     color_hexes[cur_color][1],
17326     color_hexes[cur_color][2],
17327     0
17328   };
17329 
17330   SDL_Surface *tmp_surf;
17331   SDL_Rect dest, src;
17332   wchar_t *str;
17333 
17334   /* I THINK this is unnecessary to call here; trying to prevent flicker when typing -bjk 2010.02.10 */
17335   /* hide_blinking_cursor(); */
17336 
17337 
17338   /* Keep cursor on the screen! */
17339 
17340   if (cursor_y > ((button_h * buttons_tall + r_ttools.h) - TuxPaint_Font_FontHeight(getfonthandle(cur_font))))
17341     {
17342       cursor_y = ((button_h * buttons_tall + r_ttools.h) - TuxPaint_Font_FontHeight(getfonthandle(cur_font)));
17343     }
17344 
17345 
17346   /* Render the text: */
17347 
17348   if (texttool_len > 0)
17349     {
17350 #if defined(_FRIBIDI_H) || defined(FRIBIDI_H)
17351       //FriBidiCharType baseDir = FRIBIDI_TYPE_LTR;
17352 #if defined (__MINGW32__) && (__GNUC__ <= 4 )
17353       FriBidiCharType baseDir = FRIBIDI_TYPE_WL; /* Per: Shai Ayal <shaiay@gmail.com>, 2009-01-14 */
17354 #else
17355       FriBidiParType baseDir = FRIBIDI_TYPE_WL; //EP to avoid warning on types in now commented line above
17356 #endif
17357       FriBidiChar *unicodeIn, *unicodeOut;
17358       unsigned int i;
17359 
17360       unicodeIn = (FriBidiChar *) malloc(sizeof(FriBidiChar) * (texttool_len + 1));
17361       unicodeOut = (FriBidiChar *) malloc(sizeof(FriBidiChar) * (texttool_len + 1));
17362 
17363       str = (wchar_t *) malloc(sizeof(wchar_t) * (texttool_len + 1));
17364 
17365       for (i = 0; i < texttool_len; i++)
17366         unicodeIn[i] = (FriBidiChar) texttool_str[i];
17367 
17368       fribidi_log2vis(unicodeIn, texttool_len, &baseDir, unicodeOut, 0, 0, 0);
17369 
17370       /* FIXME: If we determine that some new text was RtoL, we should
17371          reposition the text */
17372 
17373       for (i = 0; i < texttool_len; i++)
17374         str[i] = (long)unicodeOut[i];
17375 
17376       str[texttool_len] = L'\0';
17377 
17378       free(unicodeIn);
17379       free(unicodeOut);
17380 #else
17381       str = uppercase_w(texttool_str);
17382 #endif
17383 
17384       tmp_surf = render_text_w(getfonthandle(cur_font), str, color);
17385 
17386       w = tmp_surf->w;
17387       h = tmp_surf->h;
17388 
17389       cursor_textwidth = w;
17390     }
17391   else                          /* Erase the stalle letter . */
17392     {
17393       if (cur_label != LABEL_SELECT)
17394         {
17395           update_canvas_ex_r(old_dest.x - r_ttools.w, old_dest.y, old_dest.x + old_dest.w, old_dest.y + old_dest.h, 0);
17396           old_dest.x = old_dest.y = old_dest.w = old_dest.h = 0;
17397 
17398 
17399           update_canvas_ex_r(old_cursor_x - 1,
17400                              old_cursor_y - 1,
17401                              old_cursor_x + 1, old_cursor_y + 1 + TuxPaint_Font_FontHeight(getfonthandle(cur_font)), 0);
17402 
17403           /* FIXME: Do less flickery updating here (use update_canvas_ex() above, then SDL_Flip() or SDL_UpdateRect() here -bjk 2010.02.10 */
17404 
17405           old_cursor_x = cursor_x;
17406           old_cursor_y = cursor_y;
17407           cursor_textwidth = 0;
17408         }
17409       /* FIXME: Is this SDL_Flip() still needed? Pere 2011.06.28 */
17410       SDL_Flip(screen);
17411       return;
17412 
17413     }
17414 
17415 
17416   if (!do_blit)
17417     {
17418       update_canvas_ex_r(old_dest.x - r_ttools.w, old_dest.y, old_dest.x + old_dest.w, old_dest.y + old_dest.h, 0);
17419 
17420       /* update_canvas_ex_r(cursor_x - 1, */
17421       /*            cursor_y - 1, */
17422       /*            cursor_x + 1 +  TuxPaint_Font_FontHeight(getfonthandle(cur_font)) * 3, */
17423       /*            cursor_y + 1 + TuxPaint_Font_FontHeight(getfonthandle(cur_font)), 0); */
17424 
17425 
17426       /* Draw outline around text: */
17427 
17428       dest.x = cursor_x - 2 + r_ttools.w;
17429       dest.y = cursor_y - 2;
17430       dest.w = w + 4;
17431       dest.h = h + 4;
17432 
17433       if (dest.x + dest.w > WINDOW_WIDTH - r_ttoolopt.w)
17434         dest.w = WINDOW_WIDTH - r_ttoolopt.w - dest.x;
17435       if (dest.y + dest.h > (button_h * buttons_tall + r_ttools.h))
17436         dest.h = (button_h * buttons_tall + r_ttools.h) - dest.y;
17437 
17438       SDL_FillRect(screen, &dest, SDL_MapRGB(canvas->format, 0, 0, 0));
17439 
17440       old_dest.x = dest.x;
17441       old_dest.y = dest.y;
17442       old_dest.w = dest.w;
17443       old_dest.h = dest.h;
17444 
17445       /* FIXME: This would be nice if it were alpha-blended: */
17446 
17447       dest.x = cursor_x + r_ttools.w;
17448       dest.y = cursor_y;
17449       dest.w = w;
17450       dest.h = h;
17451 
17452       if (dest.x + dest.w > WINDOW_WIDTH - r_ttoolopt.w)
17453         dest.w = WINDOW_WIDTH - r_ttoolopt.w - dest.x;
17454       if (dest.y + dest.h > (button_h * buttons_tall + r_ttools.h))
17455         dest.h = (button_h * buttons_tall + r_ttools.h) - dest.y;
17456 
17457       if ((color_hexes[cur_color][0] + color_hexes[cur_color][1] + color_hexes[cur_color][2]) >= 384)
17458         {
17459           /* Grey background if blit is white!... */
17460 
17461           SDL_FillRect(screen, &dest, SDL_MapRGB(canvas->format, 64, 64, 64));
17462         }
17463       else
17464         {
17465           /* White background, normally... */
17466 
17467           SDL_FillRect(screen, &dest, SDL_MapRGB(canvas->format, 255, 255, 255));
17468         }
17469     }
17470 
17471 
17472   /* Draw the text itself! */
17473 
17474   if (tmp_surf != NULL)
17475     {
17476       dest.x = cursor_x;
17477       dest.y = cursor_y;
17478 
17479       src.x = 0;
17480       src.y = 0;
17481       src.w = tmp_surf->w;
17482       src.h = tmp_surf->h;
17483 
17484       if (dest.x + src.w > WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w)
17485         src.w = WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w - dest.x;
17486       if (dest.y + src.h > (button_h * buttons_tall + r_ttools.h))
17487         src.h = (button_h * buttons_tall + r_ttools.h) - dest.y;
17488 
17489       if (do_blit)
17490         {
17491           if ((cur_tool == TOOL_LABEL && label_node_to_edit) ||
17492               ((old_tool == TOOL_LABEL && label_node_to_edit) &&
17493                (cur_tool == TOOL_PRINT ||
17494                 cur_tool == TOOL_SAVE || cur_tool == TOOL_OPEN || cur_tool == TOOL_NEW || cur_tool == TOOL_QUIT)))
17495             {
17496               have_to_rec_label_node = TRUE;
17497               add_label_node(src.w, src.h, dest.x, dest.y, tmp_surf);
17498               simply_render_node(current_label_node);
17499             }
17500           else if (cur_tool == TOOL_LABEL ||
17501                    (old_tool == TOOL_LABEL &&
17502                     (cur_tool == TOOL_PRINT ||
17503                      cur_tool == TOOL_SAVE || cur_tool == TOOL_OPEN || cur_tool == TOOL_NEW || cur_tool == TOOL_QUIT)))
17504             {
17505               myblit(tmp_surf, &src, label, &dest);
17506 
17507               have_to_rec_label_node = TRUE;
17508               add_label_node(src.w, src.h, dest.x, dest.y, tmp_surf);
17509 
17510             }
17511           else
17512             {
17513               SDL_BlitSurface(tmp_surf, &src, canvas, &dest);
17514             }
17515           update_canvas_ex_r(dest.x - 2, dest.y - 2, dest.x + tmp_surf->w + 4, dest.y + tmp_surf->h + 4, 0);
17516         }
17517       else
17518         {
17519           dest.x = dest.x + r_ttools.w;
17520           SDL_BlitSurface(tmp_surf, &src, screen, &dest);
17521         }
17522     }
17523 
17524 
17525   /* FIXME: Only update what's changed! */
17526   SDL_Flip(screen);
17527 
17528   //update_screen_rect(&dest);
17529   free(str);
17530 
17531   if (tmp_surf != NULL)
17532     SDL_FreeSurface(tmp_surf);
17533 /*   if (tmp_label != NULL) */
17534   /*    SDL_FreeSurface(tmp_label); */
17535   // SDL_Delay(5000);
17536 
17537 }
17538 
17539 
17540 /**
17541  * FIXME
17542  */
17543 /* Return string as uppercase if that option is set: */
uppercase(const char * restrict const str)17544 static char *uppercase(const char *restrict const str)
17545 {
17546   unsigned int i, n;
17547   wchar_t *dest;
17548   char *ustr;
17549 
17550   if (!only_uppercase)
17551     return strdup(str);
17552 
17553   /* watch out: uppercase chars may need extra bytes!
17554      http://en.wikipedia.org/wiki/Turkish_dotted_and_dotless_I */
17555   n = strlen(str) + 1;
17556   dest = alloca(sizeof(wchar_t) * n);
17557   ustr = malloc(n * 2);         /* use *2 in case 'i' becomes U+0131 */
17558 
17559   mbstowcs(dest, str, sizeof(wchar_t) * n);     /* at most n wchar_t written */
17560   i = 0;
17561   do
17562     {
17563       dest[i] = towupper(dest[i]);
17564     }
17565   while (dest[i++]);
17566   wcstombs(ustr, dest, n);      /* at most n bytes written */
17567 
17568 #ifdef DEBUG
17569   printf(" ORIGINAL: %s\n" "UPPERCASE: %s\n\n", str, ustr);
17570 #endif
17571   return ustr;
17572 }
17573 
17574 /**
17575  * FIXME
17576  */
uppercase_w(const wchar_t * restrict const str)17577 static wchar_t *uppercase_w(const wchar_t * restrict const str)
17578 {
17579   unsigned n = wcslen(str) + 1;
17580   wchar_t *ustr = malloc(sizeof(wchar_t) * n);
17581 
17582   memcpy(ustr, str, sizeof(wchar_t) * n);
17583 
17584   if (only_uppercase)
17585     {
17586       unsigned i = 0;
17587 
17588       do
17589         {
17590           ustr[i] = towupper(ustr[i]);
17591         }
17592       while (ustr[i++]);
17593     }
17594 
17595   return ustr;
17596 }
17597 
17598 
17599 /**
17600  * FIXME
17601  */
17602 /* Return string in right-to-left mode, if necessary: */
textdir(const char * const str)17603 static char *textdir(const char *const str)
17604 {
17605   unsigned char *dstr;
17606   unsigned i, j;
17607   unsigned char c1, c2, c3, c4;
17608 
17609 #ifdef DEBUG
17610   printf("ORIG_DIR: %s\n", str);
17611 #endif
17612 
17613   dstr = malloc(strlen(str) + 5);
17614 
17615   if (need_right_to_left_word)
17616     {
17617       dstr[strlen(str)] = '\0';
17618 
17619       for (i = 0; i < strlen(str); i++)
17620         {
17621           j = (strlen(str) - i - 1);
17622 
17623           c1 = (unsigned char)str[i + 0];
17624           c2 = (unsigned char)str[i + 1];
17625           c3 = (unsigned char)str[i + 2];
17626           c4 = (unsigned char)str[i + 3];
17627 
17628           if (c1 < 128)         /* 0xxx xxxx - 1 byte */
17629             {
17630               dstr[j] = str[i];
17631             }
17632           else if ((c1 & 0xE0) == 0xC0) /* 110x xxxx - 2 bytes */
17633             {
17634               dstr[j - 1] = c1;
17635               dstr[j - 0] = c2;
17636               i = i + 1;
17637             }
17638           else if ((c1 & 0xF0) == 0xE0) /* 1110 xxxx - 3 bytes */
17639             {
17640               dstr[j - 2] = c1;
17641               dstr[j - 1] = c2;
17642               dstr[j - 0] = c3;
17643               i = i + 2;
17644             }
17645           else                  /* 1111 0xxx - 4 bytes */
17646             {
17647               dstr[j - 3] = c1;
17648               dstr[j - 2] = c2;
17649               dstr[j - 1] = c3;
17650               dstr[j - 0] = c4;
17651               i = i + 3;
17652             }
17653         }
17654     }
17655   else
17656     {
17657       strcpy((char *)dstr, str); /* safe; malloc'd to a sufficient size */
17658     }
17659 
17660 #ifdef DEBUG
17661   printf("L2R_DIR: %s\n", dstr);
17662 #endif
17663 
17664   return ((char *)dstr);
17665 }
17666 
17667 
17668 /**
17669  * FIXME
17670  */
17671 /* Scroll Timer */
scrolltimer_callback(Uint32 interval,void * param)17672 static Uint32 scrolltimer_callback(Uint32 interval, void *param)
17673 {
17674   /* printf("scrolltimer_callback(%d) -- ", interval); */
17675   if (scrolling)
17676     {
17677       DEBUG_PRINTF("(Still scrolling)\n");
17678       SDL_PushEvent((SDL_Event *) param);
17679       return interval;
17680     }
17681   else
17682     {
17683       DEBUG_PRINTF("(all done scrolling)\n");
17684       return 0;
17685     }
17686 }
17687 
17688 
17689 /**
17690  * FIXME
17691  */
17692 /* Controls the Text-Timer - interval == 0 removes the timer */
control_drawtext_timer(Uint32 interval,const char * const text,Uint8 locale_text)17693 static void control_drawtext_timer(Uint32 interval, const char *const text, Uint8 locale_text)
17694 {
17695   static int activated = 0;
17696   static SDL_TimerID TimerID = 0;
17697   static SDL_Event drawtext_event;
17698 
17699 
17700   /* Remove old timer if any is running */
17701 
17702   if (activated)
17703     {
17704       SDL_RemoveTimer(TimerID);
17705       activated = 0;
17706       TimerID = 0;
17707     }
17708 
17709   if (interval == 0)
17710     return;
17711 
17712   drawtext_event.type = SDL_USEREVENT;
17713   drawtext_event.user.code = USEREVENT_TEXT_UPDATE;
17714   drawtext_event.user.data1 = (void *)text;
17715   drawtext_event.user.data2 = (void *)(intptr_t) ((int)locale_text);    //EP added (intptr_t) to avoid warning on x64
17716 
17717 
17718   /* Add new timer */
17719 
17720   TimerID = SDL_AddTimer(interval, drawtext_callback, (void *)&drawtext_event);
17721   activated = 1;
17722 }
17723 
17724 
17725 /**
17726  * FIXME
17727  */
17728 /* Drawtext Timer */
drawtext_callback(Uint32 interval,void * param)17729 static Uint32 drawtext_callback(Uint32 interval, void *param)
17730 {
17731   (void)interval;
17732   SDL_PushEvent((SDL_Event *) param);
17733 
17734   return 0;                     /* Remove timer */
17735 }
17736 
17737 
17738 #ifdef DEBUG
17739 /**
17740  * FIXME
17741  */
debug_gettext(const char * str)17742 static char *debug_gettext(const char *str)
17743 {
17744   if (strcmp(str, dgettext(NULL, str)) == 0)
17745     {
17746       printf("NOTRANS: %s\n", str);
17747       printf("..TRANS: %s\n", dgettext(NULL, str));
17748       fflush(stdout);
17749     }
17750 
17751   return (dgettext(NULL, str));
17752 }
17753 #endif
17754 
17755 
17756 /**
17757  * FIXME
17758  */
great_str(void)17759 static const char *great_str(void)
17760 {
17761   return (great_strs[rand() % (sizeof(great_strs) / sizeof(char *))]);
17762 }
17763 
17764 
17765 #ifdef DEBUG
17766 /**
17767  * FIXME
17768  */
charsize(Uint16 c)17769 static int charsize(Uint16 c)
17770 {
17771   Uint16 str[2];
17772   int w, h;
17773 
17774   str[0] = c;
17775   str[1] = '\0';
17776 
17777   TTF_SizeUNICODE(getfonthandle(cur_font)->ttf_font, str, &w, &h);
17778 
17779   return w;
17780 }
17781 #endif
17782 
17783 /**
17784  * FIXME
17785  */
draw_image_title(int t,SDL_Rect dest)17786 static void draw_image_title(int t, SDL_Rect dest)
17787 {
17788   SDL_BlitSurface(img_title_on, NULL, screen, &dest);
17789 
17790   dest.x += (dest.w - img_title_names[t]->w) / 2;
17791   dest.y += (dest.h - img_title_names[t]->h) / 2;
17792   SDL_BlitSurface(img_title_names[t], NULL, screen, &dest);
17793 }
17794 
17795 
17796 /**
17797  * FIXME
17798  */
17799 /* Handle keyboard events to control the mouse: */
17800 /* Move as many pixels as bigsteps outside the areas,
17801    in the areas and 5 pixels around, move 1 pixel at a time */
handle_keymouse(SDLKey key,Uint8 updown,int steps,SDL_Rect * area1,SDL_Rect * area2)17802 static void handle_keymouse(SDLKey key, Uint8 updown, int steps, SDL_Rect * area1, SDL_Rect * area2)
17803 {
17804   int left, right, up, bottom;
17805   SDL_Event event;
17806   SDL_Rect r1, r2;
17807 
17808   if (keymouse)
17809     {
17810       /* make the compiler happy */
17811       r1.x = r1.y = r1.w = r1.h = 0;
17812       r2.x = r2.y = r2.w = r2.h = 0;
17813 
17814       if (area1)
17815         {
17816           r1.x = max(0, area1->x - 5);
17817           r1.y = max(0, area1->y - 5);
17818           r1.w = area1->x - r1.x + area1->w + 5;
17819           r1.h = area1->y - r1.y + area1->h + 5;
17820         }
17821 
17822       if (area2)
17823         {
17824           r2.x = max(0, area2->x - 5);
17825           r2.y = max(0, area2->y - 5);
17826           r2.w = area2->x - r2.x + area2->w + 5;
17827           r2.h = area2->y - r2.y + area2->h + 5;
17828         }
17829 
17830       /* The defaults */
17831       left = max(0, oldpos_x - steps);
17832       right = min(screen->w, oldpos_x + steps);
17833       up = max(0, oldpos_y - steps);
17834       bottom = min(screen->h, oldpos_y + steps);
17835 
17836       /* If Shift is pressed, go with the defaults */
17837       if (!(SDL_GetModState() & KMOD_SHIFT))
17838         {
17839           /* 1 pixel if in one of the areas */
17840           if ((area1 && oldpos_x > r1.x && oldpos_x - r1.x < r1.w && oldpos_y > r1.y && oldpos_y - r1.y < r1.h) ||
17841               (area2 && oldpos_x > r2.x && oldpos_x - r2.x < r2.w && oldpos_y > r2.y && oldpos_y - r2.y < r2.h))
17842             {
17843               left = max(0, oldpos_x - 1);
17844               right = min(screen->w, oldpos_x + 1);
17845               up = max(0, oldpos_y - 1);
17846               bottom = min(screen->h, oldpos_y + 1);
17847             }
17848 
17849           /* Not enter too deep in the areas at once */
17850           else if (area1 || area2)
17851             {
17852               if (area1)
17853                 if (oldpos_y - r1.y < r1.h && oldpos_y > r1.y)
17854                   {
17855                     if (oldpos_x - r1.x < steps)
17856                       right = min(oldpos_x + steps, r1.x + 1);
17857                     else if (oldpos_x - r1.x - r1.w < steps)
17858                       left = max(r1.x + r1.w - 1, oldpos_x - steps);
17859                   }
17860 
17861               if (oldpos_x - r1.x < r1.w && oldpos_x > r1.x)
17862                 {
17863                   if (oldpos_y - r1.y < steps)
17864                     bottom = min(r1.y + 1, oldpos_y + steps);
17865                   else if (oldpos_y - r1.y - r1.h < steps)
17866                     up = max(r1.y + r1.h - 1, oldpos_y - steps);
17867                 }
17868 
17869               if (area2)
17870                 if (oldpos_y - r2.y < r2.h && oldpos_y > r2.y)
17871                   {
17872                     if (oldpos_x - r2.x < steps)
17873                       right = min(oldpos_x + steps, r2.x + 1);
17874                     else if (oldpos_x - r2.x - r2.w < steps)
17875                       left = max(r2.x + r2.w - 1, oldpos_x - steps);
17876                   }
17877 
17878               if (oldpos_x - r2.x < r2.w && oldpos_x > r2.x)
17879                 {
17880                   if (oldpos_y - r2.y < steps)
17881                     bottom = min(r2.y + 1, oldpos_y + steps);
17882                   else if (oldpos_y - r2.y - r2.h < steps)
17883                     up = max(r2.y + r2.h - 1, oldpos_y - steps);
17884                 }
17885             }
17886         }
17887 
17888       if (updown == SDL_KEYUP)
17889         {
17890           if (key == SDLK_INSERT || key == SDLK_F5 ||
17891               ((cur_tool != TOOL_TEXT && cur_tool != TOOL_LABEL) &&
17892                (key == SDLK_SPACE || key == SDLK_5 || key == SDLK_KP5)))
17893 
17894             {
17895               event.type = SDL_MOUSEBUTTONUP;
17896               event.button.x = oldpos_x;
17897               event.button.y = oldpos_y;
17898               event.button.button = 1;
17899               SDL_PushEvent(&event);
17900             }
17901         }
17902 
17903       else
17904         {
17905           if (key == SDLK_LEFT)
17906             SDL_WarpMouse(left, oldpos_y);
17907 
17908           else if (key == SDLK_RIGHT)
17909             SDL_WarpMouse(right, oldpos_y);
17910 
17911           else if (key == SDLK_UP)
17912             SDL_WarpMouse(oldpos_x, up);
17913 
17914           else if (key == SDLK_DOWN)
17915             SDL_WarpMouse(oldpos_x, bottom);
17916 
17917           else if ((key == SDLK_INSERT || key == SDLK_F5) && !button_down)
17918             {
17919               event.type = SDL_MOUSEBUTTONDOWN;
17920               event.button.x = oldpos_x;
17921               event.button.y = oldpos_y;
17922               event.button.button = 1;
17923               SDL_PushEvent(&event);
17924             }
17925 
17926           else if (cur_tool != TOOL_TEXT && cur_tool != TOOL_LABEL)
17927             {
17928               if (!button_down && (key == SDLK_SPACE || key == SDLK_5 || key == SDLK_KP5))
17929                 {
17930                   event.type = SDL_MOUSEBUTTONDOWN;
17931                   event.button.x = oldpos_x;
17932                   event.button.y = oldpos_y;
17933                   event.button.button = 1;
17934                   SDL_PushEvent(&event);
17935                 }
17936 
17937               else if (key == SDLK_1 || key == SDLK_KP1)
17938                 SDL_WarpMouse(left, bottom);
17939 
17940               else if (key == SDLK_3 || key == SDLK_KP3)
17941                 SDL_WarpMouse(right, bottom);
17942 
17943               else if (key == SDLK_7 || key == SDLK_KP7)
17944                 SDL_WarpMouse(left, up);
17945 
17946               else if (key == SDLK_9 || key == SDLK_KP9)
17947                 SDL_WarpMouse(right, up);
17948 
17949               else if (key == SDLK_2 || key == SDLK_KP2)
17950                 SDL_WarpMouse(oldpos_x, bottom);
17951 
17952               else if (key == SDLK_8 || key == SDLK_KP8)
17953                 SDL_WarpMouse(oldpos_x, up);
17954 
17955               else if (key == SDLK_6 || key == SDLK_KP6)
17956                 SDL_WarpMouse(right, oldpos_y);
17957 
17958               else if (key == SDLK_4 || key == SDLK_KP4)
17959                 SDL_WarpMouse(left, oldpos_y);
17960 
17961               /* FIXME: This is qwerty centric and interferes with gettexted reponses for yes/no,
17962                  so disabling until either is removed or is configurable. */
17963 #if 0
17964               else if (key == SDLK_s)
17965                 SDL_WarpMouse(oldpos_x, bottom);
17966 
17967               else if (key == SDLK_w)
17968                 SDL_WarpMouse(oldpos_x, up);
17969 
17970               else if (key == SDLK_d)
17971                 SDL_WarpMouse(right, oldpos_y);
17972 
17973               else if (key == SDLK_a)
17974                 SDL_WarpMouse(left, oldpos_y);
17975 #endif
17976 
17977             }
17978         }
17979     }
17980 }
17981 
17982 /**
17983  * FIXME
17984  */
17985 /* A subset of keys that will move one button at a time and jump between r_canvas<->r_tools<->r_colors */
handle_keymouse_buttons(SDLKey key,int * whicht,int * whichc,SDL_Rect real_r_tools)17986 static void handle_keymouse_buttons(SDLKey key, int *whicht, int *whichc, SDL_Rect real_r_tools)
17987 {
17988   if (hit_test(&real_r_tools, oldpos_x, oldpos_y) &&
17989       (key == SDLK_F7 || key == SDLK_F8 || key == SDLK_F11 || key == SDLK_F12))
17990     {
17991       *whicht = tool_scroll + grid_hit_gd(&real_r_tools, oldpos_x, oldpos_y, &gd_tools);
17992 
17993       if (key == SDLK_F7 && hit_test(&real_r_tools, oldpos_x, oldpos_y))
17994         {
17995           *whicht += 2;
17996           *whicht = *whicht % NUM_TOOLS;
17997           while (!tool_avail[*whicht])
17998             {
17999               *whicht += 2;
18000               *whicht = *whicht % NUM_TOOLS;
18001             }
18002         }
18003 
18004       else if (key == SDLK_F8 && hit_test(&real_r_tools, oldpos_x, oldpos_y))
18005         {
18006           *whicht -= 2;
18007           if (*whicht < 0)
18008             *whicht += NUM_TOOLS;
18009           while (!tool_avail[*whicht])
18010             {
18011               *whicht -= 2;
18012               if (*whicht < 0)
18013                 *whicht += NUM_TOOLS;
18014             }
18015         }
18016 
18017       else if (key == SDLK_F12 && hit_test(&real_r_tools, oldpos_x, oldpos_y))
18018         {
18019           *whicht = *whicht + 1;
18020           *whicht = *whicht % NUM_TOOLS;
18021           while (!tool_avail[*whicht])
18022             {
18023               *whicht += 1;
18024               *whicht = *whicht % NUM_TOOLS;
18025             }
18026         }
18027 
18028       else if (key == SDLK_F11 && hit_test(&real_r_tools, oldpos_x, oldpos_y))
18029         {
18030           *whicht = tool_scroll + grid_hit_gd(&real_r_tools, oldpos_x, oldpos_y, &gd_tools);
18031           *whicht = *whicht - 1;
18032           if (*whicht < 0)
18033             *whicht += NUM_TOOLS;
18034           while (!tool_avail[*whicht])
18035             {
18036               *whicht -= 1;
18037               if (*whicht < 0)
18038                 *whicht += NUM_TOOLS;
18039             }
18040         }
18041 
18042       while (*whicht >= tool_scroll + 2 * real_r_tools.h / button_h)
18043         {
18044           tool_scroll += 2;
18045           draw_toolbar();
18046           update_screen_rect(&r_tools);
18047         }
18048       while (*whicht < tool_scroll)
18049         {
18050           tool_scroll -= 2;
18051           draw_toolbar();
18052           update_screen_rect(&r_tools);
18053         }
18054 
18055       SDL_WarpMouse(button_w / 2 + *whicht % 2 * button_w,
18056                     real_r_tools.y - *whicht % 2 * button_w / 2 + *whicht * button_h / 2 + 10 -
18057                     tool_scroll * button_h / 2);
18058     }
18059 
18060   else if (key == SDLK_F11 && hit_test(&r_colors, oldpos_x, oldpos_y))
18061     {
18062       *whichc = grid_hit_gd(&r_colors, oldpos_x, oldpos_y, &gd_colors);
18063       *whichc = *whichc - 1;
18064       if (*whichc < 0)
18065         *whichc += NUM_COLORS;
18066       SDL_WarpMouse(button_w * 2 + *whichc * color_button_w + 12, r_canvas.h + (r_colors.h / 2));
18067     }
18068 
18069   else if (key == SDLK_F12 && hit_test(&r_colors, oldpos_x, oldpos_y))
18070     {
18071       *whichc = grid_hit_gd(&r_colors, oldpos_x, oldpos_y, &gd_colors);
18072       *whichc = *whichc + 1;
18073       *whichc = *whichc % NUM_COLORS;
18074       SDL_WarpMouse(button_w * 2 + *whichc * color_button_w + 12, r_canvas.h + (r_colors.h / 2));
18075     }
18076 
18077   else if (key == SDLK_F4)
18078     {
18079       if (hit_test(&r_tools, oldpos_x, oldpos_y))
18080         SDL_WarpMouse(button_w * 2 + *whichc * color_button_w + 12, r_canvas.h + (r_colors.h / 2));
18081       else if (hit_test(&r_colors, oldpos_x, oldpos_y))
18082         SDL_WarpMouse(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
18083       else
18084         SDL_WarpMouse(button_w / 2 + *whicht % 2 * button_w,
18085                       real_r_tools.y - *whicht % 2 * button_w / 2 + *whicht * button_h / 2 + 10 -
18086                       tool_scroll * button_h / 2);
18087 
18088       /* Play a sound here as there is a big jump */
18089       playsound(screen, 1, SND_CLICK, 0, SNDPOS_LEFT, SNDDIST_NEAR);
18090     }
18091 }
18092 
18093 /**
18094  * FIXME
18095  */
18096 /* Unblank screen in fullscreen mode, if needed: */
handle_active(SDL_Event * event)18097 static void handle_active(SDL_Event * event)
18098 {
18099   if (event->active.state & SDL_APPACTIVE)
18100     {
18101       if (event->active.gain == 1)
18102         {
18103           if (fullscreen)
18104             SDL_Flip(screen);
18105         }
18106     }
18107   if (event->active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE))
18108     {
18109       if (event->active.gain == 1)
18110         {
18111           if (mouseaccessibility)
18112             {
18113               magic_switchin(canvas);
18114             }
18115         }
18116       else if (mouseaccessibility && emulate_button_pressed)
18117         {
18118           magic_switchout(canvas);
18119         }
18120 
18121 #ifdef _WIN32
18122       SetActivationState(event->active.gain);
18123 #endif
18124     }
18125 }
18126 
18127 
18128 /**
18129  * FIXME
18130  */
18131 /* For right-to-left languages, when word-wrapping, we need to
18132    make sure the text doesn't end up going from bottom-to-top, too! */
18133 #ifdef NO_SDLPANGO
anti_carriage_return(int left,int right,int cur_top,int new_top,int cur_bot,int line_width)18134 static void anti_carriage_return(int left, int right, int cur_top, int new_top, int cur_bot, int line_width)
18135 {
18136   SDL_Rect src, dest;
18137 
18138 
18139   /* Move current set of text down one line (and right-justify it!): */
18140 
18141   src.x = left;
18142   src.y = cur_top;
18143   src.w = line_width;
18144   src.h = cur_bot - cur_top;
18145 
18146   dest.x = right - line_width;
18147   dest.y = new_top;
18148 
18149   SDL_BlitSurface(screen, &src, screen, &dest);
18150 
18151 
18152   /* Clear the top line for new text: */
18153 
18154   dest.x = left;
18155   dest.y = cur_top;
18156   dest.w = right - left;
18157   dest.h = new_top - cur_top;
18158 
18159   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
18160 }
18161 #endif
18162 
18163 
18164 /**
18165  * FIXME
18166  */
duplicate_surface(SDL_Surface * orig)18167 static SDL_Surface *duplicate_surface(SDL_Surface * orig)
18168 {
18169   /*
18170      Uint32 amask;
18171 
18172      amask = ~(orig->format->Rmask |
18173      orig->format->Gmask |
18174      orig->format->Bmask);
18175 
18176      return(SDL_CreateRGBSurface(SDL_SWSURFACE,
18177      orig->w, orig->h,
18178      orig->format->BitsPerPixel,
18179      orig->format->Rmask,
18180      orig->format->Gmask,
18181      orig->format->Bmask,
18182      amask));
18183    */
18184 
18185   return (SDL_DisplayFormatAlpha(orig));
18186 }
18187 
18188 /**
18189  * FIXME
18190  */
mirror_starter(void)18191 static void mirror_starter(void)
18192 {
18193   SDL_Surface *orig;
18194   int x;
18195   SDL_Rect src, dest;
18196 
18197 
18198   /* Mirror overlay: */
18199 
18200   orig = img_starter;
18201   img_starter = duplicate_surface(orig);
18202 
18203   if (img_starter != NULL)
18204     {
18205       for (x = 0; x < orig->w; x++)
18206         {
18207           src.x = x;
18208           src.y = 0;
18209           src.w = 1;
18210           src.h = orig->h;
18211 
18212           dest.x = orig->w - x - 1;
18213           dest.y = 0;
18214 
18215           SDL_BlitSurface(orig, &src, img_starter, &dest);
18216         }
18217 
18218       SDL_FreeSurface(orig);
18219     }
18220   else
18221     {
18222       img_starter = orig;
18223     }
18224 
18225 
18226   /* Mirror background: */
18227 
18228   if (img_starter_bkgd != NULL)
18229     {
18230       orig = img_starter_bkgd;
18231       img_starter_bkgd = duplicate_surface(orig);
18232 
18233       if (img_starter_bkgd != NULL)
18234         {
18235           for (x = 0; x < orig->w; x++)
18236             {
18237               src.x = x;
18238               src.y = 0;
18239               src.w = 1;
18240               src.h = orig->h;
18241 
18242               dest.x = orig->w - x - 1;
18243               dest.y = 0;
18244 
18245               SDL_BlitSurface(orig, &src, img_starter_bkgd, &dest);
18246             }
18247 
18248           SDL_FreeSurface(orig);
18249         }
18250       else
18251         {
18252           img_starter_bkgd = orig;
18253         }
18254     }
18255 }
18256 
18257 
18258 /**
18259  * FIXME
18260  */
flip_starter(void)18261 static void flip_starter(void)
18262 {
18263   SDL_Surface *orig;
18264   int y;
18265   SDL_Rect src, dest;
18266 
18267 
18268   /* Flip overlay: */
18269 
18270   orig = img_starter;
18271   img_starter = duplicate_surface(orig);
18272 
18273   if (img_starter != NULL)
18274     {
18275       for (y = 0; y < orig->h; y++)
18276         {
18277           src.x = 0;
18278           src.y = y;
18279           src.w = orig->w;
18280           src.h = 1;
18281 
18282           dest.x = 0;
18283           dest.y = orig->h - y - 1;
18284 
18285           SDL_BlitSurface(orig, &src, img_starter, &dest);
18286         }
18287 
18288       SDL_FreeSurface(orig);
18289     }
18290   else
18291     {
18292       img_starter = orig;
18293     }
18294 
18295 
18296   /* Flip background: */
18297 
18298   if (img_starter_bkgd != NULL)
18299     {
18300       orig = img_starter_bkgd;
18301       img_starter_bkgd = duplicate_surface(orig);
18302 
18303       if (img_starter_bkgd != NULL)
18304         {
18305           for (y = 0; y < orig->h; y++)
18306             {
18307               src.x = 0;
18308               src.y = y;
18309               src.w = orig->w;
18310               src.h = 1;
18311 
18312               dest.x = 0;
18313               dest.y = orig->h - y - 1;
18314 
18315               SDL_BlitSurface(orig, &src, img_starter_bkgd, &dest);
18316             }
18317 
18318           SDL_FreeSurface(orig);
18319         }
18320       else
18321         {
18322           img_starter_bkgd = orig;
18323         }
18324     }
18325 }
18326 
18327 
18328 /**
18329  * FIXME
18330  */
valid_click(Uint8 button)18331 static int valid_click(Uint8 button)
18332 {
18333   if (button == 1 || ((button == 2 || button == 3) && no_button_distinction))
18334     return (1);
18335   else
18336     return (0);
18337 }
18338 
18339 
18340 /**
18341  * FIXME
18342  */
in_circle_rad(int x,int y,int rad)18343 static int in_circle_rad(int x, int y, int rad)
18344 {
18345   if ((x * x) + (y * y) - (rad * rad) < 0)
18346     return (1);
18347   else
18348     return (0);
18349 }
18350 
18351 
18352 /**
18353  * FIXME
18354  */
paintsound(int size)18355 static int paintsound(int size)
18356 {
18357   if (SND_PAINT1 + (size / 12) >= SND_PAINT4)
18358     return (SND_PAINT4);
18359   else
18360     return (SND_PAINT1 + (size / 12));
18361 }
18362 
18363 
18364 #ifndef NOSVG
18365 
18366 #ifdef OLD_SVG
18367 
18368 /**
18369  * FIXME
18370  */
18371 /* Old libcairo1, svg and svg-cairo based code
18372    Based on cairo-demo/sdl/main.c from Cairo (GPL'd, (c) 2004 Eric Windisch):
18373 */
load_svg(const char * file)18374 static SDL_Surface *load_svg(const char *file)
18375 {
18376   svg_cairo_t *scr;
18377   int bpp, btpp, stride;
18378   unsigned int width, height;
18379   unsigned int rwidth, rheight;
18380   float scale;
18381   unsigned char *image;
18382   cairo_surface_t *cairo_surface;
18383   cairo_t *cr;
18384   SDL_Surface *sdl_surface, *sdl_surface_tmp;
18385   Uint32 rmask, gmask, bmask, amask;
18386   svg_cairo_status_t res;
18387 
18388 
18389 #ifdef DEBUG
18390   printf("Attempting to load \"%s\" as an SVG\n", file);
18391 #endif
18392 
18393   /* Create the SVG cairo stuff: */
18394   if (svg_cairo_create(&scr) != SVG_CAIRO_STATUS_SUCCESS)
18395     {
18396 #ifdef DEBUG
18397       printf("svg_cairo_create() failed\n");
18398 #endif
18399       return (NULL);
18400     }
18401 
18402   /* Parse the SVG file: */
18403   if (svg_cairo_parse(scr, file) != SVG_CAIRO_STATUS_SUCCESS)
18404     {
18405       svg_cairo_destroy(scr);
18406 #ifdef DEBUG
18407       printf("svg_cairo_parse(%s) failed\n", file);
18408 #endif
18409       return (NULL);
18410     }
18411 
18412   /* Get the natural size of the SVG */
18413   svg_cairo_get_size(scr, &rwidth, &rheight);
18414 #ifdef DEBUG
18415   printf("svg_get_size(): %d x %d\n", rwidth, rheight);
18416 #endif
18417 
18418   if (rwidth == 0 || rheight == 0)
18419     {
18420       svg_cairo_destroy(scr);
18421 #ifdef DEBUG
18422       printf("SVG %s had 0 width or height!\n", file);
18423 #endif
18424       return (NULL);
18425     }
18426 
18427   /* We will create a CAIRO_FORMAT_ARGB32 surface. We don't need to match
18428      the screen SDL format, but we are interested in the alpha bit... */
18429   bpp = 32;
18430   btpp = 4;
18431 
18432   /* We want to render at full Tux Paint canvas size, so that the stamp
18433      at its largest scale remains highest quality (no pixelization):
18434      (but not messing up the aspect ratio) */
18435 
18436   scale = pick_best_scape(rwidth, rheight, r_canvas.w, r_canvas.h);
18437 
18438   width = ((float)rwidth * scale);
18439   height = ((float)rheight * scale);
18440 
18441 #ifdef DEBUG
18442   printf("scaling to %d x %d (%f scale)\n", width, height, scale);
18443 #endif
18444 
18445   /* scanline width */
18446   stride = width * btpp;
18447 
18448   /* Allocate space for an image: */
18449   image = calloc(stride * height, 1);
18450 
18451 #ifdef DEBUG
18452   printf("calling cairo_image_surface_create_for_data(..., CAIRO_FORMAT_ARGB32, %d(w), %d(h), %d(stride))\n", width,
18453          height, stride);
18454 #endif
18455 
18456   /* Create the cairo surface with the adjusted width and height */
18457 
18458   cairo_surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, stride);
18459   cr = cairo_create(cairo_surface);
18460   if (cr == NULL)
18461     {
18462       svg_cairo_destroy(scr);
18463 #ifdef DEBUG
18464       printf("cairo_create() failed\n");
18465 #endif
18466       return (NULL);
18467     }
18468 
18469   /* Scale it (proportionally) */
18470   cairo_scale(cr, scale, scale);        /* no return value :( */
18471 
18472   /* Render SVG to our surface: */
18473   res = svg_cairo_render(scr, cr);
18474 
18475   /* Clean up: */
18476   cairo_surface_destroy(cairo_surface);
18477   cairo_destroy(cr);
18478   svg_cairo_destroy(scr);
18479 
18480   if (res != SVG_CAIRO_STATUS_SUCCESS)
18481     {
18482 #ifdef DEBUG
18483       printf("svg_cairo_render() failed\n");
18484 #endif
18485       return (NULL);
18486     }
18487 
18488 
18489   /* Adjust the SDL surface to match the cairo surface created
18490      (surface mask of ARGB)  NOTE: Is this endian-agnostic? -bjk 2006.10.25 */
18491   rmask = 0x00ff0000;
18492   gmask = 0x0000ff00;
18493   bmask = 0x000000ff;
18494   amask = 0xff000000;
18495 
18496   /* Create the SDL surface using the pixel data stored: */
18497   sdl_surface_tmp = SDL_CreateRGBSurfaceFrom((void *)image, width, height, bpp, stride, rmask, gmask, bmask, amask);
18498 
18499   if (sdl_surface_tmp == NULL)
18500     {
18501 #ifdef DEBUG
18502       printf("SDL_CreateRGBSurfaceFrom() failed\n");
18503 #endif
18504       return (NULL);
18505     }
18506 
18507 
18508   /* Convert the SDL surface to the display format, for faster blitting: */
18509   sdl_surface = SDL_DisplayFormatAlpha(sdl_surface_tmp);
18510   SDL_FreeSurface(sdl_surface_tmp);
18511 
18512   if (sdl_surface == NULL)
18513     {
18514 #ifdef DEBUG
18515       printf("SDL_DisplayFormatAlpha() failed\n");
18516 #endif
18517       return (NULL);
18518     }
18519 
18520 #ifdef DEBUG
18521   printf("SDL surface from %d x %d SVG is %d x %d\n", rwidth, rheight, sdl_surface->w, sdl_surface->h);
18522 #endif
18523 
18524   return (sdl_surface);
18525 }
18526 
18527 #else
18528 
18529 /**
18530  * FIXME
18531  */
18532 /* New libcairo2, rsvg and rsvg-cairo based code */
load_svg(const char * file)18533 static SDL_Surface *load_svg(const char *file)
18534 {
18535   cairo_surface_t *cairo_surf;
18536   cairo_t *cr;
18537   RsvgHandle *rsvg_handle;
18538   GError *gerr;
18539   unsigned char *image;
18540   int rwidth, rheight;
18541   int width, height, stride;
18542   float scale;
18543   int bpp = 32, btpp = 4;
18544   RsvgDimensionData dimensions;
18545   SDL_Surface *sdl_surface, *sdl_surface_tmp;
18546   Uint32 rmask, gmask, bmask, amask;
18547 
18548 #ifdef DEBUG
18549   printf("load_svg(%s)\n", file);
18550 #endif
18551 
18552   /* Create an RSVG Handle from the SVG file: */
18553 
18554   gerr = NULL;
18555 
18556   rsvg_handle = rsvg_handle_new_from_file(file, &gerr);
18557   if (rsvg_handle == NULL)
18558     {
18559 #ifdef DEBUG
18560       fprintf(stderr, "rsvg_handle_new_from_file() failed\n");
18561 #endif
18562       return (NULL);
18563     }
18564 
18565   rsvg_handle_get_dimensions(rsvg_handle, &dimensions);
18566   rwidth = dimensions.width;
18567   rheight = dimensions.height;
18568 
18569 #ifdef DEBUG
18570   printf("SVG is %d x %d\n", rwidth, rheight);
18571 #endif
18572 
18573 
18574   /* Pick best scale to render to (for the canvas in this instance of Tux Paint) */
18575 
18576   scale = pick_best_scape(rwidth, rheight, r_canvas.w, r_canvas.h);
18577 
18578 #ifdef DEBUG
18579   printf("best scale is %.4f\n", scale);
18580 #endif
18581 
18582   width = ((float)rwidth * scale);
18583   height = ((float)rheight * scale);
18584 
18585 #ifdef DEBUG
18586   printf("scaling to %d x %d (%f scale)\n", width, height, scale);
18587 #endif
18588 
18589   /* scanline width */
18590   stride = width * btpp;
18591 
18592   /* Allocate space for an image: */
18593   image = calloc(stride * height, 1);
18594   if (image == NULL)
18595     {
18596 #ifdef DEBUG
18597       fprintf(stderr, "Unable to allocate image buffer\n");
18598 #endif
18599       g_object_unref(rsvg_handle);
18600       return (NULL);
18601     }
18602 
18603 
18604   /* Create a surface for Cairo to draw into: */
18605 
18606   cairo_surf = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, stride);
18607 
18608   if (cairo_surface_status(cairo_surf) != CAIRO_STATUS_SUCCESS)
18609     {
18610 #ifdef DEBUG
18611       fprintf(stderr, "cairo_image_surface_create() failed\n");
18612 #endif
18613       g_object_unref(rsvg_handle);
18614       free(image);
18615       return (NULL);
18616     }
18617 
18618 
18619   /* Create a new Cairo object: */
18620 
18621   cr = cairo_create(cairo_surf);
18622   if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
18623     {
18624 #ifdef DEBUG
18625       fprintf(stderr, "cairo_create() failed\n");
18626 #endif
18627       g_object_unref(rsvg_handle);
18628       cairo_surface_destroy(cairo_surf);
18629       free(image);
18630       return (NULL);
18631     }
18632 
18633 
18634   /* Ask RSVG to render the SVG into the Cairo object: */
18635 
18636   cairo_scale(cr, scale, scale);
18637 
18638   /* FIXME: We can use cairo_rotate() here to rotate stamps! -bjk 2007.06.21 */
18639 
18640   rsvg_handle_render_cairo(rsvg_handle, cr);
18641 
18642 
18643   cairo_surface_finish(cairo_surf);
18644 
18645 
18646   /* Adjust the SDL surface to match the cairo surface created
18647      (surface mask of ARGB)  NOTE: Is this endian-agnostic? -bjk 2006.10.25 */
18648   rmask = 0x00ff0000;
18649   gmask = 0x0000ff00;
18650   bmask = 0x000000ff;
18651   amask = 0xff000000;
18652 
18653   /* Create the SDL surface using the pixel data stored: */
18654   sdl_surface_tmp = SDL_CreateRGBSurfaceFrom((void *)image, width, height, bpp, stride, rmask, gmask, bmask, amask);
18655 
18656   if (sdl_surface_tmp == NULL)
18657     {
18658 #ifdef DEBUG
18659       fprintf(stderr, "SDL_CreateRGBSurfaceFrom() failed\n");
18660 #endif
18661       g_object_unref(rsvg_handle);
18662       cairo_surface_destroy(cairo_surf);
18663       free(image);
18664       cairo_destroy(cr);
18665       return (NULL);
18666     }
18667 
18668   /* Convert the SDL surface to the display format, for faster blitting: */
18669   sdl_surface = SDL_DisplayFormatAlpha(sdl_surface_tmp);
18670   SDL_FreeSurface(sdl_surface_tmp);
18671 
18672   if (sdl_surface == NULL)
18673     {
18674 #ifdef DEBUG
18675       fprintf(stderr, "SDL_DisplayFormatAlpha() failed\n");
18676 #endif
18677       g_object_unref(rsvg_handle);
18678       cairo_surface_destroy(cairo_surf);
18679       free(image);
18680       cairo_destroy(cr);
18681       return (NULL);
18682     }
18683 
18684 
18685 #ifdef DEBUG
18686   printf("SDL surface from %d x %d SVG is %d x %d\n", rwidth, rheight, sdl_surface->w, sdl_surface->h);
18687 #endif
18688 
18689 
18690   /* Clean up: */
18691 
18692   g_object_unref(rsvg_handle);
18693   cairo_surface_destroy(cairo_surf);
18694   free(image);
18695   cairo_destroy(cr);
18696 
18697   return (sdl_surface);
18698 }
18699 
18700 #endif
18701 
18702 
18703 /**
18704  * FIXME
18705  */
pick_best_scape(unsigned int orig_w,unsigned int orig_h,unsigned int max_w,unsigned int max_h)18706 static float pick_best_scape(unsigned int orig_w, unsigned int orig_h, unsigned int max_w, unsigned int max_h)
18707 {
18708   float aspect, scale, wscale, hscale;
18709 
18710   aspect = (float)orig_w / (float)orig_h;
18711 
18712 #ifdef DEBUG
18713   printf("trying to fit %d x %d (aspect: %.4f) into %d x %d\n", orig_w, orig_h, aspect, max_w, max_h);
18714 #endif
18715 
18716   wscale = (float)max_w / (float)orig_w;
18717   hscale = (float)max_h / (float)orig_h;
18718 
18719 #ifdef DEBUG
18720   printf("max_w / orig_w = wscale: %.4f\n", wscale);
18721   printf("max_h / orig_h = hscale: %.4f\n", hscale);
18722   printf("\n");
18723 #endif
18724 
18725   if (aspect >= 1)
18726     {
18727       /* Image is wider-than-tall (or square) */
18728 
18729       scale = wscale;
18730 
18731 #ifdef DEBUG
18732       printf("Wider-than-tall.  Using wscale.\n");
18733       printf("new size would be: %d x %d\n", (int)((float)orig_w * scale), (int)((float)orig_h * scale));
18734 #endif
18735 
18736       if ((float)orig_h * scale > (float)max_h)
18737         {
18738           scale = hscale;
18739 
18740 #ifdef DEBUG
18741           printf("Too tall!  Using hscale!\n");
18742           printf("new size would be: %d x %d\n", (int)((float)orig_w * scale), (int)((float)orig_h * scale));
18743 #endif
18744         }
18745     }
18746   else
18747     {
18748       /* Taller-than-wide */
18749 
18750       scale = hscale;
18751 
18752 #ifdef DEBUG
18753       printf("Taller-than-wide.  Using hscale.\n");
18754       printf("new size would be: %d x %d\n", (int)((float)orig_w * scale), (int)((float)orig_h * scale));
18755 #endif
18756 
18757       if ((float)orig_w * scale > (float)max_w)
18758         {
18759           scale = wscale;
18760 
18761 #ifdef DEBUG
18762           printf("Too wide!  Using wscale!\n");
18763           printf("new size would be: %d x %d\n", (int)((float)orig_w * scale), (int)((float)orig_h * scale));
18764 #endif
18765         }
18766     }
18767 
18768 
18769 #ifdef DEBUG
18770   printf("\n");
18771   printf("Final scale: %.4f\n", scale);
18772 #endif
18773 
18774   return (scale);
18775 }
18776 
18777 #endif
18778 
18779 /**
18780  * FIXME
18781  */
18782 /* FIXME: we can remove this after SDL folks fix their bug at http://bugzilla.libsdl.org/show_bug.cgi?id=1485 */
18783 /* Try to load an image with IMG_Load(), if it fails, then try with RWops() */
myIMG_Load_RWops(const char * file)18784 static SDL_Surface *myIMG_Load_RWops(const char *file)
18785 {
18786   SDL_Surface *surf;
18787   FILE *fi;
18788   SDL_RWops *data;
18789 
18790   surf = IMG_Load(file);
18791   if (surf != NULL)
18792     return (surf);
18793 
18794   /* From load_kpx() */
18795   fi = fopen(file, "rb");
18796   if (fi == NULL)
18797     return NULL;
18798 
18799   data = SDL_RWFromFP(fi, 1);   /* 1 = Close when we're done */
18800 
18801   if (data == NULL)
18802     return (NULL);
18803 
18804   surf = IMG_Load_RW(data, 1);  /* 1 = Free when we're done */
18805   if (surf == NULL)
18806     return (NULL);
18807 
18808   return (surf);
18809 }
18810 
18811 /**
18812  * FIXME
18813  */
18814 /* Load an image; call load_svg() (above, to call Cairo and SVG-Cairo funcs)
18815    if we notice it's an SVG file (if available!);
18816    call load_kpx() if we notice it's a KPX file (JPEG with wrapper);
18817    otherwise call SDL_Image lib's IMG_Load() (for PNGs, JPEGs, BMPs, etc.) */
myIMG_Load(const char * file)18818 static SDL_Surface *myIMG_Load(const char *file)
18819 {
18820   if (strlen(file) > 4 && strcasecmp(file + strlen(file) - 4, ".kpx") == 0)
18821     {
18822       return (load_kpx(file));
18823 #ifndef NOSVG
18824     }
18825   else if (strlen(file) > 4 && strcasecmp(file + strlen(file) - 4, ".svg") == 0)
18826     {
18827       return (load_svg(file));
18828 #endif
18829     }
18830   else
18831     {
18832       return (myIMG_Load_RWops(file));
18833     }
18834 }
18835 
18836 /**
18837  * FIXME
18838  */
load_kpx(const char * file)18839 static SDL_Surface *load_kpx(const char *file)
18840 {
18841   SDL_RWops *data;
18842   FILE *fi;
18843   SDL_Surface *surf;
18844   int i;
18845 
18846   fi = fopen(file, "rb");
18847   if (fi == NULL)
18848     return NULL;
18849 
18850   /* Skip header */
18851   for (i = 0; i < 60; i++)
18852     fgetc(fi);
18853 
18854   data = SDL_RWFromFP(fi, 1);   /* 1 = Close when we're done */
18855 
18856   if (data == NULL)
18857     return (NULL);
18858 
18859   surf = IMG_Load_RW(data, 1);  /* 1 = Free when we're done */
18860   if (surf == NULL)
18861     return (NULL);
18862 
18863   return (surf);
18864 }
18865 
18866 
18867 /**
18868  * FIXME
18869  */
load_magic_plugins(void)18870 static void load_magic_plugins(void)
18871 {
18872   int res, n, i, plc;
18873   char *place;
18874   int err;
18875   DIR *d;
18876   struct dirent *f;
18877   char fname[512];
18878   char objname[512];
18879   char funcname[512];
18880 
18881   num_plugin_files = 0;
18882   num_magics = 0;
18883 
18884   for (plc = 0; plc < NUM_MAGIC_PLACES; plc++)
18885     {
18886       if (plc == MAGIC_PLACE_GLOBAL)
18887         place = strdup(MAGIC_PREFIX);
18888       else if (plc == MAGIC_PLACE_LOCAL)
18889         place = get_fname("plugins/", DIR_DATA);
18890 #ifdef __APPLE__
18891       else if (plc == MAGIC_PLACE_ALLUSERS)
18892         place = strdup("/Library/Application Support/TuxPaint/plugins/");
18893 #endif
18894       else
18895         continue;               /* Huh? */
18896 
18897 #ifdef DEBUG
18898       printf("\n");
18899       printf("Loading magic plug-ins from %s\n", place);
18900       fflush(stdout);
18901 #endif
18902 
18903       /* Gather list of files (for sorting): */
18904 
18905       d = opendir(place);
18906 
18907       if (d != NULL)
18908         {
18909           /* Set magic API hooks: */
18910 
18911           magic_api_struct = (magic_api *) malloc(sizeof(magic_api));
18912           magic_api_struct->tp_version = strdup(VER_VERSION);
18913 
18914           if (plc == MAGIC_PLACE_GLOBAL)
18915             magic_api_struct->data_directory = strdup(DATA_PREFIX);
18916           else if (plc == MAGIC_PLACE_LOCAL)
18917             magic_api_struct->data_directory = get_fname("plugins/data/", DIR_DATA);
18918 #ifdef __APPLE__
18919           else if (plc == MAGIC_PLACE_ALLUSERS)
18920             magic_api_struct->data_directory = strdup("/Library/Application Support/TuxPaint/plugins/data");
18921 #endif
18922           else
18923             magic_api_struct->data_directory = strdup("./");
18924 
18925           magic_api_struct->update_progress_bar = update_progress_bar;
18926           magic_api_struct->sRGB_to_linear = magic_sRGB_to_linear;
18927           magic_api_struct->linear_to_sRGB = magic_linear_to_sRGB;
18928           magic_api_struct->in_circle = in_circle_rad;
18929           magic_api_struct->getpixel = magic_getpixel;
18930           magic_api_struct->putpixel = magic_putpixel;
18931           magic_api_struct->xorpixel = magic_xorpixel;
18932           magic_api_struct->line = magic_line_func;
18933           magic_api_struct->playsound = magic_playsound;
18934           magic_api_struct->stopsound = magic_stopsound;
18935           magic_api_struct->special_notify = special_notify;
18936           magic_api_struct->button_down = magic_button_down;
18937           magic_api_struct->rgbtohsv = rgbtohsv;
18938           magic_api_struct->hsvtorgb = hsvtorgb;
18939           magic_api_struct->canvas_w = canvas->w;
18940           magic_api_struct->canvas_h = canvas->h;
18941           magic_api_struct->scale = magic_scale;
18942           magic_api_struct->touched = magic_touched;
18943 
18944 
18945           do
18946             {
18947               f = readdir(d);
18948 
18949               if (f != NULL)
18950                 {
18951                   struct stat sbuf;
18952 
18953                   safe_snprintf(fname, sizeof(fname), "%s%s", place, f->d_name);
18954                   if (!stat(fname, &sbuf) && S_ISREG(sbuf.st_mode))
18955                     {
18956                       /* Get just the name of the object (e.g., "negative"), w/o filename
18957                          extension: */
18958 
18959                       safe_strncpy(objname, f->d_name, sizeof(objname));
18960                       strcpy(strchr(objname, '.'), ""); /* safe; truncating */
18961 
18962 
18963                       magic_handle[num_plugin_files] = SDL_LoadObject(fname);
18964 
18965                       if (magic_handle[num_plugin_files] != NULL)
18966                         {
18967 #ifdef DEBUG
18968                           printf("loading: %s\n", fname);
18969                           fflush(stdout);
18970 #endif
18971 
18972                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "get_tool_count");
18973                           magic_funcs[num_plugin_files].get_tool_count =
18974                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18975 
18976                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "get_name");
18977                           magic_funcs[num_plugin_files].get_name =
18978                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18979 
18980                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "get_icon");
18981                           magic_funcs[num_plugin_files].get_icon =
18982                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18983 
18984                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "get_description");
18985                           magic_funcs[num_plugin_files].get_description =
18986                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18987 
18988                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "requires_colors");
18989                           magic_funcs[num_plugin_files].requires_colors =
18990                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18991 
18992                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "modes");
18993                           magic_funcs[num_plugin_files].modes =
18994                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18995 
18996                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "set_color");
18997                           magic_funcs[num_plugin_files].set_color =
18998                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
18999 
19000                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "init");
19001                           magic_funcs[num_plugin_files].init =
19002                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19003 
19004                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "api_version");
19005                           magic_funcs[num_plugin_files].api_version =
19006                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19007 
19008                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "shutdown");
19009                           magic_funcs[num_plugin_files].shutdown =
19010                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19011 
19012                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "click");
19013                           magic_funcs[num_plugin_files].click =
19014                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19015 
19016                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "drag");
19017                           magic_funcs[num_plugin_files].drag =
19018                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19019 
19020                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "release");
19021                           magic_funcs[num_plugin_files].release =
19022                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19023 
19024                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "switchin");
19025                           magic_funcs[num_plugin_files].switchin =
19026                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19027 
19028                           safe_snprintf(funcname, sizeof(funcname), "%s_%s", objname, "switchout");
19029                           magic_funcs[num_plugin_files].switchout =
19030                             SDL_LoadFunction(magic_handle[num_plugin_files], funcname);
19031 
19032 #ifdef DEBUG
19033                           //EP added (intptr_t) to avoid warning on x64 on all lines below
19034                           printf("get_tool_count = 0x%x\n",
19035                                  (int)(intptr_t) magic_funcs[num_plugin_files].get_tool_count);
19036                           printf("get_name = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].get_name);
19037                           printf("get_icon = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].get_icon);
19038                           printf("get_description = 0x%x\n",
19039                                  (int)(intptr_t) magic_funcs[num_plugin_files].get_description);
19040                           printf("requires_colors = 0x%x\n",
19041                                  (int)(intptr_t) magic_funcs[num_plugin_files].requires_colors);
19042                           printf("modes = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].modes);
19043                           printf("set_color = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].set_color);
19044                           printf("init = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].init);
19045                           printf("api_version = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].api_version);
19046                           printf("shutdown = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].shutdown);
19047                           printf("click = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].click);
19048                           printf("drag = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].drag);
19049                           printf("release = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].release);
19050                           printf("switchin = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].switchin);
19051                           printf("switchout = 0x%x\n", (int)(intptr_t) magic_funcs[num_plugin_files].switchout);
19052 #endif
19053 
19054                           err = 0;
19055 
19056                           if (magic_funcs[num_plugin_files].get_tool_count == NULL)
19057                             {
19058                               fprintf(stderr, "Error: plugin %s is missing get_tool_count\n", fname);
19059                               err = 1;
19060                             }
19061                           if (magic_funcs[num_plugin_files].get_name == NULL)
19062                             {
19063                               fprintf(stderr, "Error: plugin %s is missing get_name\n", fname);
19064                               err = 1;
19065                             }
19066                           if (magic_funcs[num_plugin_files].get_icon == NULL)
19067                             {
19068                               fprintf(stderr, "Error: plugin %s is missing get_icon\n", fname);
19069                               err = 1;
19070                             }
19071                           if (magic_funcs[num_plugin_files].get_description == NULL)
19072                             {
19073                               fprintf(stderr, "Error: plugin %s is missing get_description\n", fname);
19074                               err = 1;
19075                             }
19076                           if (magic_funcs[num_plugin_files].requires_colors == NULL)
19077                             {
19078                               fprintf(stderr, "Error: plugin %s is missing requires_colors\n", fname);
19079                               err = 1;
19080                             }
19081                           if (magic_funcs[num_plugin_files].modes == NULL)
19082                             {
19083                               fprintf(stderr, "Error: plugin %s is missing modes\n", fname);
19084                               err = 1;
19085                             }
19086                           if (magic_funcs[num_plugin_files].set_color == NULL)
19087                             {
19088                               fprintf(stderr, "Error: plugin %s is missing set_color\n", fname);
19089                               err = 1;
19090                             }
19091                           if (magic_funcs[num_plugin_files].init == NULL)
19092                             {
19093                               fprintf(stderr, "Error: plugin %s is missing init\n", fname);
19094                               err = 1;
19095                             }
19096                           if (magic_funcs[num_plugin_files].shutdown == NULL)
19097                             {
19098                               fprintf(stderr, "Error: plugin %s is missing shutdown\n", fname);
19099                               err = 1;
19100                             }
19101                           if (magic_funcs[num_plugin_files].click == NULL)
19102                             {
19103                               fprintf(stderr, "Error: plugin %s is missing click\n", fname);
19104                               err = 1;
19105                             }
19106                           if (magic_funcs[num_plugin_files].release == NULL)
19107                             {
19108                               fprintf(stderr, "Error: plugin %s is missing release\n", fname);
19109                               err = 1;
19110                             }
19111                           if (magic_funcs[num_plugin_files].switchin == NULL)
19112                             {
19113                               fprintf(stderr, "Error: plugin %s is missing switchin\n", fname);
19114                               err = 1;
19115                             }
19116                           if (magic_funcs[num_plugin_files].switchout == NULL)
19117                             {
19118                               fprintf(stderr, "Error: plugin %s is missing switchout\n", fname);
19119                               err = 1;
19120                             }
19121                           if (magic_funcs[num_plugin_files].drag == NULL)
19122                             {
19123                               fprintf(stderr, "Error: plugin %s is missing drag\n", fname);
19124                               err = 1;
19125                             }
19126 
19127                           if (magic_funcs[num_plugin_files].api_version == NULL)
19128                             {
19129                               fprintf(stderr, "Error: plugin %s is missing api_version\n", fname);
19130                               err = 1;
19131                             }
19132                           else if (magic_funcs[num_plugin_files].api_version() != TP_MAGIC_API_VERSION)
19133                             {
19134                               fprintf(stderr,
19135                                       "Warning: plugin %s uses Tux Paint 'Magic' tool API version %x,\nbut Tux Paint needs version %x.\n",
19136                                       fname, magic_funcs[num_plugin_files].api_version(), TP_MAGIC_API_VERSION);
19137                               err = 1;
19138                             }
19139 
19140                           if (err)
19141                             {
19142                               SDL_UnloadObject(magic_handle[num_plugin_files]);
19143                             }
19144                           else
19145                             {
19146                               res = magic_funcs[num_plugin_files].init(magic_api_struct);
19147 
19148                               if (res != 0)
19149                                 n = magic_funcs[num_plugin_files].get_tool_count(magic_api_struct);
19150                               else
19151                                 {
19152                                   magic_funcs[num_plugin_files].shutdown(magic_api_struct);
19153                                   n = 0;
19154                                 }
19155 
19156 
19157                               if (n == 0)
19158                                 {
19159                                   fprintf(stderr, "Error: plugin %s failed to startup or reported 0 magic tools\n",
19160                                           fname);
19161                                   fflush(stderr);
19162                                   SDL_UnloadObject(magic_handle[num_plugin_files]);
19163                                 }
19164                               else
19165                                 {
19166                                   int j;
19167 
19168                                   for (i = 0; i < n; i++)
19169                                     {
19170                                       magics[num_magics].idx = i;
19171                                       magics[num_magics].place = plc;
19172                                       magics[num_magics].handle_idx = num_plugin_files;
19173                                       magics[num_magics].name =
19174                                         magic_funcs[num_plugin_files].get_name(magic_api_struct, i);
19175 
19176                                       magics[num_magics].avail_modes =
19177                                         magic_funcs[num_plugin_files].modes(magic_api_struct, i);
19178 
19179                                       for (j = 0; j < MAX_MODES; j++)
19180                                         {
19181                                           magics[num_magics].tip[j] = NULL;
19182                                           if (j)
19183                                             {
19184                                               if (magics[num_magics].avail_modes & MODE_FULLSCREEN)
19185                                                 magics[num_magics].tip[j] =
19186                                                   magic_funcs[num_plugin_files].get_description(magic_api_struct, i,
19187                                                                                                 MODE_FULLSCREEN);
19188                                             }
19189                                           else
19190                                             {
19191                                               if (magics[num_magics].avail_modes & MODE_PAINT)
19192                                                 magics[num_magics].tip[j] =
19193                                                   magic_funcs[num_plugin_files].get_description(magic_api_struct, i,
19194                                                                                                 MODE_PAINT);
19195                                               else if (magics[num_magics].avail_modes & MODE_ONECLICK)
19196                                                 magics[num_magics].tip[j] =
19197                                                   magic_funcs[num_plugin_files].get_description(magic_api_struct, i,
19198                                                                                                 MODE_ONECLICK);
19199                                               else if (magics[num_magics].avail_modes & MODE_PAINT_WITH_PREVIEW)
19200                                                 magics[num_magics].tip[j] =
19201                                                   magic_funcs[num_plugin_files].get_description(magic_api_struct, i,
19202                                                                                                 MODE_PAINT_WITH_PREVIEW);
19203                                             }
19204                                         }
19205 
19206                                       magics[num_magics].colors =
19207                                         magic_funcs[num_plugin_files].requires_colors(magic_api_struct, i);
19208                                       if (magics[num_magics].avail_modes & MODE_PAINT)
19209                                         magics[num_magics].mode = MODE_PAINT;
19210                                       else if (magics[num_magics].avail_modes & MODE_ONECLICK)
19211                                         magics[num_magics].mode = MODE_ONECLICK;
19212                                       else if (magics[num_magics].avail_modes & MODE_PAINT_WITH_PREVIEW)
19213                                         magics[num_magics].mode = MODE_PAINT_WITH_PREVIEW;
19214                                       else
19215                                         magics[num_magics].mode = MODE_FULLSCREEN;
19216 
19217                                       magics[num_magics].img_icon =
19218 					thumbnail( magic_funcs[num_plugin_files].get_icon(magic_api_struct, i), 40 * button_w / ORIGINAL_BUTTON_SIZE, 30 * button_h / ORIGINAL_BUTTON_SIZE, 1);
19219 
19220 #ifdef DEBUG
19221                                       printf("-- %s\n", magics[num_magics].name);
19222                                       printf("avail_modes = %d\n", magics[num_magics].avail_modes);
19223 #endif
19224 
19225                                       num_magics++;
19226                                     }
19227 
19228                                   num_plugin_files++;
19229                                 }
19230                             }
19231                         }
19232                       else
19233                         {
19234                           fprintf(stderr, "Warning: Failed to load object %s: %s\n", fname, SDL_GetError());
19235                           fflush(stderr);
19236                         }
19237                     }
19238                 }
19239             }
19240           while (f != NULL);
19241 
19242           closedir(d);
19243         }
19244     }
19245 
19246 
19247   qsort(magics, num_magics, sizeof(magic_t), magic_sort);
19248 
19249 #ifdef DEBUG
19250   printf("Loaded %d magic tools from %d plug-in files\n", num_magics, num_plugin_files);
19251   printf("\n");
19252   fflush(stdout);
19253 #endif
19254 }
19255 
19256 
19257 
19258 /**
19259  * FIXME
19260  */
magic_sort(const void * a,const void * b)19261 static int magic_sort(const void *a, const void *b)
19262 {
19263   magic_t *am = (magic_t *) a;
19264   magic_t *bm = (magic_t *) b;
19265 
19266   return (strcoll(gettext(am->name), gettext(bm->name)));
19267 }
19268 
19269 
19270 /**
19271  * FIXME
19272  */
update_progress_bar(void)19273 static void update_progress_bar(void)
19274 {
19275   show_progress_bar(screen);
19276 }
19277 
19278 /**
19279  * FIXME
19280  */
magic_line_func(void * mapi,int which,SDL_Surface * canvas,SDL_Surface * last,int x1,int y1,int x2,int y2,int step,void (* cb)(void *,int,SDL_Surface *,SDL_Surface *,int,int))19281 static void magic_line_func(void *mapi,
19282                             int which, SDL_Surface * canvas, SDL_Surface * last,
19283                             int x1, int y1, int x2, int y2, int step,
19284                             void (*cb) (void *, int, SDL_Surface *, SDL_Surface *, int, int))
19285 {
19286   int dx, dy, y;
19287   float m, b;
19288   int cnt;
19289 
19290   dx = x2 - x1;
19291   dy = y2 - y1;
19292 
19293   cnt = step - 1;
19294 
19295   if (dx != 0)
19296     {
19297       m = ((float)dy) / ((float)dx);
19298       b = y1 - m * x1;
19299 
19300       if (x2 >= x1)
19301         dx = 1;
19302       else
19303         dx = -1;
19304 
19305 
19306       while (x1 != x2)
19307         {
19308           y1 = m * x1 + b;
19309           y2 = m * (x1 + dx) + b;
19310 
19311           if (y1 > y2)
19312             {
19313               for (y = y1; y >= y2; y--)
19314                 {
19315                   cnt = (cnt + 1) % step;
19316                   if (cnt == 0)
19317                     cb((void *)mapi, which, canvas, last, x1, y);
19318                 }
19319             }
19320           else
19321             {
19322               for (y = y1; y <= y2; y++)
19323                 {
19324                   cnt = (cnt + 1) % step;
19325                   if (cnt == 0)
19326                     cb((void *)mapi, which, canvas, last, x1, y);
19327                 }
19328             }
19329 
19330           x1 = x1 + dx;
19331         }
19332     }
19333   else
19334     {
19335       if (y1 > y2)
19336         {
19337           for (y = y1; y >= y2; y--)
19338             {
19339               cnt = (cnt + 1) % step;
19340               if (cnt == 0)
19341                 cb((void *)mapi, which, canvas, last, x1, y);
19342             }
19343         }
19344       else
19345         {
19346           for (y = y1; y <= y2; y++)
19347             {
19348               cnt = (cnt + 1) % step;
19349               if (cnt == 0)
19350                 cb((void *)mapi, which, canvas, last, x1, y);
19351             }
19352         }
19353     }
19354 
19355   /* FIXME: Set and return an update rect? */
19356 }
19357 
19358 
19359 /**
19360  * FIXME
19361  */
19362 /* Handle special things that some magic tools do that
19363    need to affect more than just the current canvas: */
special_notify(int flags)19364 static void special_notify(int flags)
19365 {
19366   int tmp_int;
19367 
19368   tmp_int = cur_undo - 1;
19369   if (tmp_int < 0)
19370     tmp_int = NUM_UNDO_BUFS - 1;
19371 
19372   if (flags & SPECIAL_MIRROR)
19373     {
19374       /* Mirror starter, too! */
19375 
19376       starter_mirrored = !starter_mirrored;
19377 
19378       if (img_starter != NULL)
19379         mirror_starter();
19380 
19381       undo_starters[tmp_int] = UNDO_STARTER_MIRRORED;
19382     }
19383 
19384   if (flags & SPECIAL_FLIP)
19385     {
19386       /* Flip starter, too! */
19387 
19388       starter_flipped = !starter_flipped;
19389 
19390       if (img_starter != NULL)
19391         flip_starter();
19392 
19393       undo_starters[tmp_int] = UNDO_STARTER_FLIPPED;
19394     }
19395 }
19396 
19397 /**
19398  * FIXME
19399  */
magic_stopsound(void)19400 static void magic_stopsound(void)
19401 {
19402 #ifndef NOSOUND
19403   if (mute || !use_sound)
19404     return;
19405 
19406   Mix_HaltChannel(0);
19407 #endif
19408 }
19409 
19410 /**
19411  * FIXME
19412  */
magic_playsound(Mix_Chunk * snd,int left_right,int up_down)19413 static void magic_playsound(Mix_Chunk * snd, int left_right, int up_down)
19414 {
19415 #ifndef NOSOUND
19416 
19417   int left, dist;
19418 
19419 
19420   /* Don't play if sound is disabled (nosound), or sound is temporarily
19421      muted (Alt+S), or sound ptr is NULL */
19422 
19423   if (mute || !use_sound || snd == NULL)
19424     return;
19425 
19426 
19427   /* Don't override the same sound, if it's already playing */
19428 
19429   if (!Mix_Playing(0) || magic_current_snd_ptr != snd)
19430     Mix_PlayChannel(0, snd, 0);
19431 
19432   magic_current_snd_ptr = snd;
19433 
19434 
19435   /* Adjust panning */
19436 
19437   if (up_down < 0)
19438     up_down = 0;
19439   else if (up_down > 255)
19440     up_down = 255;
19441 
19442   dist = 255 - up_down;
19443 
19444   if (left_right < 0)
19445     left_right = 0;
19446   else if (left_right > 255)
19447     left_right = 255;
19448 
19449   if (use_stereo)
19450     {
19451       left = ((255 - dist) * (255 - left_right)) / 255;
19452     }
19453   else
19454     {
19455       /* Stereo disabled; no panning (see playsound.c) */
19456       left = (255 - dist) / 2;
19457     }
19458 
19459   Mix_SetPanning(0, left, (255 - dist) - left);
19460 #endif
19461 }
19462 
19463 /**
19464  * FIXME
19465  */
magic_linear_to_sRGB(float lin)19466 static Uint8 magic_linear_to_sRGB(float lin)
19467 {
19468   return (linear_to_sRGB(lin));
19469 }
19470 
19471 /**
19472  * FIXME
19473  */
magic_sRGB_to_linear(Uint8 srgb)19474 static float magic_sRGB_to_linear(Uint8 srgb)
19475 {
19476   return (sRGB_to_linear_table[srgb]);
19477 }
19478 
19479 /**
19480  * FIXME
19481  */
magic_button_down(void)19482 static int magic_button_down(void)
19483 {
19484   return (button_down || emulate_button_pressed);
19485 }
19486 
19487 /**
19488  * FIXME
19489  */
magic_scale(SDL_Surface * surf,int w,int h,int aspect)19490 static SDL_Surface *magic_scale(SDL_Surface * surf, int w, int h, int aspect)
19491 {
19492   return (thumbnail2(surf, w, h, aspect, 1));
19493 }
19494 
19495 /**
19496  * FIXME
19497  */
19498 /* FIXME: This, do_open() and do_slideshow() should be combined and modularized! */
do_new_dialog(void)19499 static int do_new_dialog(void)
19500 {
19501   SDL_Surface *img, *img1, *img2;
19502   int things_alloced;
19503   SDL_Surface **thumbs = NULL;
19504   DIR *d;
19505   struct dirent *f;
19506   struct dirent2 *fs;
19507   struct stat sbuf;
19508   int place;
19509   char *dirname[NUM_PLACES_TO_LOOK];
19510   char **d_names = NULL, **d_exts = NULL;
19511   int *d_places;
19512   FILE *fi;
19513   char fname[1024];
19514   int num_files, i, done, update_list, cur, which, num_files_in_dirs, j;
19515   SDL_Rect dest;
19516   SDL_Event event;
19517   SDLKey key;
19518   Uint32 last_click_time;
19519   int last_click_which, last_click_button;
19520   int places_to_look;
19521   int tot;
19522   int first_color, first_starter, first_template;
19523   int white_in_palette;
19524   int val_x, val_y, motioner;
19525   int valhat_x, valhat_y, hatmotioner;
19526   int skip, k;
19527 
19528 
19529   val_x = val_y = motioner = 0;
19530   valhat_x = valhat_y = hatmotioner = 0;
19531   do_setcursor(cursor_watch);
19532 
19533   /* Allocate some space: */
19534 
19535   things_alloced = 32;
19536 
19537   fs = (struct dirent2 *)malloc(sizeof(struct dirent2) * things_alloced);
19538 
19539   num_files = 0;
19540   cur = 0;
19541   which = 0;
19542   num_files_in_dirs = 0;
19543 
19544 
19545   /* Open directories of images: */
19546 
19547   for (places_to_look = 0; places_to_look < NUM_PLACES_TO_LOOK; places_to_look++)
19548     {
19549       if (places_to_look == PLACE_SAVED_DIR)
19550         {
19551           /* Skip saved images; only want starters! */
19552           dirname[places_to_look] = NULL;
19553           continue;             /* ugh */
19554         }
19555       else if (places_to_look == PLACE_PERSONAL_STARTERS_DIR)
19556         {
19557           /* Check for coloring-book style 'starter' images in our folder: */
19558 
19559           dirname[places_to_look] = get_fname("starters", DIR_DATA);
19560         }
19561       else if (places_to_look == PLACE_STARTERS_DIR)
19562         {
19563           /* Finally, check for system-wide coloring-book style
19564              'starter' images: */
19565 
19566           dirname[places_to_look] = strdup(DATA_PREFIX "starters");
19567         }
19568       else if (places_to_look == PLACE_PERSONAL_TEMPLATES_DIR)
19569         {
19570           /* Check for 'template' images in our folder: */
19571 
19572           dirname[places_to_look] = get_fname("templates", DIR_DATA);
19573         }
19574       else if (places_to_look == PLACE_TEMPLATES_DIR)
19575         {
19576           /* Finally, check for system-wide 'template' images: */
19577 
19578           dirname[places_to_look] = strdup(DATA_PREFIX "templates");
19579         }
19580 
19581 
19582       /* Read directory of images and build thumbnails: */
19583 
19584       d = opendir(dirname[places_to_look]);
19585 
19586       if (d != NULL)
19587         {
19588           /* Gather list of files (for sorting): */
19589 
19590           do
19591             {
19592               f = readdir(d);
19593 
19594               if (f !=NULL)
19595                 {
19596                   safe_snprintf(fname, sizeof(fname), "%s/%s", dirname[places_to_look], f->d_name);
19597                   if (!stat(fname, &sbuf) && S_ISREG(sbuf.st_mode))
19598                     {
19599                       memcpy(&(fs[num_files_in_dirs].f), f, sizeof(struct dirent));
19600                       fs[num_files_in_dirs].place = places_to_look;
19601 
19602                       num_files_in_dirs++;
19603 
19604                       if (num_files_in_dirs >= things_alloced)
19605                         {
19606                           things_alloced = things_alloced + 32;
19607 
19608                           fs = (struct dirent2 *)realloc(fs, sizeof(struct dirent2) * things_alloced);
19609                         }
19610                     }
19611                 }
19612             }
19613           while (f != NULL);
19614 
19615           closedir(d);
19616         }
19617     }
19618 
19619 
19620   /* (Re)allocate space for the information about these files: */
19621   tot = num_files_in_dirs;
19622 
19623   /* And colors... */
19624   tot += NUM_COLORS;
19625 
19626   thumbs = (SDL_Surface * *)malloc(sizeof(SDL_Surface *) * tot);
19627   d_places = (int *)malloc(sizeof(int) * tot);
19628   d_names = (char **)malloc(sizeof(char *) * tot);
19629   d_exts = (char **)malloc(sizeof(char *) * tot);
19630 
19631 
19632   /* Sort: */
19633 
19634   qsort(fs, num_files_in_dirs, sizeof(struct dirent2), (int (*)(const void *, const void *))compare_dirent2s);
19635 
19636 
19637   /* Throw the color palette at the beginning (default): */
19638 
19639   white_in_palette = -1;
19640 
19641   if (!new_colors_last)
19642     {
19643       first_color = 0;
19644       num_files = do_new_dialog_add_colors(thumbs, num_files, d_places, d_names, d_exts, &white_in_palette);
19645     }
19646 
19647   first_starter = num_files;
19648   first_template = -1;          /* In case there are none... */
19649 
19650 
19651   /* Read directory of images and build thumbnails: */
19652 
19653   for (j = 0; j < num_files_in_dirs; j++)
19654     {
19655       f = &(fs[j].f);
19656       place = fs[j].place;
19657 
19658       if ((place == PLACE_PERSONAL_TEMPLATES_DIR || place == PLACE_TEMPLATES_DIR) && first_template == -1)
19659         first_template = num_files;
19660 
19661       show_progress_bar(screen);
19662 
19663       if (f != NULL)
19664         {
19665           debug(f->d_name);
19666 
19667           if (strcasestr(f->d_name, "-t.") == NULL && strcasestr(f->d_name, "-back.") == NULL)
19668             {
19669               if (strcasestr(f->d_name, FNAME_EXTENSION) != NULL
19670                   /* Support legacy BMP files for load: */
19671                   || strcasestr(f->d_name, ".bmp") != NULL
19672                   /* Support for KPX (Kid Pix templates; just a JPEG with resource fork header): */
19673                   || strcasestr(f->d_name, ".kpx") != NULL
19674                   || strcasestr(f->d_name, ".jpg") != NULL
19675 #ifndef NOSVG
19676                   || strcasestr(f->d_name, ".svg") != NULL
19677 #endif
19678                 )
19679                 {
19680                   safe_strncpy(fname, f->d_name, sizeof(fname));
19681                   skip = 0;
19682 
19683                   if (strcasestr(fname, FNAME_EXTENSION) != NULL)
19684                     {
19685                       d_exts[num_files] = strdup(strcasestr(fname, FNAME_EXTENSION));
19686                       strcpy((char *)strcasestr(fname, FNAME_EXTENSION), ""); /* safe; truncating */
19687                     }
19688 
19689                   if (strcasestr(fname, ".bmp") != NULL)
19690                     {
19691                       d_exts[num_files] = strdup(strcasestr(fname, ".bmp"));
19692                       strcpy((char *)strcasestr(fname, ".bmp"), ""); /* safe; truncating */
19693                     }
19694 
19695 #ifndef NOSVG
19696                   if (strcasestr(fname, ".svg") != NULL)
19697                     {
19698                       d_exts[num_files] = strdup(strcasestr(fname, ".svg"));
19699                       strcpy((char *)strcasestr(fname, ".svg"), ""); /* safe; truncating */
19700                     }
19701 #endif
19702 
19703                   if (strcasestr(fname, ".kpx") != NULL)
19704                     {
19705                       d_exts[num_files] = strdup(strcasestr(fname, ".kpx"));
19706                       strcpy((char *)strcasestr(fname, ".kpx"), ""); /* safe; truncating */
19707                     }
19708 
19709                   if (strcasestr(fname, ".jpg") != NULL)
19710                     {
19711                       d_exts[num_files] = strdup(strcasestr(fname, ".jpg"));
19712                       strcpy((char *)strcasestr(fname, ".jpg"), ""); /* safe; truncating */
19713                     }
19714 
19715 #ifndef NOSVG
19716                   /* If identically-named SVG exists, skip this version */
19717                   for (k = 0; k < num_files_in_dirs; k++)
19718                     {
19719                       if (k != j)
19720                         {
19721                           struct dirent *f2;
19722                           char fname2[1024];
19723 
19724                           f2 = &(fs[k].f);
19725                           safe_strncpy(fname2, f2->d_name, sizeof(fname2));
19726 
19727                           if (strstr(fname2, fname) == fname2 && strlen(fname) == strlen(fname2)- strlen(".svg") && strcasestr(fname2, ".svg") != NULL)
19728                             {
19729                               /* SVG of this bitmap exists; we'll skip it */
19730                               skip = 1;
19731                             }
19732                         }
19733                     }
19734 #endif
19735                   if (skip)
19736                     {
19737                       free(d_exts[num_files]);
19738                     }
19739                   else
19740                     {
19741                       d_names[num_files] = strdup(fname);
19742                       d_places[num_files] = place;
19743 
19744 
19745                       /* Try to load thumbnail first: */
19746 
19747                       safe_snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
19748                                dirname[d_places[num_files]], d_names[num_files]);
19749                       debug(fname);
19750                       img = IMG_Load(fname);
19751 
19752                       if (img == NULL)
19753                         {
19754                           /* No thumbnail in the new location ("saved/.thumbs"),
19755                              try the old location ("saved/"): */
19756 
19757                           safe_snprintf(fname, sizeof(fname), "%s/%s-t.png",
19758                                    dirname[d_places[num_files]], d_names[num_files]);
19759                           debug(fname);
19760 
19761                           img = IMG_Load(fname);
19762                         }
19763 
19764                       if (img != NULL)
19765                         {
19766                           /* Loaded the thumbnail from one or the other location */
19767                           show_progress_bar(screen);
19768 
19769                           img1 = SDL_DisplayFormat(img);
19770                           SDL_FreeSurface(img);
19771 
19772                           /* if too big, or too small in both dimensions, rescale it
19773                              (for now: using old thumbnail as source for high speed,
19774                              low quality) */
19775                           if (img1->w > THUMB_W - 20 || img1->h > THUMB_H - 20
19776                               || (img1->w < THUMB_W - 20 && img1->h < THUMB_H - 20))
19777                             {
19778                               img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
19779                               SDL_FreeSurface(img1);
19780                               img1 = img2;
19781                             }
19782 
19783                           thumbs[num_files] = img1;
19784 
19785                           if (thumbs[num_files] == NULL)
19786                             {
19787                               fprintf(stderr,
19788                                       "\nError: Couldn't create a thumbnail of saved image!\n" "%s\n", fname);
19789                             }
19790 
19791                           num_files++;
19792                         }
19793                       else
19794                         {
19795                           /* No thumbnail - load original: */
19796 
19797                           if (d_places[num_files] == PLACE_PERSONAL_TEMPLATES_DIR ||
19798                               d_places[num_files] == PLACE_PERSONAL_STARTERS_DIR)
19799                             {
19800                               /* Make sure we have a ~/.tuxpaint/[starters|templates] directory: */
19801                               if (make_directory(DIR_SAVE, dirname[d_places[num_files]], "Can't create user data directory (for starters/templates) (E010)"))
19802                                 {
19803                                   /* (Make sure we have a .../[starters|templates]/.thumbs/ directory:) */
19804                                   safe_snprintf(fname, sizeof(fname), "%s/.thumbs", dirname[d_places[num_files]]);
19805                                   make_directory(DIR_SAVE, fname, "Can't create user data thumbnail directory (for starters/templates) (E011)");
19806                                 }
19807                             }
19808 
19809                           img = NULL;
19810 
19811                           if (d_places[num_files] == PLACE_STARTERS_DIR ||
19812                               d_places[num_files] == PLACE_PERSONAL_STARTERS_DIR)
19813                             {
19814                               /* Try to load a starter's background image, first!
19815                                  If it exists, it should give a better idea of what the
19816                                  starter looks like, compared to the overlay image... */
19817 
19818                               /* (Try JPEG first) */
19819                               safe_snprintf(fname, sizeof(fname), "%s/%s-back",
19820                                        dirname[d_places[num_files]], d_names[num_files]);
19821                               img = load_starter_helper(fname, "jpeg", &IMG_Load);
19822                               if (img == NULL)
19823                                 {
19824                                   safe_snprintf(fname, sizeof(fname), "%s/%s-back",
19825                                            dirname[d_places[num_files]], d_names[num_files]);
19826                                   img = load_starter_helper(fname, "jpg", &IMG_Load);
19827                                 }
19828 
19829 #ifndef NOSVG
19830                               if (img == NULL)
19831                                 {
19832                                   /* (Try SVG next) */
19833                                   safe_snprintf(fname, sizeof(fname), "%s/%s-back",
19834                                            dirname[d_places[num_files]], d_names[num_files]);
19835                                   img = load_starter_helper(fname, "svg", &load_svg);
19836                                 }
19837 #endif
19838 
19839                               if (img == NULL)
19840                                 {
19841                                   /* (Try PNG next) */
19842                                   safe_snprintf(fname, sizeof(fname), "%s/%s-back",
19843                                            dirname[d_places[num_files]], d_names[num_files]);
19844                                   img = load_starter_helper(fname, "png", &IMG_Load);
19845                                 }
19846                             }
19847 
19848                           if (img == NULL)
19849                             {
19850                               /* Didn't load a starter background (or didn't try!),
19851                                  try loading the actual image... */
19852 
19853                               safe_snprintf(fname, sizeof(fname), "%s/%s", dirname[d_places[num_files]], f->d_name);
19854                               debug(fname);
19855                               img = myIMG_Load(fname);
19856                             }
19857 
19858 
19859                           show_progress_bar(screen);
19860 
19861                           if (img == NULL)
19862                             {
19863                               fprintf(stderr,
19864                                       "\nWarning: I can't open one of the saved files!\n"
19865                                       "%s\n"
19866                                       "The Simple DirectMedia Layer error that "
19867                                       "occurred was:\n" "%s\n\n", fname, SDL_GetError());
19868 
19869                               free(d_names[num_files]);
19870                               free(d_exts[num_files]);
19871                             }
19872                           else
19873                             {
19874                               /* Turn it into a thumbnail: */
19875 
19876                               img1 = SDL_DisplayFormatAlpha(img);
19877                               img2 = thumbnail2(img1, THUMB_W - 20, THUMB_H - 20, 0, 0);
19878                               SDL_FreeSurface(img1);
19879 
19880                               show_progress_bar(screen);
19881 
19882                               thumbs[num_files] = SDL_DisplayFormat(img2);
19883                               SDL_FreeSurface(img2);
19884                               if (thumbs[num_files] == NULL)
19885                                 {
19886                                   fprintf(stderr,
19887                                           "\nError: Couldn't create a thumbnail of saved image!\n" "%s\n", fname);
19888                                 }
19889 
19890                               SDL_FreeSurface(img);
19891 
19892                               show_progress_bar(screen);
19893 
19894 
19895                               /* Let's save this thumbnail, so we don't have to
19896                                  create it again next time 'Open' is called: */
19897                               /* if (d_places[num_files] == PLACE_SAVED_DIR) *//* <-- FIXME: This test should probably go...? -bjk 2009.10.15 */
19898 
19899                               if (d_places[num_files] == PLACE_PERSONAL_STARTERS_DIR || /* We must check to not try to write to system wide dirs  Pere 2010.3.25 */
19900                                   d_places[num_files] == PLACE_PERSONAL_TEMPLATES_DIR)
19901                                 {
19902                                   debug("Saving thumbnail for this one!");
19903 
19904                                   safe_snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
19905                                            dirname[d_places[num_files]], d_names[num_files]);
19906 
19907                                   if (!make_directory(DIR_SAVE, "starters", "Can't create user data directory (for starters) (E012)") ||
19908                                       !make_directory(DIR_SAVE, "templates", "Can't create user data directory (for templates) (E013)") ||
19909                                       !make_directory(DIR_SAVE, "starters/.thumbs", "Can't create user data directory (for starters) (E014)") ||
19910                                       !make_directory(DIR_SAVE, "templates/.thumbs", "Can't create user data directory (for templates) (E015)"))
19911                                     fprintf(stderr, "Cannot save any pictures! SORRY!\n\n");
19912                                   else
19913                                     {
19914                                       fi = fopen(fname, "wb");
19915                                       if (fi == NULL)
19916                                         {
19917                                           fprintf(stderr,
19918                                                   "\nError: Couldn't save thumbnail of "
19919                                                   "saved image!\n"
19920                                                   "%s\n"
19921                                                   "The error that occurred was:\n" "%s\n\n", fname, strerror(errno));
19922                                         }
19923                                       else
19924                                         {
19925                                           do_png_save(fi, fname, thumbs[num_files], 0);
19926                                         }
19927                                     }
19928 
19929                                   show_progress_bar(screen);
19930                                 }
19931 
19932 
19933                               num_files++;
19934                             }
19935                         }
19936                     }
19937                 }
19938             }
19939           else
19940             {
19941               /* It was a thumbnail file ("...-t.png") or immutable scene starter's
19942                  overlay layer ("...-front.png") */
19943             }
19944         }
19945     }
19946 
19947   /* Throw the color palette at the end (alternative option): */
19948 
19949   if (new_colors_last)
19950     {
19951       first_color = num_files;
19952       num_files = do_new_dialog_add_colors(thumbs, num_files, d_places, d_names, d_exts, &white_in_palette);
19953     }
19954 
19955 
19956 #ifdef DEBUG
19957   printf("%d files and colors were found!\n", num_files);
19958   printf("first_color = %d\nfirst_starter = %d\nfirst_template = %d\nnum_files = %d\n\n", first_color, first_starter,
19959          first_template, num_files);
19960 #endif
19961 
19962 
19963   /* Let user choose a color or image: */
19964 
19965   /* Instructions for 'New' file/color dialog */
19966   draw_tux_text(TUX_BORED, tool_tips[TOOL_NEW], 1);
19967 
19968   /* NOTE: cur is now set above; if file_id'th file is found, it's
19969      set to that file's index; otherwise, we default to '0' */
19970 
19971   update_list = 1;
19972 
19973   done = 0;
19974 
19975   last_click_which = -1;
19976   last_click_time = 0;
19977   last_click_button = -1;
19978 
19979 
19980   do_setcursor(cursor_arrow);
19981 
19982 
19983   do
19984     {
19985       /* Update screen: */
19986 
19987       if (update_list)
19988         {
19989           /* Erase screen: */
19990 
19991           dest.x = r_ttools.w;
19992           dest.y = 0;
19993           dest.w = WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w;
19994           dest.h = button_h * buttons_tall + r_ttools.h;
19995 
19996           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
19997 
19998 
19999           /* Draw icons: */
20000 
20001           for (i = cur; i < cur + 16 && i < num_files; i++)
20002             {
20003               /* Draw cursor: */
20004 
20005               dest.x = THUMB_W * ((i - cur) % 4) + r_ttools.w;
20006               dest.y = THUMB_H * ((i - cur) / 4) + img_scroll_up->h;
20007 
20008               if (d_places[i] == PLACE_SAVED_DIR)
20009                 {
20010                   if (i == which)
20011                     {
20012                       SDL_BlitSurface(img_cursor_down, NULL, screen, &dest);
20013                       debug(d_names[i]);
20014                     }
20015                   else
20016                     SDL_BlitSurface(img_cursor_up, NULL, screen, &dest);
20017                 }
20018               else
20019                 {
20020                   if (i == which)
20021                     {
20022                       SDL_BlitSurface(img_cursor_starter_down, NULL, screen, &dest);
20023                       debug(d_names[i]);
20024                     }
20025                   else
20026                     SDL_BlitSurface(img_cursor_starter_up, NULL, screen, &dest);
20027                 }
20028 
20029 
20030 
20031               dest.x = THUMB_W * ((i - cur) % 4) + r_ttools.w + 10 + (THUMB_W - 20 - thumbs[i]->w) / 2;
20032               dest.y = THUMB_H * ((i - cur) / 4) + img_scroll_up->h + 10 + (THUMB_H - 20 - thumbs[i]->h) / 2;
20033 
20034               if (thumbs[i] != NULL)
20035                 SDL_BlitSurface(thumbs[i], NULL, screen, &dest);
20036             }
20037 
20038 
20039           /* Draw arrows: */
20040 
20041           dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
20042           dest.y = 0;
20043 
20044           if (cur > 0)
20045             SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
20046           else
20047             SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
20048 
20049           dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
20050           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
20051 
20052           if (cur < num_files - 16)
20053             SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
20054           else
20055             SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
20056 
20057 
20058           /* "Open" button: */
20059 
20060           dest.x = r_ttools.w;
20061           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
20062           SDL_BlitSurface(img_open, NULL, screen, &dest);
20063 
20064           dest.x = r_ttools.w + (button_w - img_openlabels_open->w) / 2;
20065           dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_open->h;
20066           SDL_BlitSurface(img_openlabels_open, NULL, screen, &dest);
20067 
20068 
20069           /* "Back" button: */
20070 
20071           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w;
20072           dest.y = (button_h * buttons_tall + r_ttools.h) - button_h;
20073           SDL_BlitSurface(img_back, NULL, screen, &dest);
20074 
20075           dest.x = WINDOW_WIDTH - r_ttoolopt.w - button_w + (button_w - img_openlabels_back->w) / 2;
20076           dest.y = (button_h * buttons_tall + r_ttools.h) - img_openlabels_back->h;
20077           SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
20078 
20079 
20080           SDL_Flip(screen);
20081 
20082           update_list = 0;
20083         }
20084 
20085       /* Was a call to SDL_WaitEvent(&event); before,
20086          changed to this while loop in order to get joystick working */
20087       while (SDL_PollEvent(&event))
20088         {
20089           if (event.type == SDL_QUIT)
20090             {
20091               done = 1;
20092 
20093               /* FIXME: Handle SDL_Quit better */
20094             }
20095           else if (event.type == SDL_ACTIVEEVENT)
20096             {
20097               handle_active(&event);
20098             }
20099           else if (event.type == SDL_KEYUP)
20100             {
20101               key = event.key.keysym.sym;
20102 
20103               handle_keymouse(key, SDL_KEYUP, 24, NULL, NULL);
20104             }
20105           else if (event.type == SDL_KEYDOWN)
20106             {
20107               key = event.key.keysym.sym;
20108 
20109               handle_keymouse(key, SDL_KEYDOWN, 24, NULL, NULL);
20110 
20111               /* Moved from LEFT RIGHT UP DOWN to F11 F12 F8 F7 */
20112 
20113               if (key == SDLK_F11)
20114                 {
20115                   if (which > 0)
20116                     {
20117                       which--;
20118 
20119                       if (which < cur)
20120                         cur = cur - 4;
20121 
20122                       update_list = 1;
20123                     }
20124                 }
20125               else if (key == SDLK_F12)
20126                 {
20127                   if (which < num_files - 1)
20128                     {
20129                       which++;
20130 
20131                       if (which >= cur + 16)
20132                         cur = cur + 4;
20133 
20134                       update_list = 1;
20135                     }
20136                 }
20137               else if (key == SDLK_F8)
20138                 {
20139                   if (which >= 0)
20140                     {
20141                       which = which - 4;
20142 
20143                       if (which < 0)
20144                         which = 0;
20145 
20146                       if (which < cur)
20147                         cur = cur - 4;
20148 
20149                       update_list = 1;
20150                     }
20151                 }
20152               else if (key == SDLK_F7)
20153                 {
20154                   if (which < num_files)
20155                     {
20156                       which = which + 4;
20157 
20158                       if (which >= num_files)
20159                         which = num_files - 1;
20160 
20161                       if (which >= cur + 16)
20162                         cur = cur + 4;
20163 
20164                       update_list = 1;
20165                     }
20166                 }
20167               else if (key == SDLK_RETURN)
20168                 {
20169                   /* Open */
20170 
20171                   done = 1;
20172                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
20173                 }
20174               else if (key == SDLK_ESCAPE)
20175                 {
20176                   /* Go back: */
20177 
20178                   which = -1;
20179                   done = 1;
20180                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
20181                 }
20182             }
20183           else if (event.type == SDL_MOUSEBUTTONDOWN && valid_click(event.button.button))
20184             {
20185               if (event.button.x >= r_ttools.w && event.button.x < WINDOW_WIDTH - r_ttoolopt.w &&
20186                   event.button.y >= img_scroll_up->h && event.button.y < (button_h * buttons_tall + r_ttools.h - button_h))
20187                 {
20188                   /* Picked an icon! */
20189 
20190                   which = ((event.button.x - r_ttools.w) / (THUMB_W) + (((event.button.y - img_scroll_up->h) / THUMB_H) * 4)) + cur;
20191 
20192                   if (which < num_files)
20193                     {
20194                       playsound(screen, 1, SND_BLEEP, 1, event.button.x, SNDDIST_NEAR);
20195                       update_list = 1;
20196 
20197 
20198                       if (which == last_click_which &&
20199                           SDL_GetTicks() < last_click_time + 1000 && event.button.button == last_click_button)
20200                         {
20201                           /* Double-click! */
20202 
20203                           done = 1;
20204                         }
20205 
20206                       last_click_which = which;
20207                       last_click_time = SDL_GetTicks();
20208                       last_click_button = event.button.button;
20209                     }
20210                 }
20211               else if (event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
20212                        event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2)
20213                 {
20214                   if (event.button.y < img_scroll_up->h)
20215                     {
20216                       /* Up scroll button: */
20217 
20218                       if (cur > 0)
20219                         {
20220                           cur = cur - 4;
20221                           update_list = 1;
20222                           playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
20223 
20224                           if (cur == 0)
20225                             do_setcursor(cursor_arrow);
20226                         }
20227 
20228                       if (which >= cur + 16)
20229                         which = which - 4;
20230                     }
20231                   else if (event.button.y >= (button_h * buttons_tall + r_ttools.h - button_h) &&
20232                            event.button.y < (button_h * buttons_tall + r_ttools.h - img_scroll_up->h))
20233                     {
20234                       /* Down scroll button: */
20235 
20236                       if (cur < num_files - 16)
20237                         {
20238                           cur = cur + 4;
20239                           update_list = 1;
20240                           playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
20241 
20242                           if (cur >= num_files - 16)
20243                             do_setcursor(cursor_arrow);
20244                         }
20245 
20246                       if (which < cur)
20247                         which = which + 4;
20248                     }
20249                 }
20250               else if (event.button.x >= r_ttools.w && event.button.x < r_ttools.w + button_w &&
20251                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
20252                        event.button.y < (button_h * buttons_tall + r_ttools.h))
20253                 {
20254                   /* Open */
20255 
20256                   done = 1;
20257                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_LEFT, SNDDIST_NEAR);
20258                 }
20259               else if (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w) &&
20260                        event.button.x < (WINDOW_WIDTH - r_ttoolopt.w) &&
20261                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
20262                        event.button.y < (button_h * buttons_tall + r_ttools.h))
20263                 {
20264                   /* Back */
20265 
20266                   which = -1;
20267                   done = 1;
20268                   playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR);
20269                 }
20270             }
20271           else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button >= 4 && event.button.button <= 5 && wheely)
20272             {
20273               /* Scroll wheel! */
20274 
20275               if (event.button.button == 4 && cur > 0)
20276                 {
20277                   cur = cur - 4;
20278                   update_list = 1;
20279                   playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
20280 
20281                   if (cur == 0)
20282                     do_setcursor(cursor_arrow);
20283 
20284                   if (which >= cur + 16)
20285                     which = which - 4;
20286                 }
20287               else if (event.button.button == 5 && cur < num_files - 16)
20288                 {
20289                   cur = cur + 4;
20290                   update_list = 1;
20291                   playsound(screen, 1, SND_SCROLL, 1, SNDPOS_CENTER, SNDDIST_NEAR);
20292 
20293                   if (cur >= num_files - 16)
20294                     do_setcursor(cursor_arrow);
20295 
20296                   if (which < cur)
20297                     which = which + 4;
20298                 }
20299             }
20300           else if (event.type == SDL_MOUSEMOTION)
20301             {
20302               /* Deal with mouse pointer shape! */
20303 
20304               if (event.button.y < img_scroll_up->h &&
20305                   event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
20306                   event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 && cur > 0)
20307                 {
20308                   /* Scroll up button: */
20309 
20310                   do_setcursor(cursor_up);
20311                 }
20312               else if (event.button.y >= (button_h * buttons_tall + r_ttools.h - button_h) &&
20313                        event.button.y < (button_h * buttons_tall + r_ttools.h - img_scroll_up->h) &&
20314                        event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
20315                        event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 && cur < num_files - 16)
20316                 {
20317                   /* Scroll down button: */
20318 
20319                   do_setcursor(cursor_down);
20320                 }
20321               else if (((event.button.x >= r_ttools.w && event.button.x < r_ttools.w + button_w) ||
20322                         (event.button.x >= (WINDOW_WIDTH - r_ttoolopt.w - button_w) &&
20323                          event.button.x < (WINDOW_WIDTH - r_ttoolopt.w) &&
20324                          d_places[which] != PLACE_STARTERS_DIR &&
20325                          d_places[which] != PLACE_PERSONAL_STARTERS_DIR)) &&
20326                        event.button.y >= (button_h * buttons_tall + r_ttools.h) - button_h &&
20327                        event.button.y < (button_h * buttons_tall + r_ttools.h))
20328                 {
20329                   /* One of the command buttons: */
20330 
20331                   do_setcursor(cursor_hand);
20332                 }
20333               else if (event.button.x >= r_ttools.w && event.button.x < WINDOW_WIDTH - r_ttoolopt.w &&
20334                        event.button.y > img_scroll_up->h &&
20335                        event.button.y < (button_h * buttons_tall + r_ttools.h) - button_h &&
20336                        ((((event.button.x - r_ttools.w) / (THUMB_W) +
20337                           (((event.button.y - img_scroll_up->h) / THUMB_H) * 4)) + cur) < num_files))
20338                 {
20339                   /* One of the thumbnails: */
20340 
20341                   do_setcursor(cursor_hand);
20342                 }
20343               else
20344                 {
20345                   /* Unclickable... */
20346 
20347                   do_setcursor(cursor_arrow);
20348                 }
20349               oldpos_x = event.button.x;
20350               oldpos_y = event.button.y;
20351             }
20352 
20353           else if (event.type == SDL_JOYAXISMOTION)
20354             handle_joyaxismotion(event, &motioner, &val_x, &val_y);
20355 
20356           else if (event.type == SDL_JOYHATMOTION)
20357             handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
20358 
20359           else if (event.type == SDL_JOYBALLMOTION)
20360             handle_joyballmotion(event, oldpos_x, oldpos_y);
20361 
20362           else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
20363             handle_joybuttonupdown(event, oldpos_x, oldpos_y);
20364         }
20365 
20366       if (motioner | hatmotioner)
20367         handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x, valhat_y);
20368 
20369       SDL_Delay(10);
20370     }
20371   while (!done);
20372 
20373 
20374   /* Load the chosen starter, or start with a blank solid color: */
20375 
20376   if (which != -1)
20377     {
20378       /* Save old one first? */
20379 
20380       if (!been_saved && !disable_save)
20381         {
20382           if (do_prompt_image_snd(PROMPT_OPEN_SAVE_TXT,
20383                                   PROMPT_OPEN_SAVE_YES,
20384                                   PROMPT_OPEN_SAVE_NO,
20385                                   img_tools[TOOL_SAVE], NULL, NULL, SND_AREYOUSURE, screen->w / 2, screen->h / 2))
20386             {
20387               do_save(TOOL_NEW, 1);
20388             }
20389         }
20390 
20391       /* Clear label surface */
20392 
20393       SDL_FillRect(label, NULL, SDL_MapRGBA(label->format, 0, 0, 0, 0));
20394 
20395       /* Clear all info related to label surface */
20396 
20397       delete_label_list(&start_label_node);
20398       start_label_node = current_label_node = first_label_node_in_redo_stack = highlighted_label_node =
20399         label_node_to_edit = NULL;
20400       have_to_rec_label_node = FALSE;
20401 
20402       if (which >= first_starter && (first_template == -1 || which < first_template)
20403           && (!new_colors_last || which < first_color))
20404         {
20405           /* Load a starter: */
20406 
20407           /* Figure out filename: */
20408 
20409           safe_snprintf(fname, sizeof(fname), "%s/%s%s", dirname[d_places[which]], d_names[which], d_exts[which]);
20410 
20411           img = myIMG_Load(fname);
20412 
20413           if (img == NULL)
20414             {
20415               fprintf(stderr,
20416                       "\nWarning: Couldn't load the saved image! (3)\n"
20417                       "%s\n"
20418                       "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", fname, SDL_GetError());
20419 
20420               do_prompt(PROMPT_OPEN_UNOPENABLE_TXT, PROMPT_OPEN_UNOPENABLE_YES, "", 0, 0);
20421             }
20422           else
20423             {
20424               free_surface(&img_starter);
20425               free_surface(&img_starter_bkgd);
20426               starter_mirrored = 0;
20427               starter_flipped = 0;
20428               starter_personal = 0;
20429               starter_modified = 0;
20430 
20431               autoscale_copy_smear_free(img, canvas, SDL_BlitSurface);
20432 
20433               cur_undo = 0;
20434               oldest_undo = 0;
20435               newest_undo = 0;
20436 
20437               /* Immutable 'starter' image;
20438                  we'll need to save a new image when saving...: */
20439 
20440               been_saved = 1;
20441 
20442               file_id[0] = '\0';
20443               safe_strncpy(starter_id, d_names[which], sizeof(starter_id));
20444               template_id[0] = '\0';
20445 
20446               if (d_places[which] == PLACE_PERSONAL_STARTERS_DIR)
20447                 starter_personal = 1;
20448               else
20449                 starter_personal = 0;
20450 
20451               load_starter(starter_id);
20452 
20453               canvas_color_r = 255;
20454               canvas_color_g = 255;
20455               canvas_color_b = 255;
20456 
20457               SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
20458               SDL_BlitSurface(img_starter_bkgd, NULL, canvas, NULL);
20459               SDL_BlitSurface(img_starter, NULL, canvas, NULL);
20460             }
20461         }
20462       else if (first_template != -1 && which >= first_template && (!new_colors_last || which < first_color))
20463         {
20464           /* Load a template: */
20465 
20466           /* Figure out filename: */
20467 
20468           safe_snprintf(fname, sizeof(fname), "%s/%s%s", dirname[d_places[which]], d_names[which], d_exts[which]);
20469           img = myIMG_Load(fname);
20470 
20471           if (img == NULL)
20472             {
20473               fprintf(stderr,
20474                       "\nWarning: Couldn't load the saved image! (4)\n"
20475                       "%s\n"
20476                       "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", fname, SDL_GetError());
20477 
20478               do_prompt(PROMPT_OPEN_UNOPENABLE_TXT, PROMPT_OPEN_UNOPENABLE_YES, "", 0, 0);
20479             }
20480           else
20481             {
20482               free_surface(&img_starter);
20483               free_surface(&img_starter_bkgd);
20484               template_personal = 0;
20485 
20486               autoscale_copy_smear_free(img, canvas, SDL_BlitSurface);
20487 
20488               cur_undo = 0;
20489               oldest_undo = 0;
20490               newest_undo = 0;
20491 
20492               /* Immutable 'template' image;
20493                  we'll need to save a new image when saving...: */
20494 
20495               been_saved = 1;
20496 
20497               file_id[0] = '\0';
20498               safe_strncpy(template_id, d_names[which], sizeof(template_id));
20499               starter_id[0] = '\0';
20500 
20501               if (d_places[which] == PLACE_PERSONAL_TEMPLATES_DIR)
20502                 template_personal = 1;
20503               else
20504                 template_personal = 0;
20505 
20506               load_template(template_id);
20507 
20508               canvas_color_r = 255;
20509               canvas_color_g = 255;
20510               canvas_color_b = 255;
20511 
20512               SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
20513               SDL_BlitSurface(img_starter_bkgd, NULL, canvas, NULL);
20514             }
20515         }
20516       else
20517         {
20518           /* A color! */
20519 
20520           free_surface(&img_starter);
20521           free_surface(&img_starter_bkgd);
20522           starter_mirrored = 0;
20523           starter_flipped = 0;
20524           starter_personal = 0;
20525           starter_modified = 0;
20526 
20527           which = which - first_color;
20528 
20529           /* Launch color picker if they chose that: */
20530 
20531           if (which == NUM_COLORS - 1)
20532             {
20533               if (do_color_picker() == 0)
20534                 return (0);
20535             }
20536 
20537           /* FIXME: Don't do anything and go back to Open dialog if they
20538              hit BACK in color picker! */
20539 
20540           if (which == 0)       /* White */
20541             {
20542               canvas_color_r = canvas_color_g = canvas_color_b = 255;
20543             }
20544           else if (which <= white_in_palette)   /* One of the colors before white in the pallete */
20545             {
20546               canvas_color_r = color_hexes[which - 1][0];
20547               canvas_color_g = color_hexes[which - 1][1];
20548               canvas_color_b = color_hexes[which - 1][2];
20549             }
20550           else
20551             {
20552               canvas_color_r = color_hexes[which][0];
20553               canvas_color_g = color_hexes[which][1];
20554               canvas_color_b = color_hexes[which][2];
20555             }
20556 
20557           SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, canvas_color_r, canvas_color_g, canvas_color_b));
20558 
20559           cur_undo = 0;
20560           oldest_undo = 0;
20561           newest_undo = 0;
20562 
20563           been_saved = 1;
20564           reset_avail_tools();
20565 
20566           tool_avail_bak[TOOL_UNDO] = 0;
20567           tool_avail_bak[TOOL_REDO] = 0;
20568 
20569           file_id[0] = '\0';
20570           starter_id[0] = '\0';
20571 
20572           playsound(screen, 1, SND_HARP, 1, SNDPOS_CENTER, SNDDIST_NEAR);
20573         }
20574     }
20575 
20576   update_canvas(0, 0, WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w, button_h * buttons_tall + r_ttools.h);
20577 
20578 
20579   /* Clean up: */
20580 
20581   free_surface_array(thumbs, num_files);
20582 
20583   free(thumbs);
20584 
20585   for (i = 0; i < num_files; i++)
20586     {
20587       if (d_names[i] != NULL)
20588         free(d_names[i]);
20589       if (d_exts[i] != NULL)
20590         free(d_exts[i]);
20591     }
20592 
20593   for (i = 0; i < NUM_PLACES_TO_LOOK; i++)
20594     if (dirname[i] != NULL)
20595       free(dirname[i]);
20596 
20597   free(d_names);
20598   free(d_exts);
20599   free(d_places);
20600 
20601   return (which != -1);
20602 }
20603 
20604 /* Add colors to the "New" dialog's list of choices;
20605    normally appears at the beginning (above Starts & Templates),
20606    but may be placed at the end with the "--newcolorslast" option.
20607 */
do_new_dialog_add_colors(SDL_Surface ** thumbs,int num_files,int * d_places,char ** d_names,char ** d_exts,int * white_in_palette)20608 static int do_new_dialog_add_colors(SDL_Surface * *thumbs, int num_files, int *d_places, char * *d_names,
20609                                     char * *d_exts, int *white_in_palette)
20610 {
20611   int j;
20612   int added;
20613   Uint8 r, g, b;
20614 
20615   for (j = -1; j < NUM_COLORS; j++)
20616     {
20617       added = 0;
20618 
20619       if (j < NUM_COLORS - 1)
20620         {
20621           if (j == -1 ||        /* (short circuit) */
20622               color_hexes[j][0] != 255 ||       /* Ignore white, we'll have already added it */
20623               color_hexes[j][1] != 255 || color_hexes[j][2] != 255)
20624             {
20625               /* Palette colors: */
20626 
20627               thumbs[num_files] = SDL_CreateRGBSurface(screen->flags,
20628                                                        THUMB_W - 20, THUMB_H - 20,
20629                                                        screen->format->BitsPerPixel,
20630                                                        screen->format->Rmask,
20631                                                        screen->format->Gmask, screen->format->Bmask, 0);
20632 
20633               if (thumbs[num_files] != NULL)
20634                 {
20635                   if (j == -1)
20636                     {
20637                       r = g = b = 255;  /* White */
20638                     }
20639                   else
20640                     {
20641                       r = color_hexes[j][0];
20642                       g = color_hexes[j][1];
20643                       b = color_hexes[j][2];
20644                     }
20645                   SDL_FillRect(thumbs[num_files], NULL, SDL_MapRGB(thumbs[num_files]->format, r, g, b));
20646                   added = 1;
20647                 }
20648             }
20649           else
20650             {
20651               *white_in_palette = j;
20652             }
20653         }
20654       else
20655         {
20656           /* Color picker: */
20657 
20658           thumbs[num_files] = thumbnail(img_color_picker, THUMB_W - 20, THUMB_H - 20, 0);
20659           added = 1;
20660         }
20661 
20662       if (added)
20663         {
20664           d_places[num_files] = PLACE_COLOR_PALETTE;
20665           d_names[num_files] = NULL;
20666           d_exts[num_files] = NULL;
20667 
20668           num_files++;
20669         }
20670     }
20671 
20672   return num_files;
20673 }
20674 
20675 
20676 /**
20677  * FIXME
20678  */
20679 /* FIXME: Use a bitmask! */
reset_touched(void)20680 static void reset_touched(void)
20681 {
20682   int x, y;
20683 
20684   for (y = 0; y < canvas->h; y++)
20685     {
20686       for (x = 0; x < canvas->w; x++)
20687         {
20688           touched[(y * canvas->w) + x] = 0;
20689         }
20690     }
20691 }
20692 
20693 /**
20694  * FIXME
20695  */
magic_touched(int x,int y)20696 static Uint8 magic_touched(int x, int y)
20697 {
20698   Uint8 res;
20699 
20700   if (x < 0 || x >= canvas->w || y < 0 || y >= canvas->h)
20701     return (1);
20702 
20703   res = touched[(y * canvas->w) + x];
20704   touched[(y * canvas->w) + x] = 1;
20705 
20706   return (res);
20707 }
20708 
20709 /**
20710  * FIXME
20711  */
do_color_sel(void)20712 static int do_color_sel(void)
20713 {
20714 #ifndef NO_PROMPT_SHADOWS
20715   SDL_Surface *alpha_surf;
20716 #endif
20717   SDL_Rect dest;
20718   int x, y, w;
20719   int ox, oy;
20720   int val_x, val_y, motioner;
20721   int valhat_x, valhat_y, hatmotioner;
20722   int stepx, stepy;
20723   int number_of_steps = 20;
20724   int i, dx, dy;
20725   int done, chose;
20726   int back_left, back_top;
20727   int color_sel_x = 0, color_sel_y = 0;
20728   int want_animated_popups = 1;
20729   SDL_Surface *tmp_btn_up, *tmp_btn_down;
20730 
20731   Uint32(*getpixel_tmp_btn_up) (SDL_Surface *, int, int);
20732   Uint32(*getpixel_tmp_btn_down) (SDL_Surface *, int, int);
20733   Uint32(*getpixel_img_paintwell) (SDL_Surface *, int, int);
20734   Uint32(*getpixel_img_color_picker) (SDL_Surface *, int, int);
20735   Uint8 r, g, b;
20736   double rh, gh, bh;
20737   SDL_Event event;
20738   SDLKey key;
20739   SDL_Rect r_color_sel;
20740   SDL_Rect color_example_dest;
20741   SDL_Surface *backup;
20742   SDL_Rect r_color_picker;
20743 
20744   val_x = val_y = motioner = 0;
20745   valhat_x = valhat_y = hatmotioner = 0;
20746 
20747   /* FIXME this is the first step to make animated popups optional,
20748      to be removed from here when implemented in a more general way */
20749 
20750   hide_blinking_cursor();
20751 
20752   do_setcursor(cursor_hand);
20753 
20754 
20755   /* Draw button box: */
20756 
20757   playsound(screen, 0, SND_PROMPT, 1, SNDPOS_RIGHT, 128);
20758 
20759   backup = SDL_CreateRGBSurface(screen->flags, screen->w, screen->h,
20760                                 screen->format->BitsPerPixel,
20761                                 screen->format->Rmask,
20762                                 screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
20763 
20764   SDL_BlitSurface(screen, NULL, backup, NULL);
20765 
20766   r_color_sel.x = r_canvas.x;
20767   r_color_sel.y = r_canvas.h;
20768   r_color_sel.w = r_canvas.w;
20769   r_color_sel.h = button_w;
20770 
20771 
20772   if (want_animated_popups)
20773     {
20774       /* center of button */
20775       ox = WINDOW_WIDTH - color_button_w - color_button_w / 2;
20776       oy = r_colors.y + r_colors.h / 2;
20777       dx = 0;
20778       dy = 0;
20779       w = 0;
20780       stepx = (r_color_sel.x - ox) / number_of_steps;
20781       stepy = (r_color_sel.y - oy) / number_of_steps;
20782 
20783       for (i = 0; i < number_of_steps; i++)
20784         {
20785           w = w + (128 + 6 + 4) / number_of_steps;
20786           dx = i * stepx;
20787           dy = i * stepy;
20788 
20789 
20790           dest.x = ox + dx;
20791           dest.y = oy + dy;
20792 
20793           dest.w = i * r_color_sel.w / number_of_steps;
20794           dest.h = i * r_color_sel.h / number_of_steps;
20795 
20796           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255 - w, 255 - w, 255 - w));
20797           SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
20798           SDL_Delay(2);
20799         }
20800 
20801       SDL_BlitSurface(backup, NULL, screen, NULL);
20802     }
20803 
20804 
20805 #ifndef NO_PROMPT_SHADOWS
20806   alpha_surf = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
20807                                     r_color_sel.w,
20808                                     r_color_sel.h,
20809                                     screen->format->BitsPerPixel,
20810                                     screen->format->Rmask,
20811                                     screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
20812 
20813   if (alpha_surf != NULL)
20814     {
20815       SDL_FillRect(alpha_surf, NULL, SDL_MapRGB(alpha_surf->format, 0, 0, 0));
20816       SDL_SetAlpha(alpha_surf, SDL_SRCALPHA, 64);
20817 
20818       for (i = 8; i > 0; i = i - 2)
20819         {
20820           dest.x = r_color_sel.x + i;
20821           dest.y = r_color_sel.y + i;
20822           dest.w = r_color_sel.w;
20823           dest.h = r_color_sel.h;
20824 
20825           SDL_BlitSurface(alpha_surf, NULL, screen, &dest);
20826         }
20827 
20828       SDL_FreeSurface(alpha_surf);
20829     }
20830 #endif
20831 
20832 
20833   /* Draw prompt box: */
20834 
20835   SDL_FillRect(screen, &r_color_sel, SDL_MapRGB(screen->format, 255, 255, 255));
20836 
20837 
20838 
20839   /* Determine spot for example color: */
20840 
20841 
20842   color_example_dest.x = r_color_sel.x + 2;
20843   color_example_dest.y = r_color_sel.y + 2;
20844   color_example_dest.w = r_color_sel.w - button_w - 8;
20845   color_example_dest.h = r_color_sel.h - 4;
20846 
20847 
20848   SDL_FillRect(screen, &color_example_dest, SDL_MapRGB(screen->format, 0, 0, 0));
20849 
20850   color_example_dest.x += 2;
20851   color_example_dest.y += 2;
20852   color_example_dest.w -= 4;
20853   color_example_dest.h -= 4;
20854 
20855   SDL_FillRect(screen, &color_example_dest, SDL_MapRGB(screen->format, 255, 255, 255));
20856 
20857   color_example_dest.x += 2;
20858   color_example_dest.y += 2;
20859   color_example_dest.w -= 4;
20860   color_example_dest.h -= 4;
20861 
20862 
20863 
20864   /* Draw current color picker color: */
20865 
20866   SDL_FillRect(screen, &color_example_dest,
20867                SDL_MapRGB(screen->format,
20868                           color_hexes[NUM_COLORS - 2][0],
20869                           color_hexes[NUM_COLORS - 2][1], color_hexes[NUM_COLORS - 2][2]));
20870 
20871 
20872 
20873   /* Show "Back" button */
20874 
20875   back_left = r_color_sel.x + r_color_sel.w - button_w - 4;
20876   back_top = r_color_sel.y;
20877 
20878   dest.x = back_left;
20879   dest.y = back_top;
20880 
20881   SDL_BlitSurface(img_back, NULL, screen, &dest);
20882 
20883   dest.x = back_left + (img_back->w - img_openlabels_back->w) / 2;
20884   dest.y = back_top + img_back->h - img_openlabels_back->h;
20885   SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
20886 
20887 
20888 
20889   /* Blit canvas on screen */
20890   SDL_BlitSurface(canvas, NULL, screen, &r_canvas);
20891 
20892   SDL_Flip(screen);
20893 
20894 
20895   /* Let the user pick a color, or go back: */
20896 
20897   done = 0;
20898   chose = 0;
20899   x = y = 0;
20900   SDL_WarpMouse(r_color_sel.x + r_color_sel.w / 2, r_color_sel.y + r_color_sel.h / 2);
20901 
20902   do
20903     {
20904       while (SDL_PollEvent(&event))
20905         {
20906           if (event.type == SDL_QUIT)
20907             {
20908               chose = 0;
20909               done = 1;
20910             }
20911           else if (event.type == SDL_ACTIVEEVENT)
20912             {
20913               handle_active(&event);
20914             }
20915           else if (event.type == SDL_KEYUP)
20916             {
20917               key = event.key.keysym.sym;
20918 
20919               handle_keymouse(key, SDL_KEYUP, 24, NULL, NULL);
20920             }
20921           else if (event.type == SDL_KEYDOWN)
20922             {
20923               key = event.key.keysym.sym;
20924 
20925               handle_keymouse(key, SDL_KEYDOWN, 24, &r_color_picker, NULL);
20926 
20927               if (key == SDLK_ESCAPE)
20928                 {
20929                   chose = 0;
20930                   done = 1;
20931                 }
20932             }
20933           else if (event.type == SDL_MOUSEBUTTONUP && valid_click(event.button.button))
20934             {
20935               if (event.button.x >= r_canvas.x &&
20936                   event.button.x < r_canvas.x + r_canvas.w &&
20937                   event.button.y >= r_canvas.y && event.button.y < r_canvas.y + r_canvas.h)
20938                 {
20939                   /* Picked a color! */
20940 
20941                   chose = 1;
20942                   done = 1;
20943 
20944                   x = event.button.x - r_canvas.x;
20945                   y = event.button.y - r_canvas.y;
20946 
20947                   color_sel_x = x;
20948                   color_sel_y = y;
20949                 }
20950               else if (event.button.x >= back_left &&
20951                        event.button.x < back_left + img_back->w &&
20952                        event.button.y >= back_top && event.button.y < back_top + img_back->h)
20953                 {
20954                   /* Decided to go Back */
20955 
20956                   chose = 0;
20957                   done = 1;
20958                 }
20959             }
20960           else if (event.type == SDL_MOUSEMOTION)
20961             {
20962               if (event.button.x >= r_canvas.x &&
20963                   event.button.x < r_canvas.x + r_canvas.w &&
20964                   event.button.y >= r_canvas.y && event.button.y < r_canvas.y + r_canvas.h)
20965                 {
20966                   /* Hovering over the colors! */
20967 
20968                   do_setcursor(cursor_hand);
20969 
20970 
20971                   /* Show a big solid example of the color: */
20972 
20973                   x = event.button.x - r_canvas.x;
20974                   y = event.button.y - r_canvas.y;
20975 
20976                   getpixel_img_color_picker = getpixels[canvas->format->BytesPerPixel];
20977                   SDL_GetRGB(getpixel_img_color_picker(canvas, x, y), canvas->format, &r, &g, &b);
20978 
20979                   SDL_FillRect(screen, &color_example_dest, SDL_MapRGB(screen->format, r, g, b));
20980 
20981                   SDL_UpdateRect(screen,
20982                                  color_example_dest.x,
20983                                  color_example_dest.y, color_example_dest.w, color_example_dest.h);
20984                 }
20985               else
20986                 {
20987                   /* Revert to current color picker color, so we know what it was,
20988                      and what we'll get if we go Back: */
20989 
20990                   SDL_FillRect(screen, &color_example_dest,
20991                                SDL_MapRGB(screen->format,
20992                                           color_hexes[NUM_COLORS - 2][0],
20993                                           color_hexes[NUM_COLORS - 2][1], color_hexes[NUM_COLORS - 2][2]));
20994 
20995                   SDL_UpdateRect(screen,
20996                                  color_example_dest.x,
20997                                  color_example_dest.y, color_example_dest.w, color_example_dest.h);
20998 
20999 
21000                   /* Change cursor to arrow (or hand, if over Back): */
21001 
21002                   if (event.button.x >= back_left &&
21003                       event.button.x < back_left + img_back->w &&
21004                       event.button.y >= back_top && event.button.y < back_top + img_back->h)
21005                     do_setcursor(cursor_hand);
21006                   else
21007                     do_setcursor(cursor_arrow);
21008                 }
21009 
21010               oldpos_x = event.motion.x;
21011               oldpos_y = event.motion.y;
21012             }
21013           else if (event.type == SDL_JOYAXISMOTION)
21014             handle_joyaxismotion(event, &motioner, &val_x, &val_y);
21015 
21016           else if (event.type == SDL_JOYHATMOTION)
21017             handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
21018 
21019           else if (event.type == SDL_JOYBALLMOTION)
21020             handle_joyballmotion(event, oldpos_x, oldpos_y);
21021 
21022           else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
21023             handle_joybuttonupdown(event, oldpos_x, oldpos_y);
21024         }
21025 
21026       if (motioner | hatmotioner)
21027         handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x, valhat_y);
21028 
21029       SDL_Delay(10);
21030     }
21031   while (!done);
21032 
21033 
21034   /* Set the new color: */
21035 
21036   if (chose)
21037     {
21038       getpixel_img_color_picker = getpixels[canvas->format->BytesPerPixel];
21039       SDL_GetRGB(getpixel_img_color_picker(canvas, color_sel_x, color_sel_y), canvas->format, &r, &g, &b);
21040 
21041       color_hexes[NUM_COLORS - 2][0] = r;
21042       color_hexes[NUM_COLORS - 2][1] = g;
21043       color_hexes[NUM_COLORS - 2][2] = b;
21044 
21045 
21046       /* Re-render color picker to show the current color it contains: */
21047 
21048       tmp_btn_up = thumbnail(img_btn_up, color_button_w, color_button_h, 0);
21049       tmp_btn_down = thumbnail(img_btn_down, color_button_w, color_button_h, 0);
21050       img_color_btn_off = thumbnail(img_btn_off, color_button_w, color_button_h, 0);
21051 
21052       getpixel_tmp_btn_up = getpixels[tmp_btn_up->format->BytesPerPixel];
21053       getpixel_tmp_btn_down = getpixels[tmp_btn_down->format->BytesPerPixel];
21054       getpixel_img_paintwell = getpixels[img_paintwell->format->BytesPerPixel];
21055 
21056       rh = sRGB_to_linear_table[color_hexes[NUM_COLORS - 2][0]];
21057       gh = sRGB_to_linear_table[color_hexes[NUM_COLORS - 2][1]];
21058       bh = sRGB_to_linear_table[color_hexes[NUM_COLORS - 2][2]];
21059 
21060 
21061 
21062       SDL_LockSurface(img_color_btns[NUM_COLORS - 2]);
21063       SDL_LockSurface(img_color_btns[NUM_COLORS - 2 + NUM_COLORS]);
21064 
21065       for (y = 0; y < tmp_btn_up->h /* 48 */ ; y++)
21066         {
21067           for (x = 0; x < tmp_btn_up->w; x++)
21068             {
21069               double ru, gu, bu, rd, gd, bd, aa;
21070               Uint8 a;
21071 
21072               SDL_GetRGB(getpixel_tmp_btn_up(tmp_btn_up, x, y), tmp_btn_up->format, &r, &g, &b);
21073 
21074               ru = sRGB_to_linear_table[r];
21075               gu = sRGB_to_linear_table[g];
21076               bu = sRGB_to_linear_table[b];
21077               SDL_GetRGB(getpixel_tmp_btn_down(tmp_btn_down, x, y), tmp_btn_down->format, &r, &g, &b);
21078 
21079               rd = sRGB_to_linear_table[r];
21080               gd = sRGB_to_linear_table[g];
21081               bd = sRGB_to_linear_table[b];
21082               SDL_GetRGBA(getpixel_img_paintwell(img_paintwell, x, y), img_paintwell->format, &r, &g, &b, &a);
21083 
21084               aa = a / 255.0;
21085 
21086               if (a == 255)
21087                 {
21088                   putpixels[img_color_btns[NUM_COLORS - 2]->format->BytesPerPixel]
21089                     (img_color_btns[NUM_COLORS - 2], x, y,
21090                      SDL_MapRGB(img_color_btns[i]->format,
21091                                 linear_to_sRGB(rh * aa + ru * (1.0 - aa)),
21092                                 linear_to_sRGB(gh * aa + gu * (1.0 - aa)), linear_to_sRGB(bh * aa + bu * (1.0 - aa))));
21093 
21094                   putpixels[img_color_btns[NUM_COLORS - 2 + NUM_COLORS]->format->BytesPerPixel]
21095                     (img_color_btns[NUM_COLORS - 2 + NUM_COLORS], x, y,
21096                      SDL_MapRGB(img_color_btns[i + NUM_COLORS]->format,
21097                                 linear_to_sRGB(rh * aa + rd * (1.0 - aa)),
21098                                 linear_to_sRGB(gh * aa + gd * (1.0 - aa)), linear_to_sRGB(bh * aa + bd * (1.0 - aa))));
21099                 }
21100             }
21101         }
21102 
21103       SDL_UnlockSurface(img_color_btns[NUM_COLORS - 2]);
21104       SDL_UnlockSurface(img_color_btns[NUM_COLORS - 2 + NUM_COLORS]);
21105 
21106       dest.x = (img_color_btns[NUM_COLORS - 2]->w - img_color_sel->w) / 2;
21107       dest.y = (img_color_btns[NUM_COLORS - 2]->h - img_color_sel->h) / 2;
21108       dest.w = img_color_sel->w;
21109       dest.h = img_color_sel->h;
21110       SDL_BlitSurface(img_color_sel, NULL, img_color_btns[NUM_COLORS - 2], &dest);
21111 
21112       dest.x = (img_color_btns[NUM_COLORS - 2 + NUM_COLORS]->w - img_color_sel->w) / 2;
21113       dest.y = (img_color_btns[NUM_COLORS - 2 + NUM_COLORS]->h - img_color_sel->h) / 2;
21114       SDL_BlitSurface(img_color_sel, NULL, img_color_btns[NUM_COLORS - 2 + NUM_COLORS], &dest);
21115     }
21116 
21117   return (chose);
21118 }
21119 
21120 /**
21121  * FIXME
21122  */
do_color_picker(void)21123 static int do_color_picker(void)
21124 {
21125 #ifndef NO_PROMPT_SHADOWS
21126   int i;
21127   SDL_Surface *alpha_surf;
21128 #endif
21129   SDL_Rect dest;
21130   int x, y, w;
21131   int ox, oy, oox, ooy, nx, ny;
21132   int val_x, val_y, motioner;
21133   int valhat_x, valhat_y, hatmotioner;
21134   SDL_Surface *tmp_btn_up, *tmp_btn_down;
21135   int stop;
21136 
21137   Uint32(*getpixel_tmp_btn_up) (SDL_Surface *, int, int);
21138   Uint32(*getpixel_tmp_btn_down) (SDL_Surface *, int, int);
21139   Uint32(*getpixel_img_paintwell) (SDL_Surface *, int, int);
21140   Uint32(*getpixel_img_color_picker) (SDL_Surface *, int, int);
21141   Uint8 r, g, b;
21142   double rh, gh, bh;
21143   int done, chose;
21144   SDL_Event event;
21145   SDLKey key;
21146   int color_picker_left, color_picker_top, color_picker_width, color_picker_height;
21147   int back_left, back_top;
21148   SDL_Rect color_example_dest;
21149   SDL_Surface *backup;
21150   SDL_Rect r_color_picker;
21151   SDL_Rect r_final;
21152   val_x = val_y = motioner = 0;
21153   valhat_x = valhat_y = hatmotioner = 0;
21154   hide_blinking_cursor();
21155 
21156   do_setcursor(cursor_hand);
21157 
21158 
21159   /* Draw button box: */
21160 
21161   playsound(screen, 0, SND_PROMPT, 1, SNDPOS_RIGHT, 128);
21162 
21163   backup = SDL_CreateRGBSurface(screen->flags, screen->w, screen->h,
21164                                 screen->format->BitsPerPixel,
21165                                 screen->format->Rmask,
21166                                 screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
21167 
21168   SDL_BlitSurface(screen, NULL, backup, NULL);
21169 
21170   ox = screen->w - color_button_w / 2;
21171   oy = r_colors.y + r_colors.h / 2;
21172 
21173   r_final.x = r_canvas.x + r_canvas.w / 2 - img_color_picker->w - 4;
21174   r_final.y = r_canvas.h / 2 - img_color_picker->h / 2 - 2;
21175   r_final.w = img_color_picker->w * 2;
21176   r_final.h = img_color_picker->h;
21177 
21178   stop = r_final.h / 2 + 6 + 4;
21179 
21180   for (w = 0; w <= stop; w = w + 4)
21181     {
21182       nx = PROMPT_LEFT + r_tools.w - w + PROMPTOFFSETX;
21183       ny = 2 + canvas->h / 2 - w;
21184 
21185       dest.x = ox - ((ox -r_final.x) * w) / stop;
21186       dest.y = oy - ((oy -r_final.y) * w) / stop;
21187       dest.w = w * 4;
21188       dest.h = w * 2;
21189 
21190       SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255 - (int)(w / button_scale) , 255 -(int)( w / button_scale), 255 - (int)(w / button_scale)));
21191 
21192       SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
21193       if (w % 16 == 0)
21194         SDL_Delay(1);
21195     }
21196 
21197   SDL_BlitSurface(backup, NULL, screen, NULL);
21198 
21199 #ifndef NO_PROMPT_SHADOWS
21200   alpha_surf = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
21201                                     r_final.w + 8,
21202                                     r_final.h + 16,
21203                                     screen->format->BitsPerPixel,
21204                                     screen->format->Rmask,
21205                                     screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
21206 
21207   if (alpha_surf != NULL)
21208     {
21209       SDL_FillRect(alpha_surf, NULL, SDL_MapRGB(alpha_surf->format, 0, 0, 0));
21210       SDL_SetAlpha(alpha_surf, SDL_SRCALPHA, 64);
21211 
21212       for (i = 8; i > 0; i = i - 2)
21213         {
21214           dest.x = r_final.x + i - 4;
21215 	  dest.y = r_final.y + i - 4;
21216           dest.w = r_final.w + 8;
21217           dest.h = r_final.h + 16;
21218 
21219           SDL_BlitSurface(alpha_surf, NULL, screen, &dest);
21220         }
21221 
21222       SDL_FreeSurface(alpha_surf);
21223     }
21224 #endif
21225 
21226 
21227   /* Draw prompt box: */
21228 
21229   w = w - 6;
21230   dest.x = r_final.x - 2;
21231   dest.w = r_final.w + 4;
21232   dest.h = w * 2;
21233   dest.y = r_final.y - 2;
21234   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
21235 
21236 
21237   /* Draw color palette: */
21238 
21239   color_picker_left = r_final.x;
21240   color_picker_top = r_final.y;
21241 
21242   dest.x = color_picker_left;
21243   dest.y = color_picker_top;
21244 
21245   SDL_BlitSurface(img_color_picker, NULL, screen, &dest);
21246 
21247   r_color_picker.x = dest.x;
21248   r_color_picker.y = dest.y;
21249   r_color_picker.w = dest.w;
21250   r_color_picker.h = dest.h;
21251 
21252 
21253   /* Draw last color position: */
21254 
21255   dest.x = color_picker_x + color_picker_left - 3;
21256   dest.y = color_picker_y + color_picker_top - 1;
21257   dest.w = 7;
21258   dest.h = 3;
21259 
21260   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
21261 
21262   dest.x = color_picker_x + color_picker_left - 1;
21263   dest.y = color_picker_y + color_picker_top - 3;
21264   dest.w = 3;
21265   dest.h = 7;
21266 
21267   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
21268 
21269   dest.x = color_picker_x + color_picker_left - 2;
21270   dest.y = color_picker_y + color_picker_top;
21271   dest.w = 5;
21272   dest.h = 1;
21273 
21274   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
21275 
21276   dest.x = color_picker_x + color_picker_left;
21277   dest.y = color_picker_y + color_picker_top - 2;
21278   dest.w = 1;
21279   dest.h = 5;
21280 
21281   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
21282 
21283 
21284   /* Determine spot for example color: */
21285 
21286   color_example_dest.x = color_picker_left + img_color_picker->w + 2;
21287   color_example_dest.y = color_picker_top + 2;
21288   color_example_dest.w = r_final.w / 2 - 2;
21289   color_example_dest.h = r_final.h / 2 - 4;
21290 
21291 
21292   SDL_FillRect(screen, &color_example_dest, SDL_MapRGB(screen->format, 0, 0, 0));
21293 
21294   color_example_dest.x += 2;
21295   color_example_dest.y += 2;
21296   color_example_dest.w -= 4;
21297   color_example_dest.h -= 4;
21298 
21299   SDL_FillRect(screen, &color_example_dest, SDL_MapRGB(screen->format, 255, 255, 255));
21300 
21301   color_example_dest.x += 2;
21302   color_example_dest.y += 2;
21303   color_example_dest.w -= 4;
21304   color_example_dest.h -= 4;
21305 
21306 
21307   /* Draw current color picker color: */
21308 
21309   SDL_FillRect(screen, &color_example_dest,
21310                SDL_MapRGB(screen->format,
21311                           color_hexes[NUM_COLORS - 1][0],
21312                           color_hexes[NUM_COLORS - 1][1], color_hexes[NUM_COLORS - 1][2]));
21313 
21314 
21315 
21316   /* Show "Back" button */
21317 
21318   back_left =
21319     (((PROMPT_W - 96 * 2) + w * 2 - img_color_picker->w) - img_back->w) / 2 + color_picker_left + img_color_picker->w;
21320   back_top = color_picker_top + img_color_picker->h - img_back->h - 2;
21321 
21322   dest.x = back_left;
21323   dest.y = back_top;
21324 
21325   SDL_BlitSurface(img_back, NULL, screen, &dest);
21326 
21327   dest.x = back_left + (img_back->w - img_openlabels_back->w) / 2;
21328   dest.y = back_top + img_back->h - img_openlabels_back->h;
21329   SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
21330 
21331 
21332   SDL_Flip(screen);
21333 
21334 
21335   /* Let the user pick a color, or go back: */
21336 
21337   done = 0;
21338   chose = 0;
21339   x = y = 0;
21340   SDL_WarpMouse(back_left + button_w / 2, back_top - button_w / 2);
21341 
21342   do
21343     {
21344       while (SDL_PollEvent(&event))
21345         {
21346           if (event.type == SDL_QUIT)
21347             {
21348               chose = 0;
21349               done = 1;
21350             }
21351           else if (event.type == SDL_ACTIVEEVENT)
21352             {
21353               handle_active(&event);
21354             }
21355           else if (event.type == SDL_KEYUP)
21356             {
21357               key = event.key.keysym.sym;
21358 
21359               handle_keymouse(key, SDL_KEYUP, 24, NULL, NULL);
21360             }
21361           else if (event.type == SDL_KEYDOWN)
21362             {
21363               key = event.key.keysym.sym;
21364 
21365               handle_keymouse(key, SDL_KEYDOWN, 24, &r_color_picker, NULL);
21366 
21367               if (key == SDLK_ESCAPE)
21368                 {
21369                   chose = 0;
21370                   done = 1;
21371                 }
21372             }
21373           else if (event.type == SDL_MOUSEBUTTONUP && valid_click(event.button.button))
21374             {
21375               if (event.button.x >= color_picker_left &&
21376                   event.button.x < color_picker_left + img_color_picker->w &&
21377                   event.button.y >= color_picker_top && event.button.y < color_picker_top + img_color_picker->h)
21378                 {
21379                   /* Picked a color! */
21380 
21381                   chose = 1;
21382                   done = 1;
21383 
21384                   x = event.button.x - color_picker_left;
21385                   y = event.button.y - color_picker_top;
21386 
21387                   color_picker_x = x;
21388                   color_picker_y = y;
21389                 }
21390               else if (event.button.x >= back_left &&
21391                        event.button.x < back_left + img_back->w &&
21392                        event.button.y >= back_top && event.button.y < back_top + img_back->h)
21393                 {
21394                   /* Decided to go Back */
21395 
21396                   chose = 0;
21397                   done = 1;
21398                 }
21399             }
21400           else if (event.type == SDL_MOUSEMOTION)
21401             {
21402               if (event.button.x >= color_picker_left &&
21403                   event.button.x < color_picker_left + img_color_picker->w &&
21404                   event.button.y >= color_picker_top && event.button.y < color_picker_top + img_color_picker->h)
21405                 {
21406                   /* Hovering over the colors! */
21407 
21408                   do_setcursor(cursor_hand);
21409 
21410 
21411                   /* Show a big solid example of the color: */
21412 
21413                   x = event.button.x - color_picker_left;
21414                   y = event.button.y - color_picker_top;
21415 
21416                   getpixel_img_color_picker = getpixels[img_color_picker->format->BytesPerPixel];
21417                   SDL_GetRGB(getpixel_img_color_picker(img_color_picker, x, y), img_color_picker->format, &r, &g, &b);
21418 
21419                   SDL_FillRect(screen, &color_example_dest, SDL_MapRGB(screen->format, r, g, b));
21420 
21421                   SDL_UpdateRect(screen,
21422                                  color_example_dest.x,
21423                                  color_example_dest.y, color_example_dest.w, color_example_dest.h);
21424                 }
21425               else
21426                 {
21427                   /* Revert to current color picker color, so we know what it was,
21428                      and what we'll get if we go Back: */
21429 
21430                   SDL_FillRect(screen, &color_example_dest,
21431                                SDL_MapRGB(screen->format,
21432                                           color_hexes[NUM_COLORS - 1][0],
21433                                           color_hexes[NUM_COLORS - 1][1], color_hexes[NUM_COLORS - 1][2]));
21434 
21435                   SDL_UpdateRect(screen,
21436                                  color_example_dest.x,
21437                                  color_example_dest.y, color_example_dest.w, color_example_dest.h);
21438 
21439 
21440                   /* Change cursor to arrow (or hand, if over Back): */
21441 
21442                   if (event.button.x >= back_left &&
21443                       event.button.x < back_left + img_back->w &&
21444                       event.button.y >= back_top && event.button.y < back_top + img_back->h)
21445                     do_setcursor(cursor_hand);
21446                   else
21447                     do_setcursor(cursor_arrow);
21448                 }
21449 
21450               oldpos_x = event.motion.x;
21451               oldpos_y = event.motion.y;
21452             }
21453           else if (event.type == SDL_JOYAXISMOTION)
21454             handle_joyaxismotion(event, &motioner, &val_x, &val_y);
21455 
21456           else if (event.type == SDL_JOYHATMOTION)
21457             handle_joyhatmotion(event, oldpos_x, oldpos_y, &valhat_x, &valhat_y, &hatmotioner, &old_hat_ticks);
21458 
21459           else if (event.type == SDL_JOYBALLMOTION)
21460             handle_joyballmotion(event, oldpos_x, oldpos_y);
21461 
21462           else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP)
21463             handle_joybuttonupdown(event, oldpos_x, oldpos_y);
21464         }
21465 
21466       if (motioner | hatmotioner)
21467         handle_motioners(oldpos_x, oldpos_y, motioner, hatmotioner, old_hat_ticks, val_x, val_y, valhat_x, valhat_y);
21468 
21469       SDL_Delay(10);
21470     }
21471   while (!done);
21472 
21473 
21474   /* Set the new color: */
21475 
21476   if (chose)
21477     {
21478       getpixel_img_color_picker = getpixels[img_color_picker->format->BytesPerPixel];
21479       SDL_GetRGB(getpixel_img_color_picker(img_color_picker, x, y), img_color_picker->format, &r, &g, &b);
21480 
21481       color_hexes[NUM_COLORS - 1][0] = r;
21482       color_hexes[NUM_COLORS - 1][1] = g;
21483       color_hexes[NUM_COLORS - 1][2] = b;
21484 
21485 
21486       /* Re-render color picker to show the current color it contains: */
21487 
21488       tmp_btn_up = thumbnail(img_btn_up, color_button_w, color_button_h, 0);
21489       tmp_btn_down = thumbnail(img_btn_down, color_button_w, color_button_h, 0);
21490       img_color_btn_off = thumbnail(img_btn_off, color_button_w, color_button_h, 0);
21491 
21492       getpixel_tmp_btn_up = getpixels[tmp_btn_up->format->BytesPerPixel];
21493       getpixel_tmp_btn_down = getpixels[tmp_btn_down->format->BytesPerPixel];
21494       getpixel_img_paintwell = getpixels[img_paintwell->format->BytesPerPixel];
21495 
21496       rh = sRGB_to_linear_table[color_hexes[NUM_COLORS - 1][0]];
21497       gh = sRGB_to_linear_table[color_hexes[NUM_COLORS - 1][1]];
21498       bh = sRGB_to_linear_table[color_hexes[NUM_COLORS - 1][2]];
21499 
21500       SDL_LockSurface(img_color_btns[NUM_COLORS - 1]);
21501       SDL_LockSurface(img_color_btns[NUM_COLORS - 1 + NUM_COLORS]);
21502 
21503       for (y = 0; y < tmp_btn_up->h /* 48 */ ; y++)
21504         {
21505           for (x = 0; x < tmp_btn_up->w; x++)
21506             {
21507               double ru, gu, bu, rd, gd, bd, aa;
21508               Uint8 a;
21509 
21510               SDL_GetRGB(getpixel_tmp_btn_up(tmp_btn_up, x, y), tmp_btn_up->format, &r, &g, &b);
21511 
21512               ru = sRGB_to_linear_table[r];
21513               gu = sRGB_to_linear_table[g];
21514               bu = sRGB_to_linear_table[b];
21515               SDL_GetRGB(getpixel_tmp_btn_down(tmp_btn_down, x, y), tmp_btn_down->format, &r, &g, &b);
21516 
21517               rd = sRGB_to_linear_table[r];
21518               gd = sRGB_to_linear_table[g];
21519               bd = sRGB_to_linear_table[b];
21520               SDL_GetRGBA(getpixel_img_paintwell(img_paintwell, x, y), img_paintwell->format, &r, &g, &b, &a);
21521 
21522               aa = a / 255.0;
21523 
21524               putpixels[img_color_btns[NUM_COLORS - 1]->format->BytesPerPixel]
21525                 (img_color_btns[NUM_COLORS - 1], x, y,
21526                  getpixels[img_color_picker_thumb->format->BytesPerPixel] (img_color_picker_thumb, x, y));
21527               putpixels[img_color_btns[NUM_COLORS - 1 + NUM_COLORS]->format->BytesPerPixel]
21528                 (img_color_btns[NUM_COLORS - 1 + NUM_COLORS], x, y,
21529                  getpixels[img_color_picker_thumb->format->BytesPerPixel] (img_color_picker_thumb, x, y));
21530 
21531               if (a == 255)
21532                 {
21533                   putpixels[img_color_btns[NUM_COLORS - 1]->format->BytesPerPixel]
21534                     (img_color_btns[NUM_COLORS - 1], x, y,
21535                      SDL_MapRGB(img_color_btns[i]->format,
21536                                 linear_to_sRGB(rh * aa + ru * (1.0 - aa)),
21537                                 linear_to_sRGB(gh * aa + gu * (1.0 - aa)), linear_to_sRGB(bh * aa + bu * (1.0 - aa))));
21538 
21539                   putpixels[img_color_btns[NUM_COLORS - 1 + NUM_COLORS]->format->BytesPerPixel]
21540                     (img_color_btns[NUM_COLORS - 1 + NUM_COLORS], x, y,
21541                      SDL_MapRGB(img_color_btns[i + NUM_COLORS]->format,
21542                                 linear_to_sRGB(rh * aa + rd * (1.0 - aa)),
21543                                 linear_to_sRGB(gh * aa + gd * (1.0 - aa)), linear_to_sRGB(bh * aa + bd * (1.0 - aa))));
21544                 }
21545             }
21546         }
21547 
21548       SDL_UnlockSurface(img_color_btns[NUM_COLORS - 1]);
21549       SDL_UnlockSurface(img_color_btns[NUM_COLORS - 1 + NUM_COLORS]);
21550     }
21551 
21552 
21553   /* Remove the prompt: */
21554 
21555   update_canvas(0, 0, canvas->w, canvas->h);
21556 
21557 
21558   return (chose);
21559 }
21560 
21561 
21562 /**
21563  * FIXME
21564  */
magic_putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel)21565 static void magic_putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
21566 {
21567   putpixels[surface->format->BytesPerPixel] (surface, x, y, pixel);
21568 }
21569 
21570 /**
21571  * FIXME
21572  */
magic_getpixel(SDL_Surface * surface,int x,int y)21573 static Uint32 magic_getpixel(SDL_Surface * surface, int x, int y)
21574 {
21575   return (getpixels[surface->format->BytesPerPixel] (surface, x, y));
21576 }
21577 
21578 /**
21579  * FIXME
21580  */
magic_xorpixel(SDL_Surface * surface,int x,int y)21581 static void magic_xorpixel(SDL_Surface * surface, int x, int y)
21582 {
21583   _xorpixel(surface, x, y);
21584 }
21585 
21586 
21587 /**
21588  * FIXME
21589  */
magic_switchout(SDL_Surface * last)21590 static void magic_switchout(SDL_Surface * last)
21591 {
21592   int was_clicking = 0;
21593 
21594   if (mouseaccessibility && emulate_button_pressed)
21595     {
21596       /* We were 'clicking' in mouse accessibility mode; stop clicking now */
21597       /* (EVEN if we weren't in magic tool) */
21598       emulate_button_pressed = 0;
21599       was_clicking = 1;
21600     }
21601 
21602   if (cur_tool == TOOL_MAGIC)
21603     {
21604       magic_funcs[magics[cur_magic].handle_idx].switchout(magic_api_struct,
21605                                                           magics[cur_magic].idx, magics[cur_magic].mode, canvas, last);
21606       update_canvas(0, 0, canvas->w, canvas->h);
21607 
21608       if (was_clicking && magics[cur_magic].mode == MODE_PAINT_WITH_PREVIEW)
21609         {
21610           /* Clean up preview! */
21611           do_undo();
21612           tool_avail[TOOL_REDO] = 0;    /* Don't let them 'redo' to get preview back */
21613           draw_toolbar();
21614           update_screen_rect(&r_tools);
21615         }
21616     }
21617 }
21618 
21619 /**
21620  * FIXME
21621  */
magic_switchin(SDL_Surface * last)21622 static void magic_switchin(SDL_Surface * last)
21623 {
21624   if (cur_tool == TOOL_MAGIC)
21625     {
21626       magic_funcs[magics[cur_magic].handle_idx].switchin(magic_api_struct,
21627                                                          magics[cur_magic].idx, magics[cur_magic].mode, canvas, last);
21628 
21629       /* In case the Magic tool's switchin() called update_progress_bar(),
21630          let's put the old Tux text back: */
21631 
21632       redraw_tux_text();
21633 
21634       update_canvas(0, 0, canvas->w, canvas->h);
21635     }
21636 }
21637 
21638 /**
21639  * FIXME
21640  */
magic_modeint(int mode)21641 static int magic_modeint(int mode)
21642 {
21643   if (mode == MODE_PAINT || mode == MODE_ONECLICK || mode == MODE_PAINT_WITH_PREVIEW)
21644     return 0;
21645   else if (mode == MODE_FULLSCREEN)
21646     return 1;
21647   else
21648     return 0;
21649 }
21650 
21651 /**
21652  * FIXME
21653  */
add_label_node(int w,int h,Uint16 x,Uint16 y,SDL_Surface * label_node_surface)21654 static void add_label_node(int w, int h, Uint16 x, Uint16 y, SDL_Surface * label_node_surface)
21655 {
21656   struct label_node *new_node = malloc(sizeof(struct label_node));
21657   struct label_node *aux_node;
21658 
21659   unsigned int i = 0;
21660 
21661   new_node->save_texttool_len = texttool_len;
21662   while (i < texttool_len)
21663     {
21664       new_node->save_texttool_str[i] = texttool_str[i];
21665       i = i + 1;
21666     }
21667   new_node->save_color.r = color_hexes[cur_color][0];
21668   new_node->save_color.g = color_hexes[cur_color][1];
21669   new_node->save_color.b = color_hexes[cur_color][2];
21670   new_node->save_width = w;
21671   new_node->save_height = h;
21672   new_node->save_x = x;
21673   new_node->save_y = y;
21674   new_node->save_cur_font = cur_font;
21675   new_node->save_text_state = text_state;
21676   new_node->save_text_size = text_size;
21677   new_node->save_undoid = 255;
21678 
21679   if (texttool_len > 0)
21680     {
21681       new_node->is_enabled = TRUE;
21682     }
21683   else
21684     {
21685       new_node->is_enabled = FALSE;
21686     }
21687 
21688   new_node->save_font_type = NULL;
21689 
21690   if (label_node_to_edit)
21691     {
21692       new_node->disables = label_node_to_edit;
21693     }
21694   else
21695     new_node->disables = NULL;
21696 
21697   if (label_node_surface != NULL)
21698     {
21699       new_node->label_node_surface = label_node_surface;
21700       new_node->label_node_surface->refcount++;
21701     }
21702   else
21703     new_node->label_node_surface = NULL;
21704 
21705 
21706   new_node->next_to_up_label_node = 0;
21707 
21708   new_node->next_to_down_label_node = current_label_node;
21709   if (current_label_node)
21710     {
21711       aux_node = current_label_node;
21712       aux_node->next_to_up_label_node = new_node;
21713     }
21714 
21715   current_label_node = new_node;
21716 
21717   if (start_label_node == NULL)
21718     start_label_node = current_label_node;
21719 
21720   highlighted_label_node = new_node;
21721   if (highlighted_label_node->is_enabled == FALSE)
21722     cycle_highlighted_label_node();
21723 }
21724 
21725 
21726 /**
21727  * FIXME
21728  */
search_label_list(struct label_node ** ref_head,Uint16 x,Uint16 y,int hover)21729 static struct label_node *search_label_list(struct label_node **ref_head, Uint16 x, Uint16 y, int hover)
21730 {
21731   struct label_node *current_node;
21732   struct label_node *tmp_node = NULL;
21733   unsigned u;
21734   int done = FALSE;
21735 
21736   Uint8 r, g, b, a;
21737   int i, j, k;
21738 
21739   if (*ref_head == NULL)
21740     return (NULL);
21741 
21742   current_node = *ref_head;
21743 
21744   while (done != TRUE)
21745     {
21746       if (x >= current_node->save_x)
21747         {
21748           if (y >= current_node->save_y)
21749             {
21750               if (x <= (current_node->save_x) + (current_node->save_width))
21751                 {
21752                   if (y <= (current_node->save_y) + (current_node->save_height))
21753                     {
21754                       if (current_node->is_enabled == TRUE)
21755                         {
21756                           if (hover == 1)
21757                             return (current_node);
21758                           tmp_node = current_node;
21759                           done = TRUE;
21760                         }
21761                     }
21762                 }
21763             }
21764         }
21765       current_node = current_node->next_to_down_label_node;
21766       if (current_node == NULL)
21767         current_node = current_label_node;
21768       if (current_node == *ref_head)
21769         done = TRUE;
21770     }
21771 
21772   if (tmp_node != NULL)
21773     {
21774       select_texttool_len = tmp_node->save_texttool_len;
21775 
21776       u = 0;
21777       while (u < select_texttool_len)
21778         {
21779           select_texttool_str[u] = tmp_node->save_texttool_str[u];
21780           u = u + 1;
21781         }
21782 
21783       for (k = 0; k < NUM_COLORS; k++)
21784         {
21785           if ((color_hexes[k][0] == tmp_node->save_color.r) &&
21786               (color_hexes[k][1] == tmp_node->save_color.g) &&
21787               (color_hexes[k][2] == tmp_node->save_color.b) && (k < NUM_COLORS - 1))
21788             {
21789               select_color = k;
21790               cur_color = k;
21791               break;
21792             }
21793 
21794           if (k == NUM_COLORS - 1)
21795             {
21796               cur_color = NUM_COLORS - 1;
21797               select_color = NUM_COLORS - 1;
21798               color_hexes[select_color][0] = tmp_node->save_color.r;
21799               color_hexes[select_color][1] = tmp_node->save_color.g;
21800               color_hexes[select_color][2] = tmp_node->save_color.b;
21801               SDL_LockSurface(img_color_btns[NUM_COLORS - 1]);
21802               SDL_LockSurface(img_color_btns[NUM_COLORS - 1 + NUM_COLORS]);
21803 
21804               for (j = 0; j < 48 /* 48 */ ; j++)
21805                 {
21806                   for (i = 0; i < 48; i++)
21807                     {
21808                       SDL_GetRGBA(getpixels[img_paintwell->format->BytesPerPixel] (img_paintwell, i, j),
21809                                   img_paintwell->format, &r, &g, &b, &a);
21810                       if (a == 255)
21811                         {
21812                           putpixels[img_color_btns[NUM_COLORS - 1]->format->BytesPerPixel]
21813                             (img_color_btns[NUM_COLORS - 1], i, j,
21814                              SDL_MapRGB(img_color_btns[NUM_COLORS - 1]->format,
21815                                         tmp_node->save_color.r, tmp_node->save_color.g, tmp_node->save_color.b));
21816                           putpixels[img_color_btns[NUM_COLORS - 1 + NUM_COLORS]->format->BytesPerPixel]
21817                             (img_color_btns[NUM_COLORS - 1 + NUM_COLORS], i, j,
21818                              SDL_MapRGB(img_color_btns[NUM_COLORS - 1 + NUM_COLORS]->format,
21819                                         tmp_node->save_color.r, tmp_node->save_color.g, tmp_node->save_color.b));
21820                         }
21821                     }
21822                 }
21823               SDL_UnlockSurface(img_color_btns[NUM_COLORS - 1]);
21824               SDL_UnlockSurface(img_color_btns[NUM_COLORS - 1 + NUM_COLORS]);
21825 
21826               draw_colors(COLORSEL_CLOBBER);
21827               render_brush();   /* FIXME: render_brush should be called at the start of Brush and Line tools? */
21828             }
21829         }
21830 
21831       select_width = tmp_node->save_width;
21832       select_height = tmp_node->save_height;
21833       select_x = tmp_node->save_x;
21834       select_y = tmp_node->save_y;
21835       select_cur_font = tmp_node->save_cur_font;
21836       select_text_state = tmp_node->save_text_state;
21837       select_text_size = tmp_node->save_text_size;
21838 
21839       return tmp_node;
21840     }
21841 
21842   return NULL;
21843 }
21844 
21845 /**
21846  * FIXME
21847  */
rec_undo_label(void)21848 static void rec_undo_label(void)
21849 {
21850   if (first_label_node_in_redo_stack != NULL)
21851     {
21852       delete_label_list(&first_label_node_in_redo_stack);
21853       first_label_node_in_redo_stack = NULL;
21854     }
21855 
21856   if (coming_from_undo_or_redo) // yet recorded, avoiding to write text_undo
21857     {
21858       coming_from_undo_or_redo = FALSE;
21859       return;
21860     }
21861 
21862   // FIXME:
21863   // It's all wrong to have a separate undo stack anyway. We need a way
21864   // for arbitrary code to supply callback functions and parameters when
21865   // creating an undo entry. One obvious function is a destructor for the
21866   // private data, for when it drops off the far end of the stack or gets
21867   // wiped out by an undo,draw combo. Others might be for when the level
21868   // stops being current or for when the level becomes current again.
21869 
21870   if (have_to_rec_label_node)
21871     {
21872       current_label_node->save_undoid = cur_undo;
21873       text_undo[cur_undo] = 1;
21874       have_to_rec_label_node = FALSE;
21875     }
21876   else
21877     {
21878       text_undo[cur_undo] = 0;
21879 
21880       /* Have we cycled around NUM_UNDO_BUFS? */
21881       if (current_label_node != NULL && current_label_node->save_undoid == (cur_undo + 1) % NUM_UNDO_BUFS)
21882         current_label_node->save_undoid = 255;
21883     }
21884 }
21885 
21886 /**
21887  * FIXME
21888  */
do_undo_label_node()21889 static void do_undo_label_node()
21890 {
21891   if (text_undo[(cur_undo + 1) % NUM_UNDO_BUFS] == 1)
21892     if (current_label_node != NULL)
21893       {
21894         if (current_label_node->save_undoid == (cur_undo + 1) % NUM_UNDO_BUFS)
21895           {
21896             if (current_label_node->disables != NULL)   /* If current node is an editing of an older one, reenable it. */
21897               current_label_node->disables->is_enabled = TRUE;
21898 
21899             first_label_node_in_redo_stack = current_label_node;
21900             current_label_node = current_label_node->next_to_down_label_node;
21901 
21902             if (current_label_node == NULL)
21903               start_label_node = current_label_node;
21904 
21905             highlighted_label_node = current_label_node;
21906             derender_node(&first_label_node_in_redo_stack);
21907             coming_from_undo_or_redo = TRUE;
21908           }
21909       }
21910   highlighted_label_node = current_label_node;
21911 }
21912 
21913 /**
21914  * FIXME
21915  */
do_redo_label_node()21916 static void do_redo_label_node()
21917 {
21918   if ((text_undo[cur_undo] == 1) && (first_label_node_in_redo_stack != NULL))
21919     {
21920       if (first_label_node_in_redo_stack->save_undoid == cur_undo)
21921         {
21922           current_label_node = first_label_node_in_redo_stack;
21923           first_label_node_in_redo_stack = current_label_node->next_to_up_label_node;
21924 
21925           if (start_label_node == NULL)
21926             start_label_node = current_label_node;
21927 
21928           highlighted_label_node = current_label_node;
21929           if (current_label_node->disables != NULL)     /* If this is a redo of an editing, redisable the old node. */
21930             {
21931               current_label_node->disables->is_enabled = FALSE;
21932               derender_node(&current_label_node->disables);
21933             }
21934           else
21935             simply_render_node(current_label_node);
21936 
21937           coming_from_undo_or_redo = TRUE;
21938         }
21939     }
21940 }
21941 
21942 
21943 /**
21944  * FIXME
21945  */
simply_render_node(struct label_node * node)21946 static void simply_render_node(struct label_node *node)
21947 {
21948   SDL_Surface *tmp_surf;
21949   SDL_Rect dest, src;
21950   wchar_t *str;
21951   wchar_t tmp_str[256];
21952   int j, w;
21953   unsigned i;
21954 
21955   if (node->label_node_surface == NULL)
21956     {
21957       /* Render the text: */
21958 
21959       SDL_Color color = node->save_color;
21960 
21961       text_state = node->save_text_state;
21962       text_size = node->save_text_size;
21963 
21964       i = 0;
21965       while (i < node->save_texttool_len)
21966         {
21967           tmp_str[i] = node->save_texttool_str[i];
21968           i = i + 1;
21969         }
21970       tmp_str[i] = L'\0';
21971 
21972       str = uppercase_w(tmp_str);
21973 
21974       text_state = node->save_text_state;
21975       text_size = node->save_text_size;
21976 
21977       for (j = 0; j < num_font_families; j++)
21978         {
21979           if (user_font_families[j] && user_font_families[j]->handle)
21980             {
21981               TuxPaint_Font_CloseFont(user_font_families[j]->handle);
21982               user_font_families[j]->handle = NULL;
21983             }
21984         }
21985 
21986       tmp_surf = render_text_w(getfonthandle(node->save_cur_font), str, color);
21987       if (tmp_surf != NULL)
21988 
21989         node->label_node_surface = tmp_surf;
21990     }
21991 
21992   if (node->label_node_surface != NULL)
21993     {
21994       w = node->label_node_surface->w;
21995 
21996       cursor_textwidth = w;
21997       /* Draw the text itself! */
21998 
21999       dest.x = node->save_x;
22000       dest.y = node->save_y;
22001 
22002       src.x = 0;
22003       src.y = 0;
22004       src.w = node->label_node_surface->w;
22005       src.h = node->label_node_surface->h;
22006 
22007       if (dest.x + src.w > WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w)
22008         src.w = WINDOW_WIDTH - r_ttoolopt.w - r_ttools.w - dest.x;
22009       if (dest.y + src.h > (button_h * buttons_tall + r_ttools.h))
22010         src.h = (button_h * buttons_tall + r_ttools.h) - dest.y;
22011 
22012       myblit(node->label_node_surface, &src, label, &dest);
22013 
22014       update_canvas(dest.x, dest.y, dest.x + node->label_node_surface->w, dest.y + node->label_node_surface->h);
22015 
22016       /* Setting the sizes correctly */
22017       node->save_width = node->label_node_surface->w;
22018       node->save_height = node->label_node_surface->h;
22019     }
22020 }
22021 
22022 /**
22023  * FIXME
22024  */
render_all_nodes_starting_at(struct label_node ** node)22025 static void render_all_nodes_starting_at(struct label_node **node)
22026 {
22027   struct label_node *current_node;
22028 
22029   if (*node != NULL)
22030     {
22031       current_node = *node;
22032       while (current_node != first_label_node_in_redo_stack)
22033         {
22034           if (current_node->is_enabled == TRUE)
22035             {
22036               simply_render_node(current_node);
22037             }
22038           if (current_node->next_to_up_label_node == NULL)
22039             return;
22040           current_node = current_node->next_to_up_label_node;
22041         }
22042     }
22043 }
22044 
22045 /**
22046  * FIXME
22047  */
22048 /* FIXME: This should search for the top-down of the overlaping labels and only re-render from it */
derender_node(struct label_node ** ref_head)22049 static void derender_node(__attribute__((unused)) struct label_node **ref_head)
22050 {
22051   SDL_Rect r_tmp_derender;
22052 
22053   r_tmp_derender.w = label->w;
22054   r_tmp_derender.h = label->h;
22055   r_tmp_derender.x = 0;
22056   r_tmp_derender.y = 0;
22057 
22058   SDL_FillRect(label, &r_tmp_derender, SDL_MapRGBA(label->format, 0, 0, 0, 0));
22059 
22060   render_all_nodes_starting_at(&start_label_node);
22061 }
22062 
22063 /**
22064  * FIXME
22065  */
delete_label_list(struct label_node ** ref_head)22066 static void delete_label_list(struct label_node **ref_head)
22067 {
22068   struct label_node *current = *ref_head;
22069   struct label_node *next;
22070 
22071   while (current != NULL)
22072     {
22073       fflush(stdout);
22074 
22075       next = current->next_to_up_label_node;
22076       if (current->label_node_surface)
22077         SDL_FreeSurface(current->label_node_surface);
22078       free(current);
22079       current = next;
22080     }
22081 
22082   *ref_head = NULL;
22083 }
22084 
22085 /**
22086  * FIXME
22087  */
22088 /* A custom bliter that allows to put two transparent layers toghether without having to deal with colorkeys or SDL_SRCALPHA
22089    I am always reinventing the wheel. Hope this one is not squared. Pere */
myblit(SDL_Surface * src_surf,SDL_Rect * src_rect,SDL_Surface * dest_surf,SDL_Rect * dest_rect)22090 static void myblit(SDL_Surface * src_surf, SDL_Rect * src_rect, SDL_Surface * dest_surf, SDL_Rect * dest_rect)
22091 {
22092   int x, y;
22093   Uint8 src_r, src_g, src_b, src_a;
22094   Uint8 dest_r, dest_g, dest_b, dest_a;
22095 
22096   for (x = src_rect->x; x < src_rect->w + src_rect->x; x++)
22097     for (y = src_rect->y; y < src_rect->h + src_rect->y; y++)
22098       {
22099         SDL_GetRGBA(getpixels[src_surf->format->BytesPerPixel] (src_surf, x - src_rect->x, y - src_rect->y),
22100                     src_surf->format, &src_r, &src_g, &src_b, &src_a);
22101         if (src_a != SDL_ALPHA_TRANSPARENT)
22102           {
22103             if (src_a == SDL_ALPHA_OPAQUE)
22104               putpixels[dest_surf->format->BytesPerPixel] (dest_surf, x + dest_rect->x, y + dest_rect->y,
22105                                                            SDL_MapRGBA(dest_surf->format, src_r, src_g, src_b, src_a));
22106             else
22107               {
22108                 SDL_GetRGBA(getpixels[dest_surf->format->BytesPerPixel] (dest_surf, x + dest_rect->x, y + dest_rect->y),
22109                             src_surf->format, &dest_r, &dest_g, &dest_b, &dest_a);
22110                 if (dest_a == SDL_ALPHA_TRANSPARENT)
22111                   putpixels[dest_surf->format->BytesPerPixel] (dest_surf, x + dest_rect->x, y + dest_rect->y,
22112                                                                SDL_MapRGBA(dest_surf->format, src_r, src_g, src_b,
22113                                                                            src_a));
22114                 else
22115                   {
22116                     dest_r = src_r * src_a / 255 + dest_r * dest_a * (255 - src_a) / 255 / 255;
22117                     dest_g = src_g * src_a / 255 + dest_g * dest_a * (255 - src_a) / 255 / 255;
22118                     dest_b = src_b * src_a / 255 + dest_b * dest_a * (255 - src_a) / 255 / 255;
22119                     dest_a = src_a + dest_a * (255 - src_a) / 255;
22120                     putpixels[dest_surf->format->BytesPerPixel] (dest_surf, x + dest_rect->x, y + dest_rect->y,
22121                                                                  SDL_MapRGBA(dest_surf->format, dest_r, dest_g, dest_b,
22122                                                                              dest_a));
22123                   }
22124               }
22125           }
22126       }
22127 }
22128 
22129 /**
22130  * FIXME
22131  */
load_info_about_label_surface(FILE * lfi)22132 static void load_info_about_label_surface(FILE * lfi)
22133 {
22134   struct label_node *new_node;
22135   int list_ctr;
22136   int tmp_scale_w;
22137   int tmp_scale_h;
22138   SDL_Surface *label_node_surface, *label_node_surface_aux;
22139   float new_text_size;
22140 
22141   int k;
22142   unsigned l;
22143   unsigned tmp_pos;
22144   wchar_t tmp_char;
22145   int old_width;
22146   int old_height;
22147   int new_width;
22148   int new_height;
22149   float new_ratio;
22150   float old_ratio;
22151   float new_to_old_ratio;
22152   int old_pos;
22153   int new_pos;
22154   int x, y;
22155   int tmp_fscanf_return;
22156   char *tmp_fgets_return;
22157   Uint8 a;
22158 
22159   /* Clear label surface */
22160 
22161   SDL_FillRect(label, NULL, SDL_MapRGBA(label->format, 0, 0, 0, 0));
22162 
22163   /* Clear all info related to label surface */
22164 
22165   delete_label_list(&start_label_node);
22166   start_label_node = current_label_node = first_label_node_in_redo_stack = highlighted_label_node = label_node_to_edit =
22167     NULL;
22168   have_to_rec_label_node = FALSE;
22169 
22170 
22171   if (lfi == NULL)
22172     return;
22173   tmp_fscanf_return = fscanf(lfi, "%d\n", &list_ctr);
22174   tmp_fscanf_return = fscanf(lfi, "%d\n", &tmp_scale_w);
22175   tmp_fscanf_return = fscanf(lfi, "%d\n\n", &tmp_scale_h);
22176   (void)tmp_fscanf_return;
22177 
22178   old_width = tmp_scale_w;
22179   old_height = tmp_scale_h;
22180   new_width = r_canvas.w;
22181   new_height = r_canvas.h;
22182   new_ratio = (float)new_width / new_height;
22183   old_ratio = (float)old_width / old_height;
22184   if (new_ratio < old_ratio)
22185     new_to_old_ratio = (float)new_width / old_width;
22186   else
22187     new_to_old_ratio = (float)new_height / old_height;
22188 
22189   for (k = 0; k < list_ctr; k++)
22190     {
22191       new_node = malloc(sizeof(struct label_node));
22192 
22193       tmp_fscanf_return = fscanf(lfi, "%u\n", &new_node->save_texttool_len);
22194 #ifdef WIN32
22195       char *tmpstr;
22196       wchar_t *wtmpstr;
22197 
22198       tmpstr = malloc(1024);
22199       wtmpstr = malloc(1024);
22200       fgets(tmpstr, 1024, lfi);
22201       mtw(wtmpstr, tmpstr);
22202       for (l = 0; l < new_node->save_texttool_len; l++)
22203         {
22204           new_node->save_texttool_str[l] = wtmpstr[l];
22205         }
22206 
22207 #else
22208       for (l = 0; l < new_node->save_texttool_len; l++)
22209         {
22210           tmp_fscanf_return = fscanf(lfi, "%lc", &tmp_char);
22211           new_node->save_texttool_str[l] = tmp_char;
22212         }
22213       tmp_fscanf_return = fscanf(lfi, "\n");
22214 #endif
22215       tmp_fscanf_return = fscanf(lfi, "%u\n", &l);
22216       new_node->save_color.r = (Uint8) l;
22217       tmp_fscanf_return = fscanf(lfi, "%u\n", &l);
22218       new_node->save_color.g = (Uint8) l;
22219       tmp_fscanf_return = fscanf(lfi, "%u\n", &l);
22220       new_node->save_color.b = (Uint8) l;
22221       tmp_fscanf_return = fscanf(lfi, "%d\n", &new_node->save_width);
22222       tmp_fscanf_return = fscanf(lfi, "%d\n", &new_node->save_height);
22223       tmp_fscanf_return = fscanf(lfi, "%d\n", &tmp_pos);
22224       old_pos = (int)tmp_pos;
22225 
22226       if (new_ratio < old_ratio)
22227         {
22228           new_pos = (old_pos * new_to_old_ratio);
22229           tmp_pos = new_pos;
22230           new_node->save_x = tmp_pos;
22231           tmp_fscanf_return = fscanf(lfi, "%d\n", &tmp_pos);
22232           old_pos = (int)tmp_pos;
22233           new_pos = old_pos * new_to_old_ratio + (new_height - old_height * new_to_old_ratio) / 2;
22234           tmp_pos = new_pos;
22235           new_node->save_y = tmp_pos;
22236         }
22237       else
22238         {
22239           new_pos = (old_pos * new_to_old_ratio) + (new_width - old_width * new_to_old_ratio) / 2;
22240           tmp_pos = new_pos;
22241           new_node->save_x = tmp_pos;
22242           tmp_fscanf_return = fscanf(lfi, "%d\n", &tmp_pos);
22243           old_pos = (int)tmp_pos;
22244           new_pos = (old_pos * new_to_old_ratio);
22245           tmp_pos = new_pos;
22246           new_node->save_y = tmp_pos;
22247         }
22248 
22249 #ifdef DEBUG
22250       printf("Original label size %dx%d\n", new_node->save_width, new_node->save_height);
22251 #endif
22252 
22253       tmp_fscanf_return = fscanf(lfi, "%d\n", &new_node->save_cur_font);
22254       new_node->save_cur_font = 0;
22255 
22256       new_node->save_font_type = malloc(64);
22257       tmp_fgets_return = fgets(new_node->save_font_type, 64, lfi);
22258       (void)tmp_fgets_return;
22259 
22260       tmp_fscanf_return = fscanf(lfi, "%d\n", &new_node->save_text_state);
22261       tmp_fscanf_return = fscanf(lfi, "%u\n", &new_node->save_text_size);
22262 
22263       label_node_surface = SDL_CreateRGBSurface(screen->flags,
22264                                                 new_node->save_width,
22265                                                 new_node->save_height,
22266                                                 screen->format->BitsPerPixel,
22267                                                 screen->format->Rmask,
22268                                                 screen->format->Gmask, screen->format->Bmask, TPAINT_AMASK);
22269 
22270       SDL_LockSurface(label_node_surface);
22271       for (x = 0; x < new_node->save_width; x++)
22272         for (y = 0; y < new_node->save_height; y++)
22273           {
22274             a = fgetc(lfi);
22275             putpixels[label_node_surface->format->BytesPerPixel] (label_node_surface, x, y,
22276                                                                   SDL_MapRGBA(label_node_surface->format,
22277                                                                               new_node->save_color.r,
22278                                                                               new_node->save_color.g,
22279                                                                               new_node->save_color.b, a));
22280           }
22281       SDL_UnlockSurface(label_node_surface);
22282 
22283       new_text_size = (float)new_node->save_text_size * new_to_old_ratio;
22284       label_node_surface_aux =
22285         zoom(label_node_surface, label_node_surface->w * new_to_old_ratio, label_node_surface->h * new_to_old_ratio);
22286       SDL_FreeSurface(label_node_surface);
22287       new_node->label_node_surface = label_node_surface_aux;
22288       new_node->label_node_surface->refcount++;
22289       SDL_FreeSurface(label_node_surface_aux);
22290 
22291       if ((unsigned)new_text_size > MAX_TEXT_SIZE)      /* Here we reach the limits when scaling the font size */
22292         new_node->save_text_size = MAX_TEXT_SIZE;
22293       else if ((unsigned)new_text_size > MIN_TEXT_SIZE)
22294         new_node->save_text_size = floor(new_text_size + 0.5);
22295       else
22296         new_node->save_text_size = MIN_TEXT_SIZE;
22297 
22298 
22299       new_node->save_undoid = 255;      /* A value that cur_undo will likely never reach */
22300       new_node->is_enabled = TRUE;
22301       new_node->disables = NULL;
22302       new_node->next_to_down_label_node = NULL;
22303       new_node->next_to_up_label_node = NULL;
22304       tmp_fscanf_return = fscanf(lfi, "\n");
22305 
22306       if (current_label_node == NULL)
22307         {
22308           current_label_node = new_node;
22309           start_label_node = current_label_node;
22310         }
22311       else
22312         {
22313           new_node->next_to_down_label_node = current_label_node;
22314           current_label_node->next_to_up_label_node = new_node;
22315           current_label_node = new_node;
22316         }
22317 
22318       highlighted_label_node = current_label_node;
22319       simply_render_node(current_label_node);
22320 
22321     }
22322   first_label_node_in_redo_stack = NULL;
22323   fclose(lfi);
22324 
22325   if (font_thread_done)
22326     set_label_fonts();
22327 }
22328 
22329 /**
22330  * FIXME
22331  */
set_label_fonts()22332 static void set_label_fonts()
22333 {
22334   struct label_node *node;
22335   int i;
22336   char *ttffont;
22337 
22338   node = current_label_node;
22339   while (node != NULL)
22340     {
22341       for (i = 0; i < num_font_families; i++)
22342         {
22343           Uint32 c;
22344 
22345           /* FIXME: 2009/09/13 TTF_FontFaceFamilyName() appends random "\n" at the end
22346              of the returned string.  Should investigate why, and when corrected,
22347              remove the code that deals whith the ending "\n"s in ttffont */
22348           ttffont = TTF_FontFaceFamilyName(getfonthandle(i)->ttf_font);
22349           for (c = 0; c < strlen(ttffont); c++)
22350             if (ttffont[c] == '\n')
22351               ttffont[c] = '\0';
22352           for (c = 0; c < strlen(node->save_font_type); c++)
22353             if (node->save_font_type[c] == '\n')
22354               node->save_font_type[c] = '\0';
22355 
22356 #ifdef DEBUG
22357           printf("ttffont A%sA\n", ttffont);
22358           printf("font_type B%sB\n", node->save_font_type);
22359 #endif
22360 
22361           if (strcmp(node->save_font_type, ttffont) == 0)
22362 
22363             {
22364 #ifdef DEBUG
22365               printf("Font matched %s !!!\n", ttffont);
22366 #endif
22367               node->save_cur_font = i;
22368               break;
22369             }
22370           else if (strstr(ttffont, node->save_font_type) || strstr(node->save_font_type, ttffont))
22371             {
22372 #ifdef DEBUG
22373               printf("setting %s as replacement", TTF_FontFaceFamilyName(getfonthandle(i)->ttf_font));
22374 #endif
22375               node->save_cur_font = i;
22376             }
22377         }
22378 
22379       if (node->save_cur_font > num_font_families)      /* This should never happens, setting default font. */
22380         node->save_cur_font = 0;
22381 
22382       free(node->save_font_type);       /* Not needed anymore */
22383       node->save_font_type = NULL;
22384       node = node->next_to_down_label_node;
22385     }
22386 }
22387 
22388 
22389 /**
22390  * FIXME
22391  */
tmp_apply_uncommited_text()22392 static void tmp_apply_uncommited_text()
22393 {
22394   have_to_rec_label_node_back = have_to_rec_label_node;
22395 
22396   if (texttool_len > 0)
22397     {
22398       if (cur_tool == TOOL_TEXT ||
22399           (old_tool == TOOL_TEXT &&
22400            (cur_tool == TOOL_PRINT ||
22401             cur_tool == TOOL_SAVE || cur_tool == TOOL_OPEN || cur_tool == TOOL_NEW || cur_tool == TOOL_QUIT)))
22402         {
22403           canvas_back = SDL_CreateRGBSurface(canvas->flags,
22404                                              canvas->w,
22405                                              canvas->h,
22406                                              canvas->format->BitsPerPixel,
22407                                              canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, 0);
22408           SDL_BlitSurface(canvas, NULL, canvas_back, NULL);
22409           do_render_cur_text(1);
22410         }
22411 
22412       else if (cur_tool == TOOL_LABEL ||
22413                (old_tool == TOOL_LABEL &&
22414                 (cur_tool == TOOL_PRINT ||
22415                  cur_tool == TOOL_SAVE || cur_tool == TOOL_OPEN || cur_tool == TOOL_NEW || cur_tool == TOOL_QUIT)))
22416         {
22417           do_render_cur_text(1);
22418           current_label_node->save_undoid = 253;
22419         }
22420     }
22421   else if ((cur_tool == TOOL_LABEL && label_node_to_edit) ||
22422            ((old_tool == TOOL_LABEL && label_node_to_edit) &&
22423             (cur_tool == TOOL_PRINT ||
22424              cur_tool == TOOL_SAVE || cur_tool == TOOL_OPEN || cur_tool == TOOL_NEW || cur_tool == TOOL_QUIT)))
22425     {
22426       add_label_node(0, 0, 0, 0, NULL);
22427       current_label_node->is_enabled = FALSE;
22428       current_label_node->save_undoid = 253;
22429 
22430       derender_node(&label_node_to_edit);
22431     }
22432 }
22433 
22434 /**
22435  * FIXME
22436  */
undo_tmp_applied_text()22437 static void undo_tmp_applied_text()
22438 {
22439   struct label_node *aux_label_node;
22440 
22441   if (texttool_len > 0)
22442     {
22443       if (cur_tool == TOOL_TEXT ||
22444           (cur_tool == TOOL_PRINT && old_tool == TOOL_TEXT) ||
22445           (cur_tool == TOOL_SAVE && old_tool == TOOL_TEXT) ||
22446           (cur_tool == TOOL_OPEN && old_tool == TOOL_TEXT) ||
22447           (cur_tool == TOOL_NEW && old_tool == TOOL_TEXT) || (cur_tool == TOOL_QUIT && old_tool == TOOL_TEXT))
22448         {
22449           SDL_BlitSurface(canvas_back, NULL, canvas, NULL);
22450           SDL_FreeSurface(canvas_back);
22451           do_render_cur_text(0);
22452         }
22453     }
22454   if (current_label_node != NULL && current_label_node->save_undoid == 253)
22455     {
22456       aux_label_node = current_label_node;
22457       current_label_node = current_label_node->next_to_down_label_node;
22458 
22459       if (current_label_node == NULL)
22460         start_label_node = NULL;
22461       else
22462         current_label_node->next_to_up_label_node = first_label_node_in_redo_stack;
22463 
22464       derender_node(&aux_label_node);
22465       delete_label_list(&aux_label_node);
22466       have_to_rec_label_node = have_to_rec_label_node_back;
22467       do_render_cur_text(0);
22468     }
22469 }
22470 
22471 
22472 /**
22473  * FIXME
22474  */
22475 /* Painting on the screen surface to avoid unnecessary complexity */
highlight_label_nodes()22476 static void highlight_label_nodes()
22477 {
22478   int j;
22479   SDL_Rect rect, rect1;
22480   struct label_node *aux_node;
22481 
22482   if (highlighted_label_node != NULL)
22483     {
22484       aux_node = highlighted_label_node->next_to_up_label_node;
22485       if (aux_node == first_label_node_in_redo_stack)
22486         aux_node = start_label_node;
22487 
22488 
22489       while (aux_node != highlighted_label_node)
22490         {
22491           if (aux_node->is_enabled)
22492             {
22493               rect.x = aux_node->save_x + button_w * 2;
22494               rect.y = aux_node->save_y;
22495               rect.w = aux_node->save_width;
22496               rect.h = aux_node->save_height;
22497 
22498               SDL_FillRect(screen, &rect, SDL_MapRGBA(screen->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT));
22499 
22500               for (j = 2; j < aux_node->save_height / 4; j++)
22501                 {
22502                   rect1.x = rect.x + j;
22503                   rect1.y = rect.y + j;
22504                   rect1.w = rect.w - 2 * j;
22505                   if (rect1.w < 2)
22506                     break;
22507                   rect1.h = rect.h - 2 * j;
22508                   SDL_FillRect(screen,
22509                                &rect1,
22510                                SDL_MapRGBA(screen->format,
22511                                            4 * j * 200 / aux_node->save_height,
22512                                            4 * j * 200 / aux_node->save_height,
22513                                            4 * j * 200 / aux_node->save_height, SDL_ALPHA_OPAQUE));
22514 
22515                   SDL_BlitSurface(aux_node->label_node_surface, NULL, screen, &rect);
22516                 }
22517 
22518             }
22519 
22520           aux_node = aux_node->next_to_up_label_node;
22521           if (aux_node == first_label_node_in_redo_stack)
22522             aux_node = start_label_node;
22523         }
22524 
22525       aux_node = highlighted_label_node;
22526       rect.x = aux_node->save_x + button_w * 2;
22527       rect.y = aux_node->save_y;
22528       rect.w = aux_node->save_width;
22529       rect.h = aux_node->save_height;
22530       SDL_FillRect(screen, &rect, SDL_MapRGBA(screen->format, 255, 0, 0, SDL_ALPHA_OPAQUE));
22531 
22532       for (j = 2; j < aux_node->save_height / 4; j++)
22533         {
22534           rect1.x = rect.x + j;
22535           rect1.y = rect.y + j;
22536           rect1.w = rect.w - 2 * j;
22537           if (rect1.w < 2)
22538             break;
22539           rect1.h = rect.h - 2 * j;
22540           SDL_FillRect(screen,
22541                        &rect1,
22542                        SDL_MapRGBA(screen->format, 255, 4 * j * 225 / aux_node->save_height, 0, SDL_ALPHA_OPAQUE));
22543 
22544           SDL_BlitSurface(aux_node->label_node_surface, NULL, screen, &rect);
22545         }
22546 
22547       SDL_Flip(screen);
22548     }
22549 }
22550 
22551 /**
22552  * FIXME
22553  */
cycle_highlighted_label_node()22554 static void cycle_highlighted_label_node()
22555 {
22556   struct label_node *aux_node;
22557 
22558   if (highlighted_label_node)
22559     {
22560       aux_node = highlighted_label_node->next_to_down_label_node;
22561       if (aux_node == NULL)
22562         aux_node = current_label_node;
22563       if (aux_node->is_enabled)
22564         highlighted_label_node = aux_node;
22565       else
22566         while (aux_node->is_enabled == FALSE && aux_node != highlighted_label_node)
22567           {
22568             aux_node = aux_node->next_to_down_label_node;
22569             if (aux_node == NULL)
22570               aux_node = current_label_node;
22571             if (aux_node->is_enabled)
22572               highlighted_label_node = aux_node;
22573           }
22574     }
22575 
22576 }
22577 
22578 /**
22579  * FIXME
22580  */
are_labels()22581 static int are_labels()
22582 {
22583   struct label_node *aux_node;
22584 
22585   if (current_label_node)
22586     {
22587       aux_node = current_label_node;
22588       while (aux_node)
22589         {
22590           if (aux_node->is_enabled)
22591             return (TRUE);
22592           aux_node = aux_node->next_to_down_label_node;
22593         }
22594     }
22595   return (FALSE);
22596 }
22597 
22598 /**
22599  * FIXME
22600  */
chunk_is_valid(const char * chunk_name,png_unknown_chunk unknown)22601 int chunk_is_valid(const char *chunk_name, png_unknown_chunk unknown)
22602 {
22603   unsigned int count, fields;
22604   int new_field;
22605   char *control, *softwr;
22606   int unc_size, comp;
22607 
22608   if (chunk_name[0] == unknown.name[0] &&
22609       chunk_name[1] == unknown.name[1] &&
22610       chunk_name[2] == unknown.name[2] &&
22611       chunk_name[3] == unknown.name[3] &&
22612       50 < unknown.size &&
22613       'T' == unknown.data[0] &&
22614       'u' == unknown.data[1] &&
22615       'x' == unknown.data[2] &&
22616       'p' == unknown.data[3] &&
22617       'a' == unknown.data[4] &&
22618       'i' == unknown.data[5] && 'n' == unknown.data[6] && 't' == unknown.data[7] && '\n' == unknown.data[8])
22619     {
22620       /* Passed the first test, now checking if there are  at least
22621          4 fields in the first 50 bytes of the chunk data */
22622       count = 9;
22623       fields = 1;
22624       new_field = 1;
22625       while (count < 50)
22626         {
22627           if (unknown.data[count] == '\n')
22628             {
22629               if (new_field == 1)
22630                 return (FALSE); /* Avoid empty fields */
22631               fields++;
22632               if (fields == 4)
22633                 {               /* Last check, see if the sizes match */
22634                   control = malloc(50);
22635                   softwr = malloc(50);
22636                   sscanf((char *)unknown.data, "%s\n%s\n%d\n%d\n", control, softwr, &unc_size, &comp);
22637                   free(control);
22638                   free(softwr);
22639                   if (count + comp + 1 == unknown.size)
22640                     return (TRUE);
22641                   else
22642                     return (FALSE);
22643                 }
22644               new_field = 1;
22645             }
22646           else
22647             {
22648               /* Check if there are decimal values here */
22649               if ((fields < 4 && fields > 1) &&
22650                   !((unknown.data[count] == '0') ||
22651                     (unknown.data[count] == '1') ||
22652                     (unknown.data[count] == '2') ||
22653                     (unknown.data[count] == '3') ||
22654                     (unknown.data[count] == '4') ||
22655                     (unknown.data[count] == '5') ||
22656                     (unknown.data[count] == '6') ||
22657                     (unknown.data[count] == '7') || (unknown.data[count] == '8') || (unknown.data[count] == '9')))
22658                 return (FALSE);
22659 
22660               new_field = 0;
22661             }
22662           count++;
22663         }
22664     }
22665 
22666   return (FALSE);
22667 }
22668 
22669 /**
22670  * FIXME
22671  */
get_chunk_data(FILE * fp,char * fname,png_structp png_ptr,png_infop info_ptr,const char * chunk_name,png_unknown_chunk unknown,int * unc_size)22672 Bytef *get_chunk_data(FILE * fp, char *fname, png_structp png_ptr,
22673                       png_infop info_ptr, const char *chunk_name, png_unknown_chunk unknown, int *unc_size)
22674 {
22675   unsigned int i;
22676 
22677   int f, count, comp, unc_err;
22678   char *control, *softwr;
22679   Bytef *comp_buff, *unc_buff;
22680 
22681   z_streamp zstp;
22682 
22683   control = malloc(50);
22684   softwr = malloc(50);
22685   sscanf((char *)unknown.data, "%s\n%s\n%d\n%d\n", control, softwr, unc_size, &comp);
22686   free(control);
22687   free(softwr);
22688   comp_buff = malloc(comp * sizeof(Bytef));
22689 
22690   if (comp_buff == NULL)
22691     {
22692       fclose(fp);
22693 
22694       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
22695 
22696       fprintf(stderr,
22697               "\nError: Couldn't recover the embedded data in %s\n\nUnable to allocate memory for the compressed buffer for %s\n\n",
22698               fname, chunk_name);
22699       draw_tux_text(TUX_OOPS, strerror(errno), 0);
22700       return (NULL);
22701     }
22702   f = 0;
22703   count = 0;
22704 
22705   for (i = 0; i < unknown.size; i++)
22706     {
22707       if (f > 3)
22708         {
22709           comp_buff[i - count] = unknown.data[i];
22710           // printf("%d, %d, %d    ",i-count, comp_buff[i - count], unknown.data[i]);
22711         }
22712 
22713       if (unknown.data[i] == '\n' && f < 4)
22714         {
22715           f++;
22716           count = i + 1;
22717         }
22718     }
22719 
22720   unc_buff = malloc(*unc_size * sizeof(Bytef));
22721 
22722   if (unc_buff == NULL)
22723     {
22724       fclose(fp);
22725 
22726       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
22727 
22728       fprintf(stderr,
22729               "\nError: Couldn't recover the embedded data in %s\n\nUnable to allocate memory for the compressed buffer for %s\n\n",
22730               fname, chunk_name);
22731       draw_tux_text(TUX_OOPS, strerror(errno), 0);
22732       return (NULL);
22733     }
22734 
22735   /* Seems that uncompress() has problems in 64bits systems, so using inflate() Pere 2012/03/28 */
22736   /*  unc_err = uncompress(unc_buff, (uLongf *) unc_size, comp_buff, comp); */
22737   zstp = malloc(sizeof(z_stream));
22738   zstp->next_in = comp_buff;
22739   zstp->avail_in = comp;
22740   zstp->total_in = comp;
22741 
22742   zstp->next_out = unc_buff;
22743   zstp->avail_out = *unc_size;
22744   zstp->total_out = 0;
22745 
22746   zstp->zalloc = Z_NULL;
22747   zstp->zfree = Z_NULL;
22748   zstp->opaque = Z_NULL;
22749 
22750   inflateInit(zstp);
22751   unc_err = inflate(zstp, Z_FINISH);
22752   inflateEnd(zstp);
22753 
22754   if (unc_err != Z_STREAM_END)
22755     {
22756       fprintf(stderr, "\n error %d, unc %d, comp %d\n", unc_err, *unc_size, comp);
22757       fclose(fp);
22758       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
22759       free(comp_buff);
22760       free(unc_buff);
22761 
22762       fprintf(stderr, "Can't recover the embedded data in %s, error in uncompressing data from %s\n\n", fname, chunk_name);
22763       draw_tux_text(TUX_OOPS, strerror(errno), 0);
22764       return (NULL);
22765     }
22766 
22767   free(comp_buff);
22768   return (unc_buff);
22769 
22770 }
22771 
22772 /**
22773  * FIXME
22774  */
load_embedded_data(char * fname,SDL_Surface * org_surf)22775 void load_embedded_data(char *fname, SDL_Surface * org_surf)
22776 {
22777   FILE *fi, *fp;
22778   char *control;
22779   char *CHAR_PTR_TMP;
22780   Bytef *unc_buff;
22781 
22782   int unc_size;
22783   int u;
22784   int have_background, have_foreground, have_label_delta, have_label_data;
22785   int ldelta, ldata, fgnd, bgnd;
22786   int num_unknowns = 0;
22787   SDL_Surface *aux_surf;
22788 
22789   png_structp png_ptr;
22790   png_infop info_ptr;
22791   png_unknown_chunkp unknowns;
22792 
22793   png_uint_32 ww, hh;
22794   png_uint_32 i, j;
22795 
22796 #ifdef DEBUG
22797   printf("Loading embedded data...\n");
22798   printf("%s\n", fname);
22799 #endif
22800 
22801   fp = fopen(fname, "rb");
22802   if (!fp)
22803     {
22804       SDL_FreeSurface(org_surf);
22805       return;
22806     }
22807 
22808   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
22809   if (png_ptr == NULL)
22810     {
22811       fclose(fp);
22812       png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
22813 
22814       fprintf(stderr, "\nError: Couldn't open the image!\n%s\n\n", fname);
22815       draw_tux_text(TUX_OOPS, strerror(errno), 0);
22816       SDL_FreeSurface(org_surf);
22817       return;
22818     }
22819   else
22820     {
22821 #ifdef DEBUG
22822       printf("%s\n", fname);
22823 #endif
22824 
22825       info_ptr = png_create_info_struct(png_ptr);
22826       if (info_ptr == NULL)
22827         {
22828           fclose(fp);
22829           png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
22830 
22831           fprintf(stderr, "\nError: Couldn't open the image!\n%s\n\n", fname);
22832           draw_tux_text(TUX_OOPS, strerror(errno), 0);
22833           SDL_FreeSurface(org_surf);
22834           return;
22835         }
22836 
22837       png_init_io(png_ptr, fp);
22838 
22839       png_set_keep_unknown_chunks(png_ptr, 3, NULL, 0);
22840 
22841       png_read_info(png_ptr, info_ptr);
22842 
22843       ww = png_get_image_width(png_ptr, info_ptr);
22844       hh = png_get_image_height(png_ptr, info_ptr);
22845 
22846       num_unknowns = (int)png_get_unknown_chunks(png_ptr, info_ptr, &unknowns);
22847 
22848 #ifdef DEBUG
22849       printf("num_unknowns %i\n", num_unknowns);
22850 #endif
22851       if (num_unknowns)
22852         {
22853           have_label_delta = have_label_data = have_background = have_foreground = FALSE;
22854           ldelta = ldata = fgnd = bgnd = FALSE;
22855 
22856           /* Need to get things in order, as we can't enforce any order in custom chunks,
22857              we need to go around them 3 times */
22858 
22859           /* First we search for the things that usually were in the .dat file, so if a starter or a
22860              template is found and if it is not modified, we can load it clean (i.e. not rebluring a
22861              blured when scaled one) */
22862           for (u = 0; u < num_unknowns; u++)
22863             {
22864 #ifdef DEBUG
22865               printf("%s, %d\n", unknowns[u].name, (int)unknowns[u].size);
22866 #endif
22867 
22868               if (chunk_is_valid("tpDT", unknowns[u]))
22869                 {
22870 #ifdef DEBUG
22871                   printf("Valid tpDT\n");
22872 #endif
22873                   fi = fmemopen(unknowns[u].data, unknowns[u].size, "r");
22874                   if (fi == NULL)
22875                     {
22876                       fclose(fp);
22877                       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
22878 
22879                       fprintf(stderr, "\nError: Couldn't load the data embedded in %s\n\n", fname);
22880                       draw_tux_text(TUX_OOPS, strerror(errno), 0);
22881                       SDL_FreeSurface(org_surf);
22882                       return;   /* Refusing to go further with the other chunks */
22883                     }
22884 
22885                   /* Put fi position at the right place after the chunk headers */
22886                   control = malloc(50);
22887                   CHAR_PTR_TMP = fgets(control, 49, fi);
22888                   CHAR_PTR_TMP = fgets(control, 49, fi);
22889                   CHAR_PTR_TMP = fgets(control, 49, fi);
22890                   CHAR_PTR_TMP = fgets(control, 49, fi);
22891                   (void)CHAR_PTR_TMP;
22892                   free(control);
22893 
22894                   /* fi will be closed in load_starter_id() */
22895                   load_starter_id(NULL, fi);
22896                   if (!starter_modified)
22897                     {
22898                       /* Code adapted from load_current() */
22899                       if (starter_id[0] != '\0')
22900                         {
22901                           load_starter(starter_id);
22902 
22903                           if (starter_mirrored && img_starter)
22904                             mirror_starter();
22905 
22906                           if (starter_flipped && img_starter)
22907                             flip_starter();
22908                         }
22909                       else if (template_id[0] != '\0')
22910                         {
22911                           load_template(template_id);
22912                         }
22913                     }
22914                 }
22915               /* Also check what we have there */
22916               if (chunk_is_valid("tpBK", unknowns[u]))
22917                 have_background = TRUE;
22918               if (chunk_is_valid("tpFG", unknowns[u]))
22919                 have_foreground = TRUE;
22920               if (chunk_is_valid("tpLD", unknowns[u]))
22921                 have_label_delta = TRUE;
22922               if (chunk_is_valid("tpLL", unknowns[u]))
22923                 have_label_data = TRUE;
22924             }
22925 
22926           /* Recover the labels and apply the diff from label to canvas. */
22927           if (!disable_label && have_label_delta && have_label_data)
22928             {
22929               for (u = 0; u < num_unknowns; u++)
22930                 {
22931                   if (chunk_is_valid("tpLD", unknowns[u]))
22932                     {
22933 #ifdef DEBUG
22934                       printf("Valid tpLD\n");
22935 #endif
22936 
22937                       unc_buff = get_chunk_data(fp, fname, png_ptr, info_ptr, "tpLD", unknowns[u], &unc_size);
22938                       if (unc_buff == NULL)
22939                         {
22940                           if (are_labels())
22941                             {
22942                               delete_label_list(&start_label_node);
22943                               start_label_node = current_label_node = NULL;
22944                             }
22945 
22946                           SDL_FreeSurface(org_surf);
22947                           return;
22948                         }
22949                       else
22950                         {
22951                           SDL_LockSurface(org_surf);
22952                           for (j = 0; j < hh; j++)
22953                             for (i = 0; i < ww; i++)
22954                               {
22955                                 if ((Uint8) unc_buff[4 * j * ww + 4 * i + 3] == SDL_ALPHA_OPAQUE)
22956                                   putpixels[org_surf->format->BytesPerPixel] (org_surf, i, j,
22957                                                                               SDL_MapRGB(org_surf->format,
22958                                                                                          unc_buff[4 * (j * ww + i)],
22959                                                                                          unc_buff[4 * (j * ww + i) + 1],
22960                                                                                          unc_buff[4 * (j * ww + i) +
22961                                                                                                   2]));
22962                               }
22963                         }
22964 
22965                       SDL_UnlockSurface(org_surf);
22966 
22967                       free(unc_buff);
22968                       ldelta = TRUE;
22969                     }
22970 
22971                   /* Label Data */
22972                   if (!disable_label && chunk_is_valid("tpLL", unknowns[u]))
22973                     {
22974 #ifdef DEBUG
22975                       printf("Valid tpLL\n");
22976 #endif
22977 
22978                       unc_buff = get_chunk_data(fp, fname, png_ptr, info_ptr, "tpLL", unknowns[u], &unc_size);
22979                       if (unc_buff == NULL)
22980                         {
22981                           SDL_FreeSurface(org_surf);
22982                           return;
22983                         }
22984                       else
22985                         {
22986                           fi = fmemopen(unc_buff, unc_size, "rb");
22987                           if (fi == NULL)
22988                             {
22989                               fprintf(stderr, "Can't recover the label data embedded in %s, error in create file stream\n\n",
22990                                      fname);
22991                               fclose(fp);
22992                               png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
22993                               free(unc_buff);
22994                               SDL_FreeSurface(org_surf);
22995 
22996                               draw_tux_text(TUX_OOPS, strerror(errno), 0);
22997                               return;
22998                             }
22999                           else
23000                             load_info_about_label_surface(fi);
23001                         }
23002 
23003                       free(unc_buff);
23004                       ldata = TRUE;
23005 #ifdef DEBUG
23006                       printf("Out of label data\n");
23007 #endif
23008                     }
23009                 }
23010             }
23011           /* Apply the original canvas */
23012           if (ldelta && ldata)
23013             autoscale_copy_smear_free(org_surf, canvas, SDL_BlitSurface);
23014           else
23015             SDL_FreeSurface(org_surf);
23016 
23017           /* Third run, back and foreground */
23018           if (have_background || have_foreground)
23019             {
23020               for (u = 0; u < num_unknowns; u++)
23021                 {
23022                   if ((starter_modified || !img_starter_bkgd) && chunk_is_valid("tpBG", unknowns[u]))
23023                     {
23024                       unc_buff = get_chunk_data(fp, fname, png_ptr, info_ptr, "tpBG", unknowns[u], &unc_size);
23025                       if (unc_buff == NULL)
23026                         return;
23027                       aux_surf =
23028                         SDL_CreateRGBSurface(0, ww, hh, canvas->format->BitsPerPixel,
23029                                              canvas->format->Rmask, canvas->format->Gmask, canvas->format->Gmask, 0);
23030                       if (aux_surf == NULL)
23031                         {
23032 #ifdef DEBUG
23033                           fprintf(stderr, "Can't recover the background data embedded in %s, error in create aux image\n\n",
23034                                  fname);
23035 #endif
23036                           fclose(fp);
23037                           png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
23038                           free(unc_buff);
23039 
23040                           draw_tux_text(TUX_OOPS, strerror(errno), 0);
23041 
23042                           free(unc_buff);
23043                           return;
23044                         }
23045                       SDL_LockSurface(aux_surf);
23046 
23047 #ifdef DEBUG
23048                       printf("Bkgd!!!\n");
23049 #endif
23050                       for (j = 0; j < hh; j++)
23051                         for (i = 0; i < ww; i++)
23052                           putpixels[aux_surf->format->BytesPerPixel] (aux_surf, i, j,
23053                                                                       SDL_MapRGB
23054                                                                       (aux_surf->format,
23055                                                                        unc_buff[3 * j * ww + 3 * i],
23056                                                                        unc_buff[3 * j * ww + 3 * i + 1],
23057                                                                        unc_buff[3 * j * ww + 3 * i + 2]));
23058                       SDL_UnlockSurface(aux_surf);
23059 
23060                       if (img_starter_bkgd)
23061                         SDL_FreeSurface(img_starter_bkgd);
23062 
23063                       if (aux_surf->w != canvas->w || aux_surf->h != canvas->h)
23064                         {
23065                           img_starter_bkgd = SDL_CreateRGBSurface(SDL_SWSURFACE,
23066                                                                   canvas->w,
23067                                                                   canvas->h,
23068                                                                   canvas->format->BitsPerPixel,
23069                                                                   canvas->format->Rmask,
23070                                                                   canvas->format->Gmask, canvas->format->Bmask, 0);
23071 
23072                           autoscale_copy_smear_free(aux_surf, img_starter_bkgd, SDL_BlitSurface);
23073                         }
23074                       free(unc_buff);
23075                     }
23076 
23077                   if ((starter_modified || !img_starter) && chunk_is_valid("tpFG", unknowns[u]))
23078                     {
23079 #ifdef DEBUG
23080                       printf("Frgd!!!\n");
23081 #endif
23082 
23083                       unc_buff = get_chunk_data(fp, fname, png_ptr, info_ptr, "tpFG", unknowns[u], &unc_size);
23084                       if (unc_buff == NULL)
23085                         return;
23086 
23087                       aux_surf = SDL_CreateRGBSurface(canvas->flags, ww, hh,
23088                                                       canvas->format->BitsPerPixel,
23089                                                       canvas->format->Rmask,
23090                                                       canvas->format->Gmask, canvas->format->Gmask, TPAINT_AMASK);
23091                       if (aux_surf == NULL)
23092                         {
23093                           fprintf(stderr, "Can't recover the foreground data embedded in %s, error in create aux image\n\n",
23094                                  fname);
23095                           fclose(fp);
23096                           png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
23097                           free(unc_buff);
23098 
23099                           draw_tux_text(TUX_OOPS, strerror(errno), 0);
23100 
23101                           free(unc_buff);
23102                           return;
23103                         }
23104 
23105                       SDL_LockSurface(aux_surf);
23106                       for (j = 0; j < hh; j++)
23107                         for (i = 0; i < ww; i++)
23108                           {
23109                             putpixels[aux_surf->format->BytesPerPixel] (aux_surf, i, j,
23110                                                                         SDL_MapRGBA
23111                                                                         (aux_surf->format,
23112                                                                          unc_buff[4 * j * ww + 4 * i],
23113                                                                          unc_buff[4 * j * ww + 4 * i + 1],
23114                                                                          unc_buff[4 * j * ww + 4 * i + 2],
23115                                                                          unc_buff[4 * j * ww + 4 * i + 3]));
23116                           }
23117                       SDL_UnlockSurface(aux_surf);
23118 
23119                       if (img_starter)
23120                         SDL_FreeSurface(img_starter);
23121 
23122 
23123                       /* Code adapted from load_starter */
23124                       img_starter = SDL_CreateRGBSurface(canvas->flags,
23125                                                          canvas->w, canvas->h,
23126                                                          canvas->format->BitsPerPixel,
23127                                                          canvas->format->Rmask,
23128                                                          canvas->format->Gmask, canvas->format->Bmask, TPAINT_AMASK);
23129 
23130                       /* 3rd arg ignored for RGBA surfaces */
23131                       SDL_SetAlpha(aux_surf, SDL_RLEACCEL, SDL_ALPHA_OPAQUE);
23132                       autoscale_copy_smear_free(aux_surf, img_starter, NondefectiveBlit);
23133                       SDL_SetAlpha(img_starter, SDL_RLEACCEL | SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
23134 
23135                       free(unc_buff);
23136                     }
23137                 }
23138             }
23139         }
23140     }
23141 
23142   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
23143   fclose(fp);
23144 }
23145 
23146 
23147 /* ================================================================================== */
23148 
23149 #if !defined(WIN32) && !defined(__APPLE__) && !defined(__BEOS__) && !defined(__HAIKU__)
23150 /**
23151  * FIXME
23152  */
show_available_papersizes(int exitcode)23153 static void show_available_papersizes(int exitcode)
23154 {
23155   FILE *fi = exitcode ? stderr : stdout;
23156   const struct paper *ppr;
23157   int cnt;
23158 
23159   fprintf(fi, "Usage: %s [--papersize PAPERSIZE]\n", progname);
23160   fprintf(fi, "\n");
23161   fprintf(fi, "PAPERSIZE may be one of:\n");
23162 
23163   ppr = paperfirst();
23164   cnt = 0;
23165 
23166   while (ppr != NULL)
23167     {
23168       fprintf(fi, "\t%s", papername(ppr));
23169       cnt++;
23170       if (cnt == 5)
23171         {
23172           cnt = 0;
23173           fprintf(fi, "\n");
23174         }
23175 
23176       ppr = papernext(ppr);
23177     }
23178 
23179   fprintf(fi, "\n");
23180   if (cnt != 0)
23181     fprintf(fi, "\n");
23182 }
23183 #endif
23184 
23185 /* ================================================================================== */
23186 
23187 /**
23188  * FIXME
23189  */
parse_file_options(struct cfginfo * restrict tmpcfg,const char * filename)23190 static void parse_file_options(struct cfginfo *restrict tmpcfg, const char *filename)
23191 {
23192   char str[256];
23193   char *arg;
23194   FILE *fi = fopen(filename, "r");
23195 
23196   if (!fi)
23197     return;
23198 
23199   while (fgets(str, sizeof(str), fi))
23200     {
23201       if (!isalnum(*str))
23202         continue;
23203       strip_trailing_whitespace(str);
23204       arg = strchr(str, '=');
23205       if (arg)
23206         *arg++ = '\0';
23207 
23208 #ifdef __linux__
23209       /* Perform shell expansion */
23210       wordexp_t result;
23211 
23212       wordexp(arg, &result, 0);
23213       arg = strdup(result.we_wordv[0]);
23214       wordfree(&result);
23215 #endif
23216 
23217       /* FIXME: leaking mem here, but the trouble is that these
23218          strings get mixed in with ones from .data and .rodata
23219          and free() isn't smart about the situation -- also some
23220          of the strings end up being kept around */
23221       parse_one_option(tmpcfg, str, strdup(arg), filename);
23222 #ifdef __linux__
23223       free(arg);
23224 #endif
23225     }
23226   fclose(fi);
23227 
23228   /* These interact in horrid ways. */
23229   if (tmpcfg->parsertmp_lang && tmpcfg->parsertmp_locale)
23230     fprintf(stderr,
23231             "Warning: option 'lang=%s' overrides option 'locale=%s' in '%s'\n",
23232             tmpcfg->parsertmp_lang, tmpcfg->parsertmp_locale, filename);
23233   if (tmpcfg->parsertmp_lang)
23234     tmpcfg->parsertmp_locale = PARSE_CLOBBER;
23235   else if (tmpcfg->parsertmp_locale)
23236     tmpcfg->parsertmp_lang = PARSE_CLOBBER;
23237 }
23238 
23239 /**
23240  * FIXME
23241  */
parse_argv_options(struct cfginfo * restrict tmpcfg,char * argv[])23242 static void parse_argv_options(struct cfginfo *restrict tmpcfg, char *argv[])
23243 {
23244   char *str, *arg;
23245 
23246   const char *short_synonyms[][2] = {
23247     {"-c", "copying"},
23248     {"-h", "help"},
23249     {"-u", "usage"},
23250     {"-v", "version"},
23251     {"-vv", "verbose-version"},
23252     {"-l", "lang"},
23253     {"-L", "locale"},
23254     {"-b", "startblank"},
23255     {"-f", "fullscreen"},
23256     {"-m", "mixedcase"},
23257     {"-p", "noprint"},
23258     {"-q", "nosound"},
23259     {"-s", "simpleshapes"},
23260     {"-w", "windowed"},
23261     {"-x", "noquit"},
23262     {NULL, NULL}
23263   };
23264 
23265   while ((str = *++argv))
23266     {
23267       if (str[0] == '-' && str[1] != '-' && str[1] != '\0')
23268         {
23269           int i, found = 0;
23270 
23271           for (i = 0; short_synonyms[i][0] != NULL && !found; i++)
23272             {
23273               if (strcmp(short_synonyms[i][0], str) == 0)
23274                 {
23275                   if (argv[1] && argv[1][0] != '-')
23276                     arg = *++argv;
23277                   else
23278                     arg = NULL;
23279                   parse_one_option(tmpcfg, short_synonyms[i][1], arg, NULL);
23280                   found = 1;
23281                 }
23282             }
23283           if (found)
23284             continue;
23285         }
23286       else if (str[0] == '-' && str[1] == '-' && str[2])
23287         {
23288           str += 2;
23289           arg = strchr(str, '=');
23290           if (arg)
23291             *arg++ = '\0';
23292           else if (argv[1] && argv[1][0] != '-')
23293             arg = *++argv;
23294           parse_one_option(tmpcfg, str, arg, NULL);
23295           continue;
23296         }
23297       fprintf(stderr, "%s is not understood\n", *argv);
23298       show_usage(63);
23299       exit(1);
23300     }
23301 
23302   /* These interact in horrid ways. */
23303   if (tmpcfg->parsertmp_lang && tmpcfg->parsertmp_locale)
23304     {
23305       fprintf(stderr,
23306               "Error: command line option '--lang=%s' overrides option '--locale=%s'\n",
23307               tmpcfg->parsertmp_lang, tmpcfg->parsertmp_locale);
23308       exit(92);
23309     }
23310   if (tmpcfg->parsertmp_lang)
23311     tmpcfg->parsertmp_locale = PARSE_CLOBBER;
23312   else if (tmpcfg->parsertmp_locale)
23313     tmpcfg->parsertmp_lang = PARSE_CLOBBER;
23314 }
23315 
23316 /**
23317  * FIXME
23318  */
23319 /* merge two configs, with the winner taking priority */
tmpcfg_merge(struct cfginfo * loser,const struct cfginfo * winner)23320 static void tmpcfg_merge(struct cfginfo *loser, const struct cfginfo *winner)
23321 {
23322   int i = CFGINFO_MAXOFFSET / sizeof(char *);
23323 
23324   while (i--)
23325     {
23326       const char *cfgitem;
23327 
23328       memcpy(&cfgitem, i * sizeof(const char *) + (const char *)winner, sizeof cfgitem);
23329       if (!cfgitem)
23330         continue;
23331       memcpy(i * sizeof(const char *) + (char *)loser, &cfgitem, sizeof cfgitem);
23332     }
23333 }
23334 
23335 /**
23336  * FIXME
23337  */
setup_config(char * argv[])23338 static void setup_config(char *argv[])
23339 {
23340   char str[128];
23341   char * picturesdir;
23342 
23343 #ifndef _WIN32
23344   const char *home = getenv("HOME");
23345 #endif
23346 
23347   struct cfginfo tmpcfg_usr;
23348   struct cfginfo tmpcfg_cmd;
23349   struct cfginfo tmpcfg;
23350 
23351   memset(&tmpcfg_usr, '\0', sizeof tmpcfg_usr);
23352   memset(&tmpcfg_cmd, '\0', sizeof tmpcfg_cmd);
23353   memset(&tmpcfg, '\0', sizeof tmpcfg);
23354 
23355   parse_argv_options(&tmpcfg_cmd, argv);
23356 
23357 #if defined(__APPLE__)
23358   /* EP added this conditional section for Mac to allow for a config in
23359      the current directory, that supersedes sys and user configs */
23360   /* Mac OS X: Use a "tuxpaint.cfg" file in the current folder */
23361   struct cfginfo tmpcfg_curdir;
23362 
23363   memset(&tmpcfg_curdir, '\0', sizeof tmpcfg_curdir);
23364   parse_file_options(&tmpcfg_curdir, "./tuxpaint.cfg");
23365   tmpcfg_merge(&tmpcfg_curdir, &tmpcfg_cmd);
23366 #endif
23367 
23368   /* Set default options: */
23369 
23370 #ifndef _WIN32
23371   if (!home)
23372     {
23373       /* Woah, don't know where $HOME is? */
23374       fprintf(stderr, "Error: You have no $HOME environment variable!\n");
23375       exit(1);
23376     }
23377 #endif
23378 
23379   /* == SAVEDIR == */
23380   if (tmpcfg_cmd.savedir)
23381     savedir = strdup(tmpcfg_cmd.savedir);
23382   else
23383     {
23384 #ifdef _WIN32
23385       savedir = GetDefaultSaveDir("TuxPaint");
23386 #elif __BEOS__
23387       savedir = strdup("./tuxpaint");
23388 #elif __HAIKU__
23389       /* Haiku: Make use of find_directory() */
23390       dev_t volume = dev_for_path("/boot");
23391       char buffer[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
23392       status_t result;
23393 
23394       result = find_directory(B_USER_SETTINGS_DIRECTORY, volume, false, buffer, sizeof(buffer));
23395       asprintf((char **)&savedir, "%s/%s", buffer, "TuxPaint");
23396 #elif __APPLE__
23397       savedir = strdup(apple_preferencesPath());
23398 #else
23399       int tmp;
23400 
23401       tmp = asprintf((char **)&savedir, "%s/%s", home, ".tuxpaint");
23402       if (tmp < 0)
23403         {
23404           fprintf(stderr, "Can't set savedir\n");
23405           exit(91);
23406         }
23407 #endif
23408     }
23409 
23410   /* == EXPORTDIR == */
23411   if (tmpcfg_cmd.exportdir)
23412     exportdir = strdup(tmpcfg_cmd.exportdir);
23413   else
23414     {
23415       /* FIXME: Need assist for:
23416          * __BEOS__
23417          * __HAIKU__
23418       */
23419 #ifdef WIN32
23420       picturesdir = GetUserImageDir();
23421 #elif __APPLE__
23422       picturesdir = strdup(apple_picturesPath());
23423 #else
23424       picturesdir = get_xdg_user_dir("PICTURES", "Pictures");
23425 #endif
23426       safe_snprintf(str, sizeof(str), "%s/TuxPaint", picturesdir);
23427       free(picturesdir);
23428       exportdir = strdup(str);
23429     }
23430 
23431   /* Load options from user's own configuration (".rc" / ".cfg") file: */
23432 
23433 #if defined(_WIN32)
23434   /* Default local config file in users savedir directory on Windows */
23435   safe_snprintf(str, sizeof(str), "%s/tuxpaint.cfg", savedir);       /* FIXME */
23436 #elif defined(__BEOS__) || defined(__HAIKU__)
23437   /* BeOS: Use a "tuxpaint.cfg" file: */
23438   strcpy(str, "tuxpaint.cfg"); /* safe; sufficient size */
23439 #elif defined(__APPLE__)
23440   /* macOS, iOS: Use a "tuxpaint.cfg" file in the Tux Paint application support folder */
23441   safe_snprintf(str, sizeof(str), "%s/tuxpaint.cfg", apple_preferencesPath());
23442 
23443 #else
23444   /* Linux and other Unixes:  Use 'rc' style (~/.tuxpaintrc) */
23445   /* it should it be "~/.tuxpaint/tuxpaintrc" instead, but too late now */
23446   safe_snprintf(str, sizeof(str), "%s/.tuxpaintrc", home);
23447 #endif
23448   parse_file_options(&tmpcfg_usr, str);
23449 
23450 
23451 
23452 #if defined(__APPLE__)
23453   /* EP added this conditional section for Mac */
23454   tmpcfg_merge(&tmpcfg_usr, &tmpcfg_curdir);
23455 #else
23456   tmpcfg_merge(&tmpcfg_usr, &tmpcfg_cmd);
23457 #endif
23458 
23459   if (tmpcfg_usr.parsertmp_sysconfig != PARSE_NO)
23460     {
23461       struct cfginfo tmpcfg_sys;
23462 
23463       memset(&tmpcfg_sys, '\0', sizeof tmpcfg_sys);
23464 #ifdef _WIN32
23465       /* global config file in the application directory */
23466       parse_file_options(&tmpcfg_sys, "tuxpaint.cfg");
23467 #elif defined(__APPLE__)
23468       /* EP added this conditional section for Mac to fix
23469          folder & extension inconsistency with Tux Paint Config application) */
23470       /* macOS, iOS: Use a "tuxpaint.cfg" file in the *global* Tux Paint
23471          application support folder */
23472       safe_snprintf(str, sizeof(str), "%s/tuxpaint.cfg", apple_globalPreferencesPath());
23473       parse_file_options(&tmpcfg_sys, str);
23474 #else
23475       /* normally /etc/tuxpaint/tuxpaint.conf */
23476       parse_file_options(&tmpcfg_sys, CONFDIR "tuxpaint.conf");
23477 #endif
23478       tmpcfg_merge(&tmpcfg, &tmpcfg_sys);
23479     }
23480   tmpcfg_merge(&tmpcfg, &tmpcfg_usr);
23481 
23482   if (tmpcfg.savedir)
23483     {
23484       free((char *)savedir);
23485       savedir = tmpcfg.savedir;
23486     }
23487 
23488   datadir = tmpcfg.datadir ? tmpcfg.datadir : savedir;
23489 
23490   exportdir = tmpcfg.exportdir ? tmpcfg.exportdir : exportdir;
23491 
23492   if (tmpcfg.parsertmp_lang == PARSE_CLOBBER)
23493     tmpcfg.parsertmp_lang = NULL;
23494   if (tmpcfg.parsertmp_locale == PARSE_CLOBBER)
23495     tmpcfg.parsertmp_locale = NULL;
23496   button_label_y_nudge = setup_i18n(tmpcfg.parsertmp_lang, tmpcfg.parsertmp_locale, &num_wished_langs);
23497 
23498 
23499   /* FIXME: most of this is not required before starting the font scanner */
23500 
23501 #ifdef PAPER_H
23502   if (tmpcfg_cmd.papersize && !strcmp(tmpcfg_cmd.papersize, "help"))
23503     show_available_papersizes(0);
23504 #endif
23505 
23506 #define SETBOOL(x) do{ if(tmpcfg.x) x = (tmpcfg.x==PARSE_YES); }while(0)
23507   SETBOOL(all_locale_fonts);
23508   SETBOOL(autosave_on_quit);
23509   SETBOOL(disable_label);
23510   SETBOOL(disable_magic_controls);
23511   SETBOOL(disable_shape_controls);
23512   SETBOOL(disable_print);
23513   SETBOOL(disable_quit);
23514   SETBOOL(disable_save);
23515   SETBOOL(disable_screensaver);
23516   SETBOOL(disable_stamp_controls);
23517   SETBOOL(dont_do_xor);
23518   SETBOOL(dont_load_stamps);
23519   SETBOOL(fullscreen);
23520   SETBOOL(grab_input);
23521   SETBOOL(hide_cursor);
23522   SETBOOL(keymouse);
23523   SETBOOL(mirrorstamps);
23524   SETBOOL(native_screensize);
23525   SETBOOL(new_colors_last);
23526   SETBOOL(no_button_distinction);
23527   SETBOOL(no_fancy_cursors);
23528   SETBOOL(no_system_fonts);
23529   SETBOOL(noshortcuts);
23530   SETBOOL(ok_to_use_lockfile);
23531   SETBOOL(only_uppercase);
23532   SETBOOL(simple_shapes);
23533   SETBOOL(start_blank);
23534   SETBOOL(use_print_config);
23535   SETBOOL(use_sound);
23536   SETBOOL(use_stereo);
23537   SETBOOL(wheely);
23538   SETBOOL(mouseaccessibility);
23539   SETBOOL(onscreen_keyboard);
23540   SETBOOL(onscreen_keyboard_disable_change);
23541   SETBOOL(_promptless_save_over);
23542   SETBOOL(_promptless_save_over_new);
23543   SETBOOL(_promptless_save_over_ask);
23544 #undef SETBOOL
23545 
23546   if (tmpcfg.parsertmp_windowsize)
23547     {
23548       char *endp1;
23549       char *endp2;
23550       int w = strtoul(tmpcfg.parsertmp_windowsize, &endp1, 10);
23551       int h = strtoul(endp1 + 1, &endp2, 10);
23552 
23553       if (tmpcfg.parsertmp_windowsize == endp1 || endp1 + 1 == endp2 || *endp1 != 'x' || *endp2)
23554         {
23555           fprintf(stderr, "Window size '%s' is not understood.\n", tmpcfg.parsertmp_windowsize);
23556           exit(97);
23557         }
23558       if (w < 500 || w > 32000 || h < 480 || h > 32000 || h > w * 3 || w > h * 4)
23559         {
23560           fprintf(stderr, "Window size '%s' is not reasonable.\n", tmpcfg.parsertmp_windowsize);
23561           exit(93);
23562         }
23563       WINDOW_WIDTH = w;
23564       WINDOW_HEIGHT = h;
23565     }
23566   if (tmpcfg.parsertmp_fullscreen_native)
23567     {
23568       /* should conflict with other fullscreen/native_screensize setting? */
23569       if (!strcmp(tmpcfg.parsertmp_fullscreen_native, "native"))
23570         native_screensize = 1;
23571       fullscreen = strcmp(tmpcfg.parsertmp_fullscreen_native, "no");
23572     }
23573   if (tmpcfg.button_size)
23574     {
23575       if (strtof(tmpcfg.button_size, NULL) < 24 || strtof(tmpcfg.button_size, NULL) > 192)
23576         {
23577           fprintf(stderr, "Button size (now %s) must be between 24 and 192.\n", tmpcfg.button_size);
23578           exit(1);
23579         }
23580       button_scale = strtof(tmpcfg.button_size, NULL) / ORIGINAL_BUTTON_SIZE;
23581     }
23582   else
23583     button_scale = 1;
23584   if (tmpcfg.colors_rows)
23585     {
23586       if (strtof(tmpcfg.colors_rows, NULL) > 3)
23587         {
23588           fprintf(stderr, "Color rows (now %s) must be between 1 and 3.\n", tmpcfg.colors_rows);
23589           exit(1);
23590         }
23591       colors_rows = strtof(tmpcfg.colors_rows, NULL);
23592     }
23593   else
23594     colors_rows = 1;
23595   if (tmpcfg.stamp_size_override)
23596     {
23597       if (!strcmp(tmpcfg.stamp_size_override, "default"))
23598         stamp_size_override = -1;
23599       else
23600         {
23601           /* FIXME: needs to be a scaling factor */
23602           stamp_size_override = atoi(tmpcfg.stamp_size_override);
23603           if (stamp_size_override > 10)
23604             stamp_size_override = 10;
23605         }
23606     }
23607   /* FIXME: make this dynamic (accelerometer or OLPC XO-1 rotation button) */
23608   if (tmpcfg.rotate_orientation)
23609     rotate_orientation = !strcmp(tmpcfg.rotate_orientation, "portrait");        /* alternative is "landscape" */
23610   if (tmpcfg.colorfile)
23611     safe_strncpy(colorfile, tmpcfg.colorfile, sizeof(colorfile));
23612   if (tmpcfg.print_delay)
23613     {
23614       print_delay = atoi(tmpcfg.print_delay);
23615       last_print_time = -print_delay;
23616     }
23617 #ifdef PAPER_H
23618   if (tmpcfg.printcommand)
23619     printcommand = tmpcfg.printcommand;
23620   if (tmpcfg.altprintcommand)
23621     altprintcommand = tmpcfg.altprintcommand;
23622 #endif
23623   if (tmpcfg.alt_print_command_default)
23624     {
23625       /* FIXME: probably need extra variables */
23626       if (!strcmp(tmpcfg.alt_print_command_default, "always"))
23627         alt_print_command_default = ALTPRINT_ALWAYS;
23628       else if (!strcmp(tmpcfg.alt_print_command_default, "never"))
23629         alt_print_command_default = ALTPRINT_NEVER;
23630       else
23631         alt_print_command_default = ALTPRINT_MOD;       /* default ("mod") */
23632     }
23633 #ifdef PAPER_H
23634   if (tmpcfg.papersize)
23635     papersize = tmpcfg.papersize;
23636 #endif
23637   if (tmpcfg.joystick_dev)
23638     {
23639       if (strcmp(tmpcfg.joystick_dev, "list") == 0)
23640         {
23641           joystick_dev = -1;
23642         }
23643       else
23644         {
23645           if (strtof(tmpcfg.joystick_dev, NULL) < 0 || strtof(tmpcfg.joystick_dev, NULL) > 100)
23646             {
23647               fprintf(stderr, "Joystick dev (now %s) must be between 0 and 100.\n", tmpcfg.joystick_dev);
23648               exit(1);
23649             }
23650           joystick_dev = strtof(tmpcfg.joystick_dev, NULL);
23651         }
23652     }
23653   if (tmpcfg.joystick_slowness)
23654     {
23655       if (strtof(tmpcfg.joystick_slowness, NULL) < 0 || strtof(tmpcfg.joystick_slowness, NULL) > 500)
23656         {
23657           fprintf(stderr, "Joystick slowness (now %s) must be between 0 and 500.\n", tmpcfg.joystick_slowness);
23658           exit(1);
23659         }
23660       joystick_slowness = strtof(tmpcfg.joystick_slowness, NULL);
23661     }
23662   if (tmpcfg.joystick_lowthreshold)
23663     {
23664       if (strtof(tmpcfg.joystick_lowthreshold, NULL) < 0 || strtof(tmpcfg.joystick_lowthreshold, NULL) > 32766)
23665         {
23666           /* FIXME: Find better exit code */
23667           fprintf(stderr, "Joystick lower threshold (now %s)  must be between 0 and 32766", tmpcfg.joystick_lowthreshold);
23668           exit(1);
23669         }
23670       joystick_low_threshold = strtof(tmpcfg.joystick_lowthreshold, NULL);
23671     }
23672   if (tmpcfg.joystick_maxsteps)
23673     {
23674       if (strtof(tmpcfg.joystick_maxsteps, NULL) < 1 || strtof(tmpcfg.joystick_maxsteps, NULL) > 7)
23675         {
23676           /* FIXME: Find better exit code */
23677           fprintf(stderr, "Joystick max steps (now %s)  must be between 1 and 7", tmpcfg.joystick_maxsteps);
23678           exit(1);
23679         }
23680       joystick_maxsteps = strtof(tmpcfg.joystick_maxsteps, NULL);
23681     }
23682   if (tmpcfg.joystick_hat_slowness)
23683     {
23684       if (strtof(tmpcfg.joystick_hat_slowness, NULL) < 0 || strtof(tmpcfg.joystick_hat_slowness, NULL) > 500)
23685         {
23686           fprintf(stderr, "Joystick hat slowness (now %s) must be between 0 and 500.\n", tmpcfg.joystick_hat_slowness);
23687           exit(1);
23688         }
23689       joystick_hat_slowness = strtof(tmpcfg.joystick_hat_slowness, NULL);
23690     }
23691   if (tmpcfg.joystick_hat_timeout)
23692     {
23693       if (strtof(tmpcfg.joystick_hat_timeout, NULL) < 0 || strtof(tmpcfg.joystick_hat_timeout, NULL) > 3000)
23694         {
23695           /* FIXME: Find better exit code */
23696           fprintf(stderr, "Joystick hat timeout (now %s)  must be between 0 and 3000", tmpcfg.joystick_hat_timeout);
23697           exit(1);
23698         }
23699       joystick_hat_timeout = strtof(tmpcfg.joystick_hat_timeout, NULL);
23700     }
23701   if (tmpcfg.joystick_button_escape)
23702     {
23703       if (strtof(tmpcfg.joystick_button_escape, NULL) < 0 || strtof(tmpcfg.joystick_button_escape, NULL) > 254)
23704         {
23705           /* FIXME: Find better exit code */
23706           fprintf(stderr, "Joystick button escape shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_escape);
23707           exit(1);
23708         }
23709       joystick_button_escape = strtof(tmpcfg.joystick_button_escape, NULL);
23710     }
23711   if (tmpcfg.joystick_button_selectbrushtool)
23712     {
23713       if (strtof(tmpcfg.joystick_button_selectbrushtool, NULL) < 0
23714           || strtof(tmpcfg.joystick_button_selectbrushtool, NULL) > 254)
23715         {
23716           /* FIXME: Find better exit code */
23717           fprintf(stderr, "Joystick button brush tool shortcurt (now %s)  must be between 0 and 254",
23718                  tmpcfg.joystick_button_selectbrushtool);
23719           exit(1);
23720         }
23721       joystick_button_selectbrushtool = strtof(tmpcfg.joystick_button_selectbrushtool, NULL);
23722     }
23723   if (tmpcfg.joystick_button_selectstamptool)
23724     {
23725       if (strtof(tmpcfg.joystick_button_selectstamptool, NULL) < 0
23726           || strtof(tmpcfg.joystick_button_selectstamptool, NULL) > 254)
23727         {
23728           /* FIXME: Find better exit code */
23729           fprintf(stderr, "Joystick button stamp tool shortcurt (now %s)  must be between 0 and 254",
23730                  tmpcfg.joystick_button_selectstamptool);
23731           exit(1);
23732         }
23733       joystick_button_selectstamptool = strtof(tmpcfg.joystick_button_selectstamptool, NULL);
23734     }
23735   if (tmpcfg.joystick_button_selectlinestool)
23736     {
23737       if (strtof(tmpcfg.joystick_button_selectlinestool, NULL) < 0
23738           || strtof(tmpcfg.joystick_button_selectlinestool, NULL) > 254)
23739         {
23740           /* FIXME: Find better exit code */
23741           fprintf(stderr, "Joystick button lines tool shortcurt (now %s)  must be between 0 and 254",
23742                  tmpcfg.joystick_button_selectlinestool);
23743           exit(1);
23744         }
23745       joystick_button_selectlinestool = strtof(tmpcfg.joystick_button_selectlinestool, NULL);
23746     }
23747   if (tmpcfg.joystick_button_selectshapestool)
23748     {
23749       if (strtof(tmpcfg.joystick_button_selectshapestool, NULL) < 0
23750           || strtof(tmpcfg.joystick_button_selectshapestool, NULL) > 254)
23751         {
23752           /* FIXME: Find better exit code */
23753           fprintf(stderr, "Joystick button shapes tool shortcurt (now %s)  must be between 0 and 254",
23754                  tmpcfg.joystick_button_selectshapestool);
23755           exit(1);
23756         }
23757       joystick_button_selectshapestool = strtof(tmpcfg.joystick_button_selectshapestool, NULL);
23758     }
23759   if (tmpcfg.joystick_button_selecttexttool)
23760     {
23761       if (strtof(tmpcfg.joystick_button_selecttexttool, NULL) < 0
23762           || strtof(tmpcfg.joystick_button_selecttexttool, NULL) > 254)
23763         {
23764           /* FIXME: Find better exit code */
23765           fprintf(stderr, "Joystick button text tool shortcurt (now %s)  must be between 0 and 254",
23766                  tmpcfg.joystick_button_selecttexttool);
23767           exit(1);
23768         }
23769       joystick_button_selecttexttool = strtof(tmpcfg.joystick_button_selecttexttool, NULL);
23770     }
23771   if (tmpcfg.joystick_button_selectlabeltool)
23772     {
23773       if (strtof(tmpcfg.joystick_button_selectlabeltool, NULL) < 0
23774           || strtof(tmpcfg.joystick_button_selectlabeltool, NULL) > 254)
23775         {
23776           /* FIXME: Find better exit code */
23777           fprintf(stderr, "Joystick button label tool shortcurt (now %s)  must be between 0 and 254",
23778                  tmpcfg.joystick_button_selectlabeltool);
23779           exit(1);
23780         }
23781       joystick_button_selectlabeltool = strtof(tmpcfg.joystick_button_selectlabeltool, NULL);
23782     }
23783   if (tmpcfg.joystick_button_selectmagictool)
23784     {
23785       if (strtof(tmpcfg.joystick_button_selectmagictool, NULL) < 0
23786           || strtof(tmpcfg.joystick_button_selectmagictool, NULL) > 254)
23787         {
23788           /* FIXME: Find better exit code */
23789           fprintf(stderr, "Joystick button magic tool shortcurt (now %s)  must be between 0 and 254",
23790                  tmpcfg.joystick_button_selectmagictool);
23791           exit(1);
23792         }
23793       joystick_button_selectmagictool = strtof(tmpcfg.joystick_button_selectmagictool, NULL);
23794     }
23795   if (tmpcfg.joystick_button_undo)
23796     {
23797       if (strtof(tmpcfg.joystick_button_undo, NULL) < 0 || strtof(tmpcfg.joystick_button_undo, NULL) > 254)
23798         {
23799           /* FIXME: Find better exit code */
23800           fprintf(stderr, "Joystick button undo shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_undo);
23801           exit(1);
23802         }
23803       joystick_button_undo = strtof(tmpcfg.joystick_button_undo, NULL);
23804     }
23805   if (tmpcfg.joystick_button_redo)
23806     {
23807       if (strtof(tmpcfg.joystick_button_redo, NULL) < 0 || strtof(tmpcfg.joystick_button_redo, NULL) > 254)
23808         {
23809           /* FIXME: Find better exit code */
23810           fprintf(stderr, "Joystick button redo shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_redo);
23811           exit(1);
23812         }
23813       joystick_button_redo = strtof(tmpcfg.joystick_button_redo, NULL);
23814     }
23815   if (tmpcfg.joystick_button_selecterasertool)
23816     {
23817       if (strtof(tmpcfg.joystick_button_selecterasertool, NULL) < 0
23818           || strtof(tmpcfg.joystick_button_selecterasertool, NULL) > 254)
23819         {
23820           /* FIXME: Find better exit code */
23821           fprintf(stderr, "Joystick button eraser tool shortcurt (now %s)  must be between 0 and 254",
23822                  tmpcfg.joystick_button_selecterasertool);
23823           exit(1);
23824         }
23825       joystick_button_selecterasertool = strtof(tmpcfg.joystick_button_selecterasertool, NULL);
23826     }
23827   if (tmpcfg.joystick_button_new)
23828     {
23829       if (strtof(tmpcfg.joystick_button_new, NULL) < 0 || strtof(tmpcfg.joystick_button_new, NULL) > 254)
23830         {
23831           /* FIXME: Find better exit code */
23832           fprintf(stderr, "Joystick button new shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_new);
23833           exit(1);
23834         }
23835       joystick_button_new = strtof(tmpcfg.joystick_button_new, NULL);
23836     }
23837   if (tmpcfg.joystick_button_open)
23838     {
23839       if (strtof(tmpcfg.joystick_button_open, NULL) < 0 || strtof(tmpcfg.joystick_button_open, NULL) > 254)
23840         {
23841           /* FIXME: Find better exit code */
23842           fprintf(stderr, "Joystick button open shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_open);
23843           exit(1);
23844         }
23845       joystick_button_open = strtof(tmpcfg.joystick_button_open, NULL);
23846     }
23847   if (tmpcfg.joystick_button_save)
23848     {
23849       if (strtof(tmpcfg.joystick_button_save, NULL) < 0 || strtof(tmpcfg.joystick_button_save, NULL) > 254)
23850         {
23851           /* FIXME: Find better exit code */
23852           fprintf(stderr, "Joystick button save shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_save);
23853           exit(1);
23854         }
23855       joystick_button_save = strtof(tmpcfg.joystick_button_save, NULL);
23856     }
23857   if (tmpcfg.joystick_button_pagesetup)
23858     {
23859       if (strtof(tmpcfg.joystick_button_pagesetup, NULL) < 0 || strtof(tmpcfg.joystick_button_pagesetup, NULL) > 254)
23860         {
23861           /* FIXME: Find better exit code */
23862           fprintf(stderr, "Joystick button page setup shortcurt (now %s)  must be between 0 and 254",
23863                  tmpcfg.joystick_button_pagesetup);
23864           exit(1);
23865         }
23866       joystick_button_pagesetup = strtof(tmpcfg.joystick_button_pagesetup, NULL);
23867     }
23868   if (tmpcfg.joystick_button_print)
23869     {
23870       if (strtof(tmpcfg.joystick_button_print, NULL) < 0 || strtof(tmpcfg.joystick_button_print, NULL) > 254)
23871         {
23872           /* FIXME: Find better exit code */
23873           fprintf(stderr, "Joystick button print shortcurt (now %s)  must be between 0 and 254", tmpcfg.joystick_button_print);
23874           exit(1);
23875         }
23876       joystick_button_print = strtof(tmpcfg.joystick_button_print, NULL);
23877     }
23878   if (tmpcfg.joystick_buttons_ignore)
23879     {
23880       char *token;
23881 
23882       token = strtok((char *) tmpcfg.joystick_buttons_ignore, ",");
23883       while (token != NULL)
23884         {
23885           if (strtof(token, NULL) < 0 || strtof(token, NULL) > 254)
23886             {
23887               /* FIXME: Find better exit code */
23888               fprintf(stderr, "Joystick buttons must be between 0 and 254 (don't like %s)", tmpcfg.joystick_buttons_ignore);
23889               exit(1);
23890             }
23891           joystick_buttons_ignore[joystick_buttons_ignore_len++] = strtof(token, NULL);
23892           token = strtok(NULL, ",");
23893         }
23894     }
23895 
23896 
23897   /* having any of theese implies having onscreen keyboard setted */
23898   if (tmpcfg.onscreen_keyboard_layout)
23899     {
23900       onscreen_keyboard_layout = strdup(tmpcfg.onscreen_keyboard_layout);
23901       onscreen_keyboard = TRUE;
23902     }
23903 
23904   if (tmpcfg.onscreen_keyboard_disable_change)
23905     {
23906       onscreen_keyboard = TRUE;
23907     }
23908 
23909 #ifdef DEBUG
23910   printf("\n\nPromptless save:\nask: %d\nnew: %d\nover: %d\n\n", _promptless_save_over_ask, _promptless_save_over_new,
23911          _promptless_save_over);
23912 #endif
23913 
23914   if (_promptless_save_over_ask)
23915     {
23916       promptless_save = SAVE_OVER_PROMPT;
23917     }
23918   else if (_promptless_save_over_new)
23919     {
23920       promptless_save = SAVE_OVER_NO;
23921     }
23922   else if (_promptless_save_over)
23923     {
23924       promptless_save = SAVE_OVER_ALWAYS;
23925     }
23926 }
23927 
23928 
23929 /**
23930  * FIXME
23931  */
chdir_to_binary(char * argv0)23932 static void chdir_to_binary(char *argv0)
23933 {
23934   /*
23935      char curdir[256];
23936    */
23937   /* EP added this block to print out of current directory */
23938 
23939   /*
23940      getcwd(curdir, sizeof(curdir));
23941      #ifdef DEBUG
23942      printf("Binary Path: %s\nCurrent directory at launchtime: %s\n", argv0, curdir);
23943      #endif
23944    */
23945 
23946 #if defined(__BEOS__) || defined(WIN32) || defined(__APPLE__)
23947   /* if run from gui, like OpenTracker in BeOS or Explorer in Windows,
23948      find path from which binary was run and change dir to it
23949      so all files will be local :) */
23950   /* UPDATE (2004.10.06): Since SDL 1.2.7 SDL sets that path correctly,
23951      so this code wouldn't be needed if SDL was init before anything else,
23952      (just basic init, window shouldn't be needed). */
23953   /* UPDATE (2005 July 19): Enable and make work on Windows. Makes testing
23954      with MINGW/MSYS easier */
23955 
23956   if (argv0)
23957     {
23958       char *app_path = strdup(argv0);
23959       char *slash = strrchr(app_path, '/');
23960 
23961 #if defined(__MACOS__)
23962       // On macOS, execution is deep inside the app bundle.
23963       // E.g., "/Applications/TuxPaint.app/Contents/MacOS/tuxpaint"
23964       // But we want to point somewhere higher up, say to "Contents", so we can access
23965       // the resources in Resources folder. So move up one level.
23966       int levels = 1;           /* we need to back up 1 level */
23967 
23968       while ((levels-- > 0) && (slash))
23969         {
23970           *slash = '\0';        /* this overwrites the \0 at end of string */
23971           slash = strrchr(app_path, '/');       /* so we can carry on our back-pedaling... */
23972         }
23973 #endif
23974 
23975       if (!slash)
23976         {
23977           slash = strrchr(app_path, '\\');
23978         }
23979       if (slash)
23980         {
23981           *(slash + 1) = '\0';
23982           chdir(app_path);
23983         }
23984       free(app_path);
23985       /*
23986          getcwd(curdir, sizeof(curdir));
23987          printf("New current directory for runtime: %s\n", curdir);
23988        */
23989     }
23990 #else
23991   (void)argv0;
23992 #endif
23993 }
23994 
23995 /* ================================================================================== */
23996 
23997 /**
23998  * FIXME
23999  */
setup_colors(void)24000 static void setup_colors(void)
24001 {
24002   FILE *fi;
24003   int i, j;
24004 
24005   /* Load colors, or use default ones: */
24006 
24007   if (colorfile[0] != '\0')
24008     {
24009       fi = fopen(colorfile, "r");
24010       if (fi == NULL)
24011         {
24012           fprintf(stderr, "\nWarning, could not open color file. Using defaults.\n");
24013           perror(colorfile);
24014           colorfile[0] = '\0';
24015         }
24016       else
24017         {
24018           int max = 0, per = 5;
24019           char str[80], tmp_str[80];
24020           int count;
24021 
24022           NUM_COLORS = 0;
24023 
24024           do
24025             {
24026               if (fgets(str, sizeof(str), fi))
24027                 {
24028                   if (!feof(fi))
24029                     {
24030                       if (NUM_COLORS + 1 > max)
24031                         {
24032                           color_hexes = realloc(color_hexes, sizeof(Uint8 *) * (max + per));
24033                           color_names = realloc(color_names, sizeof(char *) * (max + per));
24034 
24035                           for (i = max; i < max + per; i++)
24036                             color_hexes[i] = malloc(sizeof(Uint8) * 3);
24037 
24038                           max = max + per;
24039                         }
24040 
24041                       while (str[strlen(str) - 1] == '\n' || str[strlen(str) - 1] == '\r')
24042                         str[strlen(str) - 1] = '\0';
24043 
24044                       if (str[0] == '#')
24045                         {
24046                           /* Hex form */
24047 
24048                           sscanf(str + 1, "%s %n", tmp_str, &count);
24049 
24050                           if (strlen(tmp_str) == 6)
24051                             {
24052                               /* Byte (#rrggbb) form */
24053 
24054                               color_hexes[NUM_COLORS][0] = (hex2dec(tmp_str[0]) << 4) + hex2dec(tmp_str[1]);
24055                               color_hexes[NUM_COLORS][1] = (hex2dec(tmp_str[2]) << 4) + hex2dec(tmp_str[3]);
24056                               color_hexes[NUM_COLORS][2] = (hex2dec(tmp_str[4]) << 4) + hex2dec(tmp_str[5]);
24057 
24058                               color_names[NUM_COLORS] = strdup(str + count);
24059                               NUM_COLORS++;
24060                             }
24061                           else if (strlen(tmp_str) == 3)
24062                             {
24063                               /* Nybble (#rgb) form */
24064 
24065                               color_hexes[NUM_COLORS][0] = (hex2dec(tmp_str[0]) << 4) + hex2dec(tmp_str[0]);
24066                               color_hexes[NUM_COLORS][1] = (hex2dec(tmp_str[1]) << 4) + hex2dec(tmp_str[1]);
24067                               color_hexes[NUM_COLORS][2] = (hex2dec(tmp_str[2]) << 4) + hex2dec(tmp_str[2]);
24068 
24069                               color_names[NUM_COLORS] = strdup(str + count);
24070                               NUM_COLORS++;
24071                             }
24072                         }
24073                       else
24074                         {
24075                           /* Assume int form */
24076 
24077                           if (sscanf(str, "%hu %hu %hu %n",
24078                                      (short unsigned int *)&(color_hexes[NUM_COLORS][0]),
24079                                      (short unsigned int *)&(color_hexes[NUM_COLORS][1]),
24080                                      (short unsigned int *)&(color_hexes[NUM_COLORS][2]), &count) >= 3)
24081                             {
24082                               color_names[NUM_COLORS] = strdup(str + count);
24083                               NUM_COLORS++;
24084                             }
24085                         }
24086                     }
24087                 }
24088             }
24089           while (!feof(fi));
24090 
24091           if (NUM_COLORS < 2)
24092             {
24093               fprintf(stderr, "\nWarning, not enough colors in color file. Using defaults.\n");
24094               fprintf(stderr, "%s\n", colorfile);
24095               colorfile[0] = '\0';
24096 
24097               for (i = 0; i < NUM_COLORS; i++)
24098                 {
24099                   free(color_names[i]);
24100                   free(color_hexes[i]);
24101                 }
24102 
24103               free(color_names);
24104               free(color_hexes);
24105             }
24106         }
24107     }
24108 
24109   /* Use default, if no file specified (or trouble opening it) */
24110 
24111   if (colorfile[0] == '\0')
24112     {
24113       NUM_COLORS = NUM_DEFAULT_COLORS;
24114 
24115       color_hexes = malloc(sizeof(Uint8 *) * NUM_COLORS);
24116       color_names = malloc(sizeof(char *) * NUM_COLORS);
24117 
24118       for (i = 0; i < NUM_COLORS; i++)
24119         {
24120           color_hexes[i] = malloc(sizeof(Uint8 *) * 3);
24121 
24122           for (j = 0; j < 3; j++)
24123             color_hexes[i][j] = default_color_hexes[i][j];
24124 
24125           color_names[i] = strdup(default_color_names[i]);
24126         }
24127     }
24128 
24129 
24130   /* Add "Color Select" color: */
24131 
24132   color_hexes = (Uint8 **) realloc(color_hexes, sizeof(Uint8 *) * (NUM_COLORS + 1));
24133 
24134   color_names = (char **)realloc(color_names, sizeof(char *) * (NUM_COLORS + 1));
24135   color_names[NUM_COLORS] = strdup(gettext("Select a color from your drawing."));
24136   color_hexes[NUM_COLORS] = (Uint8 *) malloc(sizeof(Uint8) * 3);
24137   color_hexes[NUM_COLORS][0] = 0;
24138   color_hexes[NUM_COLORS][1] = 0;
24139   color_hexes[NUM_COLORS][2] = 0;
24140   NUM_COLORS++;
24141 
24142   /* Add "Color Picker" color: */
24143 
24144   color_hexes = (Uint8 **) realloc(color_hexes, sizeof(Uint8 *) * (NUM_COLORS + 1));
24145 
24146   color_names = (char **)realloc(color_names, sizeof(char *) * (NUM_COLORS + 1));
24147   color_names[NUM_COLORS] = strdup(gettext("Pick a color."));
24148   color_hexes[NUM_COLORS] = (Uint8 *) malloc(sizeof(Uint8) * 3);
24149   color_hexes[NUM_COLORS][0] = 0;
24150   color_hexes[NUM_COLORS][1] = 0;
24151   color_hexes[NUM_COLORS][2] = 0;
24152   color_picker_x = 0;
24153   color_picker_y = 0;
24154   NUM_COLORS++;
24155 
24156 }
24157 
24158 /* ================================================================================== */
24159 
24160 /**
24161  * FIXME
24162  */
do_lock_file(void)24163 static void do_lock_file(void)
24164 {
24165   FILE *fi;
24166   char *lock_fname;
24167   time_t time_lock, time_now;
24168   char *homedirdir;
24169 
24170   /* Test for lockfile, if we're using one: */
24171 
24172   if (!ok_to_use_lockfile)
24173     return;
24174 
24175   /* Get the current time: */
24176 
24177   time_now = time(NULL);
24178 
24179   /* Look for the lockfile... */
24180 
24181 #ifndef WIN32
24182   lock_fname = get_fname("lockfile.dat", DIR_SAVE);
24183 #else
24184   lock_fname = get_temp_fname("lockfile.dat");
24185 #endif
24186 
24187   fi = fopen(lock_fname, "r");
24188   if (fi != NULL)
24189     {
24190       /* If it exists, read its contents: */
24191 
24192       if (fread(&time_lock, sizeof(time_t), 1, fi) > 0)
24193         {
24194           /* Has it not been 30 seconds yet? */
24195 
24196           if (time_now < time_lock + 30)
24197             {
24198               /* FIXME: Wrap in gettext() */
24199               printf
24200                 ("You have already started tuxpaint less than 30 seconds ago.\n"
24201                  "To prevent multiple executions by mistake, TuxPaint will not run\n"
24202                  "before 30 seconds have elapsed since it was last started.\n"
24203                  "\n" "You can also use the --nolockfile argument, see tuxpaint(1).\n\n");
24204 
24205               free(lock_fname);
24206 
24207               fclose(fi);
24208               exit(0);
24209             }
24210         }
24211 
24212       fclose(fi);
24213     }
24214 
24215 
24216   /* Okay to run; create/update the lockfile */
24217 
24218   /* (Make sure the directory exists, first!) */
24219   homedirdir = get_fname("", DIR_SAVE);
24220   mkdir(homedirdir, 0755);
24221   free(homedirdir);
24222 
24223 
24224   fi = fopen(lock_fname, "w");
24225   if (fi != NULL)
24226     {
24227       /* If we can write to it, do so! */
24228 
24229       fwrite(&time_now, sizeof(time_t), 1, fi);
24230       fclose(fi);
24231     }
24232   else
24233     {
24234       fprintf(stderr,
24235               "\nWarning: I couldn't create the lockfile (%s)\n"
24236               "The error that occurred was:\n" "%s\n\n", lock_fname, strerror(errno));
24237     }
24238 
24239   free(lock_fname);
24240 }
24241 
24242 /**
24243  * FIXME
24244  */
TP_EventFilter(const SDL_Event * event)24245 int TP_EventFilter(const SDL_Event * event)
24246 {
24247   if (event->type == SDL_QUIT ||
24248       event->type == SDL_ACTIVEEVENT ||
24249       event->type == SDL_JOYAXISMOTION ||
24250       event->type == SDL_JOYBALLMOTION ||
24251       event->type == SDL_JOYHATMOTION ||
24252       event->type == SDL_JOYBUTTONDOWN ||
24253       event->type == SDL_JOYBUTTONUP ||
24254       event->type == SDL_KEYDOWN ||
24255       event->type == SDL_KEYUP ||
24256       event->type == SDL_MOUSEBUTTONDOWN ||
24257       event->type == SDL_MOUSEBUTTONUP ||
24258       event->type == SDL_MOUSEMOTION || event->type == SDL_QUIT || event->type == SDL_USEREVENT)
24259     return 1;
24260 
24261   return 0;
24262 }
24263 
24264 /* ================================================================================== */
24265 
24266 /**
24267  * FIXME
24268  */
setup(void)24269 static void setup(void)
24270 {
24271   int i;
24272   char *upstr;
24273   SDL_Color black = { 0, 0, 0, 0 };
24274   char *homedirdir;
24275   SDL_Surface *tmp_surf;
24276   SDL_Rect dest;
24277   int scale;
24278 
24279 #ifndef LOW_QUALITY_COLOR_SELECTOR
24280   int x, y;
24281   SDL_Surface *tmp_btn_up;
24282   SDL_Surface *tmp_btn_down;
24283   Uint8 r, g, b;
24284 #endif
24285   SDL_Surface *tmp_imgcurup, *tmp_imgcurdown;
24286   Uint32 init_flags;
24287   char tmp_str[128];
24288   SDL_Surface *img1;
24289 
24290   Uint32(*getpixel_tmp_btn_up) (SDL_Surface *, int, int);
24291   Uint32(*getpixel_tmp_btn_down) (SDL_Surface *, int, int);
24292   Uint32(*getpixel_img_paintwell) (SDL_Surface *, int, int);
24293   int big_title;
24294 
24295 #ifndef NO_SDLPANGO
24296   SDL_Thread *fontconfig_thread;
24297 #endif
24298 
24299 
24300 
24301 #ifdef _WIN32
24302   if (fullscreen)
24303     {
24304       InstallKeyboardHook();
24305       SetActivationState(1);
24306     }
24307 #endif
24308 
24309   im_init(&im_data, get_current_language());
24310 
24311 #ifndef NO_SDLPANGO
24312   SDLPango_Init();
24313 #endif
24314 
24315 #ifndef WIN32
24316   putenv((char *)"SDL_VIDEO_X11_WMCLASS=TuxPaint.TuxPaint");
24317 #endif
24318 
24319   if (disable_screensaver == 0)
24320     {
24321       putenv((char *)"SDL_VIDEO_ALLOW_SCREENSAVER=1");
24322       if (SDL_MAJOR_VERSION < 1 ||
24323           (SDL_MAJOR_VERSION >= 1 && SDL_MINOR_VERSION < 2) ||
24324           (SDL_MAJOR_VERSION >= 1 && SDL_MINOR_VERSION >= 2 && SDL_PATCHLEVEL < 12))
24325         {
24326           fprintf(stderr, "Note: 'allowscreensaver' requires SDL 1.2.12 or higher\n");
24327         }
24328     }
24329 
24330   if (joystick_dev != -1)
24331     do_lock_file();
24332 
24333   init_flags = SDL_INIT_VIDEO | SDL_INIT_JOYSTICK;
24334   if (use_sound)
24335     init_flags |= SDL_INIT_AUDIO;
24336   if (!fullscreen)
24337     init_flags |= SDL_INIT_NOPARACHUTE; /* allow debugger to catch crash */
24338 
24339   /* Init SDL */
24340   if (SDL_Init(init_flags) < 0)
24341     {
24342 #ifndef NOSOUND
24343       char *olderr = strdup(SDL_GetError());
24344 
24345       use_sound = 0;
24346       init_flags &= ~SDL_INIT_AUDIO;
24347       if (SDL_Init(init_flags) >= 0)
24348         {
24349           /* worked, w/o sound */
24350           fprintf(stderr,
24351                   "\nWarning: I could not initialize audio!\n"
24352                   "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", olderr);
24353           free(olderr);
24354         }
24355       else
24356 #endif
24357         {
24358           fprintf(stderr,
24359                   "\nError: I could not initialize video and/or the timer!\n"
24360                   "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24361           exit(1);
24362         }
24363     }
24364 
24365   /* Set up event filter */
24366 
24367   SDL_SetEventFilter(TP_EventFilter);
24368 
24369 
24370   /* Set up joystick */
24371 
24372   if (joystick_dev == -1)
24373     {
24374       fprintf(stderr, "%i joystick(s) were found:\n", SDL_NumJoysticks());
24375 
24376       for (i = 0; i < SDL_NumJoysticks(); i++)
24377         {
24378           fprintf(stderr, " %d: %s\n", i, SDL_JoystickName(i));
24379         }
24380 
24381       SDL_Quit();
24382       exit(0);
24383     }
24384 
24385   joystick = SDL_JoystickOpen(joystick_dev);
24386   if (joystick == NULL)
24387     {
24388       fprintf(stderr, "Could not open joystick device %d: %s\n", joystick_dev, SDL_GetError());
24389     }
24390   else
24391     {
24392       SDL_JoystickEventState(SDL_ENABLE);
24393 #ifdef DEBUG
24394       printf("Number of Axes: %d\n", SDL_JoystickNumAxes(joystick));
24395       printf("Number of Buttons: %d\n", SDL_JoystickNumButtons(joystick));
24396       printf("Number of Balls: %d\n", SDL_JoystickNumBalls(joystick));
24397       printf("Number of Hats: %d\n", SDL_JoystickNumHats(joystick));
24398 #endif
24399     }
24400 
24401 
24402 #ifndef NOSOUND
24403 #ifdef DEBUG
24404   printf("Initializing sound...\n");
24405   fflush(stdout);
24406 #endif
24407 #ifndef WIN32
24408   if (use_sound && Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 1024) < 0)
24409 #else
24410   if (use_sound && Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 2048) < 0)
24411 #endif
24412     {
24413       fprintf(stderr,
24414               "\nWarning: I could not set up audio for 44100 Hz "
24415               "16-bit stereo.\n" "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24416       use_sound = 0;
24417     }
24418 
24419   i = NUM_SOUNDS;
24420   while (use_sound && i--)
24421     {
24422       sounds[i] = Mix_LoadWAV(sound_fnames[i]);
24423 
24424       if (sounds[i] == NULL)
24425         {
24426           fprintf(stderr,
24427                   "\nWarning: I couldn't open a sound file:\n%s\n"
24428                   "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", sound_fnames[i], SDL_GetError());
24429           use_sound = 0;
24430         }
24431     }
24432 #endif
24433 
24434 
24435 #ifdef DEBUG
24436   printf("Enabling key repeat...\n");
24437   fflush(stdout);
24438 #endif
24439   /* Set-up Key-Repeat: */
24440   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
24441 
24442 #ifdef DEBUG
24443   printf("Initializing TTF...\n");
24444   fflush(stdout);
24445 #endif
24446   /* Init TTF stuff: */
24447   if (TTF_Init() < 0)
24448     {
24449       fprintf(stderr,
24450               "\nError: I could not initialize the font (TTF) library!\n"
24451               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24452 
24453       SDL_Quit();
24454       exit(1);
24455     }
24456 
24457 
24458 #ifdef DEBUG
24459   printf("Setting up colors...\n");
24460   fflush(stdout);
24461 #endif
24462   setup_colors();
24463 
24464   /* Set window icon and caption: */
24465 
24466 #ifndef __APPLE__
24467   seticon();
24468 #endif
24469   SDL_WM_SetCaption("Tux Paint", "Tux Paint");
24470 
24471   if (hide_cursor)
24472     SDL_ShowCursor(SDL_DISABLE);
24473 
24474 
24475   /* Deal with orientation rotation option */
24476 
24477   if (rotate_orientation)
24478     {
24479       if (native_screensize && fullscreen)
24480         {
24481           fprintf(stderr, "Warning: Asking for native screen size overrides request to rotate orientation.\n");
24482         }
24483       else
24484         {
24485           int tmp;
24486 
24487           tmp = WINDOW_WIDTH;
24488           WINDOW_WIDTH = WINDOW_HEIGHT;
24489           WINDOW_HEIGHT = tmp;
24490         }
24491     }
24492 
24493   /* Deal with 'native' screen size option */
24494 
24495   if (native_screensize)
24496     {
24497       if (!fullscreen)
24498         {
24499           fprintf(stderr, "Warning: Asking for native screensize in a window. Ignoring.\n");
24500         }
24501       else
24502         {
24503           WINDOW_WIDTH = 0;
24504           WINDOW_HEIGHT = 0;
24505         }
24506     }
24507 
24508 
24509   /* Open Window: */
24510 
24511   if (fullscreen)
24512     {
24513 #ifdef USE_HWSURFACE
24514       screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, VIDEO_BPP, SDL_FULLSCREEN | SDL_HWSURFACE);
24515 #else
24516       screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, VIDEO_BPP, SDL_FULLSCREEN | SDL_SWSURFACE);
24517 #endif
24518 
24519       if (screen == NULL)
24520         {
24521           fprintf(stderr,
24522                   "\nWarning: I could not open the display in fullscreen mode.\n"
24523                   "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24524 
24525           fullscreen = 0;
24526         }
24527       else
24528         {
24529           /* Get resolution if we asked for native: */
24530 
24531           if (native_screensize)
24532             {
24533               WINDOW_WIDTH = screen->w;
24534               WINDOW_HEIGHT = screen->h;
24535             }
24536         }
24537     }
24538 
24539 
24540   if (!fullscreen)
24541     {
24542       int set_window_pos = 0;
24543 
24544       if (getenv((char *)"SDL_VIDEO_WINDOW_POS") == NULL)
24545         {
24546           set_window_pos = 1;
24547           putenv((char *)"SDL_VIDEO_WINDOW_POS=center");
24548         }
24549 
24550 #ifdef USE_HWSURFACE
24551       screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, VIDEO_BPP, SDL_HWSURFACE);
24552 #else
24553       screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, VIDEO_BPP, SDL_SWSURFACE);
24554 #endif
24555 
24556       if (set_window_pos)
24557         putenv((char *)"SDL_VIDEO_WINDOW_POS=nopref");
24558     }
24559 
24560   if (screen == NULL)
24561     {
24562       fprintf(stderr,
24563               "\nError: I could not open the display.\n"
24564               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24565 
24566       cleanup();
24567       exit(1);
24568     }
24569 
24570 
24571   /* (Need to do this after native screen resolution is handled) */
24572 
24573   setup_screen_layout();
24574 
24575 
24576   /* quickly: title image, version, progress bar, and watch cursor */
24577 
24578   img_title = loadimage(DATA_PREFIX "images/title.png");
24579   img_title_credits = loadimage(DATA_PREFIX "images/title-credits.png");
24580   img_progress = loadimage(DATA_PREFIX "images/ui/progress.png");
24581 
24582   if (screen->w - img_title->w >= 410 && screen->h - img_progress->h - img_title_credits->h - 40)       /* FIXME: Font */
24583     big_title = 1;
24584   else
24585     big_title = 0;
24586 
24587 
24588   if (big_title)
24589     img_title_tuxpaint = loadimage(DATA_PREFIX "images/title-tuxpaint-2x.png");
24590   else
24591     img_title_tuxpaint = loadimage(DATA_PREFIX "images/title-tuxpaint.png");
24592 
24593   SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
24594 
24595   dest.x = ((WINDOW_WIDTH - img_title->w - (img_title_tuxpaint->w / 2)) / 2) + (img_title_tuxpaint->w / 2) + 20;
24596   dest.y = (WINDOW_HEIGHT - img_title->h);
24597 
24598   SDL_BlitSurface(img_title, NULL, screen, &dest);
24599 
24600   dest.x = 10;
24601   if (big_title)
24602     dest.y = WINDOW_HEIGHT - img_title_tuxpaint->h - img_progress->h - 40;
24603   else
24604     dest.y = (WINDOW_HEIGHT - img_title->h) + img_title_tuxpaint->h * 0.8 + 7;
24605 
24606   SDL_BlitSurface(img_title_tuxpaint, NULL, screen, &dest);
24607 
24608   dest.x = 10;
24609   dest.y = 5;
24610 
24611   SDL_BlitSurface(img_title_credits, NULL, screen, &dest);
24612 
24613   prog_bar_ctr = 0;
24614   show_progress_bar(screen);
24615 
24616   SDL_Flip(screen);
24617 
24618 
24619 #if defined(WIN32) && defined(LARGE_CURSOR_FULLSCREEN_BUG)
24620   if (fullscreen && no_fancy_cursors == 0)
24621     {
24622       fprintf(stderr, "Warning: An SDL bug causes the fancy cursors to leave\n"
24623               "trails in fullscreen mode.  Disabling fancy cursors.\n"
24624               "(You can do this yourself with 'nofancycursors' option,\n" "to avoid this warning in the future.)\n");
24625       no_fancy_cursors = 1;
24626     }
24627 #endif
24628 
24629 
24630   /* Create cursors: */
24631 
24632   scale = 1;
24633 
24634 #ifdef SMALL_CURSOR_SHAPES
24635   scale = 2;
24636 #endif
24637 
24638 #ifdef __APPLE__
24639   cursor_arrow = SDL_GetCursor();       /* use standard system cursor */
24640 #endif
24641 
24642   /* this one first, because we need it yesterday */
24643   cursor_watch = get_cursor(watch_bits, watch_mask_bits, watch_width, watch_height, 14 / scale, 14 / scale);
24644 
24645   do_setcursor(cursor_watch);
24646   show_progress_bar(screen);
24647 
24648 
24649 
24650 
24651 #ifndef NO_SDLPANGO
24652   /* Let Pango & fontcache do their work without locking up */
24653 
24654   fontconfig_thread_done = 0;
24655 
24656 #ifdef DEBUG
24657   printf("Spawning Pango thread\n");
24658   fflush(stdout);
24659 #endif
24660 
24661   fontconfig_thread = SDL_CreateThread(generate_fontconfig_cache, NULL);
24662   if (fontconfig_thread == NULL)
24663     {
24664       fprintf(stderr, "Failed to create Pango setup thread: %s\n", SDL_GetError());
24665     }
24666   else
24667     {
24668 #ifdef DEBUG
24669       printf("Thread spawned\n");
24670       fflush(stdout);
24671 #endif
24672       if (generate_fontconfig_cache_spinner(screen))    /* returns 1 if aborted */
24673         {
24674           fprintf(stderr, "Pango thread aborted!\n");
24675           fflush(stderr);
24676           SDL_KillThread(fontconfig_thread);
24677           SDL_Quit();
24678           exit(0);
24679           /* FIXME: Let's be more graceful about exiting (e.g., clean up lockfile!) -bjk 2010.04.27 */
24680         }
24681 #ifdef DEBUG
24682       printf("Done generating cache\n");
24683       fflush(stdout);
24684 #endif
24685     }
24686 
24687 
24688 #ifdef FORKED_FONTS
24689   /* NOW we can fork our own font scanner stuff, and let it run in the bgkd -bjk 2010.04.27 */
24690 #ifdef DEBUG
24691   printf("Now running font scanner\n");
24692   fflush(stdout);
24693 #endif
24694   run_font_scanner(screen, lang_prefixes[get_current_language()]);
24695 #endif
24696 
24697 #endif
24698 
24699   medium_font = TuxPaint_Font_OpenFont(PANGO_DEFAULT_FONT,
24700                                        DATA_PREFIX "fonts/default_font.ttf", (18 - (only_uppercase * 3)) * button_scale);
24701 
24702   if (medium_font == NULL)
24703     {
24704       fprintf(stderr,
24705               "\nError: Can't load font file: "
24706               DATA_PREFIX "fonts/default_font.ttf\n"
24707               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24708 
24709       cleanup();
24710       exit(1);
24711     }
24712 
24713   safe_snprintf(tmp_str, sizeof(tmp_str), "Version: %s – %s", VER_VERSION, VER_DATE);
24714 
24715   tmp_surf = render_text(medium_font, tmp_str, black);
24716   dest.x = 10;
24717   dest.y = WINDOW_HEIGHT - img_progress->h - tmp_surf->h;
24718   SDL_BlitSurface(tmp_surf, NULL, screen, &dest);
24719   SDL_FreeSurface(tmp_surf);
24720 
24721 #ifdef DEBUG
24722   printf("%s\n", tmp_str);
24723 #endif
24724 
24725   safe_snprintf(tmp_str, sizeof(tmp_str), "© 2002–2021 Bill Kendrick et al.");
24726   tmp_surf = render_text(medium_font, tmp_str, black);
24727   dest.x = 10;
24728   dest.y = WINDOW_HEIGHT - img_progress->h - (tmp_surf->h * 2);
24729   SDL_BlitSurface(tmp_surf, NULL, screen, &dest);
24730   SDL_FreeSurface(tmp_surf);
24731 
24732   SDL_Flip(screen);
24733 
24734 
24735 #ifdef FORKED_FONTS
24736   reliable_write(font_socket_fd, &no_system_fonts, sizeof no_system_fonts);
24737 #else
24738   font_thread = SDL_CreateThread(load_user_fonts_stub, NULL);
24739 #endif
24740 
24741   /* continuing on with the rest of the cursors... */
24742 
24743 
24744 #ifndef __APPLE__
24745   cursor_arrow = get_cursor(arrow_bits, arrow_mask_bits, arrow_width, arrow_height, 0, 0);
24746 #endif
24747 
24748   cursor_hand = get_cursor(hand_bits, hand_mask_bits, hand_width, hand_height, 12 / scale, 1 / scale);
24749 
24750   cursor_wand = get_cursor(wand_bits, wand_mask_bits, wand_width, wand_height, 4 / scale, 4 / scale);
24751 
24752   cursor_insertion = get_cursor(insertion_bits, insertion_mask_bits,
24753                                 insertion_width, insertion_height, 7 / scale, 4 / scale);
24754 
24755   cursor_brush = get_cursor(brush_bits, brush_mask_bits, brush_width, brush_height, 4 / scale, 28 / scale);
24756 
24757   cursor_crosshair = get_cursor(crosshair_bits, crosshair_mask_bits,
24758                                 crosshair_width, crosshair_height, 15 / scale, 15 / scale);
24759 
24760   cursor_rotate = get_cursor(rotate_bits, rotate_mask_bits, rotate_width, rotate_height, 15 / scale, 15 / scale);
24761 
24762   cursor_up = get_cursor(up_bits, up_mask_bits, up_width, up_height, 15 / scale, 1 / scale);
24763 
24764   cursor_down = get_cursor(down_bits, down_mask_bits, down_width, down_height, 15 / scale, 30 / scale);
24765 
24766   cursor_tiny = get_cursor(tiny_bits, tiny_mask_bits, tiny_width, tiny_height, 3, 3);   /* Exactly the same in SMALL (16x16) size! */
24767 
24768 
24769   //button_h * buttons_tall + r_ttools.h
24770   /* Create drawing canvas: */
24771 
24772   canvas = SDL_CreateRGBSurface(screen->flags,
24773                                 WINDOW_WIDTH - r_ttools.w - r_ttoolopt.w,
24774                                 (button_h * buttons_tall) + r_ttools.h,
24775                                 screen->format->BitsPerPixel,
24776                                 screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, 0);
24777 
24778   save_canvas = SDL_CreateRGBSurface(screen->flags,
24779                                      WINDOW_WIDTH - r_ttools.w - r_ttoolopt.w,
24780                                      (button_h * buttons_tall) + r_ttools.h,
24781                                      screen->format->BitsPerPixel,
24782                                      screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, 0);
24783 
24784 
24785   img_starter = NULL;
24786   img_starter_bkgd = NULL;
24787   starter_mirrored = 0;
24788   starter_flipped = 0;
24789   starter_personal = 0;
24790   starter_modified = 0;
24791 
24792   if (canvas == NULL)
24793     {
24794       fprintf(stderr, "\nError: Can't build drawing canvas!\n"
24795               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
24796 
24797       cleanup();
24798       exit(1);
24799     }
24800 
24801   touched = (Uint8 *) malloc(sizeof(Uint8) * (canvas->w * canvas->h));
24802   if (touched == NULL)
24803     {
24804       fprintf(stderr, "\nError: Can't build drawing touch mask for Magic!\n");
24805 
24806       cleanup();
24807       exit(1);
24808     }
24809 
24810   sim_flood_touched = (Uint8 *) malloc(sizeof(Uint8) * (canvas->w * canvas->h));
24811   if (sim_flood_touched == NULL)
24812     {
24813       fprintf(stderr, "\nError: Can't build drawing touch mask for Fill!\n");
24814 
24815       cleanup();
24816       exit(1);
24817     }
24818 
24819   canvas_color_r = 255;
24820   canvas_color_g = 255;
24821   canvas_color_b = 255;
24822 
24823   SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
24824 
24825   /* Creating the label surface: */
24826 
24827   label = SDL_CreateRGBSurface(screen->flags,
24828                                WINDOW_WIDTH - (r_ttools.w * 2),
24829                                (button_h * 7) + 40 + HEIGHTOFFSET,
24830                                screen->format->BitsPerPixel,
24831                                screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, TPAINT_AMASK);
24832 
24833   /* making the label layer transparent */
24834   SDL_FillRect(label, NULL, SDL_MapRGBA(label->format, 0, 0, 0, 0));
24835 
24836   /* Create undo buffer space: */
24837 
24838   for (i = 0; i < NUM_UNDO_BUFS; i++)
24839     {
24840       undo_bufs[i] = SDL_CreateRGBSurface(screen->flags,
24841                                           WINDOW_WIDTH - (r_ttools.w * 2),
24842                                           (button_h * 7) + 40 + HEIGHTOFFSET,
24843                                           screen->format->BitsPerPixel,
24844                                           screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, 0);
24845 
24846 
24847       if (undo_bufs[i] == NULL)
24848         {
24849           fprintf(stderr, "\nError: Can't build undo buffer! (%d of %d)\n"
24850                   "The Simple DirectMedia Layer error that occurred was:\n"
24851                   "%s\n\n", i + 1, NUM_UNDO_BUFS, SDL_GetError());
24852 
24853           cleanup();
24854           exit(1);
24855         }
24856 
24857       undo_starters[i] = UNDO_STARTER_NONE;
24858     }
24859 
24860 
24861 
24862   /* Load other images: */
24863 
24864   for (i = 0; i < NUM_TOOLS; i++)
24865     img_tools[i] = loadimagerb(tool_img_fnames[i]);
24866 
24867   img_title_on = loadimagerb(DATA_PREFIX "images/ui/title.png");
24868   img_title_large_on = loadimagerb(DATA_PREFIX "images/ui/title_large.png");
24869   img_title_off = loadimagerb(DATA_PREFIX "images/ui/no_title.png");
24870   img_title_large_off = loadimagerb(DATA_PREFIX "images/ui/no_title_large.png");
24871 
24872   img_btn_up = loadimagerb(DATA_PREFIX "images/ui/btn_up.png");
24873   img_btn_down = loadimagerb(DATA_PREFIX "images/ui/btn_down.png");
24874   img_btn_off = loadimagerb(DATA_PREFIX "images/ui/btn_off.png");
24875   img_btn_hold = loadimagerb(DATA_PREFIX "images/ui/btn_hold.png");
24876 
24877   img_btnsm_up = loadimagerb(DATA_PREFIX "images/ui/btnsm_up.png");
24878   img_btnsm_off = loadimagerb(DATA_PREFIX "images/ui/btnsm_off.png");
24879   img_btnsm_down = loadimagerb(DATA_PREFIX "images/ui/btnsm_down.png");
24880   img_btnsm_hold = loadimagerb(DATA_PREFIX "images/ui/btnsm_hold.png");
24881 
24882   img_btn_nav = loadimagerb(DATA_PREFIX "images/ui/btn_nav.png");
24883   img_btnsm_nav = loadimagerb(DATA_PREFIX "images/ui/btnsm_nav.png");
24884 
24885   img_sfx = loadimagerb(DATA_PREFIX "images/tools/sfx.png");
24886   img_speak = loadimagerb(DATA_PREFIX "images/tools/speak.png");
24887 
24888   img_black = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_SWSURFACE,
24889                                    img_btn_off->w, img_btn_off->h,
24890                                    img_btn_off->format->BitsPerPixel,
24891                                    img_btn_off->format->Rmask,
24892                                    img_btn_off->format->Gmask, img_btn_off->format->Bmask, img_btn_off->format->Amask);
24893   SDL_FillRect(img_black, NULL, SDL_MapRGBA(screen->format, 0, 0, 0, 255));
24894 
24895   img_grey = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_SWSURFACE,
24896                                   img_btn_off->w, img_btn_off->h,
24897                                   img_btn_off->format->BitsPerPixel,
24898                                   img_btn_off->format->Rmask,
24899                                   img_btn_off->format->Gmask, img_btn_off->format->Bmask, img_btn_off->format->Amask);
24900   SDL_FillRect(img_grey, NULL, SDL_MapRGBA(screen->format, 0x88, 0x88, 0x88, 255));
24901 
24902   show_progress_bar(screen);
24903 
24904   img_yes = loadimagerb(DATA_PREFIX "images/ui/yes.png");
24905   img_no = loadimagerb(DATA_PREFIX "images/ui/no.png");
24906 
24907   img_prev = loadimagerb(DATA_PREFIX "images/ui/prev.png");
24908   img_next = loadimagerb(DATA_PREFIX "images/ui/next.png");
24909 
24910   img_mirror = loadimagerb(DATA_PREFIX "images/ui/mirror.png");
24911   img_flip = loadimagerb(DATA_PREFIX "images/ui/flip.png");
24912 
24913   img_open = loadimagerb(DATA_PREFIX "images/ui/open.png");
24914   img_erase = loadimagerb(DATA_PREFIX "images/ui/erase.png");
24915   img_pict_export = loadimagerb(DATA_PREFIX "images/ui/pict_export.png");
24916   img_back = loadimagerb(DATA_PREFIX "images/ui/back.png");
24917   img_trash = loadimagerb(DATA_PREFIX "images/ui/trash.png");
24918 
24919   img_slideshow = loadimagerb(DATA_PREFIX "images/ui/slideshow.png");
24920   img_play = loadimagerb(DATA_PREFIX "images/ui/play.png");
24921   img_gif_export = loadimagerb(DATA_PREFIX "images/ui/gif_export.png");
24922   img_select_digits = loadimagerb(DATA_PREFIX "images/ui/select_digits.png");
24923 
24924   img_popup_arrow = loadimagerb(DATA_PREFIX "images/ui/popup_arrow.png");
24925 
24926   img_dead40x40 = loadimagerb(DATA_PREFIX "images/ui/dead40x40.png");
24927 
24928   img_printer = loadimagerb(DATA_PREFIX "images/ui/printer.png");
24929   img_printer_wait = loadimagerb(DATA_PREFIX "images/ui/printer_wait.png");
24930 
24931   img_save_over = loadimagerb(DATA_PREFIX "images/ui/save_over.png");
24932 
24933   img_grow = loadimagerb(DATA_PREFIX "images/ui/grow.png");
24934   img_shrink = loadimagerb(DATA_PREFIX "images/ui/shrink.png");
24935 
24936   img_magic_paint = loadimagerb(DATA_PREFIX "images/ui/magic_paint.png");
24937   img_magic_fullscreen = loadimagerb(DATA_PREFIX "images/ui/magic_fullscreen.png");
24938 
24939   img_shapes_center = loadimagerb(DATA_PREFIX "images/ui/shapes_center.png");
24940   img_shapes_corner = loadimagerb(DATA_PREFIX "images/ui/shapes_corner.png");
24941 
24942   img_bold = loadimagerb(DATA_PREFIX "images/ui/bold.png");
24943   img_italic = loadimagerb(DATA_PREFIX "images/ui/italic.png");
24944 
24945   img_label = loadimagerb(DATA_PREFIX "images/tools/label.png");
24946   img_label_select = loadimagerb(DATA_PREFIX "images/tools/label_select.png");
24947 
24948   show_progress_bar(screen);
24949 
24950   tmp_imgcurup = loadimage(DATA_PREFIX "images/ui/cursor_up_large.png");
24951   tmp_imgcurdown = loadimage(DATA_PREFIX "images/ui/cursor_down_large.png");
24952   img_cursor_up = thumbnail(tmp_imgcurup, THUMB_W, THUMB_H, 0);
24953   img_cursor_down = thumbnail(tmp_imgcurdown, THUMB_W, THUMB_H, 0);
24954 
24955   tmp_imgcurup = loadimage(DATA_PREFIX "images/ui/cursor_starter_up.png");
24956   tmp_imgcurdown = loadimage(DATA_PREFIX "images/ui/cursor_starter_down.png");
24957   img_cursor_starter_up = thumbnail(tmp_imgcurup, THUMB_W, THUMB_H, 0);
24958   img_cursor_starter_down = thumbnail(tmp_imgcurdown, THUMB_W, THUMB_H, 0);
24959   SDL_FreeSurface(tmp_imgcurup);
24960   SDL_FreeSurface(tmp_imgcurdown);
24961 
24962   show_progress_bar(screen);
24963 
24964   img_scroll_up = loadimagerb(DATA_PREFIX "images/ui/scroll_up.png");
24965   img_scroll_down = loadimagerb(DATA_PREFIX "images/ui/scroll_down.png");
24966 
24967   img_scroll_up_off = loadimagerb(DATA_PREFIX "images/ui/scroll_up_off.png");
24968   img_scroll_down_off = loadimagerb(DATA_PREFIX "images/ui/scroll_down_off.png");
24969   img_color_sel = loadimagerb(DATA_PREFIX "images/ui/csel.png");
24970 
24971 #ifdef LOW_QUALITY_COLOR_SELECTOR
24972   img_paintcan = loadimage(DATA_PREFIX "images/ui/paintcan.png");
24973 #endif
24974 
24975   if (onscreen_keyboard)
24976     {
24977       img_oskdel = loadimagerb(DATA_PREFIX "images/ui/osk_delete.png");
24978       img_osktab = loadimagerb(DATA_PREFIX "images/ui/osk_tab.png");
24979       img_oskenter = loadimagerb(DATA_PREFIX "images/ui/osk_enter.png");
24980       img_oskcapslock = loadimagerb(DATA_PREFIX "images/ui/osk_capslock.png");
24981       img_oskshift = loadimagerb(DATA_PREFIX "images/ui/osk_shift.png");
24982 
24983     }
24984   show_progress_bar(screen);
24985 
24986 
24987   /* Load brushes: */
24988   load_brush_dir(screen, DATA_PREFIX "brushes");
24989   homedirdir = get_fname("brushes", DIR_DATA);
24990   load_brush_dir(screen, homedirdir);
24991 #ifdef WIN32
24992   free(homedirdir);
24993   homedirdir = get_fname("data/brushes", DIR_DATA);
24994   load_brush_dir(screen, homedirdir);
24995 #endif
24996 
24997   if (num_brushes == 0)
24998     {
24999       fprintf(stderr, "\nError: No brushes found in " DATA_PREFIX "brushes/\n" "or %s\n\n", homedirdir);
25000       cleanup();
25001       exit(1);
25002     }
25003 
25004   free(homedirdir);
25005 
25006 
25007   /* Load system fonts: */
25008 
25009   large_font = TuxPaint_Font_OpenFont(PANGO_DEFAULT_FONT,
25010                                       DATA_PREFIX "fonts/default_font.ttf", (30 - (only_uppercase * 3)) * button_scale);
25011 
25012   if (large_font == NULL)
25013     {
25014       fprintf(stderr,
25015               "\nError: Can't load font file: "
25016               DATA_PREFIX "fonts/default_font.ttf\n"
25017               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
25018 
25019       cleanup();
25020       exit(1);
25021     }
25022 
25023 
25024   small_font = TuxPaint_Font_OpenFont(PANGO_DEFAULT_FONT, DATA_PREFIX "fonts/default_font.ttf",
25025 #ifdef __APPLE__
25026                                       (12 - (only_uppercase * 2)) * button_scale
25027 #else
25028                                       (13 - (only_uppercase * 2)) * button_scale
25029 #endif
25030   );
25031 
25032   if (small_font == NULL)
25033     {
25034       fprintf(stderr,
25035               "\nError: Can't load font file: "
25036               DATA_PREFIX "fonts/default_font.ttf\n"
25037               "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
25038 
25039       cleanup();
25040       exit(1);
25041     }
25042 
25043 
25044 #ifdef NO_SDLPANGO
25045   locale_font = load_locale_font(medium_font, 18);
25046 #else
25047   locale_font = medium_font;
25048 #endif
25049 
25050 
25051 #if 0
25052   /* put elsewhere for THREADED_FONTS */
25053   /* Load user fonts, for the text tool */
25054   load_user_fonts();
25055 #endif
25056 
25057   if (!dont_load_stamps)
25058     load_stamps(screen);
25059 
25060 
25061   /* Load magic tool plugins: */
25062 
25063   load_magic_plugins();
25064 
25065   show_progress_bar(screen);
25066 
25067   /* Load shape icons: */
25068   for (i = 0; i < NUM_SHAPES; i++)
25069     {
25070       SDL_Surface *aux_surf = loadimage(shape_img_fnames[i]);
25071       img_shapes[i] = thumbnail2(aux_surf, (aux_surf->w * button_w) / ORIGINAL_BUTTON_SIZE, (aux_surf->h * button_h) / ORIGINAL_BUTTON_SIZE, 0, 1);
25072       SDL_FreeSurface(aux_surf);
25073     }
25074 
25075   show_progress_bar(screen);
25076 
25077   /* Load fill sub-tool icons: */
25078   for (i = 0; i < NUM_FILLS; i++)
25079     {
25080       SDL_Surface *aux_surf = loadimage(fill_img_fnames[i]);
25081       img_fills[i] = thumbnail2(aux_surf, (aux_surf->w * button_w) / ORIGINAL_BUTTON_SIZE, (aux_surf->h * button_h) / ORIGINAL_BUTTON_SIZE, 0, 1);
25082       SDL_FreeSurface(aux_surf);
25083     }
25084 
25085   show_progress_bar(screen);
25086 
25087   /* Load tip tux images: */
25088   for (i = 0; i < NUM_TIP_TUX; i++)
25089     img_tux[i] = loadimagerb(tux_img_fnames[i]);
25090 
25091   show_progress_bar(screen);
25092 
25093   img_mouse = loadimagerb(DATA_PREFIX "images/ui/mouse.png");
25094   img_mouse_click = loadimagerb(DATA_PREFIX "images/ui/mouse_click.png");
25095 
25096   show_progress_bar(screen);
25097 
25098   img_color_picker = loadimagerb(DATA_PREFIX "images/ui/color_picker.png");
25099 
25100   /* Create toolbox and selector labels: */
25101 
25102   for (i = 0; i < NUM_TITLES; i++)
25103     {
25104       if (strlen(title_names[i]) > 0)
25105         {
25106           TuxPaint_Font *myfont = large_font;
25107           char *td_str = textdir(gettext(title_names[i]));
25108 
25109           if (need_own_font && strcmp(gettext(title_names[i]), title_names[i]))
25110             myfont = locale_font;
25111           upstr = uppercase(td_str);
25112           free(td_str);
25113           tmp_surf = render_text(myfont, upstr, black);
25114           free(upstr);
25115           img_title_names[i] = thumbnail(tmp_surf, min(84 * button_scale, tmp_surf->w), tmp_surf->h, 0);
25116           SDL_FreeSurface(tmp_surf);
25117         }
25118       else
25119         {
25120           img_title_names[i] = NULL;
25121         }
25122     }
25123 
25124 
25125 
25126   /* Generate color selection buttons: */
25127 
25128 #ifndef LOW_QUALITY_COLOR_SELECTOR
25129 
25130   /* Create appropriately-shaped buttons: */
25131   img1 = loadimage(DATA_PREFIX "images/ui/paintwell.png");
25132   img_paintwell = thumbnail(img1, color_button_w, color_button_h, 0);
25133   tmp_btn_up = thumbnail(img_btn_up, color_button_w, color_button_h, 0);
25134   tmp_btn_down = thumbnail(img_btn_down, color_button_w, color_button_h, 0);
25135   img_color_btn_off = thumbnail(img_btn_off, color_button_w, color_button_h, 0);
25136   SDL_FreeSurface(img1);
25137 
25138   img_color_picker_thumb = thumbnail(img_color_picker, color_button_w, color_button_h, 0);
25139 
25140   /* Create surfaces to draw them into: */
25141 
25142   img_color_btns = malloc(sizeof(SDL_Surface *) * NUM_COLORS * 2);
25143 
25144   for (i = 0; i < NUM_COLORS * 2; i++)
25145     {
25146       img_color_btns[i] = SDL_CreateRGBSurface(screen->flags,
25147                                                /* (WINDOW_WIDTH - r_ttoolopt.w) / NUM_COLORS, 48, */
25148                                                tmp_btn_up->w, tmp_btn_up->h,
25149                                                screen->format->BitsPerPixel,
25150                                                screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, 0);
25151 
25152       if (img_color_btns[i] == NULL)
25153         {
25154           fprintf(stderr, "\nError: Can't build color button!\n"
25155                   "The Simple DirectMedia Layer error that occurred was:\n" "%s\n\n", SDL_GetError());
25156 
25157           cleanup();
25158           exit(1);
25159         }
25160 
25161       SDL_LockSurface(img_color_btns[i]);
25162     }
25163 
25164 
25165   /* Generate the buttons based on the thumbnails: */
25166 
25167   SDL_LockSurface(tmp_btn_down);
25168   SDL_LockSurface(tmp_btn_up);
25169 
25170   getpixel_tmp_btn_up = getpixels[tmp_btn_up->format->BytesPerPixel];
25171   getpixel_tmp_btn_down = getpixels[tmp_btn_down->format->BytesPerPixel];
25172   getpixel_img_paintwell = getpixels[img_paintwell->format->BytesPerPixel];
25173 
25174 
25175   for (y = 0; y < tmp_btn_up->h /* 48 */ ; y++)
25176     {
25177       for (x = 0; x < tmp_btn_up->w /* (WINDOW_WIDTH - r_ttoolopt.w) / NUM_COLORS */ ;
25178            x++)
25179         {
25180           double ru, gu, bu, rd, gd, bd, aa;
25181           Uint8 a;
25182 
25183           SDL_GetRGB(getpixel_tmp_btn_up(tmp_btn_up, x, y), tmp_btn_up->format, &r, &g, &b);
25184 
25185           ru = sRGB_to_linear_table[r];
25186           gu = sRGB_to_linear_table[g];
25187           bu = sRGB_to_linear_table[b];
25188           SDL_GetRGB(getpixel_tmp_btn_down(tmp_btn_down, x, y), tmp_btn_down->format, &r, &g, &b);
25189           rd = sRGB_to_linear_table[r];
25190           gd = sRGB_to_linear_table[g];
25191           bd = sRGB_to_linear_table[b];
25192           SDL_GetRGBA(getpixel_img_paintwell(img_paintwell, x, y), img_paintwell->format, &r, &g, &b, &a);
25193           aa = a / 255.0;
25194 
25195           for (i = 0; i < NUM_COLORS; i++)
25196             {
25197               double rh = sRGB_to_linear_table[color_hexes[i][0]];
25198               double gh = sRGB_to_linear_table[color_hexes[i][1]];
25199               double bh = sRGB_to_linear_table[color_hexes[i][2]];
25200 
25201               if (i == NUM_COLORS - 1)
25202                 {
25203                   putpixels[img_color_btns[i]->format->BytesPerPixel]
25204                     (img_color_btns[i], x, y,
25205                      getpixels[img_color_picker_thumb->format->BytesPerPixel] (img_color_picker_thumb, x, y));
25206                   putpixels[img_color_btns[i + NUM_COLORS]->format->BytesPerPixel]
25207                     (img_color_btns[i + NUM_COLORS], x, y,
25208                      getpixels[img_color_picker_thumb->format->BytesPerPixel] (img_color_picker_thumb, x, y));
25209                 }
25210 
25211               if (i < NUM_COLORS - 1 || a == 255)
25212                 {
25213                   putpixels[img_color_btns[i]->format->BytesPerPixel]
25214                     (img_color_btns[i], x, y,
25215                      SDL_MapRGB(img_color_btns[i]->format,
25216                                 linear_to_sRGB(rh * aa + ru * (1.0 - aa)),
25217                                 linear_to_sRGB(gh * aa + gu * (1.0 - aa)), linear_to_sRGB(bh * aa + bu * (1.0 - aa))));
25218                   putpixels[img_color_btns[i + NUM_COLORS]->format->BytesPerPixel]
25219                     (img_color_btns[i + NUM_COLORS], x, y,
25220                      SDL_MapRGB(img_color_btns[i + NUM_COLORS]->format,
25221                                 linear_to_sRGB(rh * aa + rd * (1.0 - aa)),
25222                                 linear_to_sRGB(gh * aa + gd * (1.0 - aa)), linear_to_sRGB(bh * aa + bd * (1.0 - aa))));
25223                 }
25224             }
25225         }
25226     }
25227 
25228   for (i = 0; i < NUM_COLORS * 2; i++)
25229     {
25230       SDL_UnlockSurface(img_color_btns[i]);
25231       if (i == NUM_COLORS - 2 || i == 2 * NUM_COLORS - 2)
25232         {
25233           dest.x = (img_color_btns[i]->w - img_color_sel->w) / 2;
25234           dest.y = (img_color_btns[i]->h - img_color_sel->h) / 2;
25235           dest.w = img_color_sel->w;
25236           dest.h = img_color_sel->h;
25237           SDL_BlitSurface(img_color_sel, NULL, img_color_btns[i], &dest);
25238         }
25239     }
25240 
25241   SDL_UnlockSurface(tmp_btn_up);
25242   SDL_UnlockSurface(tmp_btn_down);
25243   SDL_FreeSurface(tmp_btn_up);
25244   SDL_FreeSurface(tmp_btn_down);
25245 
25246 #endif
25247 
25248   create_button_labels();
25249 
25250 
25251   /* Seed random-number generator: */
25252 
25253   srand(SDL_GetTicks());
25254 
25255 
25256   /* Enable Unicode support in SDL: */
25257 
25258   SDL_EnableUNICODE(1);
25259 
25260 #ifndef _WIN32
25261   /* Set up signal handler for SIGPIPE (in case printer command dies;
25262      e.g., altprintcommand=kprinter, but 'Cancel' is clicked,
25263      instead of 'Ok') */
25264 
25265   signal(SIGPIPE, signal_handler);
25266 
25267   /* Set up signal for no-questions-asked remote closing of app */
25268   signal(SIGUSR1, signal_handler);
25269   signal(SIGUSR2, signal_handler);
25270 #endif
25271 }
25272 
25273 /* ================================================================================== */
25274 
25275 /**
25276  * FIXME
25277  */
claim_to_be_ready(void)25278 static void claim_to_be_ready(void)
25279 {
25280   SDL_Rect dest;
25281   SDL_Rect src;
25282   int i;
25283 
25284   /* Let the user know we're (nearly) ready now */
25285 
25286   dest.x = 0;
25287   dest.y = WINDOW_HEIGHT - img_progress->h;
25288   dest.h = img_progress->h;
25289   dest.w = WINDOW_WIDTH;
25290   SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
25291   src.h = img_progress->h;
25292   src.w = img_title->w;
25293   src.x = 0;
25294   src.y = img_title->h - img_progress->h;
25295   dest.x = ((WINDOW_WIDTH - img_title->w - (img_title_tuxpaint->w / 2)) / 2) + (img_title_tuxpaint->w / 2) + 20;
25296   SDL_BlitSurface(img_title, &src, screen, &dest);
25297 
25298   SDL_FreeSurface(img_title);
25299   SDL_FreeSurface(img_title_credits);
25300   SDL_FreeSurface(img_title_tuxpaint);
25301 
25302   dest.x = 0;
25303   dest.w = WINDOW_WIDTH;        /* SDL mangles this! So, do repairs. */
25304   update_screen_rect(&dest);
25305 
25306   do_setcursor(cursor_arrow);
25307   playsound(screen, 0, SND_HARP, 1, SNDPOS_CENTER, SNDDIST_NEAR);
25308   do_wait(50);                  /* about 5 seconds */
25309 
25310 
25311   /* Set defaults! */
25312 
25313   cur_undo = 0;
25314   oldest_undo = 0;
25315   newest_undo = 0;
25316 
25317   cur_tool = TOOL_BRUSH;
25318   cur_color = COLOR_BLACK;
25319   colors_are_selectable = 1;
25320   cur_brush = 0;
25321   for (i = 0; i < MAX_STAMP_GROUPS; i++)
25322     cur_stamp[i] = 0;
25323   cur_shape = SHAPE_SQUARE;
25324   cur_magic = 0;
25325   cur_font = 0;
25326   cur_eraser = 0;
25327   cur_fill = 0;
25328   fill_drag_started = 0;
25329   cur_label = LABEL_LABEL;
25330   cur_select = 0;
25331   cursor_left = -1;
25332   cursor_x = -1;
25333   cursor_y = -1;
25334   cursor_textwidth = 0;
25335 
25336   oldpos_x = WINDOW_WIDTH / 2;
25337   oldpos_y = WINDOW_HEIGHT / 2;
25338 
25339   SDL_WarpMouse(oldpos_x, oldpos_y);
25340 
25341   eraser_sound = 0;
25342 
25343   img_cur_brush = NULL;
25344   render_brush();
25345 
25346   brush_scroll = 0;
25347   for (i = 0; i < MAX_STAMP_GROUPS; i++)
25348     stamp_scroll[i] = 0;
25349   stamp_group = 0;              /* reset! */
25350   font_scroll = 0;
25351   magic_scroll = 0;
25352   tool_scroll = 0;
25353   eraser_scroll = 0;
25354   fill_scroll = 0;
25355 
25356   reset_avail_tools();
25357 
25358 
25359   /* Load current image (if any): */
25360 
25361   if (start_blank == 0)
25362     load_current();
25363 
25364   been_saved = 1;
25365   tool_avail[TOOL_SAVE] = 0;
25366 
25367 
25368   /* Draw the screen! */
25369 
25370   SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
25371 
25372   draw_toolbar();
25373   draw_colors(COLORSEL_FORCE_REDRAW);
25374   draw_brushes();
25375   update_canvas(0, 0, WINDOW_WIDTH - r_ttoolopt.w, (48 * 7) + 40 + HEIGHTOFFSET);
25376 
25377   SDL_Flip(screen);
25378 
25379   draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool], 1);
25380 }
25381 
25382 /* ================================================================================== */
25383 
25384 /**
25385  * FIXME
25386  */
main(int argc,char * argv[])25387 int main(int argc, char *argv[])
25388 {
25389 #ifdef DEBUG
25390   CLOCK_TYPE time1;
25391   CLOCK_TYPE time2;
25392 #endif
25393 
25394   (void)argc;
25395 
25396 #ifdef DEBUG
25397   CLOCK_ASM(time1);
25398 #endif
25399 
25400   /* do not add code (slowness) here unless required for scanning fonts */
25401   progname = argv[0];
25402 
25403 #if defined(DEBUG) && defined(__APPLE__)
25404   /* EP added block to log messages */
25405   freopen("/tmp/tuxpaint.log", "w", stdout);    /* redirect stdout to a file */
25406   dup2(fileno(stdout), fileno(stderr)); /* redirect stderr to stdout */
25407   setvbuf(stdout, NULL, _IONBF, 0);     /* we don't want buffering to avoid de-sync'ing stdout and stderr */
25408   setvbuf(stderr, NULL, _IONBF, 0);     /* we don't want buffering to avoid de-sync'ing stdout and stderr */
25409   char logTime[100];
25410   time_t t = time(NULL);
25411 
25412   strftime(logTime, sizeof(logTime), "%A %d/%m/%Y %H:%M:%S", localtime(&t));
25413   printf("Tux Paint log - %s\n", logTime);
25414 #endif
25415 
25416   chdir_to_binary(argv[0]);
25417   setup_config(argv);
25418 
25419 #ifdef DEBUG
25420   CLOCK_ASM(time2);
25421 #endif
25422 
25423 #if defined(__MACOS__)
25424   /* Pango uses Fontconfig which requires /opt/local/etc/fonts/fonts.conf. This
25425    * file may not exist on the runtime system, however, so we copy the file
25426    * into our app bundle at compile time, and tell Fontconfig here to look for
25427    * the file within the app bundle. */
25428   putenv((char *)"FONTCONFIG_PATH=Resources/etc");
25429 #endif
25430 
25431 #ifdef FORKED_FONTS
25432   /* must start ASAP, but depends on locale which in turn needs the config */
25433 #ifdef NO_SDLPANGO
25434   /* Only fork it now if we're not planning on creating a thread to handle fontconfig stuff -bjk 2010.04.27 */
25435 #ifdef DEBUG
25436   printf("Running font scanner\n");
25437   fflush(stdout);
25438 #endif
25439   run_font_scanner(screen, lang_prefixes[get_current_language()]);
25440 #else
25441 #ifdef DEBUG
25442   printf("NOT running font scanner\n");
25443   fflush(stdout);
25444 #endif
25445 #endif
25446 #endif
25447 
25448   /* Warnings to satisfy SF.net Bug #3327493 -bjk 2011.06.24 */
25449   if (disable_save && autosave_on_quit)
25450     {
25451       fprintf(stderr, "Warning: Autosave requested, but saving is disabled.\n");
25452     }
25453   if (disable_save && (promptless_save != SAVE_OVER_UNSET))
25454     {
25455       fprintf(stderr, "Warning: Save-over option specified, but saving is disabled.\n");
25456     }
25457 
25458   if (promptless_save == SAVE_OVER_UNSET)
25459     {
25460       promptless_save = SAVE_OVER_PROMPT;
25461     }
25462 
25463   /* Set up! */
25464   setup();
25465 
25466 #ifdef DEBUG
25467   printf("Seconds in early start-up: %.3f\n", (double)(time2 - time1) / CLOCK_SPEED);
25468   printf("Seconds in late start-up:  %.3f\n", (double)(time2 - time1) / CLOCK_SPEED);
25469 #endif
25470 
25471 
25472 
25473   claim_to_be_ready();
25474 
25475   mainloop();
25476 
25477   /* Close and quit! */
25478   save_current();
25479   wait_for_sfx();
25480   cleanup();
25481   return 0;
25482 }
25483 
25484 
25485 /* Moves a file to the trashcan (or deletes it) */
25486 
25487 /**
25488  * FIXME
25489  */
trash(char * path)25490 static int trash(char *path)
25491 {
25492 #ifdef UNLINK_ONLY
25493   return (unlink(path));
25494 #else
25495   char fname[MAX_PATH], trashpath[MAX_PATH], dest[MAX_PATH], infoname[MAX_PATH], bname[MAX_PATH], ext[MAX_PATH];
25496   char deldate[32];
25497   struct tm tim;
25498   time_t now;
25499   int cnt, tmp;
25500   FILE *fi, *fo;
25501   unsigned char buf[1024];
25502   size_t len;
25503 
25504   debug(path);
25505 
25506 
25507   /* FIXME: This is Freedesktop.org-centric -bjk 2011.04.16 */
25508 
25509   if (basename(path) == NULL)
25510     {
25511       debug("Can't get basename! Deleting instead.");
25512       return (unlink(path));
25513     }
25514 
25515 #ifdef DEBUG
25516   printf("trash: basename=%s", basename(path)); /* EP */
25517 #endif
25518   safe_strncpy(fname, basename(path), sizeof(fname));
25519 
25520   if (!file_exists(path))
25521     {
25522       debug("Does't exist anyway, so skipping");
25523       return (1);
25524     }
25525 
25526 
25527   /* Move file into Trash folder */
25528 
25529   /* FIXME: Use xdg function */
25530   if (getenv("XDG_DATA_HOME") != NULL)
25531     {
25532       safe_snprintf(trashpath, sizeof(trashpath), "%s/Trash", getenv("XDG_DATA_HOME"));
25533     }
25534   else if (getenv("HOME") != NULL)
25535     {
25536       safe_snprintf(trashpath, sizeof(trashpath), "%s/.local/share/Trash", getenv("HOME"));
25537     }
25538   else
25539     {
25540       debug("Can't move to trash! Deleting instead.");
25541       return (unlink(path));
25542     }
25543 
25544   mkdir(trashpath, 0x777);
25545   safe_snprintf(dest, sizeof(dest), "%s/files", trashpath);
25546   mkdir(dest, 0x777);
25547   safe_snprintf(dest, sizeof(dest), "%s/info", trashpath);
25548   mkdir(dest, 0x777);
25549 
25550   safe_snprintf(dest, sizeof(dest), "%s/files/%s", trashpath, fname);
25551 
25552   safe_strncpy(bname, fname, sizeof(bname));
25553   if (strstr(bname, ".") != NULL)
25554     {
25555       strcpy(strstr(bname, "."), "\0"); /* FIXME: Use strncpy() (ugh, complicated) */
25556       safe_strncpy(ext, strstr(fname, ".") + 1, sizeof(ext));
25557     }
25558   else
25559     {
25560       debug("Filename format unfamiliar! Deleting instead.");
25561       return (unlink(path));
25562     }
25563 
25564   safe_snprintf(infoname, sizeof(infoname), "%s/info/%s.trashinfo", trashpath, fname);
25565 
25566   cnt = 1;
25567   while (file_exists(dest) && cnt < 100)
25568     {
25569       safe_snprintf(fname, sizeof(fname), "%s_%d.%s", bname, cnt, ext);
25570 
25571       safe_snprintf(dest, sizeof(dest), "%s/files/%s", trashpath, fname);
25572       safe_snprintf(infoname, sizeof(infoname), "%s/info/%s.trashinfo", trashpath, fname);
25573       cnt++;
25574     }
25575 
25576   if (cnt >= 100)
25577     {
25578       debug("Too many identically-named files! Deleting instead.");
25579       return (unlink(path));
25580     }
25581 
25582   debug(dest);
25583 
25584   if (rename(path, dest) == -1)
25585     {
25586       debug("Could not move to trash. Trying to copy, instead.");
25587 
25588       fi = fopen(path, "r");
25589       if (fi == NULL)
25590         {
25591           debug("Could not open source file for copy. Deleting instead.");
25592           return (unlink(path));
25593         }
25594       fo = fopen(dest, "w");
25595       if (fo == NULL)
25596         {
25597           debug("Could not open dest. file for copy. Deleting instead.");
25598           fclose(fi);
25599           return (unlink(path));
25600         }
25601       while (!feof(fi))
25602         {
25603           len = fread(buf, sizeof(unsigned char), sizeof(buf), fi);
25604           if (len > 0)
25605             {
25606               fwrite(buf, sizeof(unsigned char), sizeof(buf), fo);
25607             }
25608         }
25609       fclose(fi);
25610       fclose(fo);
25611 
25612       unlink(path);
25613     }
25614 
25615   /* Create info file */
25616   fo = fopen(infoname, "w");
25617   if (fo == NULL)
25618     {
25619       debug("Error: Couldn't create info file!");
25620       return (1);
25621     }
25622 
25623   now = time(NULL);
25624   tim = *(localtime(&now));
25625   strftime(deldate, sizeof(deldate), "%FT%T", &tim);
25626 
25627   fprintf(fo, "[Trash Info]\n");
25628   fprintf(fo, "Path=%s\n", path);
25629   fprintf(fo, "DeletionDate=%s\n", deldate);
25630   fclose(fo);
25631 
25632 
25633   /* Now we can alert the desktop GUI(s) running that something has been
25634      placed into the trash! */
25635 
25636   /* Tell KDE 4.x (e.g., Trash icon on your panel) that trash has been affected.
25637      Per dfaure (David Faure) and thiago (Thiago Macieria)
25638      on #kde-devel 2011.04.18
25639      -bjk 2011.04.18 */
25640 
25641   /* FIXME: Is this sufficient to find 'dbus-send' (rely on system to use $PATH?) -bjk 2011.04.18 */
25642   tmp = system("dbus-send / org.kde.KDirNotify.FilesAdded string:trash:/");
25643   (void)tmp;
25644 
25645 
25646   /* Note: GNOME figures out when things change because it asks the Kernel
25647      to tell it.
25648      Per cosimoc (Cosimo Cecchi) on #nautilus 2011.04.18
25649      -bjk 2011.04.18 */
25650 
25651   /* FIXME: xcfe and elsewhere: Anything to do? */
25652 
25653   /* FIXME: Windows */
25654 
25655   /* FIXME: Mac OS X */
25656 
25657   /* FIXME: Haiku */
25658 
25659   return (0);
25660 #endif /* UNLINK_ONLY */
25661 }
25662 
25663 /**
25664  * FIXME
25665  */
file_exists(char * path)25666 int file_exists(char *path)
25667 {
25668   struct stat buf;
25669   int res;
25670 
25671   res = stat(path, &buf);
25672   return (res == 0);
25673 }
25674 
25675 /**
25676  * FIXME
25677  */
25678 /* Don't move the mouse here as this is only called when an event triggers it
25679    and the joystick can be holded withouth sending any event. */
handle_joyaxismotion(SDL_Event event,int * motioner,int * val_x,int * val_y)25680 static void handle_joyaxismotion(SDL_Event event, int *motioner, int *val_x, int *val_y)
25681 {
25682   int i, j, step;
25683 
25684   if (event.jaxis.which != 0)
25685     return;
25686 
25687   i = SDL_JoystickGetAxis(joystick, 0);
25688   j = SDL_JoystickGetAxis(joystick, 1);
25689   step = 5000;
25690   if (abs(i) < joystick_low_threshold && abs(j) < joystick_low_threshold)
25691     {
25692       *motioner = FALSE;
25693       *val_x = 0;
25694       *val_y = 0;
25695     }
25696   else
25697     {
25698       if (i > joystick_low_threshold)
25699         *val_x = min((i - joystick_low_threshold) / step + 1, joystick_maxsteps);
25700       else if (i < -joystick_low_threshold)
25701         *val_x = max((i + joystick_low_threshold) / step - 1, -joystick_maxsteps);
25702       else
25703         *val_x = 0;
25704 
25705       if (j > joystick_low_threshold)
25706         *val_y = min((j - joystick_low_threshold) / step + 1, joystick_maxsteps);
25707       else if (j < -joystick_low_threshold)
25708         *val_y = max((j + joystick_low_threshold) / step - 1, -joystick_maxsteps);
25709       else
25710         *val_y = 0;
25711 
25712       if (*val_x || *val_y)
25713         {
25714           *motioner = TRUE;
25715         }
25716       else
25717         *motioner = FALSE;
25718     }
25719 }
25720 
25721 /**
25722  * FIXME
25723  */
handle_joyhatmotion(SDL_Event event,int oldpos_x,int oldpos_y,int * valhat_x,int * valhat_y,int * hatmotioner,Uint32 * old_hat_ticks)25724 static void handle_joyhatmotion(SDL_Event event, int oldpos_x, int oldpos_y, int *valhat_x, int *valhat_y,
25725                                 int *hatmotioner, Uint32 * old_hat_ticks)
25726 {
25727   *hatmotioner = 1;
25728 
25729   switch (event.jhat.value)
25730     {
25731     case SDL_HAT_CENTERED:
25732       *valhat_x = 0;
25733       *valhat_y = 0;
25734       *hatmotioner = 0;
25735       break;
25736     case SDL_HAT_UP:
25737       *valhat_x = 0;
25738       *valhat_y = -1;
25739       break;
25740     case SDL_HAT_RIGHTUP:
25741       *valhat_x = 1;
25742       *valhat_y = -1;
25743       break;
25744     case SDL_HAT_RIGHT:
25745       *valhat_x = 1;
25746       *valhat_y = 0;
25747       break;
25748     case SDL_HAT_RIGHTDOWN:
25749       *valhat_x = 1;
25750       *valhat_y = 1;
25751       break;
25752     case SDL_HAT_DOWN:
25753       *valhat_x = 0;
25754       *valhat_y = 1;
25755       break;
25756     case SDL_HAT_LEFTDOWN:
25757       *valhat_x = -1;
25758       *valhat_y = 1;
25759       break;
25760     case SDL_HAT_LEFT:
25761       *valhat_x = -1;
25762       *valhat_y = 0;
25763       break;
25764     case SDL_HAT_LEFTUP:
25765       *valhat_x = -1;
25766       *valhat_y = -1;
25767       break;
25768     }
25769   if (*valhat_x || *valhat_y)
25770     SDL_WarpMouse(oldpos_x + *valhat_x, oldpos_y + *valhat_y);
25771 
25772   *old_hat_ticks = SDL_GetTicks();
25773 }
25774 
25775 /**
25776  * FIXME
25777  */
handle_joyballmotion(SDL_Event event,int oldpos_x,int oldpos_y)25778 static void handle_joyballmotion(SDL_Event event, int oldpos_x, int oldpos_y)
25779 {
25780   int val_x, val_y;
25781 
25782   /* FIXME: NOT TESTED Should this act like handle_joyaxismotion?
25783      in the sense of setting the values for the moving but don't move the mouse here? */
25784   /* printf("\n ball movement \n"); */
25785   val_x = event.jball.xrel;
25786   val_y = event.jball.yrel;
25787   SDL_WarpMouse(oldpos_x + val_x, oldpos_y + val_y);
25788 }
25789 
25790 /**
25791  * FIXME
25792  */
handle_motioners(int oldpos_x,int oldpos_y,int motioner,int hatmotioner,int old_hat_ticks,int val_x,int val_y,int valhat_x,int valhat_y)25793 static void handle_motioners(int oldpos_x, int oldpos_y, int motioner, int hatmotioner, int old_hat_ticks, int val_x,
25794                              int val_y, int valhat_x, int valhat_y)
25795 {
25796   int vx, vy;
25797   Uint32 ticks;
25798 
25799   ticks = SDL_GetTicks();
25800   vx = vy = 0;
25801 
25802   vx = oldpos_x + val_x;
25803   vy = oldpos_y + val_y;
25804 
25805 
25806   if (ticks - old_hat_ticks > joystick_hat_timeout)
25807     {
25808       vx += valhat_x;
25809       vy += valhat_y;
25810     }
25811   SDL_WarpMouse(vx, vy);
25812 
25813   if (motioner && joystick_slowness)
25814     SDL_Delay(joystick_slowness);
25815 
25816   if (hatmotioner && joystick_hat_slowness)
25817     SDL_Delay(joystick_hat_slowness);
25818 
25819 }
25820 
25821 /**
25822  * FIXME
25823  */
handle_joybuttonupdown(SDL_Event event,int oldpos_x,int oldpos_y)25824 static void handle_joybuttonupdown(SDL_Event event, int oldpos_x, int oldpos_y)
25825 {
25826   handle_joybuttonupdownscl(event, oldpos_x, oldpos_y, r_tools);
25827 }
25828 
25829 /**
25830  * FIXME
25831  */
handle_joybuttonupdownscl(SDL_Event event,int oldpos_x,int oldpos_y,SDL_Rect real_r_tools)25832 static void handle_joybuttonupdownscl(SDL_Event event, int oldpos_x, int oldpos_y, SDL_Rect real_r_tools)
25833 {
25834   int i, ignore = 0;
25835   int eby, ts;
25836   SDL_Event ev;
25837 
25838   ev.button.x = oldpos_x;
25839   ev.button.y = oldpos_y;
25840   ev.button.button = SDL_BUTTON_LEFT;
25841   ev.button.type = SDL_MOUSEBUTTONDOWN;
25842   ev.button.state = SDL_PRESSED;
25843 
25844   if (event.type == SDL_JOYBUTTONDOWN)
25845     {
25846       /* First the actions that can be reached via keyboard shortcurts. */
25847       /* Escape is usefull to dismiss dialogs */
25848       if (event.button.button == joystick_button_escape)
25849         {
25850           ev.type = SDL_KEYDOWN;
25851           ev.key.keysym.sym = SDLK_ESCAPE;
25852           ev.key.keysym.mod = KMOD_CTRL;
25853         }
25854       else if (event.button.button == joystick_button_pagesetup)
25855         {
25856           ev.type = SDL_KEYDOWN;
25857           ev.key.keysym.sym = SDLK_p;
25858           ev.key.keysym.mod = KMOD_CTRL | KMOD_SHIFT;
25859         }
25860 
25861       /* Those could be reached too via clicks on the buttons. */
25862       else if (event.button.button == joystick_button_undo)
25863         {
25864           ev.type = SDL_KEYDOWN;
25865           ev.key.keysym.sym = SDLK_z;
25866           ev.key.keysym.mod = KMOD_CTRL;
25867         }
25868       else if (event.button.button == joystick_button_redo)
25869         {
25870           ev.type = SDL_KEYDOWN;
25871           ev.key.keysym.sym = SDLK_r;
25872           ev.key.keysym.mod = KMOD_CTRL;
25873         }
25874       else if (event.button.button == joystick_button_open)
25875         {
25876           ev.type = SDL_KEYDOWN;
25877           ev.key.keysym.sym = SDLK_o;
25878           ev.key.keysym.mod = KMOD_CTRL;
25879         }
25880       else if (event.button.button == joystick_button_new)
25881         {
25882           ev.type = SDL_KEYDOWN;
25883           ev.key.keysym.sym = SDLK_n;
25884           ev.key.keysym.mod = KMOD_CTRL;
25885         }
25886       else if (event.button.button == joystick_button_save)
25887         {
25888           ev.type = SDL_KEYDOWN;
25889           ev.key.keysym.sym = SDLK_s;
25890           ev.key.keysym.mod = KMOD_CTRL;
25891         }
25892       else if (event.button.button == joystick_button_print)
25893         {
25894           ev.type = SDL_KEYDOWN;
25895           ev.key.keysym.sym = SDLK_p;
25896           ev.key.keysym.mod = KMOD_CTRL;
25897         }
25898 
25899 
25900       /* Now the clicks on the tool buttons. */
25901       /* Note that at small window sizes there are scroll buttons in the tools rectangle */
25902       /* and some tools are hiden. */
25903       /* As any click outside of real_r_tools will not select the desired tool, */
25904       /* the workaround I came up with is to click on the scroll buttons to reveal the button, */
25905       /* then click on it. */
25906       else if (event.button.button == joystick_button_selectbrushtool ||
25907                event.button.button == joystick_button_selectstamptool ||
25908                event.button.button == joystick_button_selectlinestool ||
25909                event.button.button == joystick_button_selectshapestool ||
25910                event.button.button == joystick_button_selecttexttool ||
25911                event.button.button == joystick_button_selectlabeltool ||
25912                event.button.button == joystick_button_selectmagictool ||
25913                event.button.button == joystick_button_selecterasertool)
25914 
25915         {
25916           if (event.button.button == joystick_button_selectbrushtool)
25917             {
25918               ev.button.x = (TOOL_BRUSH % 2) * button_w + button_w / 2;
25919               ev.button.y = real_r_tools.y + TOOL_BRUSH / 2 * button_h + button_h / 2;
25920             }
25921 
25922           else if (event.button.button == joystick_button_selectstamptool)
25923             {
25924               ev.button.x = (TOOL_STAMP % 2) * button_w + button_w / 2;
25925               ev.button.y = real_r_tools.y + TOOL_STAMP / 2 * button_h + button_h / 2;
25926             }
25927 
25928           else if (event.button.button == joystick_button_selectlinestool)
25929             {
25930               ev.button.x = (TOOL_LINES % 2) * button_w + button_w / 2;
25931               ev.button.y = real_r_tools.y + TOOL_LINES / 2 * button_h + button_h / 2;
25932             }
25933 
25934           else if (event.button.button == joystick_button_selectshapestool)
25935             {
25936               ev.button.x = (TOOL_SHAPES % 2) * button_w + button_w / 2;
25937               ev.button.y = real_r_tools.y + TOOL_SHAPES / 2 * button_h + button_h / 2;
25938             }
25939 
25940           else if (event.button.button == joystick_button_selecttexttool)
25941             {
25942               ev.button.x = (TOOL_TEXT % 2) * button_w + button_w / 2;
25943               ev.button.y = real_r_tools.y + TOOL_TEXT / 2 * button_h + button_h / 2;
25944             }
25945 
25946           else if (event.button.button == joystick_button_selectlabeltool)
25947             {
25948               ev.button.x = (TOOL_LABEL % 2) * button_w + button_w / 2;
25949               ev.button.y = real_r_tools.y + TOOL_LABEL / 2 * button_h + button_h / 2;
25950             }
25951 
25952           else if (event.button.button == joystick_button_selectmagictool)
25953             {
25954               ev.button.x = (TOOL_MAGIC % 2) * button_w + button_w / 2;
25955               ev.button.y = real_r_tools.y + TOOL_MAGIC / 2 * button_h + button_h / 2;
25956             }
25957 
25958           else if (event.button.button == joystick_button_selecterasertool)
25959             {
25960               ev.button.x = (TOOL_ERASER % 2) * button_w + button_w / 2;
25961               ev.button.y = real_r_tools.y + TOOL_ERASER / 2 * button_h + button_h / 2;
25962             }
25963 
25964           /* Deal with scroll to reveal the button that should be clicked */
25965           eby = ev.button.y;
25966           ts = tool_scroll;
25967 
25968           while (eby < real_r_tools.y + ts / 2 * button_h)
25969             {
25970               ev.button.y = real_r_tools.y - 1;
25971               SDL_PushEvent(&ev);
25972               ts -= 2;
25973             }
25974 
25975           /* We don't need this ATM, but better left it ready in case the number of tools grows enough */
25976           while (eby > real_r_tools.y + real_r_tools.h + ts / 2 * button_h)
25977             {
25978               ev.button.y = real_r_tools.y + real_r_tools.h + 1;
25979               SDL_PushEvent(&ev);
25980               ts += 2;
25981             }
25982 
25983           ev.button.y = eby - ts / 2 * button_h;
25984         }
25985     }
25986   else
25987     {
25988       ev.button.type = SDL_MOUSEBUTTONUP;
25989       ev.button.state = SDL_RELEASED;
25990     }
25991 
25992 #ifdef DEBUG
25993   printf("result %d %d\n", ev.button.x, ev.button.y);
25994 #endif
25995 
25996   /* See if it's a button we ignore */
25997 
25998   for (i = 0; i < joystick_buttons_ignore_len && !ignore; i++)
25999     {
26000       if (event.button.button == joystick_buttons_ignore[i])
26001         {
26002           ignore = 1;
26003         }
26004     }
26005 
26006   if (!ignore)
26007     SDL_PushEvent(&ev);
26008 }
26009 
26010 /**
26011  * Grab the user's XDG user dir for something (e.g., ~/Pictures)
26012  *
26013  * @param char * dir_type -- the thing to query, e.g. "PICTURES" or "VIDEOS"
26014  *   (note: currently, Tux Paint only puts things in the PICTURES one)
26015  * @param char * fallback -- path, under $HOME, to use instead (e.g., "Pictures")
26016  * @return char * path (caller is expected to free() it)
26017  */
get_xdg_user_dir(const char * dir_type,const char * fallback)26018 char * get_xdg_user_dir(const char * dir_type, const char * fallback) {
26019   FILE * fi;
26020   char * config_home, * found;
26021   char tmp_path[MAX_PATH], config_path[MAX_PATH], line[MAX_PATH], search[MAX_PATH], return_path[MAX_PATH];
26022   int found_it;
26023 
26024   found_it = FALSE;
26025 
26026   /* Figure out where XDG user-dirs config exists, and use it if possible */
26027   if (getenv("XDG_CONFIG_HOME") != NULL) {
26028     config_home = strdup(getenv("XDG_CONFIG_HOME"));
26029   } else {
26030 #ifdef DEBUG
26031     fprintf(stderr, "XDG_CONFIG_HOME not set, checking $HOME/.config/\n");
26032 #endif
26033     if (getenv("HOME") != NULL) {
26034       safe_snprintf(tmp_path, MAX_PATH, "%s/.config", getenv("HOME"));
26035       config_home = strdup(tmp_path);
26036     } else {
26037 #ifdef DEBUG
26038       fprintf(stderr, "No HOME, either?! Returing fallback in current directory\n");
26039 #endif
26040       return strdup(fallback);
26041     }
26042   }
26043 
26044   if (config_home[strlen(config_home) - 1] == '/') {
26045     config_home[strlen(config_home) - 1] = '\0';
26046   }
26047   safe_snprintf(config_path, MAX_PATH, "%s/user-dirs.dirs", config_home);
26048   free(config_home);
26049 
26050 #ifdef DEBUG
26051   fprintf(stderr, "User dirs config = %s\n", config_path);
26052 #endif
26053 
26054   safe_snprintf(search, MAX_PATH, "XDG_%s_DIR=\"", dir_type);
26055 
26056   /* Read the config to find the path we want */
26057   fi = fopen(config_path, "r");
26058   if (fi != NULL) {
26059     /* Search for a line in the form of either
26060        either XDG_PICTURES_DIR="$HOME/Pictures"
26061        or XDG_PICTURES_DIR="/Path/To/Pictures"
26062     */
26063 #ifdef DEBUG
26064     fprintf(stderr, "Searching it for: %s\n", search);
26065 #endif
26066     while (fgets(line, MAX_PATH, fi) && !found_it) {
26067       /* Trim trailing CR/LF */
26068         if (line[strlen(line) - 1] == '\n' ||
26069             line[strlen(line) - 1] == '\r') {
26070           line[strlen(line) - 1] = '\0';
26071         }
26072 
26073       if (strstr(line, search) == line) {
26074         found = line + strlen(search);
26075 #ifdef DEBUG
26076         fprintf(stderr, "Found it: %s\n", found);
26077 #endif
26078         if (strstr(found, "$HOME/") == found) {
26079           safe_snprintf(return_path, MAX_PATH, "%s/%s", getenv("HOME"), found + 6 /* skip '$HOME/' */);
26080         } else {
26081           safe_strncpy(return_path, found, MAX_PATH);
26082         }
26083 
26084         /* Trim trailing " */
26085         if (return_path[strlen(return_path) - 1] == '\"') {
26086           return_path[strlen(return_path) - 1] = '\0';
26087         }
26088 
26089         found_it = TRUE;
26090       }
26091     }
26092 
26093     fclose(fi);
26094 #ifdef DEBUG
26095   } else {
26096     fprintf(stderr, "%s doesn't exist\n", config_path);
26097 #endif
26098   }
26099 
26100   if (!found_it) {
26101 #ifdef DEBUG
26102     fprintf(stderr, "Using fallback of $HOME/%s\n", fallback);
26103 #endif
26104     safe_snprintf(return_path, MAX_PATH, "%s/%s", getenv("HOME"), fallback);
26105   }
26106 
26107 #ifdef DEBUG
26108   fprintf(stderr, "Location for %s => %s\n", dir_type, return_path);
26109 #endif
26110 
26111   return strdup(return_path);
26112 }
26113 
26114 /**
26115  * After 2+ images have been selected in the Open->Slideshow
26116  * dialog, they can be exported as an animated GIF.
26117  *
26118  * Params the same as play_slideshow(), except...
26119  *
26120  * @param int speed -- how fast to play the slideshow (0 and 1 both = slowest, 10 = fasted)
26121  * @return int -- 0 if export failed or was aborted, 1 if successful
26122  */
export_gif(int * selected,int num_selected,char * dirname,char ** d_names,char ** d_exts,int speed)26123 static int export_gif(int *selected, int num_selected, char *dirname, char **d_names, char **d_exts, int speed) {
26124   char *tmp_starter_id, *tmp_template_id, *tmp_file_id;
26125   int tmp_starter_mirrored, tmp_starter_flipped, tmp_starter_personal;
26126   char * gif_fname;
26127   char fname[MAX_PATH];
26128   int i, j, done, which, x, y;
26129   SDL_Surface *img;
26130   int overall_w, overall_h, overall_area;
26131   Uint8 * bitmap;
26132   Uint8 r, g, b, a;
26133   size_t pixels_size;
26134   unsigned char *raw_8bit_pixels;
26135   uint8_t gif_palette[768]; /* 256 x 3 */
26136   liq_attr *liq_handle;
26137   liq_image *input_image;
26138   liq_result *quantization_result;
26139 #if LIQ_VERSION >= 20800
26140   liq_error qtiz_status;
26141 #endif
26142   const liq_palette *palette;
26143   int gif_speed;
26144 
26145   /* Back up the current image's IDs, because they will get
26146      clobbered below! */
26147   tmp_starter_id = strdup(starter_id);
26148   tmp_template_id = strdup(template_id);
26149   tmp_file_id = strdup(file_id);
26150   tmp_starter_mirrored = starter_mirrored;
26151   tmp_starter_flipped = starter_flipped;
26152   tmp_starter_personal = starter_personal;
26153 
26154   do_setcursor(cursor_watch);
26155   show_progress_bar(screen);
26156 
26157   gif_fname = get_export_filepath("gif");
26158   if (gif_fname == NULL)
26159     {
26160       /* Can't create export dir! Abort! */
26161       return FALSE;
26162     }
26163 
26164   /* For now, always saving GIF using the size of Tux Paint's window,
26165      which is how images appear in the slide show */
26166   overall_w = screen->w;
26167   overall_h = screen->h;
26168   overall_area = overall_w * overall_h;
26169 
26170   if (speed == 0)
26171     {
26172       gif_speed = 1;
26173     }
26174   gif_speed = (10 - speed) * 50;
26175 
26176   bitmap = malloc(num_selected * overall_area * 4);
26177   if (bitmap != NULL)
26178     {
26179       done = 0;
26180 
26181       for (i = 0; i < num_selected && !done; i++)
26182         {
26183           which = selected[i];
26184           show_progress_bar(screen);
26185 
26186 
26187           /* Figure out filename: */
26188           safe_snprintf(fname, sizeof(fname), "%s/%s%s", dirname, d_names[which], d_exts[which]);
26189 
26190           /* Load and scale the image */
26191           img = myIMG_Load(fname);
26192 
26193           if (img != NULL)
26194             {
26195               autoscale_copy_smear_free(img, screen, SDL_BlitSurface);
26196 
26197               safe_strncpy(file_id, d_names[which], sizeof(file_id));
26198 
26199               /* See if this saved image was based on a 'starter' */
26200               load_starter_id(d_names[which], NULL);
26201               if (starter_id[0] != '\0')
26202                 {
26203                   load_starter(starter_id);
26204 
26205                   if (starter_mirrored)
26206                     mirror_starter();
26207 
26208                   if (starter_flipped)
26209                     flip_starter();
26210                 }
26211               else
26212                 load_template(template_id);
26213             } else {
26214               /* Error loading !*/
26215               fprintf(stderr, "Error loading %s!\n", fname);
26216               /* FIXME Abort? */
26217             }
26218 
26219 	  /* Record the raw RGB into a big strip, to be quantized and sliced later */
26220           for (y = 0; y < overall_h; y++) {
26221             for (x = 0; x < overall_w; x++) {
26222               SDL_GetRGBA(getpixels[screen->format->BytesPerPixel](screen, x, y), screen->format, &r, &g, &b, &a);
26223 
26224               bitmap[((i * overall_area) + (y * overall_w) + x) * 4 + 0] = r;
26225               bitmap[((i * overall_area) + (y * overall_w) + x) * 4 + 1] = g;
26226               bitmap[((i * overall_area) + (y * overall_w) + x) * 4 + 2] = b;
26227               bitmap[((i * overall_area) + (y * overall_w) + x) * 4 + 3] = 255;
26228             }
26229           }
26230 
26231           SDL_Flip(screen);
26232 	  done = export_gif_monitor_events();
26233         }
26234 
26235 
26236       if (!done)
26237         {
26238           /* Quantize to max 256 (8bpp) colors and generate a suitable palette */
26239           liq_handle = liq_attr_create();
26240           input_image = liq_image_create_rgba(liq_handle, bitmap, overall_w, num_selected * overall_h, 0);
26241 	  liq_set_max_colors(liq_handle, 256);
26242 
26243           show_progress_bar(screen);
26244 	  done = export_gif_monitor_events();
26245 
26246 #if LIQ_VERSION >= 20800
26247 	  qtiz_status = liq_image_quantize(input_image, liq_handle, &quantization_result);
26248 	  done = (qtiz_status != LIQ_OK);
26249 #else
26250 	  quantization_result = liq_quantize_image(liq_handle, input_image);
26251 	  done = (quantization_result == NULL);
26252 #endif
26253           if (!done) {
26254             // Use libimagequant to make new image pixels from the palette
26255             pixels_size = num_selected * overall_area;
26256             raw_8bit_pixels = malloc(pixels_size);
26257             liq_set_dithering_level(quantization_result, 1.0);
26258 
26259             liq_write_remapped_image(quantization_result, input_image, raw_8bit_pixels, pixels_size);
26260             palette = liq_get_palette(quantization_result);
26261             free(bitmap);
26262 
26263 	    for (j = 0; j < (int) palette->count; j++) {
26264               gif_palette[j * 3 + 0] = palette->entries[j].r;
26265               gif_palette[j * 3 + 1] = palette->entries[j].g;
26266               gif_palette[j * 3 + 2] = palette->entries[j].b;
26267             }
26268 
26269             /* Open GIF */
26270             ge_GIF *gif = ge_new_gif(
26271                 gif_fname,
26272                 overall_w, overall_h,
26273                 gif_palette,
26274                 8, /* 256 colors */
26275                 0 /* infinite loop */
26276             );
26277 
26278             /* Export each frame */
26279             for (i = 0; i < num_selected && !done; i++)
26280               {
26281                 memcpy(gif->frame, raw_8bit_pixels + i * overall_area, overall_area);
26282                 ge_add_frame(gif, gif_speed);
26283 
26284                 show_progress_bar(screen);
26285 	        done = export_gif_monitor_events();
26286               }
26287 
26288             /* Close the GIF */
26289             ge_close_gif(gif);
26290           } else {
26291             fprintf(stderr, "Quantization failed\n");
26292 	    done = 1;
26293 	  }
26294 
26295           if (done)
26296             {
26297               /* Aborted; discard the partially-saved GIF */
26298               unlink(gif_fname);
26299 	    }
26300 	}
26301     }
26302   else
26303     {
26304       /* Out of memory! */
26305       done = 1;
26306     }
26307 
26308 
26309   /* Restore everything about the currently-active image
26310      that got clobbered above */
26311   strcpy(starter_id, tmp_starter_id); /* safe; originally strdup()'d from the dest. */
26312   free(tmp_starter_id);
26313 
26314   strcpy(template_id, tmp_template_id); /* safe; originally strdup()'d from the dest. */
26315   free(tmp_template_id);
26316 
26317   strcpy(file_id, tmp_file_id); /* safe; originally strdup()'d from the dest. */
26318   free(tmp_file_id);
26319 
26320   starter_mirrored = tmp_starter_mirrored;
26321   starter_flipped = tmp_starter_flipped;
26322   starter_personal = tmp_starter_personal;
26323 
26324 
26325   free(gif_fname);
26326 
26327   /* Success if we didn't have an error, and user didn't abort */
26328   return(!done);
26329 }
26330 
26331 /**
26332  * Called by export_gif() while it's iterating through images
26333  * in a few different ways, to monitor SDL event queue for
26334  * any [Esc] keypress or quit event (e.g., closing window),
26335  * which triggers an abort of the export.
26336  *
26337  * @return int 0 = keep going, 1 = abort
26338  */
export_gif_monitor_events(void)26339 int export_gif_monitor_events(void) {
26340   int done;
26341   SDL_Event event;
26342   SDLKey key;
26343 
26344   done = 0;
26345   while (SDL_PollEvent(&event))
26346     {
26347       if (event.type == SDL_QUIT)
26348         {
26349           done = 1;
26350         }
26351       else if (event.type == SDL_KEYDOWN)
26352         {
26353           key = event.key.keysym.sym;
26354           if (key == SDLK_ESCAPE) {
26355             done = 1;
26356           }
26357         }
26358     }
26359   SDL_Delay(10);
26360   return done;
26361 }
26362 
26363 /**
26364  * Copy an image (just the main PNG) from Tux Paint's "saved"
26365  * directory to the user's chosen export directory
26366  * (e.g., ~/Pictures, or whatever "--exportdir" says).
26367  *
26368  * Used when exporting a single image from the Open dialog.
26369  *
26370  * @param char * fname -- full path to the image to export
26371  * @return int 1 = success, 0 = failed
26372  */
export_pict(char * fname)26373 static int export_pict(char * fname) {
26374   FILE * fi, * fo;
26375   size_t len;
26376   unsigned char buf[1024];
26377   char * pict_fname;
26378   Uint32 time_before, time_after;
26379 
26380   do_setcursor(cursor_watch);
26381   show_progress_bar(screen);
26382 
26383   fi = fopen(fname, "rb");
26384   if (fi == NULL)
26385     {
26386       fprintf(stderr, "Cannot export from saved Tux Paint file '%s'\nThe error that occurred was:\n%s\n\n", fname, strerror(errno));
26387       return FALSE;
26388     }
26389 
26390   time_before = SDL_GetTicks();
26391   pict_fname = get_export_filepath("png");
26392   if (pict_fname == NULL)
26393     {
26394       fclose(fi);
26395       return FALSE;
26396     }
26397 
26398   fo = fopen(pict_fname, "wb");
26399   if (fo == NULL)
26400     {
26401       fprintf(stderr, "Cannot export to new file '%s'\nThe error that occurred was:\n%s\n\n", pict_fname, strerror(errno));
26402       free(pict_fname);
26403       fclose(fi);
26404       return FALSE;
26405     }
26406 
26407   while (!feof(fi))
26408     {
26409       len = fread(buf, sizeof(unsigned char), sizeof(buf), fi);
26410       if (len > 0)
26411         {
26412           fwrite(buf, sizeof(unsigned char), sizeof(buf), fo);
26413         }
26414     }
26415 
26416   /* FIXME: Probably good to check for errors here and respond accordingly -bjk 2020.07.26 */
26417 
26418   fclose(fi);
26419   fclose(fo);
26420 
26421   free(pict_fname);
26422 
26423   /* Unique filenames are timestamp-based, down to the second,
26424      so ensure at least one second has elapsed */
26425   time_after = SDL_GetTicks();
26426   if (time_after - time_before < 1000)
26427     {
26428       show_progress_bar(screen);
26429       SDL_Delay(time_after + 1000 - time_before);
26430     }
26431 
26432   return TRUE;
26433 }
26434 
26435 /**
26436  * Returns the name of a new file, located in the user's chosen
26437  * export directory (e.g., ~/Pictures/TuxPaint, or whatever "--exportdir" says).
26438  *
26439  * Also ensures that the directory exists, in the first place.
26440  *
26441  * Used when exporting animated GIFs (via "Export GIF" in the
26442  * Open->Slideshow dialog) and static PNGs (via "Export" in the
26443  * main Open dialog).
26444  *
26445  * @param const char * ext -- extnesion of the file (e.g., "png" or "gif")
26446  * @return char * -- filepath for the new file to be created
26447  *   (e.g., /home/username/Pictures/TuxPaint/2020072620110100.gif")
26448  *   Or NULL if the directory cannot be created.
26449  */
get_export_filepath(const char * ext)26450 static char * get_export_filepath(const char * ext) {
26451   char *rname;
26452   char fname[FILENAME_MAX];
26453   char timestamp[16];
26454   time_t t;
26455 
26456 
26457   /* Make sure the export dir exists */
26458   if (!make_directory(DIR_EXPORT, "", "Can't create export directory; will try to make its parent (E016)"))
26459     {
26460       /* See if perhaps we need to try and make its parent directory first? */
26461       if (make_directory(DIR_EXPORT_PARENT, "", "Can't create export directory parent (E016b)")) {
26462         if (!make_directory(DIR_EXPORT, "", "Can't create export directory (E016c)")) {
26463           return NULL;
26464         }
26465       } else {
26466         return NULL;
26467       }
26468     }
26469 
26470   /* Create a unique filename, within that dir */
26471   t = time(NULL);
26472   strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", localtime(&t));
26473   safe_snprintf(fname, sizeof(fname), "%s.%s", timestamp, ext);
26474   rname = get_fname(fname, DIR_EXPORT);
26475   debug(rname);
26476 
26477   return(rname);
26478 }
26479 
safe_strncat(char * dest,const char * src,size_t n)26480 char * safe_strncat(char *dest, const char *src, size_t n) {
26481   char * ptr;
26482   ptr = strncat(dest, src, n - 1);
26483   dest[n - 1] = '\0';
26484   return ptr;
26485 }
26486 
safe_strncpy(char * dest,const char * src,size_t n)26487 char * safe_strncpy(char *dest, const char *src, size_t n) {
26488   char * ptr;
26489   ptr = strncpy(dest, src, n - 1);
26490   dest[n - 1] = '\0';
26491   return ptr;
26492 }
26493 
safe_snprintf(char * str,size_t size,const char * format,...)26494 int safe_snprintf(char *str, size_t size, const char *format, ...) {
26495   int r;
26496   va_list ap;
26497 
26498   va_start(ap, format);
26499   r = vsnprintf(str, size - 1, format, ap);
26500   va_end(ap);
26501 
26502   str[size - 1] = '\0';
26503   return r;
26504 }
26505 
26506