11261 lines
234 KiB
C
11261 lines
234 KiB
C
/*
|
|
tuxpaint.c
|
|
|
|
Tux Paint - A simple drawing program for children.
|
|
|
|
Copyright (c) 2003 by Bill Kendrick
|
|
bill@newbreedsoftware.com
|
|
http://www.newbreedsoftware.com/tuxpaint/
|
|
|
|
June 14, 2002 - June 14, 2003
|
|
*/
|
|
|
|
|
|
#define VER_VERSION "0.9.11"
|
|
#define VER_DATE "2003.06.14"
|
|
|
|
|
|
/* #define DEBUG */
|
|
/* #define LOW_QUALITY_THUMBNAILS */
|
|
/* #define LOW_QUALITY_COLOR_SELECTOR */
|
|
/* #define LOW_QUALITY_STAMP_OUTLINE */
|
|
/* #define LOW_QUALITY_FLOOD_FILL */
|
|
/* #define NO_PROMPT_SHADOWS */
|
|
/* #define USE_HWSURFACE */
|
|
|
|
|
|
/* Disable fancy cursors in fullscreen mode, to avoid SDL bug: */
|
|
#define LARGE_CURSOR_FULLSCREEN_BUG
|
|
|
|
#define HEIGHTOFFSET (((WINDOW_HEIGHT - 480) / 48) * 48)
|
|
#define TOOLOFFSET (HEIGHTOFFSET / 48 * 2)
|
|
#define PROMPTOFFSETX (WINDOW_WIDTH - 640) / 2
|
|
#define PROMPTOFFSETY (HEIGHTOFFSET / 2)
|
|
|
|
#define MAX_FILES 256 /* Max. # of files in a dir. to worry about... */
|
|
|
|
#define REPEAT_SPEED 300 /* Initial repeat speed for scrollbars */
|
|
#define CURSOR_BLINK_SPEED 500 /* Initial repeat speed for cursor */
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
|
|
#include <locale.h>
|
|
#include <iconv.h>
|
|
|
|
#ifdef WIN32_OLD
|
|
/* The following are required by libintl.h, so must be defined first: */
|
|
#define LC_MESSAGES 1729
|
|
#define HAVE_LC_MESSAGES 1
|
|
#define ENABLE_NLS 1
|
|
#define HAVE_LOCALE_H 1
|
|
#define HAVE_GETTEXT 1
|
|
#define HAVE_DCGETTEXT 1
|
|
#endif
|
|
|
|
#if defined(sun) && defined(__svr4__)
|
|
/* Solaris needs locale.h */
|
|
#endif
|
|
|
|
#include <libintl.h>
|
|
#ifndef gettext_noop
|
|
#define gettext_noop(String) String
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG
|
|
#define gettext(String) debug_gettext(String)
|
|
#endif
|
|
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#ifdef __BEOS__
|
|
#include "BeOS_print.h"
|
|
#endif
|
|
#ifdef __APPLE__
|
|
#include "maxosx_print.h"
|
|
#endif
|
|
#else
|
|
#include "win32_dirent.h"
|
|
#include "win32_print.h"
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
|
|
|
|
/* Enables win32 apps to get a GNU compatible locale string */
|
|
extern char* g_win32_getlocale(void);
|
|
|
|
/* Set this to 0 during developement and testing in Visual-Studio
|
|
Set this to 1 to make the final executable */
|
|
|
|
#if 1
|
|
|
|
#define DOC_PREFIX "docs/"
|
|
#define DATA_PREFIX "data/"
|
|
#define LOCALEDIR "locale"
|
|
|
|
#else
|
|
|
|
#define DOC_PREFIX "../../docs/"
|
|
#define DATA_PREFIX "../../data/"
|
|
#define LOCALEDIR "../../locale"
|
|
|
|
#endif /* 1/0 */
|
|
|
|
#define mkdir(path,access) _mkdir(path)
|
|
#define strcasecmp stricmp
|
|
#define strncasecmp strnicmp
|
|
#define snprintf _snprintf
|
|
#define S_ISDIR(i) ((i&_S_IFDIR)!=0)
|
|
#define alloca _alloca
|
|
|
|
#endif /* WIN32 */
|
|
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "SDL.h"
|
|
#include "SDL_image.h"
|
|
#include "SDL_ttf.h"
|
|
#ifndef NOSOUND
|
|
#include "SDL_mixer.h"
|
|
#endif
|
|
|
|
#ifndef SAVE_AS_BMP
|
|
#include <png.h>
|
|
#define FNAME_EXTENSION ".png"
|
|
#else
|
|
#define FNAME_EXTENSION ".bmp"
|
|
#endif
|
|
|
|
#define THUMB_W ((WINDOW_WIDTH - 96 - 96) / 4)
|
|
#define THUMB_H (((48 * 7 + 40 + HEIGHTOFFSET) - 72) / 4)
|
|
|
|
#include "tools.h"
|
|
#include "titles.h"
|
|
#include "colors.h"
|
|
#include "shapes.h"
|
|
#include "magic.h"
|
|
#include "sounds.h"
|
|
#include "tip_tux.h"
|
|
#include "great.h"
|
|
|
|
|
|
#include "mouse/watch.xbm"
|
|
#include "mouse/watch-mask.xbm"
|
|
|
|
#include "mouse/hand.xbm"
|
|
#include "mouse/hand-mask.xbm"
|
|
|
|
#include "mouse/wand.xbm"
|
|
#include "mouse/wand-mask.xbm"
|
|
|
|
#include "mouse/insertion.xbm"
|
|
#include "mouse/insertion-mask.xbm"
|
|
|
|
#include "mouse/brush.xbm"
|
|
#include "mouse/brush-mask.xbm"
|
|
|
|
#include "mouse/crosshair.xbm"
|
|
#include "mouse/crosshair-mask.xbm"
|
|
|
|
#include "mouse/rotate.xbm"
|
|
#include "mouse/rotate-mask.xbm"
|
|
|
|
#include "mouse/up.xbm"
|
|
#include "mouse/up-mask.xbm"
|
|
|
|
#include "mouse/down.xbm"
|
|
#include "mouse/down-mask.xbm"
|
|
|
|
#include "mouse/tiny.xbm"
|
|
#include "mouse/tiny-mask.xbm"
|
|
|
|
#include "mouse/arrow.xbm"
|
|
#include "mouse/arrow-mask.xbm"
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
/*
|
|
The SDL stderr redirection trick doesn't seem to work for perror().
|
|
This does pretty much the same thing.
|
|
*/
|
|
void win32_perror(const char *str)
|
|
{
|
|
if ( str && *str )
|
|
fprintf(stderr,"%s : ",str);
|
|
fprintf(stderr,
|
|
"%s [%d]\n",
|
|
(errno<_sys_nerr)?_sys_errlist[errno]:"unknown",errno );
|
|
}
|
|
#define perror win32_perror
|
|
#endif
|
|
|
|
|
|
#ifndef WIN32
|
|
#define min(a,b) ((a < b) ? a : b)
|
|
#define max(a,b) ((a > b) ? a : b)
|
|
#endif
|
|
|
|
#define clamp(lo,value,hi) (min(max(value,lo),hi))
|
|
|
|
|
|
#define RENDER_TEXT TTF_RenderText_Blended
|
|
|
|
|
|
/* Possible languages: */
|
|
|
|
enum {
|
|
LANG_CA, /* Catalan */
|
|
LANG_CZ, /* Czech */
|
|
LANG_DA, /* Danish */
|
|
LANG_DE, /* German */
|
|
LANG_EL, /* Greek */
|
|
LANG_EN, /* English (American) (DEFAULT) */
|
|
LANG_EN_GB, /* English (British) */
|
|
LANG_ES, /* Spanish */
|
|
LANG_FI, /* Finnish */
|
|
LANG_FR, /* French */
|
|
LANG_HE, /* Hebrew */
|
|
LANG_HU, /* Hungarian */
|
|
LANG_ID, /* Indonesian */
|
|
LANG_IS, /* Icelandic */
|
|
LANG_IT, /* Italian */
|
|
LANG_JA, /* Japanese */
|
|
LANG_KO, /* Korean */
|
|
LANG_LT, /* Lithuanian */
|
|
LANG_NL, /* Dutch */
|
|
LANG_NN, /* Norwegian */
|
|
LANG_PL, /* Polish */
|
|
LANG_PT_BR, /* Portuguese (Brazilian) */
|
|
LANG_PT, /* Portuguese */
|
|
LANG_RO, /* Romanian */
|
|
LANG_SE, /* Swedish */
|
|
LANG_SK, /* Slovak */
|
|
LANG_TR, /* Turkish */
|
|
LANG_ZH, /* Chinese */
|
|
NUM_LANGS
|
|
};
|
|
|
|
const char * lang_prefixes[NUM_LANGS] = {
|
|
"ca",
|
|
"cz",
|
|
"da",
|
|
"de",
|
|
"el",
|
|
"en",
|
|
"en_gb",
|
|
"es",
|
|
"fi",
|
|
"fr",
|
|
"he",
|
|
"hu",
|
|
"id",
|
|
"is",
|
|
"it",
|
|
"ja",
|
|
"ko",
|
|
"lt",
|
|
"nl",
|
|
"nn",
|
|
"pl",
|
|
"pt_br",
|
|
"pt",
|
|
"ro",
|
|
"se",
|
|
"sk",
|
|
"tr",
|
|
"zh"
|
|
};
|
|
|
|
|
|
/* List of languages where we should use Unicode font rendering: */
|
|
|
|
int lang_use_unicode[] = {
|
|
-1
|
|
};
|
|
|
|
int lang_use_utf8[] = {
|
|
LANG_EL,
|
|
LANG_HE,
|
|
LANG_JA,
|
|
LANG_KO,
|
|
/* LANG_LT, */
|
|
/* LANG_PL, */
|
|
LANG_ZH,
|
|
-1
|
|
};
|
|
|
|
int lang_use_right_to_left[] = {
|
|
LANG_HE,
|
|
-1
|
|
};
|
|
|
|
|
|
typedef struct info_type {
|
|
int colorable;
|
|
int tintable;
|
|
} info_type;
|
|
|
|
|
|
enum {
|
|
SAVE_OVER_PROMPT,
|
|
SAVE_OVER_ALWAYS,
|
|
SAVE_OVER_NO
|
|
};
|
|
|
|
|
|
/* Globals: */
|
|
|
|
int use_sound, fullscreen, disable_quit, simple_shapes, language,
|
|
disable_print, print_delay, only_uppercase, promptless_save, grab_input,
|
|
wheely, no_fancy_cursors, keymouse, mouse_x, mouse_y,
|
|
mousekey_up, mousekey_down, mousekey_left, mousekey_right,
|
|
dont_do_xor, use_print_config, dont_load_stamps;
|
|
int WINDOW_WIDTH, WINDOW_HEIGHT;
|
|
char * printcommand;
|
|
int prog_bar_ctr;
|
|
SDL_Surface * screen;
|
|
|
|
SDL_Surface * canvas;
|
|
|
|
#define NUM_UNDO_BUFS 20
|
|
SDL_Surface * undo_bufs[NUM_UNDO_BUFS];
|
|
int cur_undo, oldest_undo, newest_undo;
|
|
|
|
SDL_Surface * img_title, * img_progress;
|
|
SDL_Surface * img_btn_up, * img_btn_down, * img_btn_off;
|
|
SDL_Surface * img_yes, * img_no;
|
|
SDL_Surface * img_open, * img_erase, * img_back;
|
|
SDL_Surface * img_cursor_up, * img_cursor_down;
|
|
SDL_Surface * img_scroll_up, * img_scroll_down;
|
|
SDL_Surface * img_scroll_up_off, * img_scroll_down_off;
|
|
SDL_Surface * img_paintcan;
|
|
|
|
SDL_Surface * img_sparkles;
|
|
|
|
SDL_Surface * img_title_on, * img_title_off,
|
|
* img_title_large_on, * img_title_large_off;
|
|
SDL_Surface * img_title_names[NUM_TITLES];
|
|
SDL_Surface * img_tools[NUM_TOOLS], * img_tool_names[NUM_TOOLS];
|
|
|
|
#define MAX_STAMPS 256
|
|
#define MAX_BRUSHES 64
|
|
#define MAX_FONTS 64
|
|
int num_brushes, num_stamps;
|
|
SDL_Surface * img_brushes[MAX_BRUSHES];
|
|
SDL_Surface * img_stamps[MAX_STAMPS];
|
|
char * txt_stamps[MAX_STAMPS];
|
|
info_type * inf_stamps[MAX_STAMPS];
|
|
#ifndef NOSOUND
|
|
Mix_Chunk * snd_stamps[MAX_STAMPS];
|
|
#endif
|
|
SDL_Surface * img_stamp_thumbs[MAX_STAMPS];
|
|
|
|
SDL_Surface * img_shapes[NUM_SHAPES], * img_shape_names[NUM_SHAPES];
|
|
SDL_Surface * img_magics[NUM_MAGICS], * img_magic_names[NUM_MAGICS];
|
|
SDL_Surface * img_openlabels_open, * img_openlabels_erase,
|
|
* img_openlabels_back;
|
|
|
|
SDL_Surface * img_tux[NUM_TIP_TUX];
|
|
|
|
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
|
SDL_Surface * img_color_btns[NUM_COLORS];
|
|
#endif
|
|
|
|
SDL_Surface * img_cur_brush;
|
|
int brush_counter, rainbow_color;
|
|
|
|
TTF_Font * font, * small_font, * large_font, * locale_font;
|
|
TTF_Font * fonts[MAX_FONTS];
|
|
int num_fonts;
|
|
|
|
#ifndef NOSOUND
|
|
Mix_Chunk * sounds[NUM_SOUNDS];
|
|
#endif
|
|
|
|
|
|
SDL_Cursor * cursor_hand, * cursor_arrow, * cursor_watch,
|
|
* cursor_up, * cursor_down, * cursor_tiny, * cursor_crosshair,
|
|
* cursor_brush, * cursor_wand, * cursor_insertion, * cursor_rotate;
|
|
|
|
|
|
int cur_tool, cur_color, cur_brush, cur_stamp, cur_shape, cur_magic;
|
|
int cur_font, cursor_left, cursor_x, cursor_y, cursor_textwidth;
|
|
int been_saved;
|
|
char file_id[32];
|
|
int brush_scroll, stamp_scroll, font_scroll;
|
|
int eraser_sound;
|
|
|
|
char texttool_str[256];
|
|
int texttool_len;
|
|
|
|
int tool_avail[NUM_TOOLS], tool_avail_bak[NUM_TOOLS];
|
|
|
|
typedef struct edge_type {
|
|
int y_upper;
|
|
float x_intersect, dx_per_scan;
|
|
struct edge_type * next;
|
|
} edge;
|
|
|
|
|
|
typedef struct point_type {
|
|
int x, y;
|
|
} point_type;
|
|
|
|
typedef struct fpoint_type {
|
|
float x, y;
|
|
} fpoint_type;
|
|
|
|
typedef enum { Left, Right, Bottom, Top } an_edge;
|
|
#define NUM_EDGES 4
|
|
|
|
SDL_Event scrolltimer_event;
|
|
|
|
char * langstr;
|
|
char * savedir;
|
|
|
|
|
|
/* Local function prototypes: */
|
|
|
|
void mainloop(void);
|
|
void brush_draw(int x1, int y1, int x2, int y2, int update);
|
|
void blit_brush(int x, int y);
|
|
void magic_draw(int x1, int y1, int x2, int y2);
|
|
void blit_magic(int x, int y, int x2, int y2);
|
|
void stamp_draw(int x, int y);
|
|
void rec_undo_buffer(void);
|
|
void update_canvas(int x1, int y1, int x2, int y2);
|
|
void setup(int argc, char * argv[]);
|
|
SDL_Cursor * get_cursor(char * bits, char * mask_bits,
|
|
int w, int h, int x, int y);
|
|
void seticon(void);
|
|
SDL_Surface * loadimage(char * fname);
|
|
void draw_toolbar(void);
|
|
void draw_magic(void);
|
|
void draw_colors(int enabled);
|
|
void draw_brushes(void);
|
|
void draw_stamps(void);
|
|
void draw_shapes(void);
|
|
void draw_fonts(void);
|
|
void draw_none(void);
|
|
#ifndef NOSOUND
|
|
void loadarbitrary(SDL_Surface * surfs[], char * descs[], info_type * infs[],
|
|
Mix_Chunk * sounds[], int * count, int starting, int max,
|
|
char * dir, int fatal, int maxw, int maxh);
|
|
#else
|
|
void loadarbitrary(SDL_Surface * surfs[], char * descs[], info_type * infs[],
|
|
int * count, int starting, int max,
|
|
char * dir, int fatal, int maxw, int maxh);
|
|
#endif
|
|
SDL_Surface * thumbnail(SDL_Surface * src, int max_x, int max_y,
|
|
int keep_aspect);
|
|
Uint32 getpixel(SDL_Surface * surface, int x, int y);
|
|
void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
|
|
void debug(char * str);
|
|
void do_undo(void);
|
|
void do_redo(void);
|
|
void render_brush(void);
|
|
void playsound(int chan, int s, int override);
|
|
void line_xor(int x1, int y1, int x2, int y2);
|
|
void clipped_putpixel(SDL_Surface * dest, int x, int y, Uint32 c);
|
|
void rect_xor(int x1, int y1, int x2, int y2);
|
|
void stamp_xor(int x1, int y1);
|
|
void do_eraser(int x, int y);
|
|
void disable_avail_tools(void);
|
|
void enable_avail_tools(void);
|
|
void reset_avail_tools(void);
|
|
void update_screen(int x1, int y1, int x2, int y2);
|
|
Uint8 alpha(Uint8 c1, Uint8 c2, Uint8 a);
|
|
int compare_strings(char * * s1, char * * s2);
|
|
int compare_dirents(struct dirent * f1, struct dirent * f2);
|
|
void draw_tux_text(int which_tux, char * str, int want_utf8,
|
|
int force_locale_font, int want_right_to_left);
|
|
void wordwrap_text(TTF_Font * font, char * str, SDL_Color color,
|
|
int left, int top, int right, int want_utf8,
|
|
int force_locale_font, int want_right_to_left);
|
|
char * loaddesc(char * fname);
|
|
info_type * loadinfo(char * fname);
|
|
#ifndef NOSOUND
|
|
Mix_Chunk * loadsound(char * fname);
|
|
#endif
|
|
void do_wait(void);
|
|
void load_current(void);
|
|
void save_current(void);
|
|
char * get_fname(char * name);
|
|
int do_prompt(char * text, char * btn_yes, char * btn_no);
|
|
void cleanup(void);
|
|
void free_cursor(SDL_Cursor ** cursor);
|
|
void free_surface(SDL_Surface **surface_array);
|
|
void free_surface_array(SDL_Surface *surface_array[], int count);
|
|
void update_shape(int cx, int ox1, int ox2, int cy, int oy1, int oy2,
|
|
int fixed);
|
|
void do_shape(int cx, int cy, int ox, int oy, int rotn, int use_brush);
|
|
int rotation(int ctr_x, int ctr_y, int ox, int oy);
|
|
int do_save(void);
|
|
int do_png_save(FILE * fi, char * fname, SDL_Surface * surf);
|
|
void get_new_file_id(void);
|
|
int do_quit(void);
|
|
int do_open(int want_new_tool);
|
|
void scan_fill(int cnt, point_type * pts);
|
|
int clip_polygon(int n, fpoint_type * pin, fpoint_type * pout);
|
|
void wait_for_sfx(void);
|
|
int current_language(void);
|
|
int stamp_colorable(int stamp);
|
|
int stamp_tintable(int stamp);
|
|
void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v);
|
|
void hsvtorgb(float h, float s, float v, Uint8 *r8, Uint8 *g8, Uint8 *b8);
|
|
void show_progress_bar(void);
|
|
void do_print(void);
|
|
void strip_trailing_whitespace(char * buf);
|
|
void do_render_cur_text(int do_blit);
|
|
void loadfonts(char * dir, int fatal);
|
|
char * uppercase(char * str);
|
|
unsigned char * textdir(unsigned char * str);
|
|
SDL_Surface * do_render_button_label(char * label);
|
|
static void create_button_labels(void);
|
|
int colors_close(Uint32 c1, Uint32 c2);
|
|
void do_flood_fill(int x, int y, Uint32 cur_colr, Uint32 old_colr);
|
|
static Uint32 scrolltimer_callback(Uint32 interval, void *param);
|
|
static Uint32 drawtext_callback(Uint32 interval, void *param);
|
|
void control_drawtext_timer(Uint32 interval, char* text);
|
|
void parse_options(FILE * fi);
|
|
char * debug_gettext(const char * str);
|
|
void do_setcursor(SDL_Cursor * c);
|
|
char * great_str(void);
|
|
int charsize(char c);
|
|
void draw_image_title(int t, int x);
|
|
int need_unicode(int l);
|
|
int need_utf8(int l);
|
|
int need_own_font(int l);
|
|
int need_right_to_left(int l);
|
|
void handle_keymouse(SDLKey key, Uint8 updown);
|
|
void move_keymouse(void);
|
|
void handle_active(SDL_Event * event);
|
|
char * remove_slash(char * path);
|
|
unsigned char * utf8_decode(unsigned char * str);
|
|
unsigned char * unescape(char * str);
|
|
|
|
void convert_open(const char * from);
|
|
void convert_close();
|
|
char * convert2utf8(char c);
|
|
int converts();
|
|
int delete_utf8_char(char * utf8_str, int len);
|
|
void anti_carriage_return(int left, int right, int cur_top, int new_top,
|
|
int cur_bot, int line_width);
|
|
|
|
|
|
#define MAX_UTF8_CHAR_LENGTH 6
|
|
|
|
#define USEREVENT_TEXT_UPDATE 1
|
|
|
|
|
|
/* --- MAIN --- */
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
SDL_Surface * tmp_surf;
|
|
SDL_Color black = {0, 0, 0};
|
|
SDL_Rect dest;
|
|
char tmp_str[128];
|
|
|
|
/* Set up locale support */
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
|
/* Set up! */
|
|
|
|
setup(argc, argv);
|
|
|
|
|
|
do_setcursor(cursor_arrow);
|
|
|
|
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
|
|
dest.x = (WINDOW_WIDTH - img_title->w) / 2;
|
|
dest.y = (WINDOW_HEIGHT - img_title->h);
|
|
|
|
SDL_BlitSurface(img_title, NULL, screen, &dest);
|
|
|
|
snprintf(tmp_str, sizeof(tmp_str), "%s - %s", VER_VERSION, VER_DATE);
|
|
tmp_surf = TTF_RenderText_Blended(font, tmp_str, black);
|
|
dest.x = 20 + (WINDOW_WIDTH - img_title->w) / 2;
|
|
dest.y = WINDOW_HEIGHT - 60;
|
|
SDL_BlitSurface(tmp_surf, NULL, screen, &dest);
|
|
SDL_FreeSurface(tmp_surf);
|
|
SDL_Flip(screen);
|
|
playsound(0, SND_HARP, 1);
|
|
|
|
do_wait();
|
|
SDL_FreeSurface(img_title);
|
|
|
|
|
|
/* Set defaults! */
|
|
|
|
cur_undo = 0;
|
|
oldest_undo = 0;
|
|
newest_undo = 0;
|
|
|
|
cur_tool = TOOL_BRUSH;
|
|
cur_color = COLOR_BLACK;
|
|
cur_brush = 0;
|
|
cur_stamp = 0;
|
|
cur_shape = SHAPE_SQUARE;
|
|
cur_magic = 0;
|
|
cur_font = 0;
|
|
cursor_left = -1;
|
|
cursor_x = -1;
|
|
cursor_y = -1;
|
|
cursor_textwidth = 0;
|
|
|
|
mouse_x = WINDOW_WIDTH / 2;
|
|
mouse_y = WINDOW_HEIGHT / 2;
|
|
SDL_WarpMouse(mouse_x, mouse_y);
|
|
|
|
mousekey_up = SDL_KEYUP;
|
|
mousekey_down = SDL_KEYUP;
|
|
mousekey_left = SDL_KEYUP;
|
|
mousekey_right = SDL_KEYUP;
|
|
|
|
eraser_sound = 0;
|
|
|
|
img_cur_brush = NULL;
|
|
render_brush();
|
|
|
|
brush_scroll = 0;
|
|
stamp_scroll = 0;
|
|
font_scroll = 0;
|
|
|
|
|
|
reset_avail_tools();
|
|
|
|
|
|
/* Load current image (if any): */
|
|
|
|
load_current();
|
|
been_saved = 1;
|
|
tool_avail[TOOL_SAVE] = 0;
|
|
|
|
|
|
/* Draw the screen! */
|
|
|
|
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
|
|
draw_toolbar();
|
|
draw_colors(1);
|
|
draw_brushes();
|
|
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
|
|
SDL_Flip(screen);
|
|
|
|
|
|
/* Main loop! */
|
|
|
|
mainloop();
|
|
|
|
|
|
/* Close and quit! */
|
|
|
|
save_current();
|
|
|
|
wait_for_sfx();
|
|
|
|
|
|
cleanup();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* FIXME: Move elsewhere!!! */
|
|
|
|
#define PROMPT_QUIT_TXT gettext_noop("Do you really want to quit?")
|
|
#define PROMPT_QUIT_YES gettext_noop("Yes")
|
|
#define PROMPT_QUIT_NO gettext_noop("No")
|
|
|
|
#define PROMPT_QUIT_SAVE_TXT gettext_noop("If you quit, you'll lose your picture! Save it?")
|
|
#define PROMPT_QUIT_SAVE_YES gettext_noop("Yes")
|
|
#define PROMPT_QUIT_SAVE_NO gettext_noop("No")
|
|
|
|
#define PROMPT_OPEN_SAVE_TXT gettext_noop("Save your picture first?")
|
|
#define PROMPT_OPEN_SAVE_YES gettext_noop("Yes")
|
|
#define PROMPT_OPEN_SAVE_NO gettext_noop("No")
|
|
|
|
#define PROMPT_OPEN_UNOPENABLE_TXT gettext_noop("Can't open that picture!")
|
|
#define PROMPT_OPEN_UNOPENABLE_YES gettext_noop("Okay")
|
|
|
|
#define PROMPT_NEW_TXT gettext_noop("Starting a new picture will erase the current one!")
|
|
#define PROMPT_NEW_YES gettext_noop("That's Ok")
|
|
#define PROMPT_NEW_NO gettext_noop("Never Mind!")
|
|
|
|
#define PROMPT_OPEN_NOFILES_TXT gettext_noop("There are no saved files!")
|
|
#define PROMPT_OPEN_NOFILES_YES gettext_noop("Okay")
|
|
|
|
#define PROMPT_PRINT_TXT gettext_noop("Your picture has been printed!")
|
|
#define PROMPT_PRINT_YES gettext_noop("Okay")
|
|
|
|
#define PROMPT_PRINT_TOO_SOON_TXT gettext_noop("You can't print yet!")
|
|
#define PROMPT_PRINT_TOO_SOON_YES gettext_noop("Okay")
|
|
|
|
#define PROMPT_ERASE_TXT gettext_noop("Erase this picture?")
|
|
#define PROMPT_ERASE_YES gettext_noop("Yes")
|
|
#define PROMPT_ERASE_NO gettext_noop("No")
|
|
|
|
|
|
enum {
|
|
SHAPE_TOOL_MODE_STRETCH,
|
|
SHAPE_TOOL_MODE_ROTATE,
|
|
SHAPE_TOOL_MODE_DONE
|
|
};
|
|
|
|
|
|
/* --- MAIN LOOP! --- */
|
|
|
|
void mainloop(void)
|
|
{
|
|
int done, off_y, which, button_down, old_x, old_y, new_x, new_y,
|
|
line_start_x, line_start_y, w, h, shape_tool_mode,
|
|
shape_ctr_x, shape_ctr_y, shape_outer_x, shape_outer_y;
|
|
int num_things, thing_scroll, cur_thing, old_thing, do_draw, old_tool,
|
|
tmp_int;
|
|
int cur_time, last_print_time, scrolling;
|
|
SDL_TimerID scrolltimer;
|
|
SDL_Event event;
|
|
SDLKey key, key_down;
|
|
Uint16 key_unicode;
|
|
SDLMod mod;
|
|
Uint32 last_cursor_blink, cur_cursor_blink;
|
|
|
|
|
|
num_things = 0;
|
|
thing_scroll = 0;
|
|
cur_thing = 0;
|
|
old_thing = 0;
|
|
do_draw = 0;
|
|
old_x = 0;
|
|
old_y = 0;
|
|
new_x = 0;
|
|
new_y = 0;
|
|
line_start_x = 0;
|
|
line_start_y = 0;
|
|
shape_ctr_x = 0;
|
|
shape_ctr_y = 0;
|
|
shape_outer_x = 0;
|
|
shape_outer_y =0;
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
button_down = 0;
|
|
last_print_time = -print_delay;
|
|
last_cursor_blink = 0;
|
|
texttool_len = 0;
|
|
scrolling = 0;
|
|
scrolltimer = 0;
|
|
key_down = SDLK_LAST;
|
|
key_unicode = 0;
|
|
|
|
|
|
done = 0;
|
|
|
|
do
|
|
{
|
|
while (SDL_PollEvent(&event))
|
|
{
|
|
if (event.type == SDL_QUIT)
|
|
{
|
|
done = do_quit();
|
|
}
|
|
else if (event.type == SDL_ACTIVEEVENT)
|
|
{
|
|
handle_active(&event);
|
|
}
|
|
else if (event.type == SDL_KEYUP)
|
|
{
|
|
key = event.key.keysym.sym;
|
|
|
|
handle_keymouse(key, SDL_KEYUP);
|
|
}
|
|
else if (event.type == SDL_KEYDOWN)
|
|
{
|
|
key = event.key.keysym.sym;
|
|
mod = event.key.keysym.mod;
|
|
|
|
handle_keymouse(key, SDL_KEYDOWN);
|
|
|
|
if (key == SDLK_ESCAPE)
|
|
{
|
|
done = do_quit();
|
|
}
|
|
#ifdef WIN32
|
|
else if (key == SDLK_F4 && (mod & KMOD_ALT))
|
|
{
|
|
done = do_quit();
|
|
}
|
|
#endif
|
|
else if (key == SDLK_z &&
|
|
(mod & KMOD_CTRL ||
|
|
mod & KMOD_LCTRL ||
|
|
mod & KMOD_RCTRL))
|
|
{
|
|
/* Ctrl-Z - Undo */
|
|
|
|
if (tool_avail[TOOL_UNDO])
|
|
{
|
|
if (cur_undo == newest_undo)
|
|
{
|
|
rec_undo_buffer();
|
|
do_undo();
|
|
}
|
|
do_undo();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
}
|
|
}
|
|
else if (key == SDLK_r &&
|
|
(mod & KMOD_CTRL ||
|
|
mod & KMOD_LCTRL ||
|
|
mod & KMOD_RCTRL))
|
|
{
|
|
/* Ctrl-R - Redo */
|
|
|
|
if (tool_avail[TOOL_REDO])
|
|
{
|
|
do_redo();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
}
|
|
}
|
|
else if (key == SDLK_o &&
|
|
(mod & KMOD_CTRL ||
|
|
mod & KMOD_LCTRL ||
|
|
mod & KMOD_RCTRL))
|
|
{
|
|
/* Ctrl-O - Open */
|
|
|
|
tmp_int = tool_avail[TOOL_NEW];
|
|
disable_avail_tools();
|
|
draw_toolbar();
|
|
draw_colors(0);
|
|
draw_none();
|
|
|
|
tmp_int = do_open(tmp_int);
|
|
|
|
enable_avail_tools();
|
|
tool_avail[TOOL_NEW] = tmp_int;
|
|
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
|
|
if (cur_tool == TOOL_BRUSH ||
|
|
cur_tool == TOOL_LINES ||
|
|
cur_tool == TOOL_SHAPES ||
|
|
cur_tool == TOOL_TEXT)
|
|
{
|
|
draw_colors(1);
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
draw_colors(stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp));
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC &&
|
|
cur_magic == MAGIC_FILL)
|
|
{
|
|
draw_colors(1);
|
|
}
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
draw_brushes();
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
draw_magic();
|
|
else if (cur_tool == TOOL_STAMP)
|
|
draw_stamps();
|
|
else if (cur_tool == TOOL_TEXT)
|
|
draw_fonts();
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
draw_shapes();
|
|
|
|
draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 0, 0, 1);
|
|
|
|
|
|
/* FIXME: Make delay configurable: */
|
|
|
|
control_drawtext_timer(1000, tool_tips[cur_tool]);
|
|
}
|
|
else if ((key == SDLK_n &&
|
|
((mod & KMOD_CTRL ||
|
|
mod & KMOD_LCTRL ||
|
|
mod & KMOD_RCTRL))) && tool_avail[TOOL_NEW])
|
|
{
|
|
/* Ctrl-N - New */
|
|
|
|
if (do_prompt(PROMPT_NEW_TXT,
|
|
PROMPT_NEW_YES,
|
|
PROMPT_NEW_NO))
|
|
{
|
|
SDL_FillRect(canvas, NULL,
|
|
SDL_MapRGB(canvas->format, 255, 255, 255));
|
|
update_canvas(0, 0,
|
|
WINDOW_WIDTH - 96,
|
|
(48 * 7) + 40 + HEIGHTOFFSET);
|
|
|
|
cur_undo = 0;
|
|
oldest_undo = 0;
|
|
newest_undo = 0;
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
|
|
been_saved = 1;
|
|
reset_avail_tools();
|
|
|
|
file_id[0] = '\0';
|
|
|
|
playsound(1, SND_HARP, 1);
|
|
}
|
|
else
|
|
{
|
|
draw_tux_text(tool_tux[TUX_DEFAULT], TIP_NEW_ABORT,
|
|
0, 0, 1);
|
|
}
|
|
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
else if (key == SDLK_s &&
|
|
(mod & KMOD_CTRL ||
|
|
mod & KMOD_LCTRL ||
|
|
mod & KMOD_RCTRL))
|
|
{
|
|
/* Ctrl-S - Save */
|
|
|
|
if (do_save())
|
|
{
|
|
/* Only think it's been saved if it HAS been saved :^) */
|
|
|
|
been_saved = 1;
|
|
tool_avail[TOOL_SAVE] = 0;
|
|
}
|
|
|
|
/* cur_tool = old_tool; */
|
|
draw_toolbar();
|
|
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
else
|
|
{
|
|
/* Handle key in text tool: */
|
|
|
|
if (cur_tool == TOOL_TEXT &&
|
|
cursor_x != -1 && cursor_y != -1)
|
|
{
|
|
key_down = key;
|
|
key_unicode = event.key.keysym.unicode;
|
|
|
|
#ifdef DEBUG
|
|
printf("charsize(%c) = %d\n", event.key.keysym.unicode,
|
|
charsize(event.key.keysym.unicode));
|
|
#endif
|
|
|
|
if (key_down == SDLK_BACKSPACE)
|
|
{
|
|
|
|
if (texttool_len > 0)
|
|
{
|
|
if (converts())
|
|
{
|
|
texttool_len = delete_utf8_char(texttool_str, texttool_len);
|
|
}
|
|
else
|
|
{
|
|
texttool_len--;
|
|
texttool_str[texttool_len] = '\0';
|
|
playsound(0, SND_KEYCLICK, 1);
|
|
}
|
|
|
|
do_render_cur_text(0);
|
|
}
|
|
}
|
|
else if (key_down == SDLK_RETURN)
|
|
{
|
|
if (texttool_len > 0)
|
|
{
|
|
rec_undo_buffer();
|
|
do_render_cur_text(1);
|
|
texttool_len = 0;
|
|
cursor_textwidth = 0;
|
|
}
|
|
|
|
cursor_x = cursor_left;
|
|
cursor_y = cursor_y + TTF_FontHeight(fonts[cur_font]);
|
|
|
|
if (cursor_y > ((48 * 7 + 40 + HEIGHTOFFSET) -
|
|
TTF_FontHeight(fonts[cur_font])))
|
|
{
|
|
cursor_y = ((48 * 7 + 40 + HEIGHTOFFSET) -
|
|
TTF_FontHeight(fonts[cur_font]));
|
|
}
|
|
|
|
playsound(0, SND_RETURN, 1);
|
|
}
|
|
else if (isprint(key_unicode))
|
|
{
|
|
if (texttool_len < sizeof(texttool_str) - MAX_UTF8_CHAR_LENGTH)
|
|
{
|
|
#ifdef DEBUG
|
|
printf(" key = %c\n"
|
|
"unicode = %c (%d)\n\n",
|
|
key_down, key_unicode, key_unicode);
|
|
#endif
|
|
|
|
|
|
if (converts())
|
|
{
|
|
char * str = convert2utf8(key_unicode);
|
|
int i;
|
|
size_t len = strlen(str);
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
texttool_str[texttool_len++] = str[i];
|
|
}
|
|
|
|
free(str);
|
|
}
|
|
else
|
|
{
|
|
texttool_str[texttool_len++] = key_unicode;
|
|
}
|
|
|
|
texttool_str[texttool_len] = '\0';
|
|
playsound(0, SND_KEYCLICK, 1);
|
|
do_render_cur_text(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((event.type == SDL_MOUSEBUTTONDOWN &&
|
|
event.button.button >= 1 &&
|
|
event.button.button <= 3))
|
|
{
|
|
if (/* event.button.x >= 0 && */
|
|
event.button.x < 96 &&
|
|
event.button.y >= 40 &&
|
|
event.button.y < (48 * 7) + 40) /* FIXME: FIX ME? */
|
|
{
|
|
/* A tool on the left has been pressed! */
|
|
|
|
which = ((event.button.y - 40) / 48) * 2 +
|
|
(event.button.x / 48);
|
|
|
|
if (which < NUM_TOOLS && tool_avail[which])
|
|
{
|
|
/* Render any current text: */
|
|
|
|
if (cur_tool == TOOL_TEXT && which != TOOL_TEXT &&
|
|
texttool_len > 0)
|
|
{
|
|
if (cursor_x != -1 && cursor_y != -1)
|
|
{
|
|
if (texttool_len > 0)
|
|
{
|
|
rec_undo_buffer();
|
|
do_render_cur_text(1);
|
|
texttool_len = 0;
|
|
cursor_textwidth = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
old_tool = cur_tool;
|
|
cur_tool = which;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
|
|
playsound(1, SND_CLICK, 0);
|
|
|
|
draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool],
|
|
0, 0, 1);
|
|
|
|
|
|
/* Draw items for this tool: */
|
|
|
|
if (cur_tool == TOOL_BRUSH)
|
|
{
|
|
draw_brushes();
|
|
draw_colors(1);
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
draw_stamps();
|
|
draw_colors(stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp));
|
|
}
|
|
else if (cur_tool == TOOL_LINES)
|
|
{
|
|
draw_brushes();
|
|
draw_colors(1);
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
draw_shapes();
|
|
draw_colors(1);
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
draw_fonts();
|
|
draw_colors(1);
|
|
}
|
|
else if (cur_tool == TOOL_ERASER)
|
|
{
|
|
draw_none();
|
|
draw_colors(0);
|
|
}
|
|
else if (cur_tool == TOOL_UNDO)
|
|
{
|
|
if (cur_undo == newest_undo)
|
|
{
|
|
rec_undo_buffer();
|
|
do_undo();
|
|
}
|
|
do_undo();
|
|
|
|
been_saved = 0;
|
|
tool_avail[TOOL_SAVE] = 1;
|
|
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
}
|
|
else if (cur_tool == TOOL_REDO)
|
|
{
|
|
do_redo();
|
|
|
|
been_saved = 0;
|
|
tool_avail[TOOL_SAVE] = 1;
|
|
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
}
|
|
else if (cur_tool == TOOL_OPEN)
|
|
{
|
|
tmp_int = tool_avail[TOOL_NEW];
|
|
disable_avail_tools();
|
|
draw_toolbar();
|
|
draw_colors(0);
|
|
draw_none();
|
|
|
|
tmp_int = do_open(tmp_int);
|
|
|
|
enable_avail_tools();
|
|
tool_avail[TOOL_NEW] = tmp_int;
|
|
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
|
|
draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 0, 0, 1);
|
|
|
|
if (cur_tool == TOOL_BRUSH ||
|
|
cur_tool == TOOL_LINES ||
|
|
cur_tool == TOOL_SHAPES ||
|
|
cur_tool == TOOL_TEXT)
|
|
{
|
|
draw_colors(1);
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
draw_colors(stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp));
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC &&
|
|
cur_magic == MAGIC_FILL)
|
|
{
|
|
draw_colors(1);
|
|
}
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
draw_brushes();
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
draw_magic();
|
|
else if (cur_tool == TOOL_STAMP)
|
|
draw_stamps();
|
|
else if (cur_tool == TOOL_TEXT)
|
|
draw_fonts();
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
draw_shapes();
|
|
}
|
|
else if (cur_tool == TOOL_SAVE)
|
|
{
|
|
if (do_save())
|
|
{
|
|
been_saved = 1;
|
|
tool_avail[TOOL_SAVE] = 0;
|
|
}
|
|
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
else if (cur_tool == TOOL_NEW)
|
|
{
|
|
if (do_prompt(PROMPT_NEW_TXT,
|
|
PROMPT_NEW_YES,
|
|
PROMPT_NEW_NO))
|
|
{
|
|
SDL_FillRect(canvas, NULL,
|
|
SDL_MapRGB(canvas->format,
|
|
255, 255, 255));
|
|
update_canvas(0, 0,
|
|
WINDOW_WIDTH - 96,
|
|
(48 * 7) + 40 + HEIGHTOFFSET);
|
|
|
|
cur_undo = 0;
|
|
oldest_undo = 0;
|
|
newest_undo = 0;
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
|
|
been_saved = 1;
|
|
reset_avail_tools();
|
|
|
|
file_id[0] = '\0';
|
|
|
|
playsound(1, SND_HARP, 1);
|
|
}
|
|
else
|
|
{
|
|
draw_tux_text(tool_tux[TUX_DEFAULT],
|
|
TIP_NEW_ABORT, 0, 0, 1);
|
|
}
|
|
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0, 96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
else if (cur_tool == TOOL_PRINT)
|
|
{
|
|
cur_time = SDL_GetTicks() / 1000;
|
|
|
|
#ifdef DEBUG
|
|
printf("Current time = %d\n", cur_time);
|
|
#endif
|
|
|
|
if (cur_time >= last_print_time + print_delay)
|
|
{
|
|
do_print();
|
|
|
|
last_print_time = cur_time;
|
|
}
|
|
else
|
|
{
|
|
do_prompt(PROMPT_PRINT_TOO_SOON_TXT,
|
|
PROMPT_PRINT_TOO_SOON_YES,
|
|
"");
|
|
}
|
|
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0, 96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
rainbow_color = 0;
|
|
draw_magic();
|
|
|
|
if (cur_magic == MAGIC_FILL)
|
|
draw_colors(1);
|
|
else
|
|
draw_colors(0);
|
|
}
|
|
else if (cur_tool == TOOL_QUIT)
|
|
{
|
|
done = do_quit();
|
|
cur_tool = old_tool;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0, 96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
|
|
SDL_UpdateRect(screen,
|
|
WINDOW_WIDTH - 96, 0,
|
|
96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH, 48);
|
|
}
|
|
}
|
|
else if (event.button.x >= WINDOW_WIDTH - 96 &&
|
|
event.button.x < WINDOW_WIDTH &&
|
|
event.button.y >= 40 &&
|
|
event.button.y < (48 * (7 + TOOLOFFSET / 2)) + 40)
|
|
{
|
|
/* Options on the right have been pressed! */
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_STAMP ||
|
|
cur_tool == TOOL_SHAPES || cur_tool == TOOL_LINES ||
|
|
cur_tool == TOOL_MAGIC || cur_tool == TOOL_TEXT)
|
|
{
|
|
/* Note set of things we're dealing with */
|
|
/* (stamps, brushes, etc.) */
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
{
|
|
num_things = num_brushes;
|
|
thing_scroll = brush_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
num_things = num_stamps;
|
|
thing_scroll = stamp_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
num_things = num_fonts;
|
|
thing_scroll = font_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
num_things = NUM_SHAPES;
|
|
thing_scroll = 0;
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
num_things = NUM_MAGICS;
|
|
thing_scroll = 0;
|
|
}
|
|
|
|
do_draw = 0;
|
|
|
|
|
|
/* Deal with scrollbars: */
|
|
|
|
if (num_things > 14 + TOOLOFFSET)
|
|
{
|
|
if (event.button.y < 40 + 24)
|
|
{
|
|
if (thing_scroll > 0)
|
|
{
|
|
thing_scroll = thing_scroll - 2;
|
|
do_draw = 1;
|
|
|
|
playsound(1, SND_SCROLL, 1);
|
|
|
|
if (scrolling == 0)
|
|
{
|
|
memcpy(&scrolltimer_event,
|
|
&event,
|
|
sizeof(SDL_Event));
|
|
|
|
|
|
/* FIXME: Make delay value changable: */
|
|
|
|
scrolltimer =
|
|
SDL_AddTimer(REPEAT_SPEED,
|
|
scrolltimer_callback,
|
|
(void*) &scrolltimer_event);
|
|
|
|
scrolling = 1;
|
|
}
|
|
else
|
|
{
|
|
SDL_RemoveTimer(scrolltimer);
|
|
|
|
scrolltimer =
|
|
SDL_AddTimer(REPEAT_SPEED / 3,
|
|
scrolltimer_callback,
|
|
(void*) &scrolltimer_event);
|
|
}
|
|
|
|
|
|
if (thing_scroll == 0)
|
|
{
|
|
do_setcursor(cursor_arrow);
|
|
|
|
if (scrolling == 1)
|
|
{
|
|
SDL_RemoveTimer(scrolltimer);
|
|
scrolling = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (event.button.y >=
|
|
(48 * (6 + TOOLOFFSET / 2)) + 40 + 24)
|
|
{
|
|
if (thing_scroll < num_things - 12 - TOOLOFFSET)
|
|
{
|
|
thing_scroll = thing_scroll + 2;
|
|
do_draw = 1;
|
|
|
|
playsound(1, SND_SCROLL, 1);
|
|
|
|
if (scrolling == 0)
|
|
{
|
|
memcpy(&scrolltimer_event,
|
|
&event,
|
|
sizeof(SDL_Event));
|
|
|
|
/* FIXME: Make delay value changable: */
|
|
|
|
scrolltimer =
|
|
SDL_AddTimer(REPEAT_SPEED,
|
|
scrolltimer_callback,
|
|
(void*) &scrolltimer_event);
|
|
|
|
scrolling = 1;
|
|
}
|
|
else
|
|
{
|
|
SDL_RemoveTimer(scrolltimer);
|
|
|
|
scrolltimer =
|
|
SDL_AddTimer(REPEAT_SPEED / 3,
|
|
scrolltimer_callback,
|
|
(void*) &scrolltimer_event);
|
|
}
|
|
|
|
if (thing_scroll == 0)
|
|
{
|
|
do_setcursor(cursor_arrow);
|
|
|
|
if (scrolling == 1)
|
|
{
|
|
SDL_RemoveTimer(scrolltimer);
|
|
scrolling = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
off_y = 24;
|
|
}
|
|
else
|
|
{
|
|
off_y = 0;
|
|
}
|
|
|
|
|
|
if (event.button.y > 40 + off_y &&
|
|
event.button.y <
|
|
(48 * (7 + TOOLOFFSET / 2)) + 40 - off_y)
|
|
{
|
|
which = ((event.button.y - 40 - off_y) / 48) * 2 +
|
|
((event.button.x - (WINDOW_WIDTH - 96)) / 48) +
|
|
thing_scroll;
|
|
|
|
if (which < num_things)
|
|
{
|
|
#ifndef NOSOUND
|
|
if (cur_tool != TOOL_STAMP ||
|
|
snd_stamps[which] == NULL)
|
|
{
|
|
playsound(1, SND_BLEEP, 0);
|
|
}
|
|
#endif
|
|
|
|
old_thing = cur_thing;
|
|
cur_thing = which;
|
|
do_draw = 1;
|
|
}
|
|
else
|
|
{
|
|
cur_thing = num_things - 1;
|
|
do_draw = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Assign the change(s), if any / redraw, if needed: */
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
{
|
|
cur_brush = cur_thing;
|
|
brush_scroll = thing_scroll;
|
|
render_brush();
|
|
|
|
if (do_draw)
|
|
draw_brushes();
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
cur_font = cur_thing;
|
|
font_scroll = thing_scroll;
|
|
|
|
if (do_draw)
|
|
draw_fonts();
|
|
|
|
do_render_cur_text(0);
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
#ifndef NOSOUND
|
|
if (cur_stamp != cur_thing ||
|
|
stamp_scroll == thing_scroll)
|
|
{
|
|
/* Only play when picking a different stamp, not
|
|
simply scrolling */
|
|
|
|
if (snd_stamps[cur_thing] != NULL)
|
|
Mix_PlayChannel(2, snd_stamps[cur_thing], 0);
|
|
}
|
|
#endif
|
|
cur_stamp = cur_thing;
|
|
stamp_scroll = thing_scroll;
|
|
|
|
if (do_draw)
|
|
draw_stamps();
|
|
|
|
if (txt_stamps[cur_stamp] != NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("txt_stamps[cur_stamp] = %s\n",
|
|
txt_stamps[cur_stamp]);
|
|
#endif
|
|
|
|
if (txt_stamps[cur_stamp][0] == '=')
|
|
{
|
|
/* FIXME: Stupid. Using '=' to denote UTF8 */
|
|
|
|
draw_tux_text(TUX_GREAT,
|
|
txt_stamps[cur_stamp] + 1, 1, 0, 1);
|
|
}
|
|
else
|
|
{
|
|
draw_tux_text(TUX_GREAT,
|
|
txt_stamps[cur_stamp], 0, 1, 0);
|
|
}
|
|
}
|
|
else
|
|
draw_tux_text(TUX_GREAT, "", 0, 0, 0);
|
|
|
|
|
|
/* Enable or disable color selector: */
|
|
|
|
if ((stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp)) !=
|
|
(stamp_colorable(old_thing) ||
|
|
stamp_tintable(old_thing)))
|
|
{
|
|
draw_colors(stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp));
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH, 48);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
cur_shape = cur_thing;
|
|
|
|
draw_tux_text(TUX_GREAT, shape_tips[cur_shape],
|
|
0, 0, 1);
|
|
|
|
if (do_draw)
|
|
draw_shapes();
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
if (cur_thing != cur_magic)
|
|
{
|
|
if (cur_thing == MAGIC_FILL)
|
|
draw_colors(1);
|
|
else
|
|
draw_colors(0);
|
|
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH, 48);
|
|
}
|
|
|
|
cur_magic = cur_thing;
|
|
|
|
draw_tux_text(TUX_GREAT, magic_tips[cur_magic],
|
|
0, 0, 1);
|
|
|
|
if (do_draw)
|
|
draw_magic();
|
|
}
|
|
|
|
/* Update the screen: */
|
|
|
|
if (do_draw)
|
|
SDL_UpdateRect(screen,
|
|
WINDOW_WIDTH - 96, 0,
|
|
96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
}
|
|
}
|
|
else if (event.button.x > 96 &&
|
|
/* FIXME: Need exact number here! */
|
|
event.button.x < WINDOW_WIDTH &&
|
|
event.button.y > (48 * (7 + TOOLOFFSET / 2)) + 40 &&
|
|
event.button.y <= (48 * (7 + TOOLOFFSET / 2)) + 48 + 48 &&
|
|
(cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES ||
|
|
cur_tool == TOOL_SHAPES || cur_tool == TOOL_TEXT ||
|
|
(cur_tool == TOOL_MAGIC && cur_magic == MAGIC_FILL) ||
|
|
(cur_tool == TOOL_STAMP &&
|
|
(stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp)))))
|
|
{
|
|
/* Color! */
|
|
|
|
which = ((event.button.x - 96) /
|
|
((WINDOW_WIDTH - 96) / NUM_COLORS));
|
|
|
|
if (which < NUM_COLORS)
|
|
{
|
|
cur_color = which;
|
|
playsound(1, SND_BUBBLE, 1);
|
|
draw_colors(1);
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH, 48);
|
|
render_brush();
|
|
draw_tux_text(TUX_KISS, color_names[cur_color], 0, 0, 1);
|
|
|
|
if (cur_tool == TOOL_TEXT)
|
|
do_render_cur_text(0);
|
|
}
|
|
}
|
|
else if (event.button.x > 96 &&
|
|
event.button.x < WINDOW_WIDTH - 96 &&
|
|
/* event.button.y >= 0 && */
|
|
event.button.y < (48 * 7) + 40 + HEIGHTOFFSET)
|
|
{
|
|
/* Draw something! */
|
|
|
|
button_down = 1;
|
|
|
|
old_x = event.button.x - 96;
|
|
old_y = event.button.y;
|
|
|
|
if (been_saved)
|
|
{
|
|
been_saved = 0;
|
|
tool_avail[TOOL_SAVE] = 1;
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
|
|
if (cur_tool == TOOL_BRUSH)
|
|
{
|
|
/* Start painting! */
|
|
|
|
rec_undo_buffer();
|
|
|
|
/* (Arbitrarily large, so we draw once now) */
|
|
brush_counter = 999;
|
|
|
|
brush_draw(old_x, old_y, old_x, old_y, 1);
|
|
playsound(0, SND_PAINT1 + (img_cur_brush->w) / 12, 1);
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
/* Draw a stamp! */
|
|
|
|
rec_undo_buffer();
|
|
|
|
stamp_draw(old_x, old_y);
|
|
#ifdef LOW_QUALITY_STAMP_OUTLINE
|
|
rect_xor(old_x - (img_stamps[cur_stamp]->w / 2),
|
|
old_y - (img_stamps[cur_stamp]->h / 2),
|
|
old_x + (img_stamps[cur_stamp]->w / 2),
|
|
old_y + (img_stamps[cur_stamp]->h / 2));
|
|
#else
|
|
stamp_xor(old_x - (img_stamps[cur_stamp]->w / 2),
|
|
old_y - (img_stamps[cur_stamp]->h / 2));
|
|
#endif
|
|
playsound(1, SND_STAMP, 1);
|
|
|
|
draw_tux_text(TUX_GREAT, great_str(), 0, 0, 1);
|
|
|
|
/* FIXME: Make delay configurable: */
|
|
|
|
control_drawtext_timer(1000, txt_stamps[cur_stamp]);
|
|
}
|
|
else if (cur_tool == TOOL_LINES)
|
|
{
|
|
/* Start a line! */
|
|
|
|
rec_undo_buffer();
|
|
|
|
line_start_x = old_x;
|
|
line_start_y = old_y;
|
|
|
|
/* (Arbitrarily large, so we draw once now) */
|
|
brush_counter = 999;
|
|
|
|
brush_draw(old_x, old_y, old_x, old_y, 1);
|
|
|
|
playsound(1, SND_LINE_START, 1);
|
|
draw_tux_text(TUX_BORED, TIP_LINE_START, 0, 0, 1);
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
if (shape_tool_mode == SHAPE_TOOL_MODE_DONE)
|
|
{
|
|
/* Start drawing a shape! */
|
|
|
|
rec_undo_buffer();
|
|
|
|
shape_ctr_x = old_x;
|
|
shape_ctr_y = old_y;
|
|
|
|
shape_tool_mode = SHAPE_TOOL_MODE_STRETCH;
|
|
|
|
playsound(1, SND_LINE_START, 1);
|
|
draw_tux_text(TUX_BORED, TIP_SHAPE_START, 0, 0, 1);
|
|
}
|
|
else if (shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
|
|
{
|
|
/* Draw the shape with the brush! */
|
|
|
|
/* (Arbitrarily large...) */
|
|
brush_counter = 999;
|
|
|
|
playsound(1, SND_LINE_END, 1);
|
|
do_shape(shape_ctr_x, shape_ctr_y,
|
|
shape_outer_x, shape_outer_y,
|
|
rotation(shape_ctr_x, shape_ctr_y,
|
|
event.button.x - 96,
|
|
event.button.y),
|
|
1);
|
|
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
draw_tux_text(TUX_GREAT, tool_tips[TOOL_SHAPES],
|
|
0, 0, 1);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
/* Start doing magic! */
|
|
|
|
rec_undo_buffer();
|
|
|
|
/* (Arbitrarily large, so we draw once now) */
|
|
brush_counter = 999;
|
|
|
|
if (cur_magic != MAGIC_FILL)
|
|
{
|
|
magic_draw(old_x, old_y, old_x, old_y);
|
|
}
|
|
else
|
|
{
|
|
do_flood_fill(old_x, old_y,
|
|
SDL_MapRGB(canvas->format,
|
|
color_hexes[cur_color][0],
|
|
color_hexes[cur_color][1],
|
|
color_hexes[cur_color][2]),
|
|
getpixel(canvas, old_x, old_y));
|
|
|
|
draw_tux_text(TUX_GREAT, magic_tips[MAGIC_FILL],
|
|
0, 0, 1);
|
|
}
|
|
|
|
if (cur_magic == MAGIC_FLIP ||
|
|
cur_magic == MAGIC_MIRROR ||
|
|
cur_magic == MAGIC_FILL)
|
|
{
|
|
update_canvas(0, 0, canvas->w, canvas->h);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_ERASER)
|
|
{
|
|
/* Erase! */
|
|
|
|
rec_undo_buffer();
|
|
|
|
do_eraser(old_x, old_y);
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
/* Text Tool! */
|
|
|
|
if (cursor_x != -1 && cursor_y != -1)
|
|
{
|
|
/*
|
|
if (texttool_len > 0)
|
|
{
|
|
rec_undo_buffer();
|
|
do_render_cur_text(1);
|
|
texttool_len = 0;
|
|
}
|
|
*/
|
|
}
|
|
|
|
cursor_x = old_x;
|
|
cursor_y = old_y;
|
|
cursor_left = old_x;
|
|
|
|
do_render_cur_text(0);
|
|
}
|
|
|
|
|
|
/* Make sure these commands are available now: */
|
|
|
|
if (tool_avail[TOOL_NEW] == 0)
|
|
{
|
|
tool_avail[TOOL_NEW] = 1;
|
|
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen,
|
|
0, 0,
|
|
96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
|
wheely &&
|
|
event.button.button >= 4 &&
|
|
event.button.button <= 5)
|
|
{
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_STAMP ||
|
|
cur_tool == TOOL_SHAPES || cur_tool == TOOL_LINES ||
|
|
cur_tool == TOOL_MAGIC || cur_tool == TOOL_TEXT)
|
|
{
|
|
/* Note set of things we're dealing with */
|
|
/* (stamps, brushes, etc.) */
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
{
|
|
num_things = num_brushes;
|
|
thing_scroll = brush_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
num_things = num_stamps;
|
|
thing_scroll = stamp_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
num_things = num_fonts;
|
|
thing_scroll = font_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
num_things = NUM_SHAPES;
|
|
thing_scroll = 0;
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
num_things = NUM_MAGICS;
|
|
thing_scroll = 0;
|
|
}
|
|
|
|
do_draw = 0;
|
|
|
|
|
|
/* Deal with scrollbars: */
|
|
|
|
if (num_things > 14 + TOOLOFFSET)
|
|
{
|
|
if (event.button.button == 4)
|
|
{
|
|
/* Wheelmouse - UP "button" */
|
|
|
|
if (thing_scroll > 0)
|
|
{
|
|
thing_scroll = thing_scroll - 2;
|
|
do_draw = 1;
|
|
|
|
playsound(1, SND_SCROLL, 1);
|
|
|
|
if (thing_scroll == 0)
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
}
|
|
else if (event.button.button == 5)
|
|
{
|
|
/* Wheelmouse - DOWN "button" */
|
|
|
|
if (thing_scroll < num_things - 12)
|
|
{
|
|
thing_scroll = thing_scroll + 2;
|
|
do_draw = 1;
|
|
|
|
playsound(1, SND_SCROLL, 1);
|
|
|
|
if (thing_scroll == 0)
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
}
|
|
|
|
off_y = 24;
|
|
}
|
|
else
|
|
{
|
|
off_y = 0;
|
|
}
|
|
}
|
|
/* Assign the change(s), if any / redraw, if needed: */
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
{
|
|
cur_brush = cur_thing;
|
|
brush_scroll = thing_scroll;
|
|
render_brush();
|
|
|
|
if (do_draw)
|
|
draw_brushes();
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
cur_font = cur_thing;
|
|
font_scroll = thing_scroll;
|
|
|
|
if (do_draw)
|
|
draw_fonts();
|
|
|
|
do_render_cur_text(0);
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
#ifndef NOSOUND
|
|
if (cur_stamp != cur_thing)
|
|
{
|
|
/* Only play when picking a different stamp, not
|
|
simply scrolling */
|
|
|
|
if (snd_stamps[cur_thing] != NULL)
|
|
Mix_PlayChannel(2, snd_stamps[cur_thing], 0);
|
|
}
|
|
#endif
|
|
cur_stamp = cur_thing;
|
|
stamp_scroll = thing_scroll;
|
|
|
|
if (do_draw)
|
|
draw_stamps();
|
|
|
|
if (txt_stamps[cur_stamp] != NULL)
|
|
{
|
|
if (txt_stamps[cur_stamp][0] == '=')
|
|
{
|
|
/* FIXME: Stupid. Using '=' to denote UTF8 */
|
|
|
|
draw_tux_text(TUX_GREAT,
|
|
txt_stamps[cur_stamp] + 1, 1, 0, 1);
|
|
}
|
|
else
|
|
{
|
|
draw_tux_text(TUX_GREAT, txt_stamps[cur_stamp],
|
|
0, 1, 0);
|
|
}
|
|
}
|
|
else
|
|
draw_tux_text(TUX_GREAT, "", 0, 0, 0);
|
|
|
|
|
|
/* Enable or disable color selector: */
|
|
|
|
if ((stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp)) !=
|
|
(stamp_colorable(old_thing) ||
|
|
stamp_tintable(old_thing)))
|
|
{
|
|
draw_colors(stamp_colorable(cur_stamp) ||
|
|
stamp_tintable(cur_stamp));
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH, 48);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
cur_shape = cur_thing;
|
|
|
|
draw_tux_text(TUX_GREAT, shape_tips[cur_shape],
|
|
0, 0, 1);
|
|
|
|
if (do_draw)
|
|
draw_shapes();
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
if (cur_thing != cur_magic)
|
|
{
|
|
if (cur_thing == MAGIC_FILL)
|
|
draw_colors(1);
|
|
else
|
|
draw_colors(0);
|
|
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH, 48);
|
|
}
|
|
|
|
cur_magic = cur_thing;
|
|
|
|
draw_tux_text(TUX_GREAT, magic_tips[cur_magic],
|
|
0, 0, 1);
|
|
|
|
if (do_draw)
|
|
draw_magic();
|
|
}
|
|
|
|
|
|
/* Update the screen: */
|
|
|
|
if (do_draw)
|
|
SDL_UpdateRect(screen,
|
|
WINDOW_WIDTH - 96, 0,
|
|
96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
}
|
|
else if (event.type == SDL_USEREVENT)
|
|
{
|
|
if (event.user.code == USEREVENT_TEXT_UPDATE)
|
|
{
|
|
/* Time to replace "Great!" with old tip text: */
|
|
|
|
if (event.user.data1 != NULL)
|
|
{
|
|
if (((unsigned char *) event.user.data1)[0] == '=')
|
|
{
|
|
draw_tux_text(TUX_GREAT, (char *) event.user.data1 + 1,
|
|
1, 0, 1);
|
|
}
|
|
else
|
|
{
|
|
draw_tux_text(TUX_GREAT, event.user.data1, 0, 1, 1);
|
|
}
|
|
}
|
|
else
|
|
draw_tux_text(TUX_GREAT, "", 0, 0, 1);
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONUP)
|
|
{
|
|
if (scrolling)
|
|
{
|
|
SDL_RemoveTimer(scrolltimer);
|
|
scrolling = 0;
|
|
}
|
|
|
|
if (button_down)
|
|
{
|
|
if (cur_tool == TOOL_LINES)
|
|
{
|
|
/* (Arbitrarily large, so we draw once now) */
|
|
brush_counter = 999;
|
|
|
|
brush_draw(line_start_x, line_start_y,
|
|
event.button.x - 96, event.button.y, 1);
|
|
brush_draw(event.button.x - 96, event.button.y,
|
|
event.button.x - 96, event.button.y, 1);
|
|
|
|
playsound(1, SND_LINE_END, 1);
|
|
draw_tux_text(TUX_GREAT, tool_tips[TOOL_LINES], 0, 0, 1);
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
|
|
{
|
|
/* Now we can rotate the shape... */
|
|
|
|
shape_outer_x = event.button.x - 96;
|
|
shape_outer_y = event.button.y;
|
|
|
|
if (!simple_shapes && !shape_no_rotate[cur_shape])
|
|
{
|
|
shape_tool_mode = SHAPE_TOOL_MODE_ROTATE;
|
|
|
|
SDL_WarpMouse(shape_outer_x + 96, shape_ctr_y);
|
|
do_setcursor(cursor_rotate);
|
|
|
|
|
|
/* Erase stretchy XOR: */
|
|
|
|
do_shape(shape_ctr_x, shape_ctr_y, old_x, old_y,
|
|
0, 0);
|
|
|
|
/* Make an initial rotation XOR to be erased: */
|
|
|
|
do_shape(shape_ctr_x, shape_ctr_y,
|
|
shape_outer_x, shape_outer_y,
|
|
rotation(shape_ctr_x, shape_ctr_y,
|
|
shape_outer_x, shape_outer_y),
|
|
0);
|
|
|
|
playsound(1, SND_LINE_START, 1);
|
|
draw_tux_text(TUX_BORED, TIP_SHAPE_NEXT, 0, 0, 1);
|
|
|
|
|
|
/* FIXME: Do something less intensive! */
|
|
|
|
SDL_Flip(screen);
|
|
}
|
|
else
|
|
{
|
|
brush_counter = 999; /* arbitrarily large... */
|
|
|
|
|
|
playsound(1, SND_LINE_END, 1);
|
|
do_shape(shape_ctr_x, shape_ctr_y,
|
|
shape_outer_x, shape_outer_y,
|
|
0, 1);
|
|
|
|
SDL_Flip(screen);
|
|
|
|
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
|
draw_tux_text(TUX_GREAT,
|
|
tool_tips[TOOL_SHAPES], 0, 0, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
button_down = 0;
|
|
}
|
|
else if (event.type == SDL_MOUSEMOTION)
|
|
{
|
|
new_x = event.button.x - 96;
|
|
new_y = event.button.y;
|
|
|
|
|
|
/* FIXME: Is doing this every event too intensive? */
|
|
/* Should I check current cursor first? */
|
|
|
|
if (event.button.x < 96 && event.button.y < (48 * 7) + 40 &&
|
|
event.button.y > 40)
|
|
{
|
|
/* Tools: */
|
|
|
|
if (tool_avail[(event.button.x / 48) +
|
|
((event.button.y - 40) / 48) * 2])
|
|
{
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
else
|
|
{
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
}
|
|
else if (event.button.x > 96 &&
|
|
event.button.y >= (48 * 7) + 40 + HEIGHTOFFSET &&
|
|
event.button.y <= (48 * 7) + 40 + 48 + HEIGHTOFFSET)
|
|
{
|
|
/* Color picker: */
|
|
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
else if (event.button.x >= WINDOW_WIDTH - 96 &&
|
|
event.button.y > 40 &&
|
|
event.button.y <= (48 * (7 + TOOLOFFSET / 2)) + 40)
|
|
{
|
|
/* Selector: */
|
|
|
|
/* Note set of things we're dealing with */
|
|
/* (stamps, brushes, etc.) */
|
|
|
|
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
|
{
|
|
num_things = num_brushes;
|
|
thing_scroll = brush_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_STAMP)
|
|
{
|
|
num_things = num_stamps;
|
|
thing_scroll = stamp_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
{
|
|
num_things = num_fonts;
|
|
thing_scroll = font_scroll;
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
num_things = NUM_SHAPES;
|
|
thing_scroll = 0;
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
num_things = NUM_MAGICS;
|
|
thing_scroll = 0;
|
|
}
|
|
|
|
|
|
if (num_things > 14 + TOOLOFFSET)
|
|
{
|
|
/* Are there scroll buttons? */
|
|
|
|
if (event.button.y < 40 + 24)
|
|
{
|
|
/* Up button; is it available? */
|
|
|
|
if (thing_scroll > 0)
|
|
do_setcursor(cursor_up);
|
|
else
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
else if (event.button.y > (48 * (6 + TOOLOFFSET / 2)) + 40 + 24)
|
|
{
|
|
/* Down button; is it available? */
|
|
|
|
if (thing_scroll < num_things - 12)
|
|
do_setcursor(cursor_down);
|
|
else
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
else
|
|
{
|
|
/* One of the selectors: */
|
|
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No scroll buttons - must be a selector: */
|
|
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
}
|
|
else if (event.button.x > 96 &&
|
|
event.button.x < WINDOW_WIDTH - 96 &&
|
|
event.button.y < (48 * 7) + 40 + HEIGHTOFFSET)
|
|
{
|
|
/* Canvas: */
|
|
|
|
if (cur_tool == TOOL_BRUSH)
|
|
do_setcursor(cursor_brush);
|
|
else if (cur_tool == TOOL_STAMP)
|
|
do_setcursor(cursor_tiny);
|
|
else if (cur_tool == TOOL_LINES)
|
|
do_setcursor(cursor_crosshair);
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
if (shape_tool_mode != SHAPE_TOOL_MODE_ROTATE)
|
|
do_setcursor(cursor_crosshair);
|
|
else
|
|
do_setcursor(cursor_rotate);
|
|
}
|
|
else if (cur_tool == TOOL_TEXT)
|
|
do_setcursor(cursor_insertion);
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
do_setcursor(cursor_wand);
|
|
else if (cur_tool == TOOL_ERASER)
|
|
do_setcursor(cursor_tiny);
|
|
}
|
|
else
|
|
{
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
|
|
|
|
if (button_down)
|
|
{
|
|
if (cur_tool == TOOL_BRUSH)
|
|
{
|
|
/* Pushing button and moving: Draw with the brush: */
|
|
|
|
brush_draw(old_x, old_y, new_x, new_y, 1);
|
|
|
|
playsound(0, SND_PAINT1 + (img_cur_brush->w) / 12, 0);
|
|
}
|
|
else if (cur_tool == TOOL_LINES)
|
|
{
|
|
/* Still pushing button, while moving:
|
|
Draw XOR where line will go: */
|
|
|
|
line_xor(line_start_x, line_start_y, old_x, old_y);
|
|
|
|
line_xor(line_start_x, line_start_y, new_x, new_y);
|
|
|
|
update_screen(line_start_x + 96, line_start_y,
|
|
old_x + 96, old_y);
|
|
update_screen(line_start_x + 96, line_start_y,
|
|
new_x + 96, new_y);
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES)
|
|
{
|
|
/* Still pushing button, while moving:
|
|
Draw XOR where shape will go: */
|
|
|
|
if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
|
|
{
|
|
do_shape(shape_ctr_x, shape_ctr_y, old_x, old_y,
|
|
0, 0);
|
|
|
|
do_shape(shape_ctr_x, shape_ctr_y,
|
|
new_x, new_y,
|
|
0, 0);
|
|
|
|
|
|
/* FIXME: Fix update shape function! */
|
|
|
|
/* update_shape(shape_ctr_x, old_x, new_x,
|
|
shape_ctr_y, old_y, new_y,
|
|
shape_locked[cur_shape]); */
|
|
|
|
SDL_Flip(screen);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_MAGIC)
|
|
{
|
|
/* Pushing button and moving: Do the magic: */
|
|
|
|
if (cur_magic != MAGIC_FLIP &&
|
|
cur_magic != MAGIC_MIRROR &&
|
|
cur_magic != MAGIC_FILL)
|
|
{
|
|
magic_draw(old_x, old_y, new_x, new_y);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_ERASER)
|
|
{
|
|
/* Still pushing, and moving - Erase! */
|
|
|
|
do_eraser(new_x, new_y);
|
|
}
|
|
}
|
|
|
|
|
|
if (cur_tool == TOOL_STAMP ||
|
|
(cur_tool == TOOL_ERASER && !button_down))
|
|
{
|
|
/* Moving: Draw XOR where stamp/eraser will apply: */
|
|
|
|
if (cur_tool == TOOL_STAMP)
|
|
{
|
|
w = img_stamps[cur_stamp]->w;
|
|
h = img_stamps[cur_stamp]->h;
|
|
}
|
|
else if (cur_tool == TOOL_ERASER)
|
|
{
|
|
w = 96;
|
|
h = 96;
|
|
}
|
|
|
|
if (old_x >= 0 && old_x < WINDOW_WIDTH - 96 - 96 &&
|
|
old_y >= 0 && old_y < (48 * 7) + 40 + HEIGHTOFFSET)
|
|
{
|
|
#ifndef LOW_QUALITY_STAMP_OUTLINE
|
|
if (cur_tool == TOOL_STAMP)
|
|
stamp_xor(old_x - w / 2, old_y - h / 2);
|
|
else
|
|
rect_xor(old_x - w / 2, old_y - h / 2,
|
|
old_x + w / 2, old_y + h / 2);
|
|
#else
|
|
rect_xor(old_x - w / 2, old_y - h / 2,
|
|
old_x + w / 2, old_y + h / 2);
|
|
#endif
|
|
update_screen(old_x - w / 2 + 96, old_y - h / 2,
|
|
old_x + w / 2 + 96, old_y + h / 2);
|
|
}
|
|
|
|
if (new_x >= 0 && new_x < WINDOW_WIDTH - 96 - 96 &&
|
|
new_y >= 0 && new_y < (48 * 7) + 40 + HEIGHTOFFSET)
|
|
{
|
|
#ifndef LOW_QUALITY_STAMP_OUTLINE
|
|
if (cur_tool == TOOL_STAMP)
|
|
stamp_xor(new_x - w / 2, new_y - h / 2);
|
|
else
|
|
rect_xor(new_x - w / 2, new_y - h / 2,
|
|
new_x + w / 2, new_y + h / 2);
|
|
#else
|
|
rect_xor(new_x - w / 2, new_y - h / 2,
|
|
new_x + w / 2, new_y + h / 2);
|
|
#endif
|
|
update_screen(new_x - w / 2 + 96, new_y - h / 2,
|
|
new_x + w / 2 + 96, new_y + h / 2);
|
|
}
|
|
}
|
|
else if (cur_tool == TOOL_SHAPES &&
|
|
shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
|
|
{
|
|
do_shape(shape_ctr_x, shape_ctr_y,
|
|
shape_outer_x, shape_outer_y,
|
|
rotation(shape_ctr_x, shape_ctr_y,
|
|
old_x, old_y), 0);
|
|
|
|
|
|
do_shape(shape_ctr_x, shape_ctr_y,
|
|
shape_outer_x, shape_outer_y,
|
|
rotation(shape_ctr_x, shape_ctr_y,
|
|
new_x, new_y), 0);
|
|
|
|
|
|
/* FIXME: Do something less intensive! */
|
|
|
|
SDL_Flip(screen);
|
|
}
|
|
|
|
old_x = new_x;
|
|
old_y = new_y;
|
|
}
|
|
}
|
|
|
|
|
|
SDL_Delay(10);
|
|
|
|
cur_cursor_blink = SDL_GetTicks();
|
|
|
|
|
|
if (cur_tool == TOOL_TEXT && cursor_x != -1 && cursor_y != -1 &&
|
|
cur_cursor_blink > last_cursor_blink + CURSOR_BLINK_SPEED)
|
|
{
|
|
last_cursor_blink = SDL_GetTicks();
|
|
|
|
line_xor(cursor_x + cursor_textwidth, cursor_y,
|
|
cursor_x + cursor_textwidth,
|
|
cursor_y + TTF_FontHeight(fonts[cur_font]));
|
|
|
|
update_screen(cursor_x + 96 + cursor_textwidth, cursor_y,
|
|
cursor_x + 96 + cursor_textwidth,
|
|
cursor_y + TTF_FontHeight(fonts[cur_font]));
|
|
}
|
|
}
|
|
while (!done);
|
|
}
|
|
|
|
|
|
/* Draw using the current brush: */
|
|
|
|
void brush_draw(int x1, int y1, int x2, int y2, int update)
|
|
{
|
|
int dx, dy, y;
|
|
int orig_x1, orig_y1, orig_x2, orig_y2, tmp;
|
|
float m, b;
|
|
|
|
orig_x1 = x1;
|
|
orig_y1 = y1;
|
|
|
|
orig_x2 = x2;
|
|
orig_y2 = y2;
|
|
|
|
|
|
x1 = x1 - (img_brushes[cur_brush]->w / 2);
|
|
y1 = y1 - (img_brushes[cur_brush]->h / 2);
|
|
|
|
x2 = x2 - (img_brushes[cur_brush]->w / 2);
|
|
y2 = y2 - (img_brushes[cur_brush]->h / 2);
|
|
|
|
|
|
dx = x2 - x1;
|
|
dy = y2 - y1;
|
|
|
|
if (dx != 0)
|
|
{
|
|
m = ((float) dy) / ((float) dx);
|
|
b = y1 - m * x1;
|
|
|
|
if (x2 >= x1)
|
|
dx = 1;
|
|
else
|
|
dx = -1;
|
|
|
|
|
|
while (x1 != x2)
|
|
{
|
|
y1 = m * x1 + b;
|
|
y2 = m * (x1 + dx) + b;
|
|
|
|
if (y1 > y2)
|
|
{
|
|
y = y1;
|
|
y1 = y2;
|
|
y2 = y;
|
|
}
|
|
|
|
for (y = y1; y <= y2; y++)
|
|
blit_brush(x1, y);
|
|
|
|
x1 = x1 + dx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (y1 > y2)
|
|
{
|
|
y = y1;
|
|
y1 = y2;
|
|
y2 = y;
|
|
}
|
|
|
|
for (y = y1; y <= y2; y++)
|
|
blit_brush(x1, y);
|
|
}
|
|
|
|
if (orig_x1 > orig_x2)
|
|
{
|
|
tmp = orig_x1;
|
|
orig_x1 = orig_x2;
|
|
orig_x2 = tmp;
|
|
}
|
|
|
|
if (orig_y1 > orig_y2)
|
|
{
|
|
tmp = orig_y1;
|
|
orig_y1 = orig_y2;
|
|
orig_y2 = tmp;
|
|
}
|
|
|
|
|
|
if (update)
|
|
{
|
|
update_canvas(orig_x1 - (img_brushes[cur_brush]->w / 2),
|
|
orig_y1 - (img_brushes[cur_brush]->h / 2),
|
|
orig_x2 + (img_brushes[cur_brush]->w / 2),
|
|
orig_y2 + (img_brushes[cur_brush]->h / 2));
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw the current brush in the current color: */
|
|
|
|
void blit_brush(int x, int y)
|
|
{
|
|
SDL_Rect dest;
|
|
|
|
brush_counter++;
|
|
|
|
if (brush_counter >= (img_cur_brush->h / 4))
|
|
{
|
|
brush_counter = 0;
|
|
|
|
dest.x = x;
|
|
dest.y = y;
|
|
|
|
SDL_BlitSurface(img_cur_brush, NULL, canvas, &dest);
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw using the current stamp: */
|
|
|
|
void stamp_draw(int x, int y)
|
|
{
|
|
SDL_Rect dest;
|
|
SDL_Surface * tmp_surf;
|
|
Uint32 amask;
|
|
Uint8 r, g, b, a;
|
|
int xx, yy;
|
|
float col_hue, col_sat, col_val,
|
|
stamp_hue, stamp_sat, stamp_val;
|
|
|
|
|
|
dest.x = x - (img_stamps[cur_stamp]->w / 2);
|
|
dest.y = y - (img_stamps[cur_stamp]->h / 2);
|
|
|
|
if (stamp_colorable(cur_stamp))
|
|
{
|
|
/* Render the stamp in the chosen color: */
|
|
|
|
/* FIXME: It sucks to render this EVERY TIME. Why not just when
|
|
they pick the color, or pick the stamp, like with brushes?
|
|
(Why? Because I'm LAZY! :^) ) */
|
|
|
|
|
|
amask = ~(img_stamps[cur_stamp]->format->Rmask |
|
|
img_stamps[cur_stamp]->format->Gmask |
|
|
img_stamps[cur_stamp]->format->Bmask);
|
|
|
|
tmp_surf =
|
|
SDL_CreateRGBSurface(SDL_SWSURFACE,
|
|
img_stamps[cur_stamp]->w,
|
|
img_stamps[cur_stamp]->h,
|
|
img_stamps[cur_stamp]->format->BitsPerPixel,
|
|
img_stamps[cur_stamp]->format->Rmask,
|
|
img_stamps[cur_stamp]->format->Gmask,
|
|
img_stamps[cur_stamp]->format->Bmask,
|
|
amask);
|
|
|
|
if (tmp_surf == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't render the colored stamp!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Render the stamp: */
|
|
|
|
SDL_LockSurface(img_stamps[cur_stamp]);
|
|
SDL_LockSurface(tmp_surf);
|
|
|
|
for (yy = 0; yy < img_stamps[cur_stamp]->h; yy++)
|
|
{
|
|
for (xx = 0; xx < img_stamps[cur_stamp]->w; xx++)
|
|
{
|
|
SDL_GetRGBA(getpixel(img_stamps[cur_stamp], xx, yy),
|
|
img_stamps[cur_stamp]->format,
|
|
&r, &g, &b, &a);
|
|
|
|
putpixel(tmp_surf, xx, yy,
|
|
SDL_MapRGBA(tmp_surf->format,
|
|
color_hexes[cur_color][0],
|
|
color_hexes[cur_color][1],
|
|
color_hexes[cur_color][2],
|
|
a));
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(tmp_surf);
|
|
SDL_UnlockSurface(img_stamps[cur_stamp]);
|
|
|
|
|
|
/* And blit it! */
|
|
|
|
SDL_BlitSurface(tmp_surf, NULL, canvas, &dest);
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
else if (stamp_tintable(cur_stamp))
|
|
{
|
|
/* Render the stamp in the chosen color: */
|
|
|
|
/* FIXME: It sucks to render this EVERY TIME. Why not just when
|
|
they pick the color, or pick the stamp, like with brushes?
|
|
(Why? Because I'm LAZY! :^) ) */
|
|
|
|
|
|
amask = ~(img_stamps[cur_stamp]->format->Rmask |
|
|
img_stamps[cur_stamp]->format->Gmask |
|
|
img_stamps[cur_stamp]->format->Bmask);
|
|
|
|
tmp_surf =
|
|
SDL_CreateRGBSurface(SDL_SWSURFACE,
|
|
img_stamps[cur_stamp]->w,
|
|
img_stamps[cur_stamp]->h,
|
|
img_stamps[cur_stamp]->format->BitsPerPixel,
|
|
img_stamps[cur_stamp]->format->Rmask,
|
|
img_stamps[cur_stamp]->format->Gmask,
|
|
img_stamps[cur_stamp]->format->Bmask,
|
|
amask);
|
|
|
|
if (tmp_surf == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't render the colored stamp!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
rgbtohsv(color_hexes[cur_color][0],
|
|
color_hexes[cur_color][1],
|
|
color_hexes[cur_color][2],
|
|
&col_hue, &col_sat, &col_val);
|
|
|
|
|
|
/* Render the stamp: */
|
|
|
|
SDL_LockSurface(img_stamps[cur_stamp]);
|
|
SDL_LockSurface(tmp_surf);
|
|
|
|
for (yy = 0; yy < img_stamps[cur_stamp]->h; yy++)
|
|
{
|
|
for (xx = 0; xx < img_stamps[cur_stamp]->w; xx++)
|
|
{
|
|
SDL_GetRGBA(getpixel(img_stamps[cur_stamp], xx, yy),
|
|
img_stamps[cur_stamp]->format,
|
|
&r, &g, &b, &a);
|
|
|
|
rgbtohsv(r, g, b, &stamp_hue, &stamp_sat, &stamp_val);
|
|
|
|
hsvtorgb(col_hue, col_sat, stamp_val, &r, &g, &b);
|
|
|
|
|
|
putpixel(tmp_surf, xx, yy,
|
|
SDL_MapRGBA(tmp_surf->format, r, g, b, a));
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(tmp_surf);
|
|
SDL_UnlockSurface(img_stamps[cur_stamp]);
|
|
|
|
|
|
/* And blit it! */
|
|
|
|
SDL_BlitSurface(tmp_surf, NULL, canvas, &dest);
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
else
|
|
{
|
|
/* Not a colorized stamp. Just draw it! */
|
|
|
|
SDL_BlitSurface(img_stamps[cur_stamp], NULL,
|
|
canvas, &dest);
|
|
}
|
|
|
|
update_canvas(x - (img_stamps[cur_stamp]->w / 2),
|
|
y - (img_stamps[cur_stamp]->h / 2),
|
|
x + (img_stamps[cur_stamp]->w / 2),
|
|
y + (img_stamps[cur_stamp]->h / 2));
|
|
}
|
|
|
|
|
|
/* Draw using the current brush: */
|
|
|
|
void magic_draw(int x1, int y1, int x2, int y2)
|
|
{
|
|
int dx, dy, y;
|
|
int orig_x1, orig_y1, orig_x2, orig_y2, tmp;
|
|
float m, b;
|
|
|
|
if (cur_magic == MAGIC_RAINBOW)
|
|
rainbow_color = (rainbow_color + 1) % NUM_RAINBOW_COLORS;
|
|
|
|
orig_x1 = x1;
|
|
orig_y1 = y1;
|
|
|
|
orig_x2 = x2;
|
|
orig_y2 = y2;
|
|
|
|
|
|
dx = x2 - x1;
|
|
dy = y2 - y1;
|
|
|
|
if (dx != 0)
|
|
{
|
|
m = ((float) dy) / ((float) dx);
|
|
b = y1 - m * x1;
|
|
|
|
if (x2 >= x1)
|
|
dx = 1;
|
|
else
|
|
dx = -1;
|
|
|
|
|
|
while (x1 != x2)
|
|
{
|
|
y1 = m * x1 + b;
|
|
y2 = m * (x1 + dx) + b;
|
|
|
|
if (y1 > y2)
|
|
{
|
|
for (y = y1; y >= y2; y--)
|
|
blit_magic(x1, y, x1, y - 1);
|
|
}
|
|
else
|
|
{
|
|
for (y = y1; y <= y2; y++)
|
|
blit_magic(x1, y, x1 + dx, y + 1);
|
|
}
|
|
|
|
x1 = x1 + dx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (y1 > y2)
|
|
{
|
|
for (y = y1; y >= y2; y--)
|
|
blit_magic(x1, y, x1, y - 1);
|
|
}
|
|
else
|
|
{
|
|
for (y = y1; y <= y2; y++)
|
|
blit_magic(x1, y, x1, y + 1);
|
|
}
|
|
}
|
|
|
|
if (orig_x1 > orig_x2)
|
|
{
|
|
tmp = orig_x1;
|
|
orig_x1 = orig_x2;
|
|
orig_x2 = tmp;
|
|
}
|
|
|
|
if (orig_y1 > orig_y2)
|
|
{
|
|
tmp = orig_y1;
|
|
orig_y1 = orig_y2;
|
|
orig_y2 = tmp;
|
|
}
|
|
|
|
|
|
/* Play sound: */
|
|
|
|
if (cur_magic == MAGIC_DRIP)
|
|
playsound(0, SND_DRIP, 0);
|
|
else if (cur_magic == MAGIC_CHALK)
|
|
playsound(0, SND_CHALK, 0);
|
|
else if (cur_magic == MAGIC_SPARKLES)
|
|
playsound(0, SND_SPARKLES1 + (rand() % 2), 0);
|
|
else if (cur_magic == MAGIC_FLIP)
|
|
playsound(0, SND_FLIP, 0);
|
|
else if (cur_magic == MAGIC_MIRROR)
|
|
playsound(0, SND_MIRROR, 0);
|
|
else if (cur_magic == MAGIC_NEGATIVE)
|
|
playsound(0, SND_NEGATIVE, 0);
|
|
else if (cur_magic == MAGIC_BLUR)
|
|
playsound(0, SND_BLUR, 0);
|
|
else if (cur_magic == MAGIC_THICK)
|
|
playsound(0, SND_THICK, 0);
|
|
else if (cur_magic == MAGIC_THIN)
|
|
playsound(0, SND_THIN, 0);
|
|
else if (cur_magic == MAGIC_BLOCKS && ((rand() % 10) < 5))
|
|
playsound(0, SND_BLOCKS, 0);
|
|
else if (cur_magic == MAGIC_FADE)
|
|
playsound(0, SND_FADE, 0);
|
|
else if (cur_magic == MAGIC_RAINBOW)
|
|
playsound(0, SND_RAINBOW, 0);
|
|
|
|
|
|
/* FIXME: Arbitrary? */
|
|
|
|
update_canvas(orig_x1 - 32, orig_y1 - 32,
|
|
orig_x2 + 32, orig_y2 + 32);
|
|
}
|
|
|
|
|
|
/* Draw the current brush in the current color: */
|
|
|
|
void blit_magic(int x, int y, int x2, int y2)
|
|
{
|
|
int xx, yy, w, h;
|
|
Uint32 colr;
|
|
Uint8 r, g, b,
|
|
r1, g1, b1,
|
|
r2, g2, b2,
|
|
r3, g3, b3,
|
|
r4, g4, b4;
|
|
SDL_Surface * last;
|
|
SDL_Rect src, dest;
|
|
|
|
|
|
/* In case we need to use the current canvas (just saved to undo buf)... */
|
|
|
|
if (cur_undo > 0)
|
|
last = undo_bufs[cur_undo - 1];
|
|
else
|
|
last = undo_bufs[NUM_UNDO_BUFS - 1];
|
|
|
|
|
|
brush_counter++;
|
|
|
|
if (brush_counter >= 4) /* FIXME: Arbitrary? */
|
|
{
|
|
brush_counter = 0;
|
|
|
|
|
|
if (cur_magic == MAGIC_BLUR)
|
|
{
|
|
/* FIXME: Circular "brush?" */
|
|
|
|
SDL_LockSurface(canvas);
|
|
for (yy = y - 16; yy < y + 16; yy = yy + 2)
|
|
{
|
|
for (xx = x - 16; xx < x + 16; xx = xx + 2)
|
|
{
|
|
SDL_GetRGB(getpixel(canvas, clamp(0, xx, canvas->w - 1),
|
|
clamp(0, yy - 1, canvas->h - 1)),
|
|
canvas->format,
|
|
&r1, &g1, &b1);
|
|
|
|
SDL_GetRGB(getpixel(canvas, clamp(0, xx - 1, canvas->w - 1),
|
|
clamp(0, yy, canvas->h - 1)),
|
|
canvas->format,
|
|
&r2, &g2, &b2);
|
|
|
|
SDL_GetRGB(getpixel(canvas, clamp(0, xx + 1, canvas->w - 1),
|
|
clamp(0, yy, canvas->h - 1)),
|
|
canvas->format,
|
|
&r3, &g3, &b3);
|
|
|
|
SDL_GetRGB(getpixel(canvas, clamp(0, xx, canvas->w - 1),
|
|
clamp(0, yy + 1, canvas->h - 1)),
|
|
canvas->format,
|
|
&r4, &g4, &b4);
|
|
|
|
|
|
r = (r1 + r2 + r3 + r4) >> 2;
|
|
g = (g1 + g2 + g3 + g4) >> 2;
|
|
b = (b1 + b2 + b3 + b4) >> 2;
|
|
|
|
putpixel(canvas, xx, yy,
|
|
SDL_MapRGB(canvas->format, r, g, b));
|
|
}
|
|
}
|
|
SDL_UnlockSurface(canvas);
|
|
}
|
|
else if (cur_magic == MAGIC_BLOCKS)
|
|
{
|
|
/* Put x/y on exact grid points: */
|
|
|
|
x = (x / 4) * 4;
|
|
y = (y / 4) * 4;
|
|
|
|
SDL_LockSurface(last);
|
|
SDL_LockSurface(canvas);
|
|
|
|
for (yy = y - 8; yy < y + 8; yy = yy + 4)
|
|
{
|
|
for (xx = x - 8; xx < x + 8; xx = xx + 4)
|
|
{
|
|
/* Get average color around here: */
|
|
|
|
SDL_GetRGB(getpixel(last, xx, yy), last->format,
|
|
&r1, &g1, &b1);
|
|
|
|
SDL_GetRGB(getpixel(last, xx + 2, yy), last->format,
|
|
&r2, &g2, &b2);
|
|
|
|
SDL_GetRGB(getpixel(last, xx, yy + 2), last->format,
|
|
&r3, &g3, &b3);
|
|
|
|
SDL_GetRGB(getpixel(last, xx + 2, yy + 2), last->format,
|
|
&r4, &g4, &b4);
|
|
|
|
r = (r1 + r2 + r3 + r4) / 4;
|
|
g = (g1 + g2 + g3 + g4) / 4;
|
|
b = (b1 + b2 + b3 + b4) / 4;
|
|
|
|
|
|
/* Draw block: */
|
|
|
|
dest.x = xx;
|
|
dest.y = yy;
|
|
dest.w = 4;
|
|
dest.h = 4;
|
|
|
|
SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, r, g, b));
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(canvas);
|
|
SDL_UnlockSurface(last);
|
|
}
|
|
else if (cur_magic == MAGIC_NEGATIVE)
|
|
{
|
|
SDL_LockSurface(last);
|
|
SDL_LockSurface(canvas);
|
|
|
|
for (yy = y - 16; yy < y + 16; yy++)
|
|
{
|
|
for (xx = x - 16; xx < x + 16; xx++)
|
|
{
|
|
/* Get average color around here: */
|
|
|
|
SDL_GetRGB(getpixel(last, xx, yy), last->format,
|
|
&r, &g, &b);
|
|
|
|
r = 0xFF - r;
|
|
g = 0xFF - g;
|
|
b = 0xFF - b;
|
|
|
|
putpixel(canvas, xx, yy, SDL_MapRGB(canvas->format, r, g, b));
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(canvas);
|
|
SDL_UnlockSurface(last);
|
|
}
|
|
else if (cur_magic == MAGIC_FADE)
|
|
{
|
|
SDL_LockSurface(last);
|
|
SDL_LockSurface(canvas);
|
|
|
|
for (yy = y - 16; yy < y + 16; yy++)
|
|
{
|
|
for (xx = x - 16; xx < x + 16; xx++)
|
|
{
|
|
/* Get average color around here: */
|
|
|
|
SDL_GetRGB(getpixel(last, xx, yy), last->format,
|
|
&r, &g, &b);
|
|
|
|
r = min(r + 48, 255);
|
|
g = min(g + 48, 255);
|
|
b = min(b + 48, 255);
|
|
|
|
putpixel(canvas, xx, yy, SDL_MapRGB(canvas->format, r, g, b));
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(canvas);
|
|
SDL_UnlockSurface(last);
|
|
}
|
|
else if (cur_magic == MAGIC_RAINBOW)
|
|
{
|
|
/* Pick next color: */
|
|
|
|
colr = SDL_MapRGB(canvas->format,
|
|
rainbow_hexes[rainbow_color][0],
|
|
rainbow_hexes[rainbow_color][1],
|
|
rainbow_hexes[rainbow_color][2]);
|
|
|
|
|
|
/* Draw the shape: */
|
|
|
|
for (yy = 0; yy <= 16; yy++)
|
|
{
|
|
w = (yy * yy) / 16;
|
|
|
|
|
|
/* Top half: */
|
|
|
|
dest.x = x - 16 + w;
|
|
dest.w = 32 - (w * 2);
|
|
dest.y = y - yy;;
|
|
dest.h = 1;
|
|
|
|
SDL_FillRect(canvas, &dest, colr);
|
|
|
|
|
|
/* Bottom half: */
|
|
|
|
dest.x = x - 16 + w;
|
|
dest.w = 32 - (w * 2);
|
|
dest.y = y + yy;
|
|
dest.h = 1;
|
|
|
|
SDL_FillRect(canvas, &dest, colr);
|
|
}
|
|
}
|
|
else if (cur_magic == MAGIC_CHALK)
|
|
{
|
|
SDL_LockSurface(last);
|
|
|
|
for (yy = y - 8; yy <= y + 8; yy = yy + 4)
|
|
{
|
|
for (xx = x - 8; xx <= x + 8; xx = xx + 4)
|
|
{
|
|
dest.x = xx + ((rand() % 5) - 2);
|
|
dest.y = yy + ((rand() % 5) - 2);
|
|
dest.w = (rand() % 4) + 2;
|
|
dest.h = (rand() % 4) + 2;
|
|
|
|
colr = getpixel(last, clamp(0, xx, canvas->w-1),
|
|
clamp(0, yy, canvas->h-1));
|
|
SDL_FillRect(canvas, &dest, colr);
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(last);
|
|
}
|
|
else if (cur_magic == MAGIC_DRIP)
|
|
{
|
|
for (xx = x - 8; xx <= x + 8; xx++)
|
|
{
|
|
h = (rand() % 8) + 8;
|
|
|
|
for (yy = y; yy <= y + h; yy++)
|
|
{
|
|
src.x = xx;
|
|
src.y = y;
|
|
src.w = 1;
|
|
src.h = 16;
|
|
|
|
dest.x = xx;
|
|
dest.y = yy;
|
|
|
|
SDL_BlitSurface(last, &src, canvas, &dest);
|
|
}
|
|
}
|
|
}
|
|
else if (cur_magic == MAGIC_SPARKLES)
|
|
{
|
|
if ((rand() % 10) < 2)
|
|
{
|
|
src.x = 0;
|
|
src.y = (rand() % 4) * 32;
|
|
src.w = 32;
|
|
src.h = 32;
|
|
|
|
dest.x = x - 16;
|
|
dest.y = y - 16;
|
|
|
|
SDL_BlitSurface(img_sparkles, &src, canvas, &dest);
|
|
}
|
|
}
|
|
else if (cur_magic == MAGIC_FLIP)
|
|
{
|
|
for (yy = 0; yy < canvas->h; yy++)
|
|
{
|
|
src.x = 0;
|
|
src.y = yy;
|
|
src.w = canvas->w;
|
|
src.h = 1;
|
|
|
|
dest.x = 0;
|
|
dest.y = canvas->h - yy - 1;
|
|
|
|
SDL_BlitSurface(last, &src, canvas, &dest);
|
|
}
|
|
}
|
|
else if (cur_magic == MAGIC_MIRROR)
|
|
{
|
|
for (xx = 0; xx < canvas->w; xx++)
|
|
{
|
|
src.x = xx;
|
|
src.y = 0;
|
|
src.w = 1;
|
|
src.h = canvas->h;
|
|
|
|
dest.x = canvas->w - xx - 1;
|
|
dest.y = 0;
|
|
|
|
SDL_BlitSurface(last, &src, canvas, &dest);
|
|
}
|
|
}
|
|
else if (cur_magic == MAGIC_THIN || cur_magic == MAGIC_THICK)
|
|
{
|
|
SDL_LockSurface(last);
|
|
SDL_LockSurface(canvas);
|
|
|
|
for (xx = -8; xx <= 8; xx++)
|
|
{
|
|
for (yy = -8; yy <= 8; yy++)
|
|
{
|
|
SDL_GetRGB(getpixel(last, x + xx, y + yy), last->format,
|
|
&r, &g, &b);
|
|
|
|
r = min(r, 255);
|
|
g = min(g, 255);
|
|
b = min(b, 255);
|
|
|
|
if ((cur_magic == MAGIC_THIN && (((r + g + b) / 3) > 128)) ||
|
|
(cur_magic == MAGIC_THICK && (((r + g + b) / 3) <= 128)))
|
|
{
|
|
putpixel(canvas, x + xx + 0, y + yy - 1,
|
|
SDL_MapRGB(canvas->format, r, g, b));
|
|
putpixel(canvas, x + xx - 1, y + yy + 0,
|
|
SDL_MapRGB(canvas->format, r, g, b));
|
|
putpixel(canvas, x + xx + 1, y + yy + 0,
|
|
SDL_MapRGB(canvas->format, r, g, b));
|
|
putpixel(canvas, x + xx + 0, y + yy + 1,
|
|
SDL_MapRGB(canvas->format, r, g, b));
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(canvas);
|
|
SDL_UnlockSurface(last);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Store canvas into undo buffer: */
|
|
|
|
void rec_undo_buffer(void)
|
|
{
|
|
int wanna_update_toolbar;
|
|
|
|
wanna_update_toolbar = 0;
|
|
|
|
|
|
SDL_BlitSurface(canvas, NULL, undo_bufs[cur_undo], NULL);
|
|
|
|
cur_undo = (cur_undo + 1) % NUM_UNDO_BUFS;
|
|
|
|
if (cur_undo == oldest_undo)
|
|
oldest_undo = (oldest_undo + 1) % NUM_UNDO_BUFS;
|
|
|
|
newest_undo = cur_undo;
|
|
|
|
#ifdef DEBUG
|
|
printf("DRAW: Current=%d Oldest=%d Newest=%d\n",
|
|
cur_undo, oldest_undo, newest_undo);
|
|
#endif
|
|
|
|
|
|
/* Update toolbar buttons, if needed: */
|
|
|
|
if (tool_avail[TOOL_UNDO] == 0)
|
|
{
|
|
tool_avail[TOOL_UNDO] = 1;
|
|
wanna_update_toolbar = 1;
|
|
}
|
|
|
|
if (tool_avail[TOOL_REDO])
|
|
{
|
|
tool_avail[TOOL_REDO] = 0;
|
|
wanna_update_toolbar = 1;
|
|
}
|
|
|
|
if (wanna_update_toolbar)
|
|
{
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0, 96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
}
|
|
|
|
|
|
/* Update the screen with the new canvas: */
|
|
|
|
void update_canvas(int x1, int y1, int x2, int y2)
|
|
{
|
|
SDL_Rect dest;
|
|
|
|
dest.x = 96;
|
|
dest.y = 0;
|
|
SDL_BlitSurface(canvas, NULL, screen, &dest);
|
|
update_screen(x1 + 96, y1, x2 + 96, y2);
|
|
}
|
|
|
|
|
|
/* Show program version: */
|
|
|
|
void show_version(void)
|
|
{
|
|
printf("\nTux Paint\n");
|
|
printf(" Version " VER_VERSION " (" VER_DATE ")\n");
|
|
|
|
#ifdef LOW_QUALITY_THUMBNAILS
|
|
printf(" Low Quality Thumbnails enabled\n");
|
|
#endif
|
|
|
|
#ifdef LOW_QUALITY_COLOR_SELECTOR
|
|
printf(" Low Quality Color Selector enabled\n");
|
|
#endif
|
|
|
|
#ifdef LOW_QUALITY_STAMP_OUTLINE
|
|
printf(" Low Quality Stamp Outline enabled\n");
|
|
#endif
|
|
|
|
#ifdef LOW_QUALITY_FLOOD_FILL
|
|
printf(" Low Quality Flood Fill enabled\n");
|
|
#endif
|
|
|
|
#ifdef NO_PROMPT_SHADOWS
|
|
printf(" Prompt Shadows disabled\n");
|
|
#endif
|
|
|
|
#ifdef NOSOUND
|
|
printf(" Sound disabled\n");
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
printf(" Verbose debugging enabled\n");
|
|
#endif
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
/* Show usage display: */
|
|
|
|
void show_usage(FILE * f, char * prg)
|
|
{
|
|
fprintf(f,
|
|
"Usage: %s {--usage | --help | --version | --copying}\n"
|
|
" %s [--fullscreen] [--800x600] [--nosound] [--noquit] [--noprint]\n"
|
|
" [--simpleshapes] [--uppercase] [--grab] [--nowheelmouse]\n"
|
|
" [--nofancycursors] [--keyboard] [--nooutlines] [--nostamps]\n"
|
|
" [--savedir DIRECTORY] [--saveover] [--saveovernew]\n"
|
|
" %s [--windowed] [--640x480] [--sound] [--quit] [--print]\n"
|
|
" [--complexshapes] [--mixedcase] [--dontgrab] [--wheelmouse]\n"
|
|
" [--fancycursors] [--mouse] [--outlines] [--stamps]\n"
|
|
" [--saveoverask]\n"
|
|
" %s [--printcfg | --noprintcfg] (Windows only)\n"
|
|
" %s [--printdelay=SECONDS]\n"
|
|
" [--lang LANGUAGE | --locale LOCALE]\n"
|
|
" %s [--nosysconfig]\n"
|
|
"\n"
|
|
"LANGUAGE may be one of:\n"
|
|
" english american-english\n"
|
|
" brazilian brazilian-portuguese portugues-brazilian\n"
|
|
" british british-english\n"
|
|
" catalan catala\n"
|
|
" chinese\n"
|
|
" czech cesky\n"
|
|
" danish dansk\n"
|
|
" dutch\n"
|
|
" finnish suomi\n"
|
|
" french francais\n"
|
|
" german deutsch\n"
|
|
" greek\n"
|
|
" hebrew\n"
|
|
" hungarian magyar\n"
|
|
" icelandic islenska\n"
|
|
" indonesian bahasa-indonesia\n"
|
|
" italian italiano\n"
|
|
" japanese\n"
|
|
" korean\n"
|
|
" lithuanian lietuviu\n"
|
|
" norwegian nynorsk norsk\n"
|
|
" polish polski\n"
|
|
" portuguese portugues\n"
|
|
" romanian\n"
|
|
" slovak\n"
|
|
" spanish espanol\n"
|
|
" swedish svenska\n"
|
|
" turkish\n\n",
|
|
prg, prg, prg, prg, prg, prg);
|
|
}
|
|
|
|
|
|
const char *getfilename(const char* path)
|
|
{
|
|
char *p;
|
|
|
|
if ( (p = strrchr( path, '\\' )) != NULL )
|
|
return p+1;
|
|
if ( (p = strrchr( path, '/' )) != NULL )
|
|
return p+1;
|
|
return path;
|
|
}
|
|
|
|
|
|
/* Setup: */
|
|
|
|
void setup(int argc, char * argv[])
|
|
{
|
|
int i, ok_to_use_sysconfig;
|
|
char str[128];
|
|
char * upstr;
|
|
SDL_Color black = {0, 0, 0, 0};
|
|
char * homedirdir;
|
|
FILE * fi;
|
|
SDL_Surface * tmp_surf;
|
|
SDL_Rect dest;
|
|
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
|
int x, y;
|
|
SDL_Surface * tmp_btn;
|
|
Uint8 r, g, b, a;
|
|
#endif
|
|
|
|
|
|
#ifdef __BEOS__
|
|
/* if run from gui, like OpenTracker in BeOS or Explorer in Windows,
|
|
find path from which binary was run and change dir to it
|
|
so all files will be local :) */
|
|
|
|
if (argc && argv[0])
|
|
{
|
|
char * slash = strrchr(argv[0], '/');
|
|
*(slash + 1) = '\0';
|
|
chdir(argv[0]);
|
|
*(slash + 1) = '/';
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Set default options: */
|
|
|
|
use_sound = 1;
|
|
fullscreen = 0;
|
|
dont_do_xor = 0;
|
|
keymouse = 0;
|
|
wheely = 1;
|
|
grab_input = 0;
|
|
no_fancy_cursors = 0;
|
|
simple_shapes = 0;
|
|
only_uppercase = 0;
|
|
promptless_save = SAVE_OVER_PROMPT;
|
|
disable_quit = 0;
|
|
disable_print = 0;
|
|
dont_load_stamps = 0;
|
|
print_delay = 0;
|
|
printcommand = "pngtopnm | pnmtops | lpr";
|
|
langstr = NULL;
|
|
use_print_config = 0;
|
|
WINDOW_WIDTH = 640;
|
|
WINDOW_HEIGHT = 480;
|
|
|
|
|
|
#ifdef __BEOS__
|
|
/* Fancy cursors on BeOS are buggy in SDL */
|
|
|
|
no_fancy_cursors = 1;
|
|
#endif
|
|
|
|
|
|
#ifdef WIN32
|
|
savedir = strdup("userdata");
|
|
#elif __BEOS__
|
|
savedir = strdup("./userdata");
|
|
#else
|
|
savedir = NULL;
|
|
#endif
|
|
|
|
|
|
/* Load options from global config file: */
|
|
|
|
#ifndef WIN32
|
|
|
|
/* Check to see if it's ok first: */
|
|
|
|
ok_to_use_sysconfig = 1;
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "--nosysconfig") == 0)
|
|
{
|
|
ok_to_use_sysconfig = 0;
|
|
i = argc; /* aka break; */
|
|
}
|
|
}
|
|
|
|
|
|
if (ok_to_use_sysconfig)
|
|
{
|
|
snprintf(str, sizeof(str), "%s/tuxpaint.conf", CONFDIR);
|
|
|
|
fi = fopen(str, "r");
|
|
if (fi != NULL)
|
|
{
|
|
parse_options(fi);
|
|
fclose(fi);
|
|
}
|
|
else
|
|
debug(str);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* Load options from .rc file: */
|
|
|
|
#if !defined(WIN32) && !defined(__BEOS__)
|
|
if (getenv("HOME") != NULL)
|
|
{
|
|
/* Should it be "~/.tuxpaint/tuxpaintrc" instead???
|
|
Comments welcome ... bill@newbreedsoftware.com */
|
|
|
|
snprintf(str, sizeof(str), "%s/.tuxpaintrc", getenv("HOME"));
|
|
}
|
|
else
|
|
{
|
|
strcpy(str, "tuxpaint.cfg");
|
|
}
|
|
#else
|
|
strcpy(str, "tuxpaint.cfg");
|
|
#endif
|
|
|
|
|
|
fi = fopen(str, "r");
|
|
if (fi != NULL)
|
|
{
|
|
parse_options(fi);
|
|
fclose(fi);
|
|
}
|
|
else
|
|
debug(str);
|
|
|
|
|
|
/* Handle command-line arguments: */
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "--fullscreen") == 0 || strcmp(argv[i], "-f") == 0)
|
|
{
|
|
fullscreen = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--windowed") == 0 || strcmp(argv[i], "-w") == 0)
|
|
{
|
|
fullscreen = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--800x600") == 0)
|
|
{
|
|
WINDOW_WIDTH = 800;
|
|
WINDOW_HEIGHT = 600;
|
|
}
|
|
else if (strcmp(argv[i], "--640x480") == 0)
|
|
{
|
|
WINDOW_WIDTH = 640;
|
|
WINDOW_HEIGHT = 480;
|
|
}
|
|
else if (strcmp(argv[i], "--nooutlines") == 0)
|
|
{
|
|
dont_do_xor = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--outlines") == 0)
|
|
{
|
|
dont_do_xor = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--keyboard") == 0)
|
|
{
|
|
keymouse = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--mouse") == 0)
|
|
{
|
|
keymouse = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--nowheelmouse") == 0)
|
|
{
|
|
wheely = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--wheelmouse") == 0)
|
|
{
|
|
wheely = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--grab") == 0)
|
|
{
|
|
grab_input = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--dontgrab") == 0)
|
|
{
|
|
grab_input = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--nofancycursors") == 0)
|
|
{
|
|
no_fancy_cursors = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--fancycursors") == 0)
|
|
{
|
|
no_fancy_cursors = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--saveover") == 0)
|
|
{
|
|
promptless_save = SAVE_OVER_ALWAYS;
|
|
}
|
|
else if (strcmp(argv[i], "--saveoverask") == 0)
|
|
{
|
|
promptless_save = SAVE_OVER_PROMPT;
|
|
}
|
|
else if (strcmp(argv[i], "--saveovernew") == 0)
|
|
{
|
|
promptless_save = SAVE_OVER_NO;
|
|
}
|
|
else if (strcmp(argv[i], "--uppercase") == 0 || strcmp(argv[i], "-u") == 0)
|
|
{
|
|
only_uppercase = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--mixedcase") == 0 || strcmp(argv[i], "-m") == 0)
|
|
{
|
|
only_uppercase = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--simpleshapes") == 0 ||
|
|
strcmp(argv[i], "-s") == 0)
|
|
{
|
|
simple_shapes = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--complexshapes") == 0)
|
|
{
|
|
simple_shapes = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--noquit") == 0 || strcmp(argv[i], "-x") == 0)
|
|
{
|
|
disable_quit = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--quit") == 0)
|
|
{
|
|
disable_quit = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--nostamps") == 0)
|
|
{
|
|
dont_load_stamps = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--stamps") == 0)
|
|
{
|
|
dont_load_stamps = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--noprint") == 0 || strcmp(argv[i], "-p") == 0)
|
|
{
|
|
disable_print = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--print") == 0)
|
|
{
|
|
disable_print = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--noprintcfg") == 0)
|
|
{
|
|
#ifndef WIN32
|
|
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
|
#endif
|
|
use_print_config = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--printcfg") == 0)
|
|
{
|
|
#ifndef WIN32
|
|
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
|
#endif
|
|
use_print_config = 1;
|
|
}
|
|
else if (strstr(argv[i], "--printdelay=") == argv[i])
|
|
{
|
|
sscanf(strstr(argv[i], "--printdelay=") + 13, "%d", &print_delay);
|
|
#ifdef DEBUG
|
|
printf("Print delay set to %d seconds\n", print_delay);
|
|
#endif
|
|
}
|
|
else if (strcmp(argv[i], "--nosound") == 0 || strcmp(argv[i], "-q") == 0)
|
|
{
|
|
use_sound = 0;
|
|
}
|
|
else if (strcmp(argv[i], "--sound") == 0)
|
|
{
|
|
use_sound = 1;
|
|
}
|
|
else if (strcmp(argv[i], "--locale") == 0 || strcmp(argv[i], "-L") == 0)
|
|
{
|
|
if (i < argc - 1)
|
|
{
|
|
snprintf(str, sizeof(str), "LANG=%s", argv[i + 1]);
|
|
putenv(str);
|
|
|
|
setlocale(LC_ALL, ""); /* argv[i + 1]) ? */
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* Forgot to specify the language! */
|
|
|
|
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
|
show_usage(stderr, (char *) getfilename(argv[0]));
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (strcmp(argv[i], "--lang") == 0 || strcmp(argv[i], "-l") == 0)
|
|
{
|
|
if (i < argc - 1)
|
|
{
|
|
if (langstr != NULL)
|
|
free(langstr);
|
|
|
|
langstr = strdup(argv[i + 1]);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* Forgot to specify the language! */
|
|
|
|
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
|
show_usage(stderr, (char *) getfilename(argv[0]));
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (strstr(argv[i], "--savedir") == argv[i])
|
|
{
|
|
if (i < argc - 1)
|
|
{
|
|
if (savedir != NULL)
|
|
free(savedir);
|
|
|
|
savedir = strdup(argv[i + 1]);
|
|
remove_slash(savedir);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* Forgot to specify the directory name! */
|
|
|
|
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
|
show_usage(stderr, (char *) getfilename(argv[0]));
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0)
|
|
{
|
|
show_version();
|
|
exit(0);
|
|
}
|
|
else if (strcmp(argv[i], "--copying") == 0 || strcmp(argv[i], "-c") == 0)
|
|
{
|
|
show_version();
|
|
printf(
|
|
"\n"
|
|
"This program is free software; you can redistribute it\n"
|
|
"and/or modify it under the terms of the GNU General Public\n"
|
|
"License as published by the Free Software Foundation;\n"
|
|
"either version 2 of the License, or (at your option) any\n"
|
|
"later version.\n"
|
|
"\n"
|
|
"This program is distributed in the hope that it will be\n"
|
|
"useful and entertaining, but WITHOUT ANY WARRANTY; without\n"
|
|
"even the implied warranty of MERCHANTABILITY or FITNESS\n"
|
|
"FOR A PARTICULAR PURPOSE. See the GNU General Public\n"
|
|
"License for more details.\n"
|
|
"\n"
|
|
"You should have received a copy of the GNU General Public\n"
|
|
"License along with this program; if not, write to the Free\n"
|
|
"Software Foundation, Inc., 59 Temple Place, Suite 330,\n"
|
|
"Boston, MA 02111-1307 USA\n"
|
|
"\n"
|
|
);
|
|
exit(0);
|
|
}
|
|
else if (strcmp(argv[i], "--help") == 0 ||
|
|
strcmp(argv[i], "-h") == 0)
|
|
{
|
|
printf("\n");
|
|
show_version();
|
|
printf("\n");
|
|
show_usage(stdout, (char *) getfilename(argv[0]));
|
|
|
|
printf(
|
|
"\n"
|
|
"See: " DOC_PREFIX "README.txt\n"
|
|
"\n");
|
|
exit(0);
|
|
}
|
|
else if (strcmp(argv[i], "--usage") == 0 ||
|
|
strcmp(argv[i], "-u") == 0)
|
|
{
|
|
show_usage(stdout, (char *) getfilename(argv[0]));
|
|
exit(0);
|
|
}
|
|
else if (strcmp(argv[i], "--nosysconfig") == 0)
|
|
{
|
|
debug("Not using system config.");
|
|
}
|
|
else
|
|
{
|
|
show_usage(stderr, (char *) getfilename(argv[0]));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
/* Set up language: */
|
|
|
|
if (langstr == NULL && getenv("LANG") != NULL &&
|
|
strncasecmp(getenv("LANG"), "lt_LT", 5) == 0)
|
|
{
|
|
langstr = strdup("lithuanian");
|
|
}
|
|
|
|
if (langstr == NULL && getenv("LANG") != NULL &&
|
|
strncasecmp(getenv("LANG"), "pl_PL", 5) == 0)
|
|
{
|
|
langstr = strdup("polish");
|
|
}
|
|
|
|
if (langstr != NULL)
|
|
{
|
|
if (strcmp(langstr, "english") == 0 ||
|
|
strcmp(langstr, "american-english") == 0)
|
|
{
|
|
putenv("LANG=C");
|
|
putenv("LC_ALL=C");
|
|
}
|
|
else if (strcmp(langstr, "catalan") == 0 ||
|
|
strcmp(langstr, "catala") == 0)
|
|
{
|
|
putenv("LANG=ca_ES");
|
|
putenv("LC_ALL=ca_ES");
|
|
}
|
|
else if (strcmp(langstr, "czech") == 0 ||
|
|
strcmp(langstr, "cesky") == 0)
|
|
{
|
|
putenv("LANG=cs_CZ");
|
|
putenv("LC_ALL=cs_CZ");
|
|
}
|
|
else if (strcmp(langstr, "danish") == 0 ||
|
|
strcmp(langstr, "dansk") == 0)
|
|
{
|
|
putenv("LANG=da_DK");
|
|
putenv("LC_ALL=da_DK");
|
|
}
|
|
else if (strcmp(langstr, "german") == 0 ||
|
|
strcmp(langstr, "deutsch") == 0)
|
|
{
|
|
putenv("LANG=de_DE@euro");
|
|
putenv("LC_ALL=de_DE@euro");
|
|
}
|
|
else if (strcmp(langstr, "greek") == 0)
|
|
{
|
|
putenv("LANG=el_GR.UTF8");
|
|
putenv("LC_ALL=el_GR.UTF8");
|
|
}
|
|
else if (strcmp(langstr, "british-english") == 0 ||
|
|
strcmp(langstr, "british") == 0)
|
|
{
|
|
putenv("LANG=en_GB");
|
|
putenv("LC_ALL=en_GB");
|
|
}
|
|
else if (strcmp(langstr, "spanish") == 0 ||
|
|
strcmp(langstr, "espanol") == 0)
|
|
{
|
|
putenv("LANG=es_ES@euro");
|
|
putenv("LC_ALL=es_ES@euro");
|
|
}
|
|
else if (strcmp(langstr, "finnish") == 0 ||
|
|
strcmp(langstr, "suomi") == 0)
|
|
{
|
|
putenv("LANG=fi_FI@euro");
|
|
putenv("LC_ALL=fi_FI@euro");
|
|
}
|
|
else if (strcmp(langstr, "french") == 0 ||
|
|
strcmp(langstr, "francais") == 0)
|
|
{
|
|
putenv("LANG=fr_FR@euro");
|
|
putenv("LC_ALL=fr_FR@euro");
|
|
}
|
|
else if (strcmp(langstr, "hebrew") == 0)
|
|
{
|
|
putenv("LANG=he_IL");
|
|
putenv("LC_ALL=he_IL");
|
|
}
|
|
else if (strcmp(langstr, "hungarian") == 0 ||
|
|
strcmp(langstr, "magyar") == 0)
|
|
{
|
|
putenv("LANG=hu_HU");
|
|
putenv("LC_ALL=hu_HU");
|
|
}
|
|
else if (strcmp(langstr, "indonesian") == 0 ||
|
|
strcmp(langstr, "bahasa-indonesia") == 0)
|
|
{
|
|
putenv("LANG=id_ID");
|
|
putenv("LC_ALL=id_ID");
|
|
}
|
|
else if (strcmp(langstr, "icelandic") == 0 ||
|
|
strcmp(langstr, "islenska") == 0)
|
|
{
|
|
putenv("LANG=is_IS");
|
|
putenv("LC_ALL=is_IS");
|
|
}
|
|
else if (strcmp(langstr, "italian") == 0 ||
|
|
strcmp(langstr, "italiano") == 0)
|
|
{
|
|
putenv("LANG=it_IT@euro");
|
|
putenv("LC_ALL=it_IT@euro");
|
|
}
|
|
else if (strcmp(langstr, "japanese") == 0)
|
|
{
|
|
putenv("LANG=ja_JP.UTF-8");
|
|
putenv("LC_ALL=ja_JP.UTF-8");
|
|
}
|
|
else if (strcmp(langstr, "korean") == 0)
|
|
{
|
|
putenv("LANG=ko_KR.UTF-8");
|
|
putenv("LC_ALL=ko_KR.UTF-8");
|
|
}
|
|
else if (strcmp(langstr, "lithuanian") == 0 ||
|
|
strcmp(langstr, "lietuviu") == 0)
|
|
{
|
|
putenv("LANG=lt_LT");
|
|
putenv("LC_ALL=lt_LT");
|
|
}
|
|
else if (strcmp(langstr, "dutch") == 0)
|
|
{
|
|
putenv("LANG=nl_NL@euro");
|
|
putenv("LC_ALL=nl_NL@euro");
|
|
}
|
|
else if (strcmp(langstr, "norwegian") == 0 ||
|
|
strcmp(langstr, "nynorsk") == 0 ||
|
|
strcmp(langstr, "norsk") == 0)
|
|
{
|
|
putenv("LANG=nn_NO");
|
|
putenv("LC_ALL=nn_NO");
|
|
}
|
|
else if (strcmp(langstr, "polish") == 0 ||
|
|
strcmp(langstr, "polski") == 0)
|
|
{
|
|
putenv("LANG=pl_PL");
|
|
putenv("LC_ALL=pl_PL");
|
|
}
|
|
else if (strcmp(langstr, "brazilian-portuguese") == 0 ||
|
|
strcmp(langstr, "portugues-brazilian") == 0 ||
|
|
strcmp(langstr, "brazilian") == 0)
|
|
{
|
|
putenv("LANG=pt_BR");
|
|
putenv("LC_ALL=pt_BR");
|
|
}
|
|
else if (strcmp(langstr, "portuguese") == 0 ||
|
|
strcmp(langstr, "portugues") == 0)
|
|
{
|
|
putenv("LANG=pt_PT@euro");
|
|
putenv("LC_ALL=pt_PT@euro");
|
|
}
|
|
else if (strcmp(langstr, "romanian") == 0)
|
|
{
|
|
putenv("LANG=ro_RO");
|
|
putenv("LC_ALL=ro_RO");
|
|
}
|
|
else if (strcmp(langstr, "slovak") == 0)
|
|
{
|
|
putenv("LANG=sk_SK");
|
|
putenv("LC_ALL=sk_SK");
|
|
}
|
|
else if (strcmp(langstr, "swedish") == 0 ||
|
|
strcmp(langstr, "svenska") == 0)
|
|
{
|
|
putenv("LANG=sv_SE@euro");
|
|
putenv("LC_ALL=sv_SE@euro");
|
|
}
|
|
else if (strcmp(langstr, "turkish") == 0)
|
|
{
|
|
putenv("LANG=tr_TR@euro");
|
|
putenv("LC_ALL=tr_TR@euro");
|
|
}
|
|
else if (strcmp(langstr, "chinese") == 0)
|
|
{
|
|
putenv("LANG=zh_CN.UTF-8");
|
|
putenv("LC_ALL=zh_CN.UTF-8");
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "%s is an invalid language\n", langstr);
|
|
show_usage(stderr, (char *) getfilename(argv[0]));
|
|
free(langstr);
|
|
exit(1);
|
|
}
|
|
|
|
setlocale(LC_ALL, "");
|
|
free(langstr);
|
|
}
|
|
|
|
|
|
bindtextdomain("tuxpaint", LOCALEDIR);
|
|
|
|
textdomain("tuxpaint");
|
|
|
|
language = current_language();
|
|
|
|
if (language == LANG_JA)
|
|
{
|
|
putenv("OUTPUT_CHARSET=ja_JP.UTF-8");
|
|
}
|
|
else if (language == LANG_HE)
|
|
{
|
|
putenv("OUTPUT_CHARSET=he_IL");
|
|
convert_open("ISO8859-8");
|
|
}
|
|
else if (language == LANG_PL)
|
|
{
|
|
putenv("OUTPUT_CHARSET=pl_PL.UTF-8");
|
|
convert_open("ISO8859-2");
|
|
}
|
|
else if (language == LANG_LT)
|
|
{
|
|
putenv("OUTPUT_CHARSET=lt_LT.UTF-8");
|
|
convert_open("ISO8859-13");
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
printf("DEBUG: Language is %s (%d)\n", lang_prefixes[language], language);
|
|
#endif
|
|
|
|
#ifndef WIN32
|
|
putenv("SDL_VIDEO_X11_WMCLASS=TuxPaint.TuxPaint");
|
|
#endif
|
|
|
|
|
|
/* Init SDL Video: */
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I could not initialize video!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Set-up Key-Repeat: */
|
|
|
|
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
|
|
SDL_DEFAULT_REPEAT_INTERVAL);
|
|
|
|
|
|
/* Init SDL Timer: */
|
|
|
|
if (SDL_Init(SDL_INIT_TIMER) < 0)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I could not initialize timer!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Init TTF stuff: */
|
|
|
|
if (TTF_Init() < 0)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I could not initialize the font (TTF) library!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
SDL_Quit();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Init SDL Audio and set-up Mixer: */
|
|
|
|
#ifndef NOSOUND
|
|
if (use_sound)
|
|
{
|
|
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: I could not initialize audio!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
use_sound = 0;
|
|
}
|
|
else
|
|
{
|
|
#ifndef WIN32
|
|
if (Mix_OpenAudio(44100, AUDIO_S16, 2, 256) < 0)
|
|
#else
|
|
if (Mix_OpenAudio(44100, AUDIO_S16, 2, 2048) < 0)
|
|
#endif
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: I could not set up audio for 44100 Hz "
|
|
"16-bit stereo.\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
use_sound = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Load sounds: */
|
|
|
|
for (i = 0; i < NUM_SOUNDS; i++)
|
|
{
|
|
sounds[i] = Mix_LoadWAV(sound_fnames[i]);
|
|
|
|
if (sounds[i] == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: I couldn't open a sound file:\n%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", sound_fnames[i], SDL_GetError());
|
|
use_sound = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Open Window: */
|
|
|
|
if (fullscreen)
|
|
{
|
|
#ifdef USE_HWSURFACE
|
|
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
|
|
16, SDL_FULLSCREEN | SDL_HWSURFACE);
|
|
#else
|
|
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
|
|
16, SDL_FULLSCREEN | SDL_SWSURFACE);
|
|
#endif
|
|
|
|
if (screen == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: I could not open the display in fullscreen mode.\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
fullscreen = 0;
|
|
}
|
|
}
|
|
|
|
|
|
if (!fullscreen)
|
|
{
|
|
#ifdef USE_HWSURFACE
|
|
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_HWSURFACE);
|
|
#else
|
|
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_SWSURFACE);
|
|
#endif
|
|
}
|
|
|
|
if (screen == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I could not open the display.\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
#if defined(WIN32) && defined(LARGE_CURSOR_FULLSCREEN_BUG)
|
|
if (fullscreen && no_fancy_cursors == 0)
|
|
{
|
|
fprintf(stderr, "Warning: An SDL bug causes the fancy cursors to leave\n"
|
|
"trails in fullscreen mode. Disabling fancy cursors.\n"
|
|
"(You can do this yourself with 'nofancycursors' option,\n"
|
|
"to avoid this warning in the future.)\n");
|
|
no_fancy_cursors = 1;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Create cursors: */
|
|
|
|
cursor_hand = get_cursor(hand_bits, hand_mask_bits,
|
|
hand_width, hand_height,
|
|
12, 1);
|
|
|
|
cursor_wand = get_cursor(wand_bits, wand_mask_bits,
|
|
wand_width, wand_height,
|
|
4, 4);
|
|
|
|
cursor_insertion = get_cursor(insertion_bits, insertion_mask_bits,
|
|
insertion_width, insertion_height,
|
|
7, 4);
|
|
|
|
cursor_brush = get_cursor(brush_bits, brush_mask_bits,
|
|
brush_width, brush_height,
|
|
4, 28);
|
|
|
|
cursor_crosshair = get_cursor(crosshair_bits, crosshair_mask_bits,
|
|
crosshair_width, crosshair_height,
|
|
15, 15);
|
|
|
|
cursor_rotate = get_cursor(rotate_bits, rotate_mask_bits,
|
|
rotate_width, rotate_height,
|
|
15, 15);
|
|
|
|
cursor_watch = get_cursor(watch_bits, watch_mask_bits,
|
|
watch_width, watch_height,
|
|
14, 14);
|
|
|
|
cursor_arrow = get_cursor(arrow_bits, arrow_mask_bits,
|
|
arrow_width, arrow_height,
|
|
0, 0);
|
|
|
|
cursor_up = get_cursor(up_bits, up_mask_bits,
|
|
up_width, up_height,
|
|
15, 1);
|
|
|
|
cursor_down = get_cursor(down_bits, down_mask_bits,
|
|
down_width, down_height,
|
|
15, 30);
|
|
|
|
cursor_tiny = get_cursor(tiny_bits, tiny_mask_bits,
|
|
tiny_width, tiny_height,
|
|
3, 3);
|
|
|
|
do_setcursor(cursor_watch);
|
|
|
|
|
|
|
|
/* Set window icon and caption: */
|
|
|
|
seticon();
|
|
SDL_WM_SetCaption("Tux Paint", "Tux Paint");
|
|
|
|
|
|
/* Create drawing canvas: */
|
|
|
|
canvas = SDL_CreateRGBSurface(screen->flags,
|
|
WINDOW_WIDTH - (96 * 2),
|
|
(48 * 7) + 40 + HEIGHTOFFSET,
|
|
screen->format->BitsPerPixel,
|
|
screen->format->Rmask,
|
|
screen->format->Gmask,
|
|
screen->format->Bmask,
|
|
0);
|
|
|
|
|
|
if (canvas == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't build drawing canvas!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
|
|
|
|
|
|
/* Create undo buffer space: */
|
|
|
|
for (i = 0; i < NUM_UNDO_BUFS; i++)
|
|
{
|
|
undo_bufs[i] = SDL_CreateRGBSurface(screen->flags,
|
|
WINDOW_WIDTH - (96 * 2),
|
|
(48 * 7) + 40 + HEIGHTOFFSET,
|
|
screen->format->BitsPerPixel,
|
|
screen->format->Rmask,
|
|
screen->format->Gmask,
|
|
screen->format->Bmask,
|
|
0);
|
|
|
|
|
|
if (undo_bufs[i] == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't build undo buffer! (%d of %d)\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", i + 1, NUM_UNDO_BUFS, SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
/* Load and display title image: */
|
|
|
|
img_title = loadimage(DATA_PREFIX "images/title.png");
|
|
img_progress = loadimage(DATA_PREFIX "images/ui/progress.png");
|
|
|
|
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
|
|
dest.x = (WINDOW_WIDTH - img_title->w) / 2;
|
|
dest.y = (WINDOW_HEIGHT - img_title->h);
|
|
|
|
SDL_BlitSurface(img_title, NULL, screen, &dest);
|
|
|
|
prog_bar_ctr = 0;
|
|
show_progress_bar();
|
|
|
|
SDL_Flip(screen);
|
|
|
|
|
|
/* Load other images: */
|
|
|
|
for (i = 0; i < NUM_TOOLS; i++)
|
|
img_tools[i] = loadimage(tool_img_fnames[i]);
|
|
|
|
img_title_on = loadimage(DATA_PREFIX "images/ui/title.png");
|
|
img_title_large_on = loadimage(DATA_PREFIX "images/ui/title_large.png");
|
|
img_title_off = loadimage(DATA_PREFIX "images/ui/no_title.png");
|
|
img_title_large_off = loadimage(DATA_PREFIX "images/ui/no_title_large.png");
|
|
|
|
img_btn_up = loadimage(DATA_PREFIX "images/ui/btn_up.png");
|
|
img_btn_down = loadimage(DATA_PREFIX "images/ui/btn_down.png");
|
|
img_btn_off = loadimage(DATA_PREFIX "images/ui/btn_off.png");
|
|
|
|
show_progress_bar();
|
|
|
|
img_yes = loadimage(DATA_PREFIX "images/ui/yes.png");
|
|
img_no = loadimage(DATA_PREFIX "images/ui/no.png");
|
|
|
|
img_open = loadimage(DATA_PREFIX "images/ui/open.png");
|
|
img_erase = loadimage(DATA_PREFIX "images/ui/erase.png");
|
|
img_back = loadimage(DATA_PREFIX "images/ui/back.png");
|
|
|
|
show_progress_bar();
|
|
|
|
|
|
/* FIXME: Hmm... how should we REALLY deal with this? */
|
|
#ifdef SVGA
|
|
img_cursor_up = loadimage(DATA_PREFIX "images/ui/cursor_up_large.png");
|
|
img_cursor_down = loadimage(DATA_PREFIX "images/ui/cursor_down_large.png");
|
|
#else
|
|
img_cursor_up = loadimage(DATA_PREFIX "images/ui/cursor_up.png");
|
|
img_cursor_down = loadimage(DATA_PREFIX "images/ui/cursor_down.png");
|
|
#endif
|
|
|
|
img_scroll_up = loadimage(DATA_PREFIX "images/ui/scroll_up.png");
|
|
img_scroll_down = loadimage(DATA_PREFIX "images/ui/scroll_down.png");
|
|
|
|
img_scroll_up_off = loadimage(DATA_PREFIX "images/ui/scroll_up_off.png");
|
|
img_scroll_down_off = loadimage(DATA_PREFIX "images/ui/scroll_down_off.png");
|
|
|
|
img_paintcan = loadimage(DATA_PREFIX "images/ui/paintcan.png");
|
|
|
|
show_progress_bar();
|
|
|
|
img_sparkles = loadimage(DATA_PREFIX "images/ui/sparkles.png");
|
|
|
|
|
|
/* Load brushes: */
|
|
|
|
#ifndef NOSOUND
|
|
loadarbitrary(img_brushes, NULL, NULL, NULL, &num_brushes, 0,
|
|
MAX_BRUSHES, DATA_PREFIX "brushes", 1, 40, 40);
|
|
#else
|
|
loadarbitrary(img_brushes, NULL, NULL, &num_brushes, 0,
|
|
MAX_BRUSHES, DATA_PREFIX "brushes", 1, 40, 40);
|
|
#endif
|
|
|
|
|
|
homedirdir = get_fname("brushes");
|
|
#ifndef NOSOUND
|
|
loadarbitrary(img_brushes, NULL, NULL, NULL, &num_brushes, num_brushes,
|
|
MAX_BRUSHES, homedirdir, 0, 40, 40);
|
|
#else
|
|
loadarbitrary(img_brushes, NULL, NULL, &num_brushes, num_brushes,
|
|
MAX_BRUSHES, homedirdir, 0, 40, 40);
|
|
#endif
|
|
|
|
if (num_brushes == 0)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: No brushes found in " DATA_PREFIX "brushes/\n"
|
|
"or %s\n\n", homedirdir);
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
free(homedirdir);
|
|
|
|
|
|
/* Load system fonts: */
|
|
|
|
font = TTF_OpenFont(DATA_PREFIX "fonts/default_font.ttf",
|
|
18 - (only_uppercase * 3));
|
|
|
|
if (font == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't load font file: "
|
|
DATA_PREFIX "fonts/default_font.ttf\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
large_font = TTF_OpenFont(DATA_PREFIX "fonts/default_font.ttf",
|
|
36 - (only_uppercase * 3));
|
|
|
|
if (large_font == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't load font file: "
|
|
DATA_PREFIX "fonts/default_font.ttf\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
small_font = TTF_OpenFont(DATA_PREFIX "fonts/default_font.ttf",
|
|
14 - (only_uppercase * 2));
|
|
|
|
if (small_font == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't load font file: "
|
|
DATA_PREFIX "fonts/default_font.ttf\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
if (need_own_font(language))
|
|
{
|
|
snprintf(str, sizeof(str), "%sfonts/locale/%s.ttf",
|
|
DATA_PREFIX, lang_prefixes[language]);
|
|
|
|
locale_font = TTF_OpenFont(str, 18);
|
|
|
|
if (locale_font == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Can't load font for this locale:\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n"
|
|
"Will use default (American English) instead.\n\n",
|
|
str, SDL_GetError());
|
|
|
|
|
|
/* Revert to default: */
|
|
|
|
putenv("LANG=C");
|
|
putenv("OUTPUT_CHARSET=C");
|
|
setlocale(LC_ALL, "C");
|
|
|
|
bindtextdomain("tuxpaint", LOCALEDIR);
|
|
textdomain("tuxpaint");
|
|
language = current_language();
|
|
}
|
|
}
|
|
|
|
|
|
if (locale_font == NULL)
|
|
locale_font = font;
|
|
|
|
|
|
/* Load other available fonts: */
|
|
|
|
num_fonts = 0;
|
|
|
|
loadfonts(DATA_PREFIX "fonts", 1);
|
|
|
|
homedirdir = get_fname("fonts");
|
|
loadfonts(homedirdir, 0);
|
|
free(homedirdir);
|
|
|
|
|
|
/* Load stamps: */
|
|
|
|
if (dont_load_stamps == 0)
|
|
{
|
|
#ifndef NOSOUND
|
|
loadarbitrary(img_stamps, txt_stamps, inf_stamps, snd_stamps, &num_stamps,
|
|
0, MAX_STAMPS, DATA_PREFIX "stamps", 0, -1, -1);
|
|
#else
|
|
loadarbitrary(img_stamps, txt_stamps, inf_stamps, &num_stamps,
|
|
0, MAX_STAMPS, DATA_PREFIX "stamps", 0, -1, -1);
|
|
#endif
|
|
|
|
|
|
homedirdir = get_fname("stamps");
|
|
#ifndef NOSOUND
|
|
loadarbitrary(img_stamps, txt_stamps, inf_stamps, snd_stamps,
|
|
&num_stamps, num_stamps,
|
|
MAX_STAMPS, homedirdir, 0, -1, -1);
|
|
#else
|
|
loadarbitrary(img_stamps, txt_stamps, inf_stamps, &num_stamps, num_stamps,
|
|
MAX_STAMPS, homedirdir, 0, -1, -1);
|
|
#endif
|
|
|
|
|
|
if (num_stamps == 0)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: No stamps found in " DATA_PREFIX "stamps/\n"
|
|
"or %s\n\n", homedirdir);
|
|
}
|
|
|
|
free(homedirdir);
|
|
|
|
|
|
/* Create stamp thumbnails: */
|
|
|
|
for (i = 0; i < num_stamps; i++)
|
|
{
|
|
if (img_stamps[i]->w > 40 ||
|
|
img_stamps[i]->h > 40)
|
|
{
|
|
img_stamp_thumbs[i] = thumbnail(img_stamps[i], 40, 40, 1);
|
|
}
|
|
else
|
|
{
|
|
img_stamp_thumbs[i] = NULL;
|
|
}
|
|
|
|
show_progress_bar();
|
|
}
|
|
}
|
|
|
|
|
|
/* Load magic icons: */
|
|
|
|
for (i = 0; i < NUM_MAGICS; i++)
|
|
{
|
|
img_magics[i] = loadimage(magic_img_fnames[i]);
|
|
show_progress_bar();
|
|
}
|
|
|
|
|
|
/* Load shape icons: */
|
|
|
|
for (i = 0; i < NUM_SHAPES; i++)
|
|
{
|
|
img_shapes[i] = loadimage(shape_img_fnames[i]);
|
|
show_progress_bar();
|
|
}
|
|
|
|
|
|
/* Load tip tux images: */
|
|
|
|
for (i = 0; i < NUM_TIP_TUX; i++)
|
|
{
|
|
img_tux[i] = loadimage(tux_img_fnames[i]);
|
|
show_progress_bar();
|
|
}
|
|
|
|
|
|
/* Create toolbox and selector labels: */
|
|
|
|
for (i = 0; i < NUM_TITLES; i++)
|
|
{
|
|
if (strlen(title_names[i]) > 0)
|
|
{
|
|
if (need_unicode(language) && locale_font != NULL &&
|
|
strcmp(gettext(title_names[i]), title_names[i]) != 0)
|
|
{
|
|
tmp_surf = TTF_RenderUNICODE_Blended(locale_font,
|
|
(Uint16 *) textdir(gettext(title_names[i])),
|
|
black);
|
|
img_title_names[i] = thumbnail(tmp_surf,
|
|
min(84, tmp_surf->w), tmp_surf->h, 0);
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
else if (need_utf8(language) && locale_font != NULL &&
|
|
strcmp(gettext(title_names[i]), title_names[i]) != 0)
|
|
{
|
|
tmp_surf = TTF_RenderUTF8_Blended(locale_font,
|
|
textdir(gettext(title_names[i])), black);
|
|
img_title_names[i] = thumbnail(tmp_surf,
|
|
min(84, tmp_surf->w), tmp_surf->h, 0);
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
else
|
|
{
|
|
upstr = uppercase(textdir(gettext(title_names[i])));
|
|
tmp_surf = RENDER_TEXT(large_font, upstr, black);
|
|
img_title_names[i] = thumbnail(tmp_surf,
|
|
min(84, tmp_surf->w), tmp_surf->h, 0);
|
|
SDL_FreeSurface(tmp_surf);
|
|
free(upstr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
img_title_names[i] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Generate color selection buttons: */
|
|
|
|
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
|
|
|
/* Create appropriately-shaped buttons: */
|
|
|
|
tmp_btn = thumbnail(img_btn_down, (WINDOW_WIDTH - 96) / NUM_COLORS, 48, 0);
|
|
|
|
|
|
/* Create surfaces to draw them into: */
|
|
|
|
for (i = 0; i < NUM_COLORS; i++)
|
|
{
|
|
|
|
img_color_btns[i] = SDL_CreateRGBSurface(screen->flags,
|
|
/* (WINDOW_WIDTH - 96) / NUM_COLORS, 48, */
|
|
tmp_btn->w, tmp_btn->h,
|
|
screen->format->BitsPerPixel,
|
|
screen->format->Rmask,
|
|
screen->format->Gmask,
|
|
screen->format->Bmask,
|
|
0);
|
|
|
|
if (img_color_btns[i] == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't build color button! (%d of %d)\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", i + 1, NUM_COLORS, SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
SDL_LockSurface(img_color_btns[i]);
|
|
}
|
|
|
|
|
|
/* Generate the buttons based on the thumbnails: */
|
|
|
|
|
|
SDL_LockSurface(tmp_btn);
|
|
|
|
for (y = 0; y < tmp_btn->h /* 48 */; y++)
|
|
{
|
|
for (x = 0; x < tmp_btn->w /* (WINDOW_WIDTH - 96) / NUM_COLORS */; x++)
|
|
{
|
|
SDL_GetRGB(getpixel(tmp_btn, x, y), tmp_btn->format,
|
|
&r, &g, &b);
|
|
a = 255 - ((r + g + b) / 3);
|
|
|
|
for (i = 0; i < NUM_COLORS; i++)
|
|
{
|
|
putpixel(img_color_btns[i], x, y,
|
|
SDL_MapRGB(img_color_btns[i]->format,
|
|
alpha(color_hexes[i][0], 255, a),
|
|
alpha(color_hexes[i][1], 255, a),
|
|
alpha(color_hexes[i][2], 255, a)));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NUM_COLORS; i++)
|
|
SDL_UnlockSurface(img_color_btns[i]);
|
|
|
|
SDL_UnlockSurface(tmp_btn);
|
|
SDL_FreeSurface(tmp_btn);
|
|
|
|
#endif
|
|
|
|
create_button_labels();
|
|
|
|
|
|
/* Seed random-number generator: */
|
|
|
|
srand(SDL_GetTicks());
|
|
|
|
|
|
SDL_EnableUNICODE(1);
|
|
|
|
|
|
/* Seed random-number generator: */
|
|
|
|
srand(SDL_GetTicks());
|
|
}
|
|
|
|
|
|
/* Render a button label using the appropriate string/font: */
|
|
|
|
SDL_Surface * do_render_button_label(char * label)
|
|
{
|
|
char * str;
|
|
SDL_Surface * tmp_surf, * surf;
|
|
SDL_Color black = {0, 0, 0, 0};
|
|
|
|
if (need_unicode(language) && locale_font != NULL &&
|
|
strcmp(gettext(label), label) != 0)
|
|
{
|
|
tmp_surf = TTF_RenderUNICODE_Blended(locale_font, (Uint16 *) textdir(gettext(label)),
|
|
black);
|
|
surf = thumbnail(tmp_surf, min(48, tmp_surf->w), tmp_surf->h, 0);
|
|
}
|
|
else if (need_utf8(language) && locale_font != NULL &&
|
|
strcmp(gettext(label), label) != 0)
|
|
{
|
|
tmp_surf = TTF_RenderUTF8_Blended(locale_font, textdir(gettext(label)), black);
|
|
surf = thumbnail(tmp_surf, min(48, tmp_surf->w), tmp_surf->h, 0);
|
|
}
|
|
else
|
|
{
|
|
str = uppercase(textdir(gettext(label)));
|
|
tmp_surf = RENDER_TEXT(small_font, str, black);
|
|
surf = thumbnail(tmp_surf, min(48, tmp_surf->w), tmp_surf->h, 0);
|
|
free(str);
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
|
|
return surf;
|
|
}
|
|
|
|
static void create_button_labels(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_TOOLS; i++)
|
|
img_tool_names[i] = do_render_button_label(tool_names[i]);
|
|
|
|
for (i = 0; i < NUM_MAGICS; i++)
|
|
img_magic_names[i] = do_render_button_label(magic_names[i]);
|
|
|
|
for (i = 0; i < NUM_SHAPES; i++)
|
|
img_shape_names[i] = do_render_button_label(shape_names[i]);
|
|
|
|
|
|
/* 'Open' label: */
|
|
|
|
img_openlabels_open = do_render_button_label(textdir(gettext_noop("Open")));
|
|
|
|
|
|
/* 'Erase' label: */
|
|
|
|
img_openlabels_erase = do_render_button_label(textdir(gettext_noop("Erase")));
|
|
|
|
|
|
/* 'Back' label: */
|
|
|
|
img_openlabels_back = do_render_button_label(textdir(gettext_noop("Back")));
|
|
}
|
|
|
|
|
|
void seticon(void)
|
|
{
|
|
int masklen;
|
|
Uint8 * mask;
|
|
SDL_Surface * icon;
|
|
|
|
|
|
/* Load icon into a surface: */
|
|
|
|
#ifndef WIN32
|
|
icon = IMG_Load(DATA_PREFIX "images/icon.png");
|
|
#else
|
|
icon = IMG_Load(DATA_PREFIX "images/icon-win32.png");
|
|
#endif
|
|
|
|
if (icon == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: I could not load the icon image: %s\n"
|
|
"The Simple DirectMedia error that occurred was:\n"
|
|
"%s\n\n", DATA_PREFIX "images/icon.png", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
|
|
/* Create mask: */
|
|
|
|
masklen = (((icon -> w) + 7) / 8) * (icon -> h);
|
|
mask = malloc(masklen * sizeof(Uint8));
|
|
memset(mask, 0xFF, masklen);
|
|
|
|
|
|
/* Set icon: */
|
|
|
|
SDL_WM_SetIcon(icon, mask);
|
|
|
|
|
|
/* Free icon surface & mask: */
|
|
|
|
free(mask);
|
|
SDL_FreeSurface(icon);
|
|
|
|
|
|
/* Grab keyboard and mouse, if requested: */
|
|
|
|
if (grab_input)
|
|
{
|
|
debug("Grabbing input!");
|
|
SDL_WM_GrabInput(SDL_GRAB_ON);
|
|
}
|
|
}
|
|
|
|
|
|
/* Load a mouse pointer (cursor) shape: */
|
|
|
|
SDL_Cursor * get_cursor(char * bits, char * mask_bits,
|
|
int width, int height, int x, int y)
|
|
{
|
|
Uint8 b;
|
|
Uint8 temp_bitmap[128], temp_bitmask[128];
|
|
int i;
|
|
|
|
|
|
if (((width + 7) / 8) * height > 128)
|
|
{
|
|
fprintf(stderr, "Error: Cursor is too large!\n");
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
for (i = 0; i < ((width + 7) / 8) * height; i++)
|
|
{
|
|
b = bits[i];
|
|
|
|
temp_bitmap[i] = (((b & 0x01) << 7) |
|
|
((b & 0x02) << 5) |
|
|
((b & 0x04) << 3) |
|
|
((b & 0x08) << 1) |
|
|
((b & 0x10) >> 1) |
|
|
((b & 0x20) >> 3) |
|
|
((b & 0x40) >> 5) |
|
|
((b & 0x80) >> 7));
|
|
|
|
b = mask_bits[i];
|
|
|
|
temp_bitmask[i] = (((b & 0x01) << 7) |
|
|
((b & 0x02) << 5) |
|
|
((b & 0x04) << 3) |
|
|
((b & 0x08) << 1) |
|
|
((b & 0x10) >> 1) |
|
|
((b & 0x20) >> 3) |
|
|
((b & 0x40) >> 5) |
|
|
((b & 0x80) >> 7));
|
|
}
|
|
|
|
return(SDL_CreateCursor(temp_bitmap, temp_bitmask, width, height, x, y));
|
|
}
|
|
|
|
|
|
/* Load an image: */
|
|
|
|
SDL_Surface * loadimage(char * fname)
|
|
{
|
|
SDL_Surface * s, * disp_fmt_s;
|
|
|
|
|
|
/* Load the image file: */
|
|
|
|
s = IMG_Load(fname);
|
|
if (s == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I couldn't load a graphics file:\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", fname, SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Convert to the display format: */
|
|
|
|
disp_fmt_s = SDL_DisplayFormatAlpha(s);
|
|
if (disp_fmt_s == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I couldn't convert a graphics file:\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", fname, SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Free the temp. surface & return the converted one: */
|
|
|
|
SDL_FreeSurface(s);
|
|
|
|
return(disp_fmt_s);
|
|
}
|
|
|
|
|
|
/* Draw the toolbar: */
|
|
|
|
void draw_toolbar(void)
|
|
{
|
|
int i;
|
|
SDL_Rect dest;
|
|
|
|
|
|
/* FIXME: A hack to make 'Print' button act just like 'New' button: */
|
|
|
|
if (!disable_print)
|
|
tool_avail[TOOL_PRINT] = tool_avail[TOOL_NEW];
|
|
|
|
|
|
draw_image_title(TITLE_TOOLS, 0);
|
|
|
|
for (i = 0; i < NUM_TOOLS + TOOLOFFSET; i++)
|
|
{
|
|
dest.x = ((i % 2) * 48);
|
|
dest.y = ((i / 2) * 48) + 40;
|
|
|
|
|
|
if (i < NUM_TOOLS)
|
|
{
|
|
if (i == cur_tool)
|
|
{
|
|
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
|
}
|
|
else if (tool_avail[i])
|
|
{
|
|
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
|
|
dest.x = ((i % 2) * 48) + 4;
|
|
dest.y = ((i / 2) * 48) + 40 + 4;
|
|
|
|
SDL_BlitSurface(img_tools[i], NULL, screen, &dest);
|
|
|
|
|
|
dest.x = ((i % 2) * 48) + 4 + (40 - img_tool_names[i]->w) / 2;
|
|
dest.y = ((i / 2) * 48) + 40 + 4 + (44 - img_tool_names[i]->h);
|
|
|
|
SDL_BlitSurface(img_tool_names[i], NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw magic controls: */
|
|
|
|
void draw_magic(void)
|
|
{
|
|
int i;
|
|
SDL_Rect dest;
|
|
|
|
|
|
/* FIXME: Should we worry about more than 14 magic effects? :^/ */
|
|
|
|
|
|
draw_image_title(TITLE_MAGIC, WINDOW_WIDTH - 96);
|
|
|
|
for (i = 0; i < 14 + TOOLOFFSET; i++)
|
|
{
|
|
dest.x = WINDOW_WIDTH - 96 + ((i % 2) * 48);
|
|
dest.y = ((i / 2) * 48) + 40;
|
|
|
|
|
|
if (i < NUM_MAGICS)
|
|
{
|
|
if (i == cur_magic)
|
|
{
|
|
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
|
}
|
|
|
|
dest.x = WINDOW_WIDTH - 96 + ((i % 2) * 48) + 4;
|
|
dest.y = ((i / 2) * 48) + 40 + 4;
|
|
|
|
SDL_BlitSurface(img_magics[i], NULL, screen, &dest);
|
|
|
|
|
|
dest.x = WINDOW_WIDTH - 96 + ((i % 2) * 48) + 4 +
|
|
(40 - img_magic_names[i]->w) / 2;
|
|
dest.y = ((i / 2) * 48) + 40 + 4 + (44 - img_magic_names[i]->h);
|
|
|
|
SDL_BlitSurface(img_magic_names[i], NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw color selector: */
|
|
|
|
void draw_colors(int enabled)
|
|
{
|
|
int i;
|
|
SDL_Rect dest;
|
|
|
|
dest.x = 0;
|
|
dest.y = 40 + ((NUM_TOOLS / 2) * 48) + HEIGHTOFFSET;
|
|
|
|
if (enabled)
|
|
{
|
|
SDL_BlitSurface(img_title_large_on, NULL, screen, &dest);
|
|
|
|
dest.x = 0;
|
|
dest.y = 40 + ((NUM_TOOLS / 2) * 48) + HEIGHTOFFSET;
|
|
SDL_BlitSurface(img_title_large_on, NULL, screen, &dest);
|
|
|
|
dest.x = (96 - img_title_names[TITLE_COLORS]->w) / 2;
|
|
dest.y = (40 + ((NUM_TOOLS / 2) * 48) + HEIGHTOFFSET +
|
|
(48 - img_title_names[TITLE_COLORS]->h) / 2);
|
|
SDL_BlitSurface(img_title_names[TITLE_COLORS], NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_title_large_off, NULL, screen, &dest);
|
|
}
|
|
|
|
for (i = 0; i < NUM_COLORS; i++)
|
|
{
|
|
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
|
dest.x = (i * ((WINDOW_WIDTH - 96) / NUM_COLORS)) + 96;
|
|
dest.y = 40 + ((NUM_TOOLS / 2) * 48) + HEIGHTOFFSET;
|
|
|
|
if (enabled)
|
|
SDL_BlitSurface(img_color_btns[i], NULL, screen, &dest);
|
|
else
|
|
SDL_BlitSurface(img_color_btns[COLOR_WHITE], NULL, screen, &dest);
|
|
#else
|
|
dest.x = (i * ((WINDOW_WIDTH - 96) / NUM_COLORS)) + 96;
|
|
dest.y = 40 + ((NUM_TOOLS / 2) * 48) + HEIGHTOFFSET;
|
|
dest.w = ((WINDOW_WIDTH - 96) / NUM_COLORS);
|
|
dest.h = 48 + HEIGHTOFFSET;
|
|
|
|
if (enabled)
|
|
SDL_FillRect(screen, &dest,
|
|
SDL_MapRGB(screen->format,
|
|
color_hexes[i][0],
|
|
color_hexes[i][1],
|
|
color_hexes[i][2]));
|
|
else
|
|
SDL_FillRect(screen, &dest,
|
|
SDL_MapRGB(screen->format, 240, 240, 240));
|
|
#endif
|
|
|
|
if (i == cur_color && enabled)
|
|
{
|
|
dest.x = (i * ((WINDOW_WIDTH - 96) / NUM_COLORS)) + 96;
|
|
dest.y = 40 + ((NUM_TOOLS / 2) * 48) + HEIGHTOFFSET;
|
|
|
|
SDL_BlitSurface(img_paintcan, NULL, screen, &dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw brushes: */
|
|
|
|
void draw_brushes(void)
|
|
{
|
|
int i, off_y, max, brush;
|
|
SDL_Rect dest;
|
|
|
|
|
|
/* Draw the title: */
|
|
|
|
draw_image_title(TITLE_BRUSHES, WINDOW_WIDTH - 96);
|
|
|
|
|
|
/* Do we need scrollbars? */
|
|
|
|
if (num_brushes > 14 + TOOLOFFSET)
|
|
{
|
|
off_y = 24;
|
|
max = 12;
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 40;
|
|
|
|
if (brush_scroll > 0)
|
|
{
|
|
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
|
}
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
|
|
|
if (brush_scroll < num_brushes - 12 - TOOLOFFSET)
|
|
{
|
|
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
off_y = 0;
|
|
max = 14 + TOOLOFFSET;
|
|
}
|
|
|
|
|
|
/* Draw each of the shown brushes: */
|
|
|
|
for (brush = brush_scroll;
|
|
brush < brush_scroll + max;
|
|
brush++)
|
|
{
|
|
i = brush - brush_scroll;
|
|
|
|
|
|
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
|
dest.y = ((i / 2) * 48) + 40 + off_y;
|
|
|
|
if (brush == cur_brush)
|
|
{
|
|
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
|
}
|
|
else if (brush < num_brushes)
|
|
{
|
|
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
|
|
if (brush < num_brushes)
|
|
{
|
|
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96) +
|
|
((48 - (img_brushes[brush]->w)) / 2);
|
|
|
|
/* FIXME: Shouldn't that be ->h??? */
|
|
|
|
dest.y = ((i / 2) * 48) + 40 + ((48 - (img_brushes[brush]->w)) / 2) +
|
|
off_y;
|
|
|
|
SDL_BlitSurface(img_brushes[brush], NULL, screen, &dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw fonts: */
|
|
|
|
void draw_fonts(void)
|
|
{
|
|
int i, off_y, max, font;
|
|
SDL_Rect dest, src;
|
|
SDL_Surface * tmp_surf;
|
|
SDL_Color black = {0, 0, 0, 0};
|
|
|
|
|
|
/* Draw the title: */
|
|
|
|
draw_image_title(TITLE_LETTERS, WINDOW_WIDTH - 96);
|
|
|
|
|
|
/* Do we need scrollbars? */
|
|
|
|
if (num_fonts > 14 + TOOLOFFSET)
|
|
{
|
|
off_y = 24;
|
|
max = 12 + TOOLOFFSET;
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 40;
|
|
|
|
if (font_scroll > 0)
|
|
{
|
|
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
|
}
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
|
|
|
if (font_scroll < num_fonts - 12 - TOOLOFFSET)
|
|
{
|
|
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
off_y = 0;
|
|
max = 14 + TOOLOFFSET;
|
|
}
|
|
|
|
|
|
/* Draw each of the shown fonts: */
|
|
|
|
for (font = font_scroll;
|
|
font < font_scroll + max;
|
|
font++)
|
|
{
|
|
i = font - font_scroll;
|
|
|
|
|
|
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
|
dest.y = ((i / 2) * 48) + 40 + off_y;
|
|
|
|
if (font == cur_font)
|
|
{
|
|
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
|
}
|
|
else if (font < num_fonts)
|
|
{
|
|
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
|
|
|
|
/* FIXME: We should render the font buttons once, at startup! */
|
|
|
|
if (font < num_fonts)
|
|
{
|
|
tmp_surf = RENDER_TEXT(fonts[font], "A", black);
|
|
|
|
src.x = (tmp_surf->w - 48) / 2;
|
|
src.y = (tmp_surf->h - 48) / 2;
|
|
src.w = 48;
|
|
src.h = 48;
|
|
|
|
if (src.x < 0)
|
|
src.x = 0;
|
|
if (src.y < 0)
|
|
src.y = 0;
|
|
|
|
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
|
if (src.w > tmp_surf->w)
|
|
{
|
|
src.w = tmp_surf->w;
|
|
dest.x = dest.x + ((48 - (tmp_surf->w)) / 2);
|
|
}
|
|
|
|
|
|
dest.y = ((i / 2) * 48) + 40 + off_y;
|
|
if (src.h > tmp_surf->h)
|
|
{
|
|
src.h = tmp_surf->h;
|
|
dest.y = dest.y + ((48 - (tmp_surf->h)) / 2);
|
|
}
|
|
|
|
SDL_BlitSurface(tmp_surf, &src, screen, &dest);
|
|
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw stamps: */
|
|
|
|
void draw_stamps(void)
|
|
{
|
|
int i, off_y, max, stamp;
|
|
SDL_Rect dest;
|
|
SDL_Surface * img;
|
|
|
|
|
|
/* Draw the title: */
|
|
|
|
draw_image_title(TITLE_STAMPS, WINDOW_WIDTH - 96);
|
|
|
|
|
|
/* Do we need scrollbars? */
|
|
|
|
if (num_stamps > 14 + TOOLOFFSET)
|
|
{
|
|
off_y = 24;
|
|
max = 12 + TOOLOFFSET;
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 40;
|
|
|
|
if (stamp_scroll > 0)
|
|
{
|
|
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
|
}
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
|
|
|
if (stamp_scroll < num_stamps - 12 - TOOLOFFSET)
|
|
{
|
|
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
off_y = 0;
|
|
max = 14 + TOOLOFFSET;
|
|
}
|
|
|
|
|
|
/* Draw each of the shown stamps: */
|
|
|
|
for (stamp = stamp_scroll;
|
|
stamp < stamp_scroll + max;
|
|
stamp++)
|
|
{
|
|
i = stamp - stamp_scroll;
|
|
|
|
|
|
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
|
dest.y = ((i / 2) * 48) + 40 + off_y;
|
|
|
|
if (stamp == cur_stamp)
|
|
{
|
|
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
|
}
|
|
else if (stamp < num_stamps)
|
|
{
|
|
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
|
|
if (stamp < num_stamps)
|
|
{
|
|
if (img_stamp_thumbs[stamp] != NULL)
|
|
img = img_stamp_thumbs[stamp];
|
|
else
|
|
img = img_stamps[stamp];
|
|
|
|
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96) +
|
|
((48 - (img->w)) / 2);
|
|
|
|
dest.y = ((i / 2) * 48) + 40 + ((48 - (img->h)) / 2) + off_y;
|
|
|
|
SDL_BlitSurface(img, NULL, screen, &dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw the shape selector: */
|
|
|
|
void draw_shapes(void)
|
|
{
|
|
int i;
|
|
SDL_Rect dest;
|
|
|
|
|
|
draw_image_title(TITLE_SHAPES, WINDOW_WIDTH - 96);
|
|
|
|
for (i = 0; i < 14 + TOOLOFFSET; i++)
|
|
{
|
|
dest.x = ((i % 2) * 48) + WINDOW_WIDTH - 96;
|
|
dest.y = ((i / 2) * 48) + 40;
|
|
|
|
|
|
if (i == cur_shape)
|
|
{
|
|
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
|
}
|
|
else if (i < NUM_SHAPES)
|
|
{
|
|
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
|
}
|
|
else
|
|
{
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
|
|
|
|
if (i < NUM_SHAPES)
|
|
{
|
|
dest.x = ((i % 2) * 48) + 4 + WINDOW_WIDTH - 96;
|
|
dest.y = ((i / 2) * 48) + 40 + 4;
|
|
|
|
SDL_BlitSurface(img_shapes[i], NULL, screen, &dest);
|
|
|
|
dest.x = ((i % 2) * 48) + 4 + WINDOW_WIDTH - 96 +
|
|
(40 - img_shape_names[i]->w) / 2;
|
|
dest.y = ((i / 2) * 48) + 40 + 4 + (44 - img_shape_names[i]->h);
|
|
|
|
SDL_BlitSurface(img_shape_names[i], NULL, screen, &dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Draw no selectables: */
|
|
|
|
void draw_none(void)
|
|
{
|
|
int i;
|
|
SDL_Rect dest;
|
|
|
|
dest.x = WINDOW_WIDTH - 96;
|
|
dest.y = 0;
|
|
SDL_BlitSurface(img_title_off, NULL, screen, &dest);
|
|
|
|
for (i = 0; i < 14 + TOOLOFFSET; i++)
|
|
{
|
|
dest.x = ((i % 2) * 48) + WINDOW_WIDTH - 96;
|
|
dest.y = ((i / 2) * 48) + 40;
|
|
|
|
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
|
}
|
|
}
|
|
|
|
|
|
/* Load an arbitrary set of images into an array (e.g., brushes or stamps) */
|
|
|
|
#ifndef NOSOUND
|
|
void loadarbitrary(SDL_Surface * surfs[], char * descs[], info_type * infs[],
|
|
Mix_Chunk * sounds[],
|
|
int * count, int starting, int max,
|
|
char * dir, int fatal, int maxw, int maxh)
|
|
#else
|
|
void loadarbitrary(SDL_Surface * surfs[], char * descs[], info_type * infs[],
|
|
int * count, int starting, int max,
|
|
char * dir, int fatal, int maxw, int maxh)
|
|
#endif
|
|
{
|
|
DIR * d;
|
|
struct dirent * f;
|
|
struct stat sbuf;
|
|
char fname[512];
|
|
char * d_names[MAX_FILES];
|
|
int num_files, i;
|
|
|
|
|
|
*count = starting;
|
|
|
|
/* Open the directory: */
|
|
|
|
d = opendir(dir);
|
|
if (d == NULL)
|
|
{
|
|
if (fatal)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I can't find a directory of images\n"
|
|
"%s\n"
|
|
"The system error that occurred was: %s\n",
|
|
dir, strerror(errno));
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/* Read directory for images: */
|
|
|
|
num_files = 0;
|
|
do
|
|
{
|
|
f = readdir(d);
|
|
|
|
if (f != NULL)
|
|
{
|
|
d_names[num_files] = strdup(f->d_name);
|
|
num_files++;
|
|
}
|
|
}
|
|
while (f != NULL && num_files < MAX_FILES);
|
|
|
|
closedir(d);
|
|
|
|
|
|
qsort(d_names, num_files, sizeof(char *),
|
|
(int(*)(const void *, const void *))compare_strings);
|
|
|
|
|
|
/* Do something with each file (load if PNG, recurse if directory): */
|
|
|
|
for (i = 0; i < num_files && *count < max; i++)
|
|
{
|
|
/* Ignore things starting with "." (e.g., "." and ".." dirs): */
|
|
|
|
if (strstr(d_names[i], ".") != d_names[i])
|
|
{
|
|
/* If it's a directory, recurse down into it: */
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s", dir, d_names[i]);
|
|
debug(fname);
|
|
|
|
stat(fname, &sbuf);
|
|
|
|
if (S_ISDIR(sbuf.st_mode))
|
|
{
|
|
debug("...is a directory");
|
|
|
|
#ifndef NOSOUND
|
|
loadarbitrary(surfs, descs, infs, sounds, count, *count, max, fname,
|
|
fatal, maxw, maxh);
|
|
#else
|
|
loadarbitrary(surfs, descs, infs, count, *count, max, fname,
|
|
fatal, maxw, maxh);
|
|
#endif
|
|
}
|
|
else if (strstr(d_names[i], ".png") != NULL)
|
|
{
|
|
/* If it has ".png" in the filename, assume we can try to load it: */
|
|
|
|
surfs[*count] = loadimage(fname);
|
|
|
|
if ((surfs[*count]->w <= maxw &&
|
|
surfs[*count]->h <= maxh) ||
|
|
(maxw == -1 || maxh == -1))
|
|
{
|
|
/* Check for a companion ".txt" file! */
|
|
|
|
if (descs != NULL)
|
|
descs[*count] = loaddesc(fname);
|
|
|
|
if (infs != NULL)
|
|
infs[*count] = loadinfo(fname);
|
|
|
|
|
|
#ifndef NOSOUND
|
|
if (use_sound)
|
|
{
|
|
if (sounds != NULL)
|
|
sounds[*count] = loadsound(fname);
|
|
}
|
|
#endif
|
|
|
|
|
|
*count = *count + 1;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Image too large (%d x %d - max: %d x %d)\n"
|
|
"%s\n\n",
|
|
surfs[*count]->w, surfs[*count]->h, maxw, maxh,
|
|
fname);
|
|
}
|
|
|
|
show_progress_bar();
|
|
}
|
|
}
|
|
|
|
free(d_names[i]);
|
|
}
|
|
|
|
|
|
/* Give warning if too many files were found (e.g., some not loaded): */
|
|
|
|
if (*count == max)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Reached maximum images (%d) which can be stored in:\n"
|
|
"%s\n\n",
|
|
max, dir);
|
|
}
|
|
|
|
debug("loadarbitrary() ends...");
|
|
}
|
|
|
|
|
|
/* Create a thumbnail: */
|
|
|
|
SDL_Surface * thumbnail(SDL_Surface * src, int max_x, int max_y,
|
|
int keep_aspect)
|
|
{
|
|
int x, y, src_x, src_y, off_x, off_y;
|
|
SDL_Surface * s;
|
|
Uint32 amask, tr, tg, tb, ta;
|
|
Uint8 r, g, b, a;
|
|
float xscale, yscale;
|
|
int tmp;
|
|
|
|
|
|
/* Determine scale and centering offsets: */
|
|
|
|
if (!keep_aspect)
|
|
{
|
|
yscale = (float) ((float) src->h / (float) max_y);
|
|
xscale = (float) ((float) src->w / (float) max_x);
|
|
|
|
// off_x = ((src->h / yscale) - (src->w / xscale)) / 2;
|
|
// off_y = ((src->w / xscale) - (src->h / yscale)) / 2;
|
|
|
|
off_x = 0;
|
|
off_y = 0;
|
|
}
|
|
else
|
|
{
|
|
if (src->h > src->w)
|
|
{
|
|
yscale = (float) ((float) src->h / (float) max_y);
|
|
xscale = yscale;
|
|
off_x = ((src->h - src->w) / xscale) / 2;
|
|
off_y = 0;
|
|
}
|
|
else
|
|
{
|
|
xscale = (float) ((float) src->w / (float) max_x);
|
|
yscale = xscale;
|
|
off_x = 0;
|
|
off_y = ((src->w - src->h) / xscale) / 2;
|
|
}
|
|
}
|
|
|
|
|
|
/* Create surface for thumbnail: */
|
|
|
|
amask = ~(src->format->Rmask |
|
|
src->format->Gmask |
|
|
src->format->Bmask);
|
|
|
|
s = SDL_CreateRGBSurface(SDL_SWSURFACE,
|
|
max_x, max_y,
|
|
src->format->BitsPerPixel,
|
|
src->format->Rmask,
|
|
src->format->Gmask,
|
|
src->format->Bmask,
|
|
amask);
|
|
|
|
|
|
if (s == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't build stamp thumbnails\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Create thumbnail itself: */
|
|
|
|
SDL_LockSurface(src);
|
|
SDL_LockSurface(s);
|
|
|
|
for (y = 0; y < max_y; y++)
|
|
{
|
|
for (x = 0; x < max_x; x++)
|
|
{
|
|
#ifndef LOW_QUALITY_THUMBNAILS
|
|
tr = 0;
|
|
tg = 0;
|
|
tb = 0;
|
|
ta = 0;
|
|
|
|
tmp = 0;
|
|
|
|
for (src_y = y * yscale; src_y < y * yscale + yscale &&
|
|
src_y < src->h; src_y++)
|
|
{
|
|
for (src_x = x * xscale; src_x < x * xscale + xscale &&
|
|
src_x < src->w; src_x++)
|
|
{
|
|
SDL_GetRGBA(getpixel(src, src_x, src_y),
|
|
src->format,
|
|
&r, &g, &b, &a);
|
|
|
|
tr = tr + r;
|
|
tb = tb + b;
|
|
tg = tg + g;
|
|
ta = ta + a;
|
|
|
|
tmp++;
|
|
}
|
|
}
|
|
|
|
if (tmp != 0)
|
|
{
|
|
tr = tr / tmp;
|
|
tb = tb / tmp;
|
|
tg = tg / tmp;
|
|
ta = ta / tmp;
|
|
|
|
putpixel(s, x + off_x, y + off_y, SDL_MapRGBA(s->format,
|
|
(Uint8) tr,
|
|
(Uint8) tg,
|
|
(Uint8) tb,
|
|
(Uint8) ta));
|
|
}
|
|
#else
|
|
src_x = x * xscale;
|
|
src_y = y * yscale;
|
|
|
|
putpixel(s, x + off_x, y + off_y, getpixel(src, src_x, src_y));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(s);
|
|
SDL_UnlockSurface(src);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/* Get a pixel: */
|
|
|
|
Uint32 getpixel(SDL_Surface * surface, int x, int y)
|
|
{
|
|
int bpp;
|
|
Uint8 * p;
|
|
Uint32 pixel;
|
|
|
|
pixel = 0;
|
|
|
|
|
|
/* Assuming the X/Y values are within the bounds of this surface... */
|
|
|
|
if (x >= 0 && y >= 0 && x < surface -> w && y < surface -> h)
|
|
{
|
|
/* SDL_LockSurface(surface); */
|
|
|
|
|
|
/* Determine bytes-per-pixel for the surface in question: */
|
|
|
|
bpp = surface->format->BytesPerPixel;
|
|
|
|
|
|
/* Set a pointer to the exact location in memory of the pixel
|
|
in question: */
|
|
|
|
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start at top of RAM */
|
|
(y * surface->pitch) + /* Go down Y lines */
|
|
(x * bpp)); /* Go in X pixels */
|
|
|
|
|
|
/* Return the correctly-sized piece of data containing the
|
|
* pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
|
|
* RGB value) */
|
|
|
|
if (bpp == 1) /* 8-bit display */
|
|
pixel = *p;
|
|
else if (bpp == 2) /* 16-bit display */
|
|
pixel = *(Uint16 *)p;
|
|
else if (bpp == 3) /* 24-bit display */
|
|
{
|
|
/* Depending on the byte-order, it could be stored RGB or BGR! */
|
|
|
|
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
pixel = p[0] << 16 | p[1] << 8 | p[2];
|
|
else
|
|
pixel = p[0] | p[1] << 8 | p[2] << 16;
|
|
}
|
|
else if (bpp == 4) /* 32-bit display */
|
|
pixel = *(Uint32 *)p;
|
|
|
|
/* SDL_UnlockSurface(surface); */
|
|
}
|
|
|
|
return pixel;
|
|
}
|
|
|
|
|
|
/* Draw a single pixel into the surface: */
|
|
|
|
void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
|
|
{
|
|
int bpp;
|
|
Uint8 * p;
|
|
|
|
|
|
/* Assuming the X/Y values are within the bounds of this surface... */
|
|
|
|
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
|
|
{
|
|
/* SDL_LockSurface(surface); */
|
|
|
|
|
|
/* Determine bytes-per-pixel for the surface in question: */
|
|
|
|
bpp = surface->format->BytesPerPixel;
|
|
|
|
|
|
/* Set a pointer to the exact location in memory of the pixel
|
|
* in question: */
|
|
|
|
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start: beginning of RAM */
|
|
(y * surface->pitch) + /* Go down Y lines */
|
|
(x * bpp)); /* Go in X pixels */
|
|
|
|
|
|
/* Set the (correctly-sized) piece of data in the surface's RAM
|
|
* to the pixel value sent in: */
|
|
|
|
if (bpp == 1)
|
|
*p = pixel;
|
|
else if (bpp == 2)
|
|
*(Uint16 *)p = pixel;
|
|
else if (bpp == 3)
|
|
{
|
|
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
{
|
|
p[0] = (pixel >> 16) & 0xff;
|
|
p[1] = (pixel >> 8) & 0xff;
|
|
p[2] = pixel & 0xff;
|
|
}
|
|
else
|
|
{
|
|
p[0] = pixel & 0xff;
|
|
p[1] = (pixel >> 8) & 0xff;
|
|
p[2] = (pixel >> 16) & 0xff;
|
|
}
|
|
}
|
|
else if (bpp == 4)
|
|
{
|
|
*(Uint32 *)p = pixel;
|
|
}
|
|
|
|
/* SDL_UnlockSurface(surface); */
|
|
}
|
|
}
|
|
|
|
|
|
/* Show debugging stuff: */
|
|
|
|
void debug(char * str)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s\n", str);
|
|
fflush(stderr);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Undo! */
|
|
|
|
void do_undo(void)
|
|
{
|
|
int wanna_update_toolbar;
|
|
|
|
wanna_update_toolbar = 0;
|
|
|
|
if (cur_undo != oldest_undo)
|
|
{
|
|
cur_undo--;
|
|
|
|
if (cur_undo < 0)
|
|
cur_undo = NUM_UNDO_BUFS - 1;
|
|
|
|
#ifdef DEBUG
|
|
printf("BLITTING: %d\n", cur_undo);
|
|
#endif
|
|
SDL_BlitSurface(undo_bufs[cur_undo], NULL, canvas, NULL);
|
|
update_canvas(0, 0, (WINDOW_WIDTH - 96), (48 * 7) + 40 + HEIGHTOFFSET);
|
|
|
|
if (cur_undo == oldest_undo)
|
|
{
|
|
tool_avail[TOOL_UNDO] = 0;
|
|
wanna_update_toolbar = 1;
|
|
}
|
|
|
|
if (tool_avail[TOOL_REDO] == 0)
|
|
{
|
|
tool_avail[TOOL_REDO] = 1;
|
|
wanna_update_toolbar = 1;
|
|
}
|
|
|
|
if (wanna_update_toolbar)
|
|
{
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0, 96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
|
|
been_saved = 0;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("UNDO: Current=%d Oldest=%d Newest=%d\n",
|
|
cur_undo, oldest_undo, newest_undo);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Redo! */
|
|
|
|
void do_redo(void)
|
|
{
|
|
if (cur_undo != newest_undo)
|
|
{
|
|
cur_undo = (cur_undo + 1) % NUM_UNDO_BUFS;
|
|
|
|
#ifdef DEBUG
|
|
printf("BLITTING: %d\n", cur_undo);
|
|
#endif
|
|
SDL_BlitSurface(undo_bufs[cur_undo], NULL, canvas, NULL);
|
|
update_canvas(0, 0, (WINDOW_WIDTH - 96), (48 * 7) + 40 + HEIGHTOFFSET);
|
|
|
|
been_saved = 0;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("REDO: Current=%d Oldest=%d Newest=%d\n",
|
|
cur_undo, oldest_undo, newest_undo);
|
|
#endif
|
|
|
|
|
|
if (((cur_undo + 1) % NUM_UNDO_BUFS) == newest_undo)
|
|
{
|
|
tool_avail[TOOL_REDO] = 0;
|
|
}
|
|
|
|
tool_avail[TOOL_UNDO] = 1;
|
|
|
|
draw_toolbar();
|
|
SDL_UpdateRect(screen, 0, 0, 96, (48 * (7 + TOOLOFFSET / 2)) + 40);
|
|
}
|
|
|
|
|
|
/* Create the current brush in the current color: */
|
|
|
|
void render_brush(void)
|
|
{
|
|
Uint32 amask;
|
|
int x, y;
|
|
Uint8 r, g, b, a;
|
|
|
|
|
|
/* Free the old rendered brush (if any): */
|
|
|
|
if (img_cur_brush != NULL)
|
|
{
|
|
SDL_FreeSurface(img_cur_brush);
|
|
}
|
|
|
|
|
|
/* Create a surface to render into: */
|
|
|
|
amask = ~(img_brushes[cur_brush]->format->Rmask |
|
|
img_brushes[cur_brush]->format->Gmask |
|
|
img_brushes[cur_brush]->format->Bmask);
|
|
|
|
img_cur_brush =
|
|
SDL_CreateRGBSurface(SDL_SWSURFACE,
|
|
img_brushes[cur_brush]->w,
|
|
img_brushes[cur_brush]->h,
|
|
img_brushes[cur_brush]->format->BitsPerPixel,
|
|
img_brushes[cur_brush]->format->Rmask,
|
|
img_brushes[cur_brush]->format->Gmask,
|
|
img_brushes[cur_brush]->format->Bmask,
|
|
amask);
|
|
|
|
if (img_cur_brush == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Can't render a brush!\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", SDL_GetError());
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Render the new brush: */
|
|
|
|
SDL_LockSurface(img_brushes[cur_brush]);
|
|
SDL_LockSurface(img_cur_brush);
|
|
|
|
for (y = 0; y < img_brushes[cur_brush]->h; y++)
|
|
{
|
|
for (x = 0; x < img_brushes[cur_brush]->w; x++)
|
|
{
|
|
SDL_GetRGBA(getpixel(img_brushes[cur_brush], x, y),
|
|
img_brushes[cur_brush]->format,
|
|
&r, &g, &b, &a);
|
|
|
|
putpixel(img_cur_brush, x, y,
|
|
SDL_MapRGBA(img_cur_brush->format,
|
|
color_hexes[cur_color][0],
|
|
color_hexes[cur_color][1],
|
|
color_hexes[cur_color][2],
|
|
a));
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(img_cur_brush);
|
|
SDL_UnlockSurface(img_brushes[cur_brush]);
|
|
|
|
brush_counter = 0;
|
|
}
|
|
|
|
|
|
/* Play a sound: */
|
|
|
|
void playsound(int chan, int s, int override)
|
|
{
|
|
#ifndef NOSOUND
|
|
if (use_sound)
|
|
{
|
|
if (override || !Mix_Playing(chan))
|
|
Mix_PlayChannel(chan, sounds[s], 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Draw a XOR line: */
|
|
|
|
void line_xor(int x1, int y1, int x2, int y2)
|
|
{
|
|
int dx, dy, y, num_drawn;
|
|
float m, b;
|
|
|
|
|
|
/* Kludgey, but it works: */
|
|
|
|
SDL_LockSurface(screen);
|
|
|
|
dx = x2 - x1;
|
|
dy = y2 - y1;
|
|
|
|
num_drawn = 0;
|
|
|
|
if (dx != 0)
|
|
{
|
|
m = ((float) dy) / ((float) dx);
|
|
b = y1 - m * x1;
|
|
|
|
if (x2 >= x1)
|
|
dx = 1;
|
|
else
|
|
dx = -1;
|
|
|
|
|
|
while (x1 != x2)
|
|
{
|
|
y1 = m * x1 + b;
|
|
y2 = m * (x1 + dx) + b;
|
|
|
|
if (y1 > y2)
|
|
{
|
|
y = y1;
|
|
y1 = y2;
|
|
y2 = y;
|
|
}
|
|
|
|
for (y = y1; y <= y2; y++)
|
|
{
|
|
num_drawn++;
|
|
if (num_drawn < 10 || dont_do_xor == 0)
|
|
clipped_putpixel(screen, x1 + 96, y,
|
|
0xFFFFFFFF - getpixel(screen, x1 + 96, y));
|
|
}
|
|
|
|
x1 = x1 + dx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (y1 > y2)
|
|
{
|
|
for (y = y1; y >= y2; y--)
|
|
{
|
|
num_drawn++;
|
|
|
|
if (num_drawn < 10 || dont_do_xor == 0)
|
|
{
|
|
clipped_putpixel(screen, x1 + 96, y,
|
|
0xFFFFFFFF - getpixel(screen, x1 + 96, y));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (y = y1; y <= y2; y++)
|
|
{
|
|
num_drawn++;
|
|
|
|
if (num_drawn < 10 || dont_do_xor == 0)
|
|
{
|
|
clipped_putpixel(screen, x1 + 96, y,
|
|
0xFFFFFFFF - getpixel(screen, x1 + 96, y));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(screen);
|
|
}
|
|
|
|
|
|
/* Should really clip at the line level, but oh well... */
|
|
|
|
void clipped_putpixel(SDL_Surface * dest, int x, int y, Uint32 c)
|
|
{
|
|
if (x >= 96 && x < (WINDOW_WIDTH - 96) &&
|
|
y >= 0 && y < (48 * 7 + 40 + HEIGHTOFFSET))
|
|
{
|
|
putpixel(dest, x, y, c);
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw a XOR rectangle: */
|
|
|
|
void rect_xor(int x1, int y1, int x2, int y2)
|
|
{
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
|
|
if (x2 < 0)
|
|
x2 = 0;
|
|
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
|
|
if (x1 >= (WINDOW_WIDTH - 96 - 96))
|
|
x1 = (WINDOW_WIDTH - 96 - 96) - 1;
|
|
|
|
if (x2 >= (WINDOW_WIDTH - 96 - 96))
|
|
x2 = (WINDOW_WIDTH - 96 - 96) - 1;
|
|
|
|
if (y1 >= (48 * 7) + 40 + HEIGHTOFFSET)
|
|
y1 = (48 * 7) + 40 + HEIGHTOFFSET - 1;
|
|
|
|
if (y2 >= (48 * 7) + 40 + HEIGHTOFFSET)
|
|
y2 = (48 * 7) + 40 + HEIGHTOFFSET - 1;
|
|
|
|
line_xor(x1, y1, x2, y1);
|
|
line_xor(x2, y1, x2, y2);
|
|
line_xor(x2, y2, x1, y2);
|
|
line_xor(x1, y2, x1, y1);
|
|
}
|
|
|
|
|
|
/* Erase at the cursor! */
|
|
|
|
void do_eraser(int x, int y)
|
|
{
|
|
SDL_Rect dest;
|
|
|
|
dest.x = x - 48;
|
|
dest.y = y - 48;
|
|
dest.w = 96;
|
|
dest.h = 96;
|
|
|
|
SDL_FillRect(canvas, &dest,
|
|
SDL_MapRGB(canvas->format, 255, 255, 255));
|
|
|
|
|
|
#ifndef NOSOUND
|
|
if (use_sound)
|
|
{
|
|
if (!Mix_Playing(0))
|
|
{
|
|
eraser_sound = (eraser_sound + 1) % 2;
|
|
|
|
playsound(0, SND_ERASER1 + eraser_sound, 0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
update_canvas(x - 48, y - 48, x + 48, y + 48);
|
|
|
|
rect_xor(x - 48, y - 48,
|
|
x + 48, y + 48);
|
|
}
|
|
|
|
|
|
/* Reset available tools (for new image / starting out): */
|
|
|
|
void reset_avail_tools(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_TOOLS; i++)
|
|
{
|
|
tool_avail[i] = 1;
|
|
}
|
|
|
|
|
|
/* Unavailable at the beginning of a new canvas: */
|
|
|
|
tool_avail[TOOL_UNDO] = 0;
|
|
tool_avail[TOOL_REDO] = 0;
|
|
tool_avail[TOOL_NEW] = 0;
|
|
|
|
if (been_saved)
|
|
tool_avail[TOOL_SAVE] = 0;
|
|
|
|
|
|
/* Unavailable in rare circumstances: */
|
|
|
|
if (num_stamps == 0)
|
|
tool_avail[TOOL_STAMP] = 0;
|
|
|
|
|
|
/* Disable quit? */
|
|
|
|
if (disable_quit)
|
|
tool_avail[TOOL_QUIT] = 0;
|
|
|
|
|
|
#ifdef WIN32
|
|
disable_print = !IsPrinterAvailable();
|
|
#endif
|
|
|
|
#ifdef __BEOS__
|
|
disable_print = !IsPrinterAvailable();
|
|
#endif
|
|
|
|
|
|
/* Disable print? */
|
|
|
|
if (disable_print)
|
|
tool_avail[TOOL_PRINT] = 0;
|
|
}
|
|
|
|
|
|
/* Save and disable available tools (for Open-Dialog) */
|
|
|
|
void disable_avail_tools(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_TOOLS; i++)
|
|
{
|
|
tool_avail_bak[i] = tool_avail[i];
|
|
tool_avail[i]=0;
|
|
}
|
|
}
|
|
|
|
/* Restore and enable available tools (for End-Of-Open-Dialog) */
|
|
|
|
void enable_avail_tools(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_TOOLS; i++)
|
|
{
|
|
tool_avail[i] = tool_avail_bak[i];
|
|
}
|
|
}
|
|
|
|
|
|
/* Update a rect. based on two x/y coords (not necessarly in order): */
|
|
|
|
void update_screen(int x1, int y1, int x2, int y2)
|
|
{
|
|
int tmp;
|
|
|
|
if (x1 > x2)
|
|
{
|
|
tmp = x1;
|
|
x1 = x2;
|
|
x2 = tmp;
|
|
}
|
|
|
|
if (y1 > y2)
|
|
{
|
|
tmp = y1;
|
|
y1 = y2;
|
|
y2 = tmp;
|
|
}
|
|
|
|
x1 = x1 - 1;
|
|
x2 = x2 + 1;
|
|
y1 = y1 - 1;
|
|
y2 = y2 + 1;
|
|
|
|
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
if (x2 < 0)
|
|
x2 = 0;
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
|
|
if (x1 >= WINDOW_WIDTH)
|
|
x1 = WINDOW_WIDTH - 1;
|
|
if (x2 >= WINDOW_WIDTH)
|
|
x2 = WINDOW_WIDTH - 1;
|
|
if (y1 >= WINDOW_HEIGHT)
|
|
y1 = WINDOW_HEIGHT - 1;
|
|
if (y2 >= WINDOW_HEIGHT)
|
|
y2 = WINDOW_HEIGHT - 1;
|
|
|
|
SDL_UpdateRect(screen, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
|
|
}
|
|
|
|
|
|
/* Build a color based on two colors and an alpha... */
|
|
|
|
Uint8 alpha(Uint8 c1, Uint8 c2, Uint8 a)
|
|
{
|
|
Uint16 c, nc1, nc2, na;
|
|
|
|
na = a;
|
|
nc1 = c1;
|
|
nc2 = c2;
|
|
|
|
if (nc1 > 200)
|
|
nc1 = 200;
|
|
|
|
c = ((nc1 * na) / 255 + (nc2 * (255 - na)) / 255);
|
|
|
|
return (Uint8) c;
|
|
}
|
|
|
|
|
|
/* For qsort() call in loadarbitrary()... */
|
|
|
|
int compare_strings(char * * s1, char * * s2)
|
|
{
|
|
return (strcmp(*s1, *s2));
|
|
}
|
|
|
|
|
|
/* For qsort() call in do_open()... */
|
|
|
|
int compare_dirents(struct dirent * f1, struct dirent * f2)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("compare_dirents: %s\t%s\n", f1->d_name, f2->d_name);
|
|
#endif
|
|
|
|
return (strcmp(f1->d_name, f2->d_name));
|
|
}
|
|
|
|
|
|
/* Draw tux's text on the screen: */
|
|
|
|
void draw_tux_text(int which_tux, char * str, int want_utf8,
|
|
int force_locale_font, int want_right_to_left)
|
|
{
|
|
SDL_Rect dest;
|
|
SDL_Color black = {0, 0, 0, 0};
|
|
char * upper_str;
|
|
|
|
|
|
/* Remove any text-changing timer if one is running: */
|
|
|
|
control_drawtext_timer(0, "");
|
|
|
|
|
|
/* Clear first: */
|
|
|
|
dest.x = 0;
|
|
dest.y = (48 * 7) + 40 + 48 + HEIGHTOFFSET;
|
|
dest.w = WINDOW_WIDTH;
|
|
dest.h = WINDOW_HEIGHT - ((48 * 7) + 40 + 48 + HEIGHTOFFSET);
|
|
|
|
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
|
|
|
|
/* Draw tux: */
|
|
|
|
dest.x = 0;
|
|
dest.y = WINDOW_HEIGHT - (img_tux[which_tux] -> h);
|
|
|
|
if (dest.y < ((48 * 7) + 40 + 48 + HEIGHTOFFSET))
|
|
dest.y = ((48 * 7) + 40 + 48 + HEIGHTOFFSET);
|
|
|
|
SDL_BlitSurface(img_tux[which_tux], NULL, screen, &dest);
|
|
|
|
|
|
upper_str = uppercase(str);
|
|
wordwrap_text(font, upper_str, black,
|
|
img_tux[which_tux] -> w + 5,
|
|
(48 * 7) + 40 + 48 + HEIGHTOFFSET + 5,
|
|
WINDOW_WIDTH, want_utf8, force_locale_font,
|
|
want_right_to_left);
|
|
|
|
free(upper_str);
|
|
|
|
|
|
/* Update the display: */
|
|
|
|
SDL_UpdateRect(screen,
|
|
0, (48 * 7) + 40 + 48 + HEIGHTOFFSET,
|
|
WINDOW_WIDTH,
|
|
WINDOW_HEIGHT - ((48 * 7) + 40 + 48 + HEIGHTOFFSET));
|
|
}
|
|
|
|
|
|
void wordwrap_text(TTF_Font * font, char * str, SDL_Color color,
|
|
int left, int top, int right, int want_utf8,
|
|
int force_locale_font, int want_right_to_left)
|
|
{
|
|
int x, y, i, j;
|
|
char substr[512];
|
|
unsigned char * locale_str;
|
|
char * tstr;
|
|
Uint16 unicode_char[2];
|
|
unsigned char utf8_char[5];
|
|
int len;
|
|
SDL_Surface * text;
|
|
SDL_Rect dest, src;
|
|
|
|
int utf8_str_len, last_text_height;
|
|
unsigned char utf8_str[512];
|
|
|
|
|
|
/* Cursor starting position: */
|
|
|
|
x = left;
|
|
y = top;
|
|
|
|
last_text_height = 0;
|
|
|
|
debug(str);
|
|
debug(gettext(str));
|
|
debug("...");
|
|
|
|
if (strcmp(str, "") != 0 &&
|
|
(want_utf8 ||
|
|
(need_utf8(language) && strcmp(gettext(str), str) != 0)))
|
|
{
|
|
if (want_utf8 || want_right_to_left == 0)
|
|
locale_str = strdup(gettext(str));
|
|
else
|
|
locale_str = strdup(textdir(gettext(str)));
|
|
|
|
|
|
/* For each UTF8 character: */
|
|
|
|
utf8_str_len = 0;
|
|
utf8_str[0] = '\0';
|
|
|
|
for (i = 0; i <= strlen(locale_str); i++)
|
|
{
|
|
if (locale_str[i] < 128)
|
|
{
|
|
utf8_str[utf8_str_len++] = locale_str[i];
|
|
utf8_str[utf8_str_len] = '\0';
|
|
|
|
|
|
/* Space? Blit the word! (Word-wrap if necessary) */
|
|
|
|
if (locale_str[i] == ' ' || locale_str[i] == '\0')
|
|
{
|
|
text = TTF_RenderUTF8_Blended(locale_font, utf8_str, color);
|
|
|
|
/* ----------- */
|
|
if (text->w > right - left)
|
|
{
|
|
/* Move left and down (if not already at left!) */
|
|
|
|
if (x > left)
|
|
{
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
|
x - left);
|
|
|
|
x = left;
|
|
y = y + text->h;
|
|
}
|
|
|
|
|
|
/* Junk the blitted word; it's too long! */
|
|
|
|
last_text_height = text->h;
|
|
SDL_FreeSurface(text);
|
|
|
|
|
|
/* For each UTF8 character: */
|
|
|
|
for (j = 0; j < utf8_str_len; j++)
|
|
{
|
|
/* How many bytes does this character need? */
|
|
|
|
if (utf8_str[j] < 128) /* 0xxx xxxx - 1 byte */
|
|
{
|
|
utf8_char[0] = utf8_str[j];
|
|
utf8_char[1] = '\0';
|
|
}
|
|
else if ((utf8_str[j] & 0xE0) == 0xC0) /* 110x xxxx - 2 bytes */
|
|
{
|
|
utf8_char[0] = utf8_str[j];
|
|
utf8_char[1] = utf8_str[j + 1];
|
|
utf8_char[2] = '\0';
|
|
j = j + 1;
|
|
}
|
|
else if ((utf8_str[j] & 0xF0) == 0xE0) /* 1110 xxxx - 3 bytes */
|
|
{
|
|
utf8_char[0] = utf8_str[j];
|
|
utf8_char[1] = utf8_str[j + 1];
|
|
utf8_char[2] = utf8_str[j + 2];
|
|
utf8_char[3] = '\0';
|
|
j = j + 2;
|
|
}
|
|
else /* 1111 0xxx - 4 bytes */
|
|
{
|
|
utf8_char[0] = utf8_str[j];
|
|
utf8_char[1] = utf8_str[j + 1];
|
|
utf8_char[2] = utf8_str[j + 2];
|
|
utf8_char[3] = utf8_str[j + 3];
|
|
utf8_char[4] = '\0';
|
|
j = j + 3;
|
|
}
|
|
|
|
|
|
if (utf8_char[0] != '\0')
|
|
{
|
|
text = TTF_RenderUTF8_Blended(locale_font, utf8_char, color);
|
|
if (text != NULL)
|
|
{
|
|
if (x + text->w > right)
|
|
{
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
anti_carriage_return(left, right, top, top + text->h,
|
|
y + text->h, x - left);
|
|
|
|
x = left;
|
|
y = y + text->h;
|
|
}
|
|
|
|
dest.x = x;
|
|
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
dest.y = top;
|
|
else
|
|
dest.y = y;
|
|
|
|
SDL_BlitSurface(text, NULL, screen, &dest);
|
|
|
|
last_text_height = text->h;
|
|
SDL_FreeSurface(text);
|
|
|
|
x = x + text->w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Not too wide for one line... */
|
|
|
|
if (x + text->w > right)
|
|
{
|
|
/* This word needs to move down? */
|
|
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
|
x - left);
|
|
|
|
x = left;
|
|
y = y + text->h;
|
|
}
|
|
|
|
dest.x = x;
|
|
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
dest.y = top;
|
|
else
|
|
dest.y = y;
|
|
|
|
SDL_BlitSurface(text, NULL, screen, &dest);
|
|
|
|
last_text_height = text->h;
|
|
SDL_FreeSurface(text);
|
|
x = x + text->w;
|
|
}
|
|
|
|
|
|
utf8_str_len = 0;
|
|
utf8_str[0] = '\0';
|
|
}
|
|
}
|
|
else if ((locale_str[i] & 0xE0) == 0xC0)
|
|
{
|
|
utf8_str[utf8_str_len++] = locale_str[i];
|
|
utf8_str[utf8_str_len++] = locale_str[i + 1];
|
|
utf8_str[utf8_str_len] = '\0';
|
|
i++;
|
|
}
|
|
else if ((locale_str[i] & 0xF0) == 0xE0)
|
|
{
|
|
utf8_str[utf8_str_len++] = locale_str[i];
|
|
utf8_str[utf8_str_len++] = locale_str[i + 1];
|
|
utf8_str[utf8_str_len++] = locale_str[i + 2];
|
|
utf8_str[utf8_str_len] = '\0';
|
|
i = i + 2;
|
|
}
|
|
else
|
|
{
|
|
utf8_str[utf8_str_len++] = locale_str[i];
|
|
utf8_str[utf8_str_len++] = locale_str[i + 1];
|
|
utf8_str[utf8_str_len++] = locale_str[i + 2];
|
|
utf8_str[utf8_str_len++] = locale_str[i + 3];
|
|
utf8_str[utf8_str_len] = '\0';
|
|
i = i + 3;
|
|
}
|
|
}
|
|
|
|
free(locale_str);
|
|
}
|
|
else if (need_unicode(language) && locale_font != NULL &&
|
|
strcmp(gettext(str), str) != 0 && strcmp(str, "") != 0)
|
|
{
|
|
if (want_right_to_left == 0)
|
|
locale_str = strdup(gettext(str));
|
|
else
|
|
locale_str = strdup(textdir(gettext(str)));
|
|
|
|
|
|
/* For each pair of bytes... */
|
|
|
|
for (i = 0; i < strlen(locale_str); i = i + 2)
|
|
{
|
|
/* FIXME: Is this endian-safe? */
|
|
|
|
unicode_char[0] = (locale_str[i] << 8) + (locale_str[i + 1]);
|
|
unicode_char[1] = 0;
|
|
|
|
text = TTF_RenderUNICODE_Blended(locale_font, unicode_char, color);
|
|
|
|
|
|
/* Wrap, if needed: */
|
|
|
|
if (x + text->w > right)
|
|
{
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
|
x - left);
|
|
|
|
x = left;
|
|
y = y + text->h;
|
|
}
|
|
|
|
|
|
dest.x = x;
|
|
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
dest.y = top;
|
|
else
|
|
dest.y = y;
|
|
|
|
SDL_BlitSurface(text, NULL, screen, &dest);
|
|
|
|
x = x + text->w;
|
|
|
|
last_text_height = text->h;
|
|
SDL_FreeSurface(text);
|
|
}
|
|
|
|
free(locale_str);
|
|
}
|
|
else if (strlen(str) != 0)
|
|
{
|
|
/* Truncate if too big! (sorry!) */
|
|
|
|
if (want_right_to_left == 0)
|
|
tstr = strdup(uppercase(gettext(str)));
|
|
else
|
|
tstr = strdup(uppercase(textdir(gettext(str))));
|
|
|
|
if (strlen(tstr) > sizeof(substr) - 1)
|
|
tstr[sizeof(substr) - 1] = '\0';
|
|
|
|
|
|
/* For each word... */
|
|
|
|
for (i = 0; i < strlen(tstr); i++)
|
|
{
|
|
/* Figure out the word... */
|
|
|
|
len = 0;
|
|
|
|
for (j = i; tstr[j] != ' ' && tstr[j] != '\0'; j++)
|
|
{
|
|
substr[len++] = tstr[j];
|
|
}
|
|
|
|
substr[len++] = ' ';
|
|
substr[len] = '\0';
|
|
|
|
|
|
/* Render the word for display... */
|
|
|
|
//if (force_locale_font && locale_font != NULL)
|
|
// text = RENDER_TEXT(locale_font, substr, color);
|
|
//else
|
|
text = RENDER_TEXT(font, substr, color);
|
|
|
|
|
|
/* If it won't fit on this line, move to the next! */
|
|
|
|
if (x + text->w > right) /* Correct? */
|
|
{
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
|
x - left);
|
|
|
|
x = left;
|
|
y = y + text->h;
|
|
}
|
|
|
|
|
|
/* Draw the word: */
|
|
|
|
dest.x = x;
|
|
|
|
if (need_right_to_left(language) && want_right_to_left)
|
|
dest.y = top;
|
|
else
|
|
dest.y = y;
|
|
|
|
SDL_BlitSurface(text, NULL, screen, &dest);
|
|
|
|
|
|
/* Move the cursor one word's worth: */
|
|
|
|
x = x + text->w;
|
|
|
|
|
|
/* Free the temp. surface: */
|
|
|
|
last_text_height = text->h;
|
|
SDL_FreeSurface(text);
|
|
|
|
|
|
/* Now on to the next word... */
|
|
|
|
i = j;
|
|
}
|
|
|
|
free(tstr);
|
|
}
|
|
|
|
|
|
/* Right-justify the final line of text, in right-to-left mode: */
|
|
|
|
if (need_right_to_left(language) && want_right_to_left && last_text_height > 0)
|
|
{
|
|
src.x = left;
|
|
src.y = top;
|
|
src.w = x - left;
|
|
src.h = last_text_height;
|
|
|
|
dest.x = right - src.w;
|
|
dest.y = top;
|
|
|
|
SDL_BlitSurface(screen, &src, screen, &dest);
|
|
|
|
dest.x = left;
|
|
dest.y = top;
|
|
dest.w = (right - left - src.w);
|
|
dest.h = last_text_height;
|
|
|
|
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
}
|
|
}
|
|
|
|
|
|
/* Load a file's sound: */
|
|
|
|
#ifndef NOSOUND
|
|
|
|
Mix_Chunk * loadsound(char * fname)
|
|
{
|
|
char * snd_fname;
|
|
char tmp_str[64];
|
|
Mix_Chunk * tmp_snd;
|
|
|
|
|
|
debug(fname);
|
|
|
|
|
|
/* First, check for localized version of sound: */
|
|
|
|
snd_fname = malloc(strlen(fname) + strlen(lang_prefixes[language]) + 2);
|
|
|
|
strcpy(snd_fname, fname);
|
|
snprintf(tmp_str, sizeof(tmp_str), "_%s.wav", lang_prefixes[language]);
|
|
|
|
|
|
if (strstr(snd_fname, ".png") != NULL)
|
|
{
|
|
strcpy(strstr(snd_fname, ".png"), tmp_str);
|
|
debug(snd_fname);
|
|
|
|
tmp_snd = Mix_LoadWAV(snd_fname);
|
|
|
|
if (tmp_snd == NULL)
|
|
{
|
|
debug("...No local version of sound!");
|
|
|
|
/* Now, check for default sound: */
|
|
|
|
free(snd_fname);
|
|
|
|
snd_fname = strdup(fname);
|
|
|
|
if (strstr(snd_fname, ".png") != NULL)
|
|
{
|
|
strcpy(strstr(snd_fname, ".png"), ".wav");
|
|
debug(snd_fname);
|
|
tmp_snd = Mix_LoadWAV(snd_fname);
|
|
free(snd_fname);
|
|
|
|
if (tmp_snd == NULL)
|
|
{
|
|
debug("...No default version of sound!");
|
|
return NULL;
|
|
}
|
|
|
|
return (tmp_snd);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return (tmp_snd);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* Strip any trailing spaces: */
|
|
|
|
void strip_trailing_whitespace( char *buf )
|
|
{
|
|
while (buf[strlen(buf) - 1] == ' ' ||
|
|
buf[strlen(buf) - 1] == '\r' ||
|
|
buf[strlen(buf) - 1] == '\n')
|
|
{
|
|
buf[strlen(buf) - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
/* Load a file's description: */
|
|
|
|
char * loaddesc(char * fname)
|
|
{
|
|
char * txt_fname;
|
|
char buf[256], def_buf[256];
|
|
int found, got_first, in_utf8, in_html_escape;
|
|
FILE * fi;
|
|
|
|
|
|
txt_fname = strdup(fname);
|
|
|
|
if (strstr(txt_fname, ".png") != NULL)
|
|
{
|
|
strcpy(strstr(txt_fname, ".png"), ".txt");
|
|
|
|
fi = fopen(txt_fname, "r");
|
|
|
|
if (fi == NULL)
|
|
{
|
|
/*
|
|
fprintf(stderr, "\nWarning: Couldn't open a description file:\n");
|
|
perror(txt_fname);
|
|
fprintf(stderr, "\n");
|
|
*/
|
|
|
|
free(txt_fname);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
free(txt_fname);
|
|
|
|
got_first = 0;
|
|
found = 0;
|
|
in_utf8 = 0;
|
|
in_html_escape = 0;
|
|
|
|
strcpy(def_buf, "");
|
|
|
|
do
|
|
{
|
|
fgets(buf, sizeof(buf), fi);
|
|
|
|
if (!feof(fi))
|
|
{
|
|
strip_trailing_whitespace(buf);
|
|
|
|
|
|
if (!got_first)
|
|
{
|
|
/* First one is the default: */
|
|
|
|
strcpy(def_buf, buf);
|
|
got_first = 1;
|
|
}
|
|
|
|
|
|
debug(buf);
|
|
|
|
|
|
/* See if it's the one for this locale... */
|
|
|
|
if (strstr(buf, lang_prefixes[language]) == buf)
|
|
{
|
|
|
|
debug(buf + strlen(lang_prefixes[language]));
|
|
|
|
if (buf[strlen(lang_prefixes[language])] == '=')
|
|
{
|
|
found = 1;
|
|
in_utf8 = 0;
|
|
}
|
|
else if (strstr(buf + strlen(lang_prefixes[language]), ".utf8=") ==
|
|
buf + strlen(lang_prefixes[language]))
|
|
{
|
|
found = 1;
|
|
in_utf8 = 1;
|
|
|
|
debug("...IS UTF-8!");
|
|
}
|
|
else if (strstr(buf + strlen(lang_prefixes[language]), ".esc=") ==
|
|
buf + strlen(lang_prefixes[language]))
|
|
{
|
|
found = 1;
|
|
in_html_escape = 1;
|
|
|
|
debug("...IS HTML ESCAPED!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (!feof(fi) && !found);
|
|
|
|
fclose(fi);
|
|
|
|
|
|
/* Return the string: */
|
|
|
|
if (found)
|
|
{
|
|
if (in_utf8 == 1)
|
|
{
|
|
/* UTF format! Decode! */
|
|
|
|
/* FIXME: Stupid... Using '=' at beginning to mark as a UTF8 string */
|
|
|
|
return(strdup(buf + (strlen(lang_prefixes[language])) + 5));
|
|
}
|
|
else if (in_html_escape == 1)
|
|
{
|
|
/* HTML escape-code style! Unescape! */
|
|
|
|
return(unescape(buf + (strlen(lang_prefixes[language])) + 5));
|
|
}
|
|
else
|
|
{
|
|
/* Plain old ASCII... just copy it: */
|
|
|
|
return(strdup(buf + (strlen(lang_prefixes[language])) + 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No locale-specific translation; use the default (English) */
|
|
|
|
return(strdup(def_buf));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Load a file's info: */
|
|
|
|
info_type * loadinfo(char * fname)
|
|
{
|
|
char * dat_fname;
|
|
char buf[256];
|
|
info_type inf;
|
|
info_type * inf_ret;
|
|
FILE * fi;
|
|
|
|
|
|
/* Clear info struct first: */
|
|
|
|
inf.colorable = 0;
|
|
inf.tintable = 0;
|
|
|
|
|
|
/* Load info! */
|
|
|
|
dat_fname = strdup(fname);
|
|
|
|
if (strstr(dat_fname, ".png") != NULL)
|
|
{
|
|
strcpy(strstr(dat_fname, ".png"), ".dat");
|
|
|
|
fi = fopen(dat_fname, "r");
|
|
|
|
if (fi == NULL)
|
|
{
|
|
/*
|
|
fprintf(stderr, "\nWarning: Couldn't open an info file:\n");
|
|
perror(txt_fname);
|
|
fprintf(stderr, "\n");
|
|
*/
|
|
|
|
free(dat_fname);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
free(dat_fname);
|
|
|
|
|
|
do
|
|
{
|
|
fgets(buf, sizeof(buf), fi);
|
|
|
|
if (!feof(fi))
|
|
{
|
|
strip_trailing_whitespace(buf);
|
|
|
|
if (strcmp(buf, "colorable") == 0)
|
|
inf.colorable = 1;
|
|
else if (strcmp(buf, "tintable") == 0)
|
|
inf.tintable = 1;
|
|
}
|
|
}
|
|
while (!feof(fi));
|
|
|
|
fclose(fi);
|
|
|
|
|
|
/* Return the info: */
|
|
|
|
inf_ret = malloc(sizeof(info_type));
|
|
/* FIXME: Check for errors! */
|
|
|
|
memcpy(inf_ret, &inf, sizeof(info_type));
|
|
|
|
return(inf_ret);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Wait for a keypress or mouse click */
|
|
|
|
void do_wait(void)
|
|
{
|
|
SDL_Event event;
|
|
int done, counter;
|
|
|
|
done = 0;
|
|
|
|
counter = 50; /* About 5 seconds */
|
|
|
|
do
|
|
{
|
|
while (SDL_PollEvent(&event))
|
|
{
|
|
if (event.type == SDL_QUIT)
|
|
{
|
|
done = 1;
|
|
|
|
/* FIXME: Handle SDL_Quit better */
|
|
}
|
|
else if (event.type == SDL_ACTIVEEVENT)
|
|
{
|
|
handle_active(&event);
|
|
}
|
|
else if (event.type == SDL_KEYDOWN)
|
|
{
|
|
done = 1;
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
|
event.button.button >= 1 &&
|
|
event.button.button <= 3)
|
|
{
|
|
done = 1;
|
|
}
|
|
}
|
|
|
|
counter--;
|
|
SDL_Delay(100);
|
|
}
|
|
while (!done && counter > 0);
|
|
}
|
|
|
|
|
|
/* Load current (if any) image: */
|
|
|
|
void load_current(void)
|
|
{
|
|
SDL_Surface * tmp;
|
|
char * fname;
|
|
char ftmp[1024];
|
|
FILE * fi;
|
|
SDL_Rect dest;
|
|
|
|
|
|
/* Determine the current picture's ID: */
|
|
|
|
fname = get_fname("current_id.txt");
|
|
|
|
fi = fopen(fname, "r");
|
|
if (fi == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Couldn't determine the current image's ID\n"
|
|
"%s\n"
|
|
"The system error that occurred was:\n"
|
|
"%s\n\n", fname, strerror(errno));
|
|
file_id[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
fgets(file_id, sizeof(file_id), fi);
|
|
if (strlen(file_id) > 0)
|
|
{
|
|
file_id[strlen(file_id) - 1] = '\0';
|
|
}
|
|
fclose(fi);
|
|
}
|
|
|
|
free(fname);
|
|
|
|
|
|
/* Load that image: */
|
|
|
|
if (file_id[0] != '\0')
|
|
{
|
|
snprintf(ftmp, sizeof(ftmp), "saved/%s%s", file_id, FNAME_EXTENSION);
|
|
|
|
fname = get_fname(ftmp);
|
|
|
|
#ifdef SAVE_AS_BMP
|
|
tmp = SDL_LoadBMP(fname);
|
|
#else
|
|
tmp = IMG_Load(fname);
|
|
#endif
|
|
|
|
if (tmp == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Couldn't load any current image.\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", fname, SDL_GetError());
|
|
|
|
file_id[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
|
|
|
|
dest.x = (canvas->w - tmp->w) / 2;
|
|
dest.y = (canvas->h - tmp->h) / 2;
|
|
SDL_BlitSurface(tmp, NULL, canvas, &dest);
|
|
|
|
SDL_FreeSurface(tmp);
|
|
|
|
tool_avail[TOOL_NEW] = 1;
|
|
}
|
|
|
|
free(fname);
|
|
}
|
|
}
|
|
|
|
|
|
/* Save the current image to disk: */
|
|
|
|
void save_current(void)
|
|
{
|
|
char * fname;
|
|
int res;
|
|
FILE * fi;
|
|
|
|
|
|
fname = get_fname("");
|
|
|
|
res = mkdir(fname, 0755);
|
|
|
|
if (res != 0 && errno != EEXIST)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't create user data directory:\n"
|
|
"%s\n"
|
|
"The error that occurred was:\n"
|
|
"%s\n\n", fname, strerror(errno));
|
|
|
|
draw_tux_text(TUX_OOPS, strerror(errno), 0, 0, 0);
|
|
}
|
|
|
|
free(fname);
|
|
|
|
|
|
fname = get_fname("current_id.txt");
|
|
|
|
fi = fopen(fname, "w");
|
|
if (fi == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't keep track of current image.\n"
|
|
"%s\n"
|
|
"The error that occred was:\n"
|
|
"%s\n\n", fname, strerror(errno));
|
|
|
|
draw_tux_text(TUX_OOPS, strerror(errno), 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
fprintf(fi, "%s\n", file_id);
|
|
fclose(fi);
|
|
}
|
|
|
|
free(fname);
|
|
}
|
|
|
|
|
|
/* The filename for the current image: */
|
|
|
|
char * get_fname(char * name)
|
|
{
|
|
char f[512];
|
|
const char * tux_settings_dir;
|
|
|
|
|
|
#ifdef WIN32
|
|
snprintf(f, sizeof(f), "%s/%s", savedir, name);
|
|
#elif __BEOS__
|
|
if (*name == '\0')
|
|
strcpy(f, savedir);
|
|
else
|
|
snprintf(f, sizeof(f), "%s/%s", savedir, name);
|
|
#else
|
|
|
|
#ifdef __APPLE__
|
|
tux_settings_dir = "Library/Preferences/tuxpaint";
|
|
#else
|
|
tux_settings_dir = ".tuxpaint";
|
|
#endif
|
|
|
|
if (savedir == NULL)
|
|
{
|
|
/* Save directory not overridden: */
|
|
|
|
if (getenv("HOME") != NULL)
|
|
{
|
|
if (*name == '\0')
|
|
{
|
|
/* (Some mkdir()'s don't like trailing slashes) */
|
|
|
|
snprintf(f, sizeof(f), "%s/%s", getenv("HOME"), tux_settings_dir);
|
|
}
|
|
else
|
|
{
|
|
snprintf(f, sizeof(f), "%s/%s/%s",
|
|
getenv("HOME"), tux_settings_dir, name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strcpy(f, name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("savedir set to: %s\n", savedir);
|
|
|
|
snprintf(f, sizeof(f), "%s/%s", savedir, name);
|
|
}
|
|
#endif
|
|
|
|
return strdup(f);
|
|
}
|
|
|
|
|
|
/* Prompt the user with a yes/no question: */
|
|
|
|
int do_prompt(char * text, char * btn_yes, char * btn_no)
|
|
{
|
|
SDL_Event event;
|
|
SDL_Rect dest;
|
|
int done, ans, w;
|
|
SDL_Color black = {0, 0, 0, 0};
|
|
SDLKey key;
|
|
SDLKey key_y, key_n;
|
|
char keystr[200];
|
|
#ifndef NO_PROMPT_SHADOWS
|
|
int i;
|
|
SDL_Surface * alpha_surf;
|
|
#endif
|
|
|
|
|
|
/* FIXME: Move elsewhere! Or not?! */
|
|
|
|
strcpy(keystr, textdir(gettext("Yes")));
|
|
key_y = tolower(keystr[0]);
|
|
|
|
strcpy(keystr, textdir(gettext("No")));
|
|
key_n = tolower(keystr[0]);
|
|
|
|
|
|
do_setcursor(cursor_arrow);
|
|
|
|
|
|
/* Move cursor automatically if in keymouse mode: */
|
|
|
|
if (keymouse)
|
|
{
|
|
mouse_x = WINDOW_WIDTH / 2;
|
|
mouse_y = WINDOW_HEIGHT / 2;
|
|
SDL_WarpMouse(mouse_x, mouse_y);
|
|
}
|
|
|
|
|
|
/* Draw button box: */
|
|
|
|
playsound(0, SND_PROMPT, 1);
|
|
|
|
for (w = 0; w <= 96; w = w + 4)
|
|
{
|
|
dest.x = 160 + 96 - w + PROMPTOFFSETX;
|
|
dest.y = 94 + 96 - w + PROMPTOFFSETY;
|
|
dest.w = (320 - 96 * 2) + w * 2;
|
|
dest.h = w * 2;
|
|
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
|
|
|
|
SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
|
|
SDL_Delay(10);
|
|
}
|
|
|
|
|
|
#ifndef NO_PROMPT_SHADOWS
|
|
alpha_surf = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
|
|
(320 - 96 * 2) + (w - 4) * 2,
|
|
(w - 4) * 2,
|
|
screen->format->BitsPerPixel,
|
|
screen->format->Rmask,
|
|
screen->format->Gmask,
|
|
screen->format->Bmask,
|
|
screen->format->Amask);
|
|
|
|
if (alpha_surf != NULL)
|
|
{
|
|
SDL_FillRect(alpha_surf, NULL, SDL_MapRGB(alpha_surf->format, 0, 0, 0));
|
|
SDL_SetAlpha(alpha_surf, SDL_SRCALPHA, 64);
|
|
|
|
for (i = 8; i > 0; i = i - 2)
|
|
{
|
|
dest.x = 160 + 96 - (w - 4) + i + PROMPTOFFSETX;
|
|
dest.y = 94 + 96 - (w - 4) + i + PROMPTOFFSETY;
|
|
dest.w = (320 - 96 * 2) + (w - 4) * 2;
|
|
dest.h = (w - 4) * 2;
|
|
|
|
SDL_BlitSurface(alpha_surf, NULL, screen, &dest);
|
|
}
|
|
|
|
SDL_FreeSurface(alpha_surf);
|
|
}
|
|
#endif
|
|
|
|
|
|
w = w - 6;
|
|
|
|
dest.x = 160 + 96 - w + PROMPTOFFSETX;
|
|
dest.y = 94 + 96 - w + PROMPTOFFSETY;
|
|
dest.w = (320 - 96 * 2) + w * 2;
|
|
dest.h = w * 2;
|
|
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
|
|
|
|
/* Draw the question: */
|
|
|
|
wordwrap_text(font, text, black,
|
|
166 + PROMPTOFFSETX, 100 + PROMPTOFFSETY, 475, 0, 0, 1);
|
|
|
|
|
|
/* Draw yes button: */
|
|
|
|
dest.x = 166 + PROMPTOFFSETX;
|
|
dest.y = 178 + PROMPTOFFSETY;
|
|
SDL_BlitSurface(img_yes, NULL, screen, &dest);
|
|
|
|
wordwrap_text(font, btn_yes, black, 166 + PROMPTOFFSETX + 48 + 4,
|
|
183 + PROMPTOFFSETY, 475, 0, 0, 1);
|
|
|
|
|
|
/* Draw no button: */
|
|
|
|
if (strlen(btn_no) != 0)
|
|
{
|
|
dest.x = 166 + PROMPTOFFSETX;
|
|
dest.y = 230 + PROMPTOFFSETY;
|
|
SDL_BlitSurface(img_no, NULL, screen, &dest);
|
|
|
|
wordwrap_text(font, btn_no, black,
|
|
166 + PROMPTOFFSETX + 48 + 4, 235 + PROMPTOFFSETY, 475,
|
|
0, 0, 1);
|
|
}
|
|
|
|
|
|
/* Draw Tux, waiting... */
|
|
|
|
draw_tux_text(TUX_BORED, "", 0, 0, 0);
|
|
|
|
SDL_Flip(screen);
|
|
|
|
done = 0;
|
|
ans = 0;
|
|
|
|
do
|
|
{
|
|
SDL_WaitEvent(&event);
|
|
|
|
if (event.type == SDL_QUIT)
|
|
{
|
|
ans = 0;
|
|
done = 1;
|
|
}
|
|
else if (event.type == SDL_ACTIVEEVENT)
|
|
{
|
|
handle_active(&event);
|
|
}
|
|
else if (event.type == SDL_KEYUP)
|
|
{
|
|
key = event.key.keysym.sym;
|
|
|
|
handle_keymouse(key, SDL_KEYUP);
|
|
}
|
|
else if (event.type == SDL_KEYDOWN)
|
|
{
|
|
key = event.key.keysym.sym;
|
|
|
|
handle_keymouse(key, SDL_KEYDOWN);
|
|
|
|
|
|
/* FIXME: Should use SDLK_{c} instead of '{c}'? How? */
|
|
|
|
if (key == key_y || key == SDLK_RETURN)
|
|
{
|
|
/* Y or ENTER - Yes! */
|
|
|
|
ans = 1;
|
|
done = 1;
|
|
}
|
|
else if (key == key_n || key == SDLK_ESCAPE)
|
|
{
|
|
/* N or ESCAPE - No! */
|
|
|
|
if (strlen(btn_no) != 0)
|
|
{
|
|
ans = 0;
|
|
done = 1;
|
|
}
|
|
else
|
|
{
|
|
if (key == SDLK_ESCAPE)
|
|
{
|
|
/* ESCAPE also simply dismisses if there's no Yes/No choice: */
|
|
|
|
ans = 1;
|
|
done = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
|
event.button.button >= 1 &&
|
|
event.button.button <= 3)
|
|
{
|
|
if (event.button.x >= 166 + PROMPTOFFSETX &&
|
|
event.button.x < 166 + PROMPTOFFSETX + 48)
|
|
{
|
|
if (event.button.y >= 178 + PROMPTOFFSETY &&
|
|
event.button.y < 178 + PROMPTOFFSETY + 48)
|
|
{
|
|
ans = 1;
|
|
done = 1;
|
|
}
|
|
else if (strlen(btn_no) != 0 &&
|
|
event.button.y >= 230 + PROMPTOFFSETY &&
|
|
event.button.y < 230 + PROMPTOFFSETY + 48)
|
|
{
|
|
ans = 0;
|
|
done = 1;
|
|
}
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEMOTION)
|
|
{
|
|
if (event.button.x >= 166 + PROMPTOFFSETX &&
|
|
event.button.x < 166 + 48 + PROMPTOFFSETX &&
|
|
((event.button.y >= 178 + PROMPTOFFSETY &&
|
|
event.button.y < 178 + 48 + PROMPTOFFSETY) ||
|
|
(strlen(btn_no) != 0 &&
|
|
event.button.y >= 230 && event.button.y < 230 + 48)))
|
|
{
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
else
|
|
{
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
}
|
|
}
|
|
while (!done);
|
|
|
|
|
|
/* FIXME: Sound effect! */
|
|
/* ... */
|
|
|
|
|
|
/* Erase question prompt: */
|
|
|
|
update_canvas(0, 0, WINDOW_WIDTH - 96 - 96, 48 * 7 + 40 + HEIGHTOFFSET);
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
/* Free memory and prepare to quit: */
|
|
|
|
void cleanup(void)
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < MAX_STAMPS; ++i )
|
|
{
|
|
if ( txt_stamps[i] )
|
|
{
|
|
free(txt_stamps[i]);
|
|
txt_stamps[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < MAX_STAMPS; ++i )
|
|
{
|
|
if ( inf_stamps[i] )
|
|
{
|
|
free(inf_stamps[i]);
|
|
inf_stamps[i] = NULL;
|
|
}
|
|
}
|
|
|
|
free_surface_array( img_brushes, MAX_BRUSHES );
|
|
free_surface_array( img_stamps, MAX_STAMPS );
|
|
free_surface_array( img_tools, NUM_TOOLS );
|
|
free_surface_array( img_tool_names, NUM_TOOLS );
|
|
free_surface_array( img_title_names, NUM_TITLES );
|
|
free_surface_array( img_magics, NUM_MAGICS );
|
|
free_surface_array( img_magic_names, NUM_MAGICS );
|
|
free_surface_array( img_shapes, NUM_SHAPES );
|
|
free_surface_array( img_shape_names, NUM_SHAPES );
|
|
free_surface_array( img_tux, NUM_TIP_TUX );
|
|
|
|
free_surface( &img_openlabels_open );
|
|
free_surface( &img_openlabels_erase );
|
|
free_surface( &img_openlabels_back );
|
|
|
|
free_surface( &img_progress );
|
|
|
|
free_surface( &img_yes );
|
|
free_surface( &img_no );
|
|
|
|
free_surface( &img_title_on );
|
|
free_surface( &img_title_off );
|
|
free_surface( &img_title_large_on );
|
|
free_surface( &img_title_large_off );
|
|
|
|
free_surface( &img_open );
|
|
free_surface( &img_erase );
|
|
free_surface( &img_back );
|
|
|
|
free_surface( &img_btn_up );
|
|
free_surface( &img_btn_down );
|
|
free_surface( &img_btn_off );
|
|
|
|
free_surface( &img_cursor_up );
|
|
free_surface( &img_cursor_down );
|
|
|
|
free_surface( &img_scroll_up );
|
|
free_surface( &img_scroll_down );
|
|
free_surface( &img_scroll_up_off );
|
|
free_surface( &img_scroll_down_off );
|
|
|
|
free_surface( &img_paintcan );
|
|
|
|
free_surface( &img_sparkles );
|
|
|
|
free_surface_array( undo_bufs, NUM_UNDO_BUFS );
|
|
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
|
free_surface_array( img_color_btns, NUM_COLORS );
|
|
#endif
|
|
free_surface_array( img_stamp_thumbs, MAX_STAMPS );
|
|
|
|
free_surface( &screen );
|
|
free_surface( &canvas );
|
|
free_surface( &img_cur_brush );
|
|
|
|
if (font != NULL)
|
|
{
|
|
TTF_CloseFont(font);
|
|
font = NULL;
|
|
}
|
|
|
|
if (small_font != NULL)
|
|
{
|
|
TTF_CloseFont(small_font);
|
|
small_font = NULL;
|
|
}
|
|
|
|
if (large_font != NULL)
|
|
{
|
|
TTF_CloseFont(large_font);
|
|
large_font = NULL;
|
|
}
|
|
|
|
for (i = 0; i < MAX_FONTS; i++)
|
|
{
|
|
if (fonts[i])
|
|
{
|
|
TTF_CloseFont(fonts[i]);
|
|
fonts[i] = NULL;
|
|
}
|
|
}
|
|
|
|
#ifndef NOSOUND
|
|
if (use_sound)
|
|
{
|
|
for (i = 0; i < NUM_SOUNDS; i++)
|
|
{
|
|
if (sounds[i])
|
|
{
|
|
Mix_FreeChunk(sounds[i]);
|
|
sounds[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_stamps; i++)
|
|
{
|
|
if (snd_stamps[i])
|
|
{
|
|
Mix_FreeChunk(snd_stamps[i]);
|
|
snd_stamps[i] = NULL;
|
|
}
|
|
}
|
|
|
|
Mix_CloseAudio();
|
|
}
|
|
#endif
|
|
|
|
|
|
free_cursor(&cursor_hand);
|
|
free_cursor(&cursor_arrow);
|
|
free_cursor(&cursor_watch);
|
|
free_cursor(&cursor_up);
|
|
free_cursor(&cursor_down);
|
|
free_cursor(&cursor_tiny);
|
|
free_cursor(&cursor_crosshair);
|
|
free_cursor(&cursor_brush);
|
|
free_cursor(&cursor_wand);
|
|
free_cursor(&cursor_insertion);
|
|
free_cursor(&cursor_rotate);
|
|
|
|
|
|
/* (Just in case...) */
|
|
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
|
|
|
|
|
/* Close up! */
|
|
|
|
TTF_Quit();
|
|
SDL_Quit();
|
|
}
|
|
|
|
|
|
void free_cursor(SDL_Cursor ** cursor)
|
|
{
|
|
if (*cursor)
|
|
{
|
|
SDL_FreeCursor(*cursor);
|
|
*cursor = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void free_surface(SDL_Surface **surface_array)
|
|
{
|
|
if (*surface_array)
|
|
{
|
|
SDL_FreeSurface(*surface_array);
|
|
*surface_array = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void free_surface_array(SDL_Surface *surface_array[], int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
free_surface(&surface_array[i]);
|
|
}
|
|
}
|
|
|
|
|
|
/* Update screen where shape is/was: */
|
|
|
|
void update_shape(int cx, int ox1, int ox2, int cy, int oy1, int oy2, int fix)
|
|
{
|
|
int rx, ry;
|
|
|
|
rx = abs(ox1 - cx);
|
|
if (abs(ox2 - cx) > rx)
|
|
rx = abs(ox2 - cx);
|
|
|
|
ry = abs(oy1 - cy);
|
|
if (abs(oy2 - cy) > ry)
|
|
ry = abs(oy2 - cy);
|
|
|
|
if (fix)
|
|
{
|
|
if (ry > rx)
|
|
rx = ry;
|
|
else
|
|
ry = rx;
|
|
}
|
|
|
|
SDL_UpdateRect(screen, max((cx - rx), 0) + 96, max(cy - ry, 0),
|
|
min((cx + rx) + 96, screen->w),
|
|
min(cy + ry, screen->h));
|
|
}
|
|
|
|
|
|
|
|
/* Draw a shape! */
|
|
|
|
void do_shape(int cx, int cy, int ox, int oy, int rotn, int use_brush)
|
|
{
|
|
int side, angle_skip, init_ang, rx, ry, rmax, x1, y1, x2, y2, xp, yp,
|
|
old_brush, step;
|
|
float a1, a2, rotn_rad;
|
|
#ifdef SCAN_FILL
|
|
point_type pts[1024]; /* Careful! */
|
|
fpoint_type fpts_orig[1024], fpts_new[1024];
|
|
int i;
|
|
int num_pts;
|
|
#else
|
|
int xx;
|
|
#endif
|
|
|
|
|
|
/* Determine radius/shape of the shape to draw: */
|
|
|
|
old_brush = 0;
|
|
rx = ox - cx;
|
|
ry = oy - cy;
|
|
|
|
|
|
/* If the shape has a 1:1 ("locked") aspect ratio, use the larger radius: */
|
|
|
|
if (shape_locked[cur_shape])
|
|
{
|
|
if (rx > ry)
|
|
ry = rx;
|
|
else
|
|
rx = ry;
|
|
}
|
|
|
|
|
|
/* Render a default brush: */
|
|
|
|
if (use_brush)
|
|
{
|
|
old_brush = cur_brush;
|
|
cur_brush = 0; /* Kludgy! */
|
|
render_brush();
|
|
}
|
|
|
|
|
|
/* Draw the shape: */
|
|
|
|
angle_skip = 360 / shape_sides[cur_shape];
|
|
|
|
init_ang = shape_init_ang[cur_shape];
|
|
|
|
|
|
#ifdef SCAN_FILL
|
|
num_pts = 0;
|
|
#endif
|
|
|
|
|
|
step = 1;
|
|
|
|
if (dont_do_xor && !use_brush)
|
|
{
|
|
/* If we're in light outline mode & not drawing the shape with the brush,
|
|
if it has lots of sides (like a circle), reduce the number of sides: */
|
|
|
|
if (shape_sides[cur_shape] > 5)
|
|
step = (shape_sides[cur_shape] / 8);
|
|
}
|
|
|
|
|
|
for (side = 0; side < shape_sides[cur_shape]; side = side + step)
|
|
{
|
|
a1 = (angle_skip * side + init_ang) * M_PI / 180;
|
|
a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
|
|
|
|
x1 = (int) (cos(a1) * rx);
|
|
y1 = (int) (-sin(a1) * ry);
|
|
|
|
x2 = (int) (cos(a2) * rx);
|
|
y2 = (int) (-sin(a2) * ry);
|
|
|
|
|
|
/* Rotate the line: */
|
|
|
|
if (rotn != 0)
|
|
{
|
|
rotn_rad = rotn * M_PI / 180;
|
|
|
|
xp = x1 * cos(rotn_rad) - y1 * sin(rotn_rad);
|
|
yp = x1 * sin(rotn_rad) + y1 * cos(rotn_rad);
|
|
|
|
x1 = xp;
|
|
y1 = yp;
|
|
|
|
xp = x2 * cos(rotn_rad) - y2 * sin(rotn_rad);
|
|
yp = x2 * sin(rotn_rad) + y2 * cos(rotn_rad);
|
|
|
|
x2 = xp;
|
|
y2 = yp;
|
|
}
|
|
|
|
|
|
/* Center the line around the center of the shape: */
|
|
|
|
x1 = x1 + cx;
|
|
y1 = y1 + cy;
|
|
x2 = x2 + cx;
|
|
y2 = y2 + cy;
|
|
|
|
|
|
/* Draw: */
|
|
|
|
if (!use_brush)
|
|
{
|
|
/* (XOR) */
|
|
|
|
line_xor(x1, y1, x2, y2);
|
|
}
|
|
else
|
|
{
|
|
/* Brush */
|
|
|
|
brush_draw(x1, y1, x2, y2, 0);
|
|
}
|
|
|
|
#ifdef SCAN_FILL
|
|
fpts_orig[num_pts].x = (float) x2;
|
|
fpts_orig[num_pts].y = (float) y2;
|
|
num_pts++;
|
|
#endif
|
|
}
|
|
|
|
|
|
if (use_brush && shape_filled[cur_shape])
|
|
{
|
|
#ifdef SCAN_FILL
|
|
/* FIXME: This is all broken!!! */
|
|
|
|
num_pts = clip_polygon(num_pts, fpts_orig, fpts_new);
|
|
|
|
for (i = 0; i < num_pts; i++)
|
|
{
|
|
pts[i].x = (int) (fpts_new[i].x);
|
|
pts[i].y = (int) (fpts_new[i].y);
|
|
}
|
|
|
|
scan_fill(num_pts, pts);
|
|
#else
|
|
/* FIXME: In the meantime, we'll do this lame radius-based fill: */
|
|
|
|
for (xx = abs(rx); xx >= 0; xx--)
|
|
{
|
|
for (side = 0; side < shape_sides[cur_shape]; side++)
|
|
{
|
|
a1 = (angle_skip * side + init_ang) * M_PI / 180;
|
|
a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
|
|
|
|
x1 = (int) (cos(a1) * xx);
|
|
y1 = (int) (-sin(a1) * ry);
|
|
|
|
x2 = (int) (cos(a2) * xx);
|
|
y2 = (int) (-sin(a2) * ry);
|
|
|
|
|
|
/* Rotate the line: */
|
|
|
|
if (rotn != 0)
|
|
{
|
|
rotn_rad = rotn * M_PI / 180;
|
|
|
|
xp = x1 * cos(rotn_rad) - y1 * sin(rotn_rad);
|
|
yp = x1 * sin(rotn_rad) + y1 * cos(rotn_rad);
|
|
|
|
x1 = xp;
|
|
y1 = yp;
|
|
|
|
xp = x2 * cos(rotn_rad) - y2 * sin(rotn_rad);
|
|
yp = x2 * sin(rotn_rad) + y2 * cos(rotn_rad);
|
|
|
|
x2 = xp;
|
|
y2 = yp;
|
|
}
|
|
|
|
|
|
/* Center the line around the center of the shape: */
|
|
|
|
x1 = x1 + cx;
|
|
y1 = y1 + cy;
|
|
x2 = x2 + cx;
|
|
y2 = y2 + cy;
|
|
|
|
|
|
/* Draw: */
|
|
|
|
brush_draw(x1, y1, x2, y2, 0);
|
|
}
|
|
|
|
if (xx % 10 == 0)
|
|
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Update it! */
|
|
|
|
if (use_brush)
|
|
{
|
|
if (abs(rx) > abs(ry))
|
|
rmax = abs(rx) + 20;
|
|
else
|
|
rmax = abs(ry) + 20;
|
|
|
|
update_canvas(cx - rmax, cy - rmax,
|
|
cx + rmax, cy + rmax);
|
|
}
|
|
|
|
|
|
/* Return to normal brush (for paint brush and line tools): */
|
|
|
|
if (use_brush)
|
|
{
|
|
cur_brush = old_brush;
|
|
render_brush();
|
|
}
|
|
}
|
|
|
|
|
|
/* What angle is the mouse away from the center of a shape? */
|
|
|
|
int rotation(int ctr_x, int ctr_y, int ox, int oy)
|
|
{
|
|
return(atan2(oy - ctr_y, ox - ctr_x) * 180 / M_PI);
|
|
}
|
|
|
|
|
|
/* FIXME: Move elsewhere!!! */
|
|
|
|
#define PROMPT_SAVE_OVER_TXT gettext_noop("Save over the older version of this drawing?")
|
|
#define PROMPT_SAVE_OVER_YES gettext_noop("Yes")
|
|
#define PROMPT_SAVE_OVER_NO gettext_noop("No, save a new file")
|
|
|
|
|
|
/* Save the current image: */
|
|
|
|
int do_save(void)
|
|
{
|
|
int res;
|
|
char * fname;
|
|
char tmp[1024];
|
|
SDL_Surface * thm;
|
|
#ifndef SAVE_AS_BMP
|
|
FILE * fi;
|
|
#endif
|
|
|
|
|
|
if (promptless_save == SAVE_OVER_NO)
|
|
{
|
|
/* Never save over - _always_ save a new file! */
|
|
|
|
get_new_file_id();
|
|
}
|
|
else if (promptless_save == SAVE_OVER_PROMPT)
|
|
{
|
|
/* Saving the same picture? */
|
|
|
|
if (file_id[0] != '\0')
|
|
{
|
|
/* We sure we want to do that? */
|
|
|
|
if (do_prompt(PROMPT_SAVE_OVER_TXT,
|
|
PROMPT_SAVE_OVER_YES,
|
|
PROMPT_SAVE_OVER_NO) == 0)
|
|
{
|
|
/* No - Let's save a new picture! */
|
|
|
|
get_new_file_id();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Saving a new picture: */
|
|
|
|
get_new_file_id();
|
|
}
|
|
}
|
|
else if (promptless_save == SAVE_OVER_ALWAYS)
|
|
{
|
|
if (file_id[0] == '\0')
|
|
get_new_file_id();
|
|
}
|
|
|
|
|
|
/* Make sure we have a ~/.tuxpaint directory: */
|
|
|
|
show_progress_bar();
|
|
do_setcursor(cursor_watch);
|
|
|
|
fname = get_fname("");
|
|
|
|
res = mkdir(fname, 0755);
|
|
|
|
if (res != 0 && errno != EEXIST)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't create user data directory:\n"
|
|
"%s\n"
|
|
"The error that occurred was:\n"
|
|
"%s\n\n", fname, strerror(errno));
|
|
|
|
fprintf(stderr,
|
|
"Cannot save the any pictures! SORRY!\n\n");
|
|
|
|
draw_tux_text(TUX_OOPS, SDL_GetError(), 0, 0, 0);
|
|
|
|
free(fname);
|
|
return 0;
|
|
}
|
|
free(fname);
|
|
|
|
show_progress_bar();
|
|
|
|
|
|
/* Make sure we have a ~/.tuxpaint/saved directory: */
|
|
|
|
fname = get_fname("saved");
|
|
|
|
res = mkdir(fname, 0755);
|
|
|
|
if (res != 0 && errno != EEXIST)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Can't create user data directory:\n"
|
|
"%s\n"
|
|
"The error that occurred was:\n"
|
|
"%s\n\n", fname, strerror(errno));
|
|
|
|
fprintf(stderr,
|
|
"Cannot save the any pictures! SORRY!\n\n");
|
|
|
|
draw_tux_text(TUX_OOPS, SDL_GetError(), 0, 0, 0);
|
|
|
|
free(fname);
|
|
return 0;
|
|
}
|
|
free(fname);
|
|
|
|
show_progress_bar();
|
|
|
|
|
|
/* Save the file: */
|
|
|
|
snprintf(tmp, sizeof(tmp), "saved/%s%s", file_id, FNAME_EXTENSION);
|
|
fname = get_fname(tmp);
|
|
debug(fname);
|
|
|
|
#ifdef SAVE_AS_BMP
|
|
if (SDL_SaveBMP(canvas, fname))
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't save the current image!\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n", fname, SDL_GetError());
|
|
|
|
draw_tux_text(TUX_OOPS, SDL_GetError(), 0, 0, 0);
|
|
|
|
free(fname);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* Ta-Da! */
|
|
|
|
playsound(0, SND_SAVE, 1);
|
|
draw_tux_text(TUX_DEFAULT, tool_tips[TOOL_SAVE], 0, 0, 1);
|
|
}
|
|
#else
|
|
fi = fopen(fname, "wb");
|
|
if (fi == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't save the current image!\n"
|
|
"%s\n"
|
|
"The system error that occurred was:\n"
|
|
"%s\n\n",
|
|
fname, strerror(errno));
|
|
|
|
draw_tux_text(TUX_OOPS, strerror(errno), 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
if (!do_png_save(fi, fname, canvas))
|
|
{
|
|
free(fname);
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
free(fname);
|
|
|
|
show_progress_bar();
|
|
|
|
|
|
/* Save thumbnail, too: */
|
|
|
|
snprintf(tmp, sizeof(tmp), "saved/%s-t%s", file_id, FNAME_EXTENSION);
|
|
fname = get_fname(tmp);
|
|
debug(fname);
|
|
|
|
thm = thumbnail(canvas, THUMB_W - 20, THUMB_H - 20, 0);
|
|
|
|
fi = fopen(fname, "wb");
|
|
if (fi == NULL)
|
|
{
|
|
fprintf(stderr, "\nError: Couldn't save thumbnail of image!\n"
|
|
"%s\n"
|
|
"The system error that occurred was:\n"
|
|
"%s\n\n",
|
|
fname, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
do_png_save(fi, fname, thm);
|
|
}
|
|
SDL_FreeSurface(thm);
|
|
|
|
free(fname);
|
|
|
|
|
|
/* All happy! */
|
|
|
|
playsound(0, SND_SAVE, 1);
|
|
draw_tux_text(TUX_DEFAULT, tool_tips[TOOL_SAVE], 0, 0, 1);
|
|
do_setcursor(cursor_arrow);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Actually save the PNG data to the file stream: */
|
|
|
|
int do_png_save(FILE * fi, char * fname, SDL_Surface * surf)
|
|
{
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
unsigned char ** png_rows;
|
|
Uint8 r, g, b;
|
|
int x, y;
|
|
|
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
if (png_ptr == NULL)
|
|
{
|
|
fclose(fi);
|
|
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
|
|
|
fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
|
|
draw_tux_text(TUX_OOPS, strerror(errno), 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
if (info_ptr == NULL)
|
|
{
|
|
fclose(fi);
|
|
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
|
|
|
fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
|
|
draw_tux_text(TUX_OOPS, strerror(errno), 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
fclose(fi);
|
|
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
|
|
|
fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
|
|
draw_tux_text(TUX_OOPS, strerror(errno), 0, 0, 0);
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
png_init_io(png_ptr, fi);
|
|
|
|
info_ptr->width = surf->w;
|
|
info_ptr->height = surf->h;
|
|
info_ptr->bit_depth = 8;
|
|
info_ptr->color_type = PNG_COLOR_TYPE_RGB;
|
|
info_ptr->interlace_type = 1;
|
|
info_ptr->valid = 0;
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
|
|
/* Save the picture: */
|
|
|
|
png_rows = malloc(sizeof(char *) * surf->h);
|
|
|
|
for (y = 0; y < surf->h; y++)
|
|
{
|
|
png_rows[y] = malloc(sizeof(char) * 3 * surf->w);
|
|
|
|
for (x = 0; x < surf->w; x++)
|
|
{
|
|
SDL_GetRGB(getpixel(surf, x, y), surf->format, &r, &g, &b);
|
|
|
|
png_rows[y][x * 3 + 0] = r;
|
|
png_rows[y][x * 3 + 1] = g;
|
|
png_rows[y][x * 3 + 2] = b;
|
|
}
|
|
}
|
|
|
|
png_write_image(png_ptr, png_rows);
|
|
|
|
for (y = 0; y < surf->h; y++)
|
|
free(png_rows[y]);
|
|
|
|
free(png_rows);
|
|
|
|
|
|
png_write_end(png_ptr, NULL);
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(fi);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Pick a new file ID: */
|
|
|
|
void get_new_file_id(void)
|
|
{
|
|
time_t t;
|
|
|
|
t = time(NULL);
|
|
|
|
strftime(file_id, sizeof(file_id), "%Y%m%d%H%M%S", localtime(&t));
|
|
debug(file_id);
|
|
|
|
|
|
/* FIXME: Show thumbnail and prompt for title: */
|
|
}
|
|
|
|
|
|
/* Handle quitting (and prompting to save, if necessary!) */
|
|
|
|
int do_quit(void)
|
|
{
|
|
int done;
|
|
|
|
done = do_prompt(PROMPT_QUIT_TXT,
|
|
PROMPT_QUIT_YES,
|
|
PROMPT_QUIT_NO);
|
|
|
|
if (done && !been_saved)
|
|
{
|
|
if (do_prompt(PROMPT_QUIT_SAVE_TXT,
|
|
PROMPT_QUIT_SAVE_YES,
|
|
PROMPT_QUIT_SAVE_NO))
|
|
{
|
|
if (do_save())
|
|
{
|
|
do_prompt(tool_tips[TOOL_SAVE],
|
|
"Okay",
|
|
"");
|
|
}
|
|
else
|
|
{
|
|
/* Couldn't save! Abort quit! */
|
|
|
|
done = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(done);
|
|
}
|
|
|
|
|
|
/* Open a saved image: */
|
|
|
|
int do_open(int want_new_tool)
|
|
{
|
|
SDL_Surface * img, * img1, * img2;
|
|
SDL_Surface * thumbs[MAX_FILES];
|
|
DIR * d;
|
|
struct dirent * f;
|
|
#ifndef __BEOS__
|
|
struct dirent fs[MAX_FILES];
|
|
#endif
|
|
char * dirname, * rfname;
|
|
char * d_names[MAX_FILES], * d_exts[MAX_FILES];
|
|
FILE * fi;
|
|
char fname[1024];
|
|
int num_files, i, done, update_list, want_erase, cur, which,
|
|
num_files_in_dir, j;
|
|
SDL_Rect dest;
|
|
SDL_Event event;
|
|
SDLKey key;
|
|
Uint32 last_click_time;
|
|
int last_click_which, last_click_button;
|
|
#ifdef __BEOS__
|
|
char * dot = NULL;
|
|
#endif
|
|
|
|
|
|
|
|
do_setcursor(cursor_watch);
|
|
|
|
|
|
/* Open directory of images: */
|
|
|
|
dirname = get_fname("saved");
|
|
|
|
d = opendir(dirname);
|
|
if (d == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: There's no directory of saved images\n"
|
|
"%s\n"
|
|
"The system error that occurred was: %s\n",
|
|
dirname, strerror(errno));
|
|
}
|
|
|
|
|
|
/* Read directory of images and build thumbnails: */
|
|
|
|
num_files = 0;
|
|
|
|
if (d != NULL)
|
|
{
|
|
/* Gather list of files (for sorting): */
|
|
|
|
#ifdef __BEOS__
|
|
num_files_in_dir = 0;
|
|
do
|
|
{
|
|
f = readdir(d);
|
|
|
|
if ( f && (dot = strstr(f->d_name, FNAME_EXTENSION)) != NULL)
|
|
{
|
|
if( strstr(f->d_name, "-t") == NULL)
|
|
{
|
|
d_exts[num_files_in_dir] = strdup(dot);
|
|
*dot = 0;
|
|
d_names[num_files_in_dir] = strdup(f->d_name);
|
|
|
|
|
|
/* Try to load thumbnail first: */
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s-t%s",
|
|
dirname, d_names[num_files], FNAME_EXTENSION);
|
|
|
|
img = IMG_Load(fname);
|
|
if (img != NULL)
|
|
{
|
|
show_progress_bar();
|
|
thumbs[num_files] = SDL_DisplayFormat(img);
|
|
SDL_FreeSurface(img);
|
|
if (thumbs[num_files] == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't create a thumbnail of "
|
|
"saved image!\n"
|
|
"%s\n", fname);
|
|
}
|
|
num_files++;
|
|
}
|
|
else
|
|
{
|
|
/* No thumbnail - load original: */
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s%s",
|
|
dirname, d_names[num_files], FNAME_EXTENSION);
|
|
img = IMG_Load(fname);
|
|
show_progress_bar();
|
|
if (img != NULL)
|
|
{
|
|
/* Turn it into a thumbnail: */
|
|
img1 = SDL_DisplayFormat(img);
|
|
img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
|
|
SDL_FreeSurface(img1);
|
|
show_progress_bar();
|
|
thumbs[num_files] = SDL_DisplayFormat(img2);
|
|
SDL_FreeSurface(img2);
|
|
if (thumbs[num_files] == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't create a thumbnail of "
|
|
"saved image!\n"
|
|
"%s\n", fname);
|
|
}
|
|
SDL_FreeSurface(img);
|
|
show_progress_bar();
|
|
|
|
|
|
/* Let's save this thumbnail, so we don't have to create it
|
|
again next time 'Open' is called: */
|
|
|
|
debug("Saving thumbnail for this one!");
|
|
snprintf(fname, sizeof(fname), "%s/%s-t%s",
|
|
dirname, d_names[num_files], FNAME_EXTENSION);
|
|
fi = fopen(fname, "wb");
|
|
if (fi == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't save thumbnail of "
|
|
"saved image!\n"
|
|
"%s\n"
|
|
"The error that occurred was:\n"
|
|
"%s\n\n",
|
|
fname, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
do_png_save(fi, fname, thumbs[num_files]);
|
|
|
|
/* NOTE: fi is closed there so no need to fclose it here */
|
|
}
|
|
|
|
show_progress_bar();
|
|
num_files++;
|
|
}
|
|
}
|
|
|
|
*dot = '.';
|
|
num_files_in_dir++;
|
|
}
|
|
}
|
|
}
|
|
while (f != NULL && num_files_in_dir < MAX_FILES);
|
|
|
|
closedir(d);
|
|
}
|
|
|
|
#else
|
|
|
|
num_files_in_dir = 0;
|
|
do
|
|
{
|
|
f = readdir(d);
|
|
|
|
if (f != NULL)
|
|
{
|
|
memcpy(&(fs[num_files_in_dir]), f, sizeof(struct dirent));
|
|
num_files_in_dir++;
|
|
}
|
|
}
|
|
while (f != NULL && num_files_in_dir < MAX_FILES);
|
|
|
|
|
|
closedir(d);
|
|
|
|
|
|
/* Sort: */
|
|
|
|
qsort(fs, num_files_in_dir, sizeof(struct dirent),
|
|
(int(*)(const void *, const void *))compare_dirents);
|
|
|
|
|
|
/* Read directory of images and build thumbnails: */
|
|
|
|
for (j = 0; j < num_files_in_dir; j++)
|
|
{
|
|
f = &(fs[j]);
|
|
|
|
show_progress_bar();
|
|
|
|
if (f != NULL)
|
|
{
|
|
debug(f->d_name);
|
|
|
|
if (strstr(f->d_name, "-t") == NULL)
|
|
{
|
|
if (strstr(f->d_name, FNAME_EXTENSION) != NULL
|
|
#ifndef SAVE_AS_BMP
|
|
/* Support legacy BMP files for load: */
|
|
|
|
|| strstr(f->d_name, ".bmp") != NULL
|
|
#endif
|
|
)
|
|
{
|
|
strcpy(fname, f->d_name);
|
|
if (strstr(fname, FNAME_EXTENSION) != NULL)
|
|
{
|
|
strcpy(strstr(fname, FNAME_EXTENSION), "");
|
|
d_exts[num_files] = strdup(FNAME_EXTENSION);
|
|
}
|
|
|
|
#ifndef SAVE_AS_BMP
|
|
if (strstr(fname, ".bmp") != NULL)
|
|
{
|
|
strcpy(strstr(fname, ".bmp"), "");
|
|
d_exts[num_files] = strdup(".bmp");
|
|
}
|
|
#endif
|
|
|
|
d_names[num_files] = strdup(fname);
|
|
|
|
|
|
/* Try to load thumbnail first: */
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s-t.png", dirname,
|
|
d_names[num_files]);
|
|
debug(fname);
|
|
img = IMG_Load(fname);
|
|
|
|
if (img != NULL)
|
|
{
|
|
show_progress_bar();
|
|
|
|
thumbs[num_files] = SDL_DisplayFormat(img);
|
|
SDL_FreeSurface(img);
|
|
|
|
if (thumbs[num_files] == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't create a thumbnail of "
|
|
"saved image!\n"
|
|
"%s\n", fname);
|
|
}
|
|
|
|
num_files++;
|
|
}
|
|
else
|
|
{
|
|
/* No thumbnail - load original: */
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s",
|
|
dirname, f->d_name);
|
|
debug(fname);
|
|
#ifdef SAVE_AS_BMP
|
|
img = SDL_LoadBMP(fname);
|
|
#else
|
|
img = IMG_Load(fname);
|
|
#endif
|
|
|
|
show_progress_bar();
|
|
|
|
if (img == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: I can't open one of the "
|
|
"saved files!\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that "
|
|
"occurred was:\n"
|
|
"%s\n\n",
|
|
fname, SDL_GetError());
|
|
|
|
free(d_names[num_files]);
|
|
free(d_exts[num_files]);
|
|
}
|
|
else
|
|
{
|
|
/* Turn it into a thumbnail: */
|
|
|
|
img1 = SDL_DisplayFormat(img);
|
|
img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
|
|
SDL_FreeSurface(img1);
|
|
|
|
show_progress_bar();
|
|
|
|
thumbs[num_files] = SDL_DisplayFormat(img2);
|
|
SDL_FreeSurface(img2);
|
|
if (thumbs[num_files] == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't create a thumbnail of "
|
|
"saved image!\n"
|
|
"%s\n", fname);
|
|
}
|
|
|
|
SDL_FreeSurface(img);
|
|
|
|
show_progress_bar();
|
|
|
|
|
|
/* Let's save this thumbnail, so we don't have to
|
|
create it again next time 'Open' is called: */
|
|
|
|
debug("Saving thumbnail for this one!");
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s-t.png", dirname,
|
|
d_names[num_files]);
|
|
|
|
fi = fopen(fname, "wb");
|
|
if (fi == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: Couldn't save thumbnail of "
|
|
"saved image!\n"
|
|
"%s\n"
|
|
"The error that occurred was:\n"
|
|
"%s\n\n",
|
|
fname, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
do_png_save(fi, fname, thumbs[num_files]);
|
|
}
|
|
|
|
show_progress_bar();
|
|
|
|
|
|
num_files++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* It was a thumbnail file ("...-t.png") */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
free(dirname);
|
|
|
|
|
|
#ifdef DEBUG
|
|
printf("%d saved files were found!\n", num_files);
|
|
#endif
|
|
|
|
if (num_files == 0)
|
|
{
|
|
do_prompt(PROMPT_OPEN_NOFILES_TXT, PROMPT_OPEN_NOFILES_YES, "");
|
|
}
|
|
else
|
|
{
|
|
/* Let user choose an image: */
|
|
|
|
draw_tux_text(TUX_BORED,
|
|
textdir(gettext_noop("Choose the picture you want, "
|
|
"then click 'Open'")), 0, 0, 1);
|
|
|
|
cur = 0;
|
|
update_list = 1;
|
|
want_erase = 0;
|
|
|
|
done = 0;
|
|
which = 0;
|
|
|
|
last_click_which = -1;
|
|
last_click_time = 0;
|
|
last_click_button = -1;
|
|
|
|
|
|
do_setcursor(cursor_arrow);
|
|
|
|
|
|
do
|
|
{
|
|
/* Update screen: */
|
|
|
|
if (update_list)
|
|
{
|
|
/* Erase: */
|
|
|
|
dest.x = 96;
|
|
dest.y = 0;
|
|
dest.w = WINDOW_WIDTH - 96 - 96;
|
|
dest.h = 48 * 7 + 40 + HEIGHTOFFSET;
|
|
|
|
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
|
|
255, 255, 255));
|
|
|
|
|
|
/* Draw icons: */
|
|
|
|
for (i = cur; i < cur + 16 && i < num_files; i++)
|
|
{
|
|
/* Draw cursor: */
|
|
|
|
dest.x = THUMB_W * ((i - cur) % 4) + 96;
|
|
dest.y = THUMB_H * ((i - cur) / 4) + 24;
|
|
|
|
if (i == which)
|
|
{
|
|
SDL_BlitSurface(img_cursor_down, NULL, screen, &dest);
|
|
debug(d_names[i]);
|
|
}
|
|
else
|
|
SDL_BlitSurface(img_cursor_up, NULL, screen, &dest);
|
|
|
|
|
|
|
|
dest.x = THUMB_W * ((i - cur) % 4) + 96 + 10 +
|
|
(THUMB_W - 20 - thumbs[i]->w) / 2;
|
|
dest.y = THUMB_H * ((i - cur) / 4) + 24 + 10 +
|
|
(THUMB_H - 20 - thumbs[i]->h) / 2;
|
|
|
|
if (thumbs[i] != NULL)
|
|
SDL_BlitSurface(thumbs[i], NULL, screen, &dest);
|
|
}
|
|
|
|
|
|
/* Draw arrows: */
|
|
|
|
dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
|
|
dest.y = 0;
|
|
|
|
if (cur > 0)
|
|
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
|
else
|
|
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
|
|
|
dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
|
|
|
if (cur < num_files - 16)
|
|
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
|
else
|
|
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
|
|
|
|
|
/* "Open" button: */
|
|
|
|
dest.x = 96;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
|
SDL_BlitSurface(img_open, NULL, screen, &dest);
|
|
|
|
dest.x = 96 + (48 - img_openlabels_open->w) / 2;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_open->h;
|
|
SDL_BlitSurface(img_openlabels_open, NULL, screen, &dest);
|
|
|
|
|
|
/* "Back" button: */
|
|
|
|
dest.x = WINDOW_WIDTH - 96 - 48;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
|
SDL_BlitSurface(img_back, NULL, screen, &dest);
|
|
|
|
dest.x = WINDOW_WIDTH - 96 - 48 + (48 - img_openlabels_back->w) / 2;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_back->h;
|
|
SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
|
|
|
|
|
|
/* "Erase" button: */
|
|
|
|
dest.x = WINDOW_WIDTH - 96 - 48 - 48;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
|
SDL_BlitSurface(img_erase, NULL, screen, &dest);
|
|
|
|
dest.x = WINDOW_WIDTH - 96 - 48 - 48 + (48 - img_openlabels_erase->w) / 2;
|
|
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_erase->h;
|
|
SDL_BlitSurface(img_openlabels_erase, NULL, screen, &dest);
|
|
|
|
|
|
SDL_Flip(screen);
|
|
|
|
update_list = 0;
|
|
}
|
|
|
|
|
|
SDL_WaitEvent(&event);
|
|
|
|
if (event.type == SDL_QUIT)
|
|
{
|
|
done = 1;
|
|
|
|
/* FIXME: Handle SDL_Quit better */
|
|
}
|
|
else if (event.type == SDL_ACTIVEEVENT)
|
|
{
|
|
handle_active(&event);
|
|
}
|
|
else if (event.type == SDL_KEYUP)
|
|
{
|
|
key = event.key.keysym.sym;
|
|
|
|
handle_keymouse(key, SDL_KEYUP);
|
|
}
|
|
else if (event.type == SDL_KEYDOWN)
|
|
{
|
|
key = event.key.keysym.sym;
|
|
|
|
handle_keymouse(key, SDL_KEYDOWN);
|
|
|
|
if (key == SDLK_LEFT)
|
|
{
|
|
if (which > 0)
|
|
{
|
|
which--;
|
|
|
|
if (which < cur)
|
|
cur = cur - 4;
|
|
|
|
update_list = 1;
|
|
}
|
|
}
|
|
else if (key == SDLK_RIGHT)
|
|
{
|
|
if (which < num_files - 1)
|
|
{
|
|
which++;
|
|
|
|
if (which >= cur + 16)
|
|
cur = cur + 4;
|
|
|
|
update_list = 1;
|
|
}
|
|
}
|
|
else if (key == SDLK_UP)
|
|
{
|
|
if (which >= 0)
|
|
{
|
|
which = which - 4;
|
|
|
|
if (which < 0)
|
|
which = 0;
|
|
|
|
if (which < cur)
|
|
cur = cur - 4;
|
|
|
|
update_list = 1;
|
|
}
|
|
}
|
|
else if (key == SDLK_DOWN)
|
|
{
|
|
if (which < num_files)
|
|
{
|
|
which = which + 4;
|
|
|
|
if (which >= num_files)
|
|
which = num_files - 1;
|
|
|
|
if (which >= cur + 16)
|
|
cur = cur + 4;
|
|
|
|
update_list = 1;
|
|
}
|
|
}
|
|
else if (key == SDLK_RETURN || key == SDLK_SPACE)
|
|
{
|
|
/* Open */
|
|
|
|
done = 1;
|
|
playsound(1, SND_CLICK, 1);
|
|
}
|
|
else if (key == SDLK_ESCAPE)
|
|
{
|
|
/* Go back: */
|
|
|
|
which = -1;
|
|
done = 1;
|
|
playsound(1, SND_CLICK, 1);
|
|
}
|
|
else if (key == SDLK_d &&
|
|
(event.key.keysym.mod & KMOD_CTRL ||
|
|
event.key.keysym.mod & KMOD_LCTRL ||
|
|
event.key.keysym.mod & KMOD_RCTRL))
|
|
{
|
|
/* Delete! */
|
|
|
|
want_erase = 1;
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
|
event.button.button >= 1 &&
|
|
event.button.button <= 3)
|
|
{
|
|
if (event.button.x >= 96 && event.button.x < WINDOW_WIDTH - 96 &&
|
|
event.button.y >= 24 &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET - 48))
|
|
{
|
|
/* Picked an icon! */
|
|
|
|
which = ((event.button.x - 96) / (THUMB_W) +
|
|
(((event.button.y - 24) / THUMB_H) * 4)) + cur;
|
|
|
|
if (which < num_files)
|
|
{
|
|
playsound(1, SND_BLEEP, 1);
|
|
update_list = 1;
|
|
|
|
|
|
if (which == last_click_which &&
|
|
SDL_GetTicks() < last_click_time + 1000 &&
|
|
event.button.button == last_click_button)
|
|
{
|
|
/* Double-click! */
|
|
|
|
done = 1;
|
|
}
|
|
|
|
last_click_which = which;
|
|
last_click_time = SDL_GetTicks();
|
|
last_click_button = event.button.button;
|
|
}
|
|
}
|
|
else if (event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
|
|
event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2)
|
|
{
|
|
if (event.button.y < 24)
|
|
{
|
|
/* Up scroll button: */
|
|
|
|
if (cur > 0)
|
|
{
|
|
cur = cur - 4;
|
|
update_list = 1;
|
|
playsound(1, SND_SCROLL, 1);
|
|
|
|
if (cur == 0)
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
|
|
if (which > cur + 16)
|
|
which = which - 4;
|
|
}
|
|
else if (event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET - 48) &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET - 24))
|
|
{
|
|
/* Down scroll button: */
|
|
|
|
if (cur < num_files - 16)
|
|
{
|
|
cur = cur + 4;
|
|
update_list = 1;
|
|
playsound(1, SND_SCROLL, 1);
|
|
|
|
if (cur >= num_files - 16)
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
|
|
if (which < cur)
|
|
which = which + 4;
|
|
}
|
|
}
|
|
else if (event.button.x >= 96 && event.button.x < 96 + 48 &&
|
|
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
|
{
|
|
/* Open */
|
|
|
|
done = 1;
|
|
playsound(1, SND_CLICK, 1);
|
|
}
|
|
else if (event.button.x >= (WINDOW_WIDTH - 96 - 48) &&
|
|
event.button.x < (WINDOW_WIDTH - 96) &&
|
|
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
|
{
|
|
/* Back */
|
|
|
|
which = -1;
|
|
done = 1;
|
|
playsound(1, SND_CLICK, 1);
|
|
}
|
|
else if (event.button.x >= (WINDOW_WIDTH - 96 - 48 - 48) &&
|
|
event.button.x < (WINDOW_WIDTH - 48 - 96) &&
|
|
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
|
{
|
|
/* Erase */
|
|
|
|
want_erase = 1;
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEMOTION)
|
|
{
|
|
/* Deal with mouse pointer shape! */
|
|
|
|
if (event.button.y < 24 &&
|
|
event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
|
|
event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 &&
|
|
cur > 0)
|
|
{
|
|
/* Scroll up button: */
|
|
|
|
do_setcursor(cursor_up);
|
|
}
|
|
else if (event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET - 48) &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET - 24) &&
|
|
event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
|
|
event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 &&
|
|
cur < num_files - 16)
|
|
{
|
|
/* Scroll down button: */
|
|
|
|
do_setcursor(cursor_down);
|
|
}
|
|
else if (((event.button.x >= 96 && event.button.x < 96 + 48) ||
|
|
(event.button.x >= (WINDOW_WIDTH - 96 - 48) &&
|
|
event.button.x < (WINDOW_WIDTH - 96)) ||
|
|
(event.button.x >= (WINDOW_WIDTH - 96 - 48 - 48) &&
|
|
event.button.x < (WINDOW_WIDTH - 48 - 96))) &&
|
|
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
|
{
|
|
/* One of the command buttons: */
|
|
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
else if (event.button.x >= 96 && event.button.x < WINDOW_WIDTH - 96 &&
|
|
event.button.y > 24 &&
|
|
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
|
((((event.button.x - 96) / (THUMB_W) +
|
|
(((event.button.y - 24) / THUMB_H) * 4)) +
|
|
cur) < num_files))
|
|
{
|
|
/* One of the thumbnails: */
|
|
|
|
do_setcursor(cursor_hand);
|
|
}
|
|
else
|
|
{
|
|
/* Unclickable... */
|
|
|
|
do_setcursor(cursor_arrow);
|
|
}
|
|
}
|
|
|
|
|
|
if (want_erase)
|
|
{
|
|
want_erase = 0;
|
|
|
|
if (do_prompt(PROMPT_ERASE_TXT,
|
|
PROMPT_ERASE_YES, PROMPT_ERASE_NO))
|
|
{
|
|
snprintf(fname, sizeof(fname), "saved/%s%s",
|
|
d_names[which], d_exts[which]);
|
|
|
|
rfname = get_fname(fname);
|
|
debug(rfname);
|
|
|
|
if (unlink(rfname) == 0)
|
|
{
|
|
thumbs[which] = NULL;
|
|
update_list = 1;
|
|
|
|
|
|
/* Delete the thumbnail, too: */
|
|
|
|
snprintf(fname, sizeof(fname), "saved/%s-t.png",
|
|
d_names[which]);
|
|
|
|
free(rfname);
|
|
rfname = get_fname(fname);
|
|
debug(rfname);
|
|
|
|
unlink(rfname);
|
|
|
|
|
|
/* Move all other files up a notch: */
|
|
|
|
free(d_names[which]);
|
|
free(d_exts[which]);
|
|
free_surface(&thumbs[which]);
|
|
|
|
for (i = which; i < num_files - 1; i++)
|
|
{
|
|
d_names[i] = d_names[i + 1];
|
|
d_exts[i] = d_exts[i + 1];
|
|
thumbs[i] = thumbs[i + 1];
|
|
}
|
|
|
|
num_files--;
|
|
|
|
|
|
/* Make sure the cursor doesn't go off the end! */
|
|
|
|
if (which >= num_files)
|
|
which = num_files - 1;
|
|
|
|
|
|
/* No files to open now? */
|
|
|
|
if (which < 0)
|
|
{
|
|
do_prompt(PROMPT_OPEN_NOFILES_TXT,
|
|
PROMPT_OPEN_NOFILES_YES, "");
|
|
done = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
perror(rfname);
|
|
|
|
do_prompt("CAN'T", "OK", "");
|
|
update_list = 1;
|
|
}
|
|
|
|
free(rfname);
|
|
}
|
|
else
|
|
{
|
|
update_list = 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
while (!done);
|
|
|
|
|
|
/* Load the chosen picture: */
|
|
|
|
if (which != -1)
|
|
{
|
|
/* Save old one first? */
|
|
|
|
if (!been_saved)
|
|
{
|
|
if (do_prompt(PROMPT_OPEN_SAVE_TXT,
|
|
PROMPT_OPEN_SAVE_YES,
|
|
PROMPT_OPEN_SAVE_NO))
|
|
{
|
|
do_save();
|
|
}
|
|
}
|
|
|
|
snprintf(fname, sizeof(fname), "saved/%s%s",
|
|
d_names[which], d_exts[which]);
|
|
|
|
rfname = get_fname(fname);
|
|
|
|
#ifdef SAVE_AS_BMP
|
|
img = SDL_LoadBMP(rfname);
|
|
#else
|
|
img = IMG_Load(rfname);
|
|
#endif
|
|
|
|
if (img == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Couldn't load the saved image!\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred "
|
|
"was:\n"
|
|
"%s\n\n", fname, SDL_GetError());
|
|
|
|
do_prompt(PROMPT_OPEN_UNOPENABLE_TXT,
|
|
PROMPT_OPEN_UNOPENABLE_YES, "");
|
|
}
|
|
else
|
|
{
|
|
SDL_FillRect(canvas, NULL,
|
|
SDL_MapRGB(canvas->format, 255, 255, 255));
|
|
|
|
/* FIXME: What to do when in 640x480 mode, and loading an
|
|
800x600 image!? */
|
|
|
|
dest.x = (canvas->w - img->w) / 2;
|
|
dest.y = (canvas->h - img->h) / 2;
|
|
|
|
SDL_BlitSurface(img, NULL, canvas, &dest);
|
|
SDL_FreeSurface(img);
|
|
|
|
cur_undo = 0;
|
|
oldest_undo = 0;
|
|
newest_undo = 0;
|
|
|
|
been_saved = 1;
|
|
reset_avail_tools();
|
|
tool_avail[TOOL_NEW] = 1;
|
|
|
|
tool_avail_bak[TOOL_UNDO] = 0;
|
|
tool_avail_bak[TOOL_REDO] = 0;
|
|
|
|
strcpy(file_id, d_names[which]);
|
|
|
|
want_new_tool = 1;
|
|
}
|
|
|
|
free(rfname);
|
|
}
|
|
|
|
|
|
update_canvas(0, 0, WINDOW_WIDTH - 96 - 96, 48 * 7 + 40 + HEIGHTOFFSET);
|
|
}
|
|
|
|
|
|
/* Clean up: */
|
|
|
|
free_surface_array(thumbs, num_files);
|
|
|
|
for (i = 0; i < num_files; i++)
|
|
{
|
|
free(d_names[i]);
|
|
free(d_exts[i]);
|
|
}
|
|
|
|
return(want_new_tool);
|
|
}
|
|
|
|
|
|
/* -------------- Poly Fill Stuff -------------- */
|
|
|
|
#ifdef SCANLINE_POLY_FILL
|
|
|
|
void insert_edge(edge * list, edge * edg)
|
|
{
|
|
edge * p, * q;
|
|
|
|
debug("insert_edge()");
|
|
|
|
q = list;
|
|
|
|
p = q->next;
|
|
while (p != NULL)
|
|
{
|
|
if (edg->x_intersect < p->x_intersect)
|
|
{
|
|
p = NULL;
|
|
}
|
|
else
|
|
{
|
|
q = p;
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
edg->next = q->next;
|
|
q->next = edg;
|
|
}
|
|
|
|
|
|
int y_next(int k, int cnt, point_type * pts)
|
|
{
|
|
int j;
|
|
|
|
debug("y_next()");
|
|
|
|
if ((k + 1) > (cnt - 1))
|
|
j = 0;
|
|
else
|
|
j = k + 1;
|
|
|
|
while (pts[k].y == pts[j].y)
|
|
{
|
|
if ((j + 1) > (cnt - 1))
|
|
j = 0;
|
|
else
|
|
j++;
|
|
}
|
|
|
|
return (pts[j].y);
|
|
}
|
|
|
|
|
|
void make_edge_rec(point_type lower, point_type upper,
|
|
int y_comp, edge * edg, edge * edges[])
|
|
{
|
|
debug("make_edge_rec()");
|
|
|
|
edg->dx_per_scan = (float)((upper.x - lower.x) / (upper.y - lower.y));
|
|
edg->x_intersect = lower.x;
|
|
|
|
if (upper.y < y_comp)
|
|
edg->y_upper = upper.y - 1;
|
|
else
|
|
edg->y_upper = upper.y;
|
|
|
|
insert_edge(edges[lower.y], edg);
|
|
}
|
|
|
|
|
|
void build_edge_list(int cnt, point_type * pts, edge * edges[])
|
|
{
|
|
edge * edg;
|
|
point_type v1, v2;
|
|
int i, y_prev;
|
|
|
|
debug("build_edge_list()");
|
|
|
|
y_prev = pts[cnt - 2].y;
|
|
|
|
v1.x = pts[cnt - 1].x;
|
|
v1.y = pts[cnt - 1].y;
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
{
|
|
v2 = pts[i];
|
|
if (v1.y != v2.y)
|
|
{
|
|
edg = (edge *) malloc(sizeof(edge));
|
|
if (v1.y < v2.y)
|
|
make_edge_rec(v1, v2, y_next(i, cnt, pts), edg, edges);
|
|
else
|
|
make_edge_rec(v2, v1, y_prev, edg, edges);
|
|
}
|
|
|
|
y_prev = v1.y;
|
|
v1 = v2;
|
|
}
|
|
}
|
|
|
|
|
|
void build_active_list(int scan, edge * active, edge * edges[])
|
|
{
|
|
edge * p, * q;
|
|
|
|
debug("build_active_list()");
|
|
|
|
p = edges[scan]->next;
|
|
while (p != NULL)
|
|
{
|
|
q = p->next;
|
|
insert_edge(active, p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
|
|
void fill_scan(int scan, edge * active)
|
|
{
|
|
edge * p1, * p2;
|
|
int i;
|
|
Uint32 color;
|
|
|
|
|
|
debug("fill_scan()");
|
|
|
|
color = SDL_MapRGB(canvas->format,
|
|
color_hexes[cur_color][0] / 2,
|
|
color_hexes[cur_color][1] / 2,
|
|
color_hexes[cur_color][2] / 2);
|
|
|
|
SDL_LockSurface(canvas);
|
|
|
|
p1 = active->next;
|
|
while (p1 != NULL)
|
|
{
|
|
p2 = p1->next;
|
|
|
|
for (i = p1->x_intersect; i < p2->x_intersect; i++)
|
|
{
|
|
putpixel(canvas, i, scan, color);
|
|
}
|
|
|
|
p1 = p2->next;
|
|
}
|
|
|
|
SDL_UnlockSurface(canvas);
|
|
}
|
|
|
|
|
|
void delete_after(edge * q)
|
|
{
|
|
edge * p;
|
|
|
|
debug("delete_after()");
|
|
|
|
p = q->next;
|
|
q->next = p->next;
|
|
free(p);
|
|
}
|
|
|
|
|
|
void update_active_list(int scan, edge * active)
|
|
{
|
|
edge * q, * p;
|
|
|
|
debug("update_active_list()");
|
|
|
|
q = active;
|
|
p = active->next;
|
|
|
|
while (p != NULL)
|
|
{
|
|
if (scan >= p->y_upper)
|
|
{
|
|
p = p->next;
|
|
delete_after(q);
|
|
}
|
|
else
|
|
{
|
|
p->x_intersect = p->x_intersect + p->dx_per_scan;
|
|
q = p;
|
|
p = p->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void resort_active_list(edge * active)
|
|
{
|
|
edge * q, * p;
|
|
|
|
debug("resort_active_list()");
|
|
|
|
p = active->next;
|
|
|
|
active->next = NULL;
|
|
|
|
while (p != NULL)
|
|
{
|
|
q = p->next;
|
|
insert_edge(active, p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
|
|
void scan_fill(int cnt, point_type * pts)
|
|
{
|
|
/* edge * edges[48 * 7 + 40 + HEIGHTOFFSET + 5], * active; */
|
|
edge * * edges = alloca((48 * 7 + 40 + HEIGHTOFFSET + 5) * sizeof(edge*)),
|
|
* active;
|
|
int i, scan;
|
|
|
|
debug("scan_fill()");
|
|
|
|
|
|
/* Create empty edges: */
|
|
|
|
for (i = 0; i < 48 * 7 + 40 + HEIGHTOFFSET + 5; i++)
|
|
{
|
|
edges[i] = (edge *) malloc(sizeof(edge));
|
|
edges[i]->next = NULL;
|
|
}
|
|
|
|
|
|
/* Build edge list: */
|
|
|
|
build_edge_list(cnt, pts, edges);
|
|
|
|
|
|
/* Set active edge: */
|
|
|
|
active = (edge *) malloc(sizeof(edge));
|
|
active->next = NULL;
|
|
|
|
|
|
/* Scan! */
|
|
|
|
for (scan = 0; scan < 48 * 7 + 40 + HEIGHTOFFSET; scan++)
|
|
{
|
|
build_active_list(scan, active, edges);
|
|
|
|
if (active->next)
|
|
{
|
|
fill_scan(scan, active);
|
|
|
|
update_canvas(0, scan, WINDOW_WIDTH - 96, scan);
|
|
SDL_Flip(screen);
|
|
SDL_Delay(10);
|
|
|
|
update_active_list(scan, active);
|
|
resort_active_list(active);
|
|
}
|
|
}
|
|
|
|
|
|
/* Free edge list: */
|
|
|
|
debug("Freeing...");
|
|
|
|
for (i = 0; i < 48 * 7 + 40 + HEIGHTOFFSET; i++)
|
|
{
|
|
free(edges[i]);
|
|
}
|
|
}
|
|
|
|
|
|
/* ------------- Poly clipping stuff: -------------- */
|
|
|
|
int inside(fpoint_type p, an_edge b)
|
|
{
|
|
if (b == Left)
|
|
{
|
|
if (p.x < 0)
|
|
return 0;
|
|
}
|
|
else if (b == Right)
|
|
{
|
|
if (p.x >= WINDOW_WIDTH - 96)
|
|
return 0;
|
|
}
|
|
else if (b == Bottom)
|
|
{
|
|
if (p.y >= 48 * 7 + 40 + HEIGHTOFFSET)
|
|
return 0;
|
|
}
|
|
else if (b == Top)
|
|
{
|
|
if (p.y < 0)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int cross(fpoint_type p1, fpoint_type p2, an_edge b)
|
|
{
|
|
if (inside(p1, b) == inside(p2, b))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
|
|
fpoint_type intersect(fpoint_type p1, fpoint_type p2, an_edge b)
|
|
{
|
|
fpoint_type ipt;
|
|
float m;
|
|
|
|
if (p1.x != p2.x)
|
|
m = (p1.y - p2.y) / (p1.x - p2.x);
|
|
else
|
|
m = 1.0;
|
|
|
|
if (b == Left)
|
|
{
|
|
ipt.x = 0;
|
|
ipt.y = p2.y + (-p2.x) * m;
|
|
}
|
|
else if (b == Right)
|
|
{
|
|
ipt.x = WINDOW_WIDTH - 96 - 1;
|
|
ipt.y = p2.y + ((WINDOW_WIDTH - 96 - 1) - p2.x) * m;
|
|
}
|
|
else if (b == Top)
|
|
{
|
|
ipt.y = 0;
|
|
if (p1.x != p2.x)
|
|
ipt.x = p2.x + (-p2.y) / m;
|
|
else
|
|
ipt.x = p2.x;
|
|
}
|
|
else if (b == Bottom)
|
|
{
|
|
ipt.y = (48 * 7 + 40 + HEIGHTOFFSET) - 1;
|
|
if (p1.x != p2.x)
|
|
ipt.x = p2.x + (((48 * 7 + 40 + HEIGHTOFFSET) - 1) - p2.y) / m;
|
|
else
|
|
ipt.x = p2.x;
|
|
}
|
|
|
|
return(ipt);
|
|
}
|
|
|
|
|
|
void clip_point(fpoint_type p, an_edge b, fpoint_type * pout, int * cnt,
|
|
fpoint_type * first[], fpoint_type * s)
|
|
{
|
|
fpoint_type ipt;
|
|
|
|
if (first[b] == NULL)
|
|
{
|
|
first[b] = &p;
|
|
}
|
|
else
|
|
{
|
|
if (cross(p, s[b], b))
|
|
{
|
|
ipt = intersect(p, s[b], b);
|
|
|
|
if (b < Top) /* Should be NUM_EDGES? */
|
|
{
|
|
clip_point(ipt, b + 1, pout, cnt, first, s);
|
|
}
|
|
else
|
|
{
|
|
pout[*cnt] = ipt;
|
|
(*cnt)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
s[b] = p;
|
|
|
|
|
|
if (inside(p, b))
|
|
{
|
|
if (b < Top) /* Should be NUM_EDGES? */
|
|
{
|
|
clip_point(p, b + 1, pout, cnt, first, s);
|
|
}
|
|
else
|
|
{
|
|
pout[*cnt] = p;
|
|
(*cnt)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void close_clip(fpoint_type * pout, int * cnt, fpoint_type * first[],
|
|
fpoint_type * s)
|
|
{
|
|
fpoint_type i;
|
|
an_edge b;
|
|
|
|
for (b = Left; b <= Top; b++)
|
|
{
|
|
if (cross(s[b], *first[b], b))
|
|
{
|
|
i = intersect(s[b], *first[b], b);
|
|
if (b < Top)
|
|
{
|
|
clip_point(i, b + 1, pout, cnt, first, s);
|
|
}
|
|
else
|
|
{
|
|
pout[*cnt] = i;
|
|
(*cnt)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int clip_polygon(int n, fpoint_type * pin, fpoint_type * pout)
|
|
{
|
|
fpoint_type * first[NUM_EDGES] = {0, 0, 0, 0};
|
|
fpoint_type s[NUM_EDGES];
|
|
int i, cnt;
|
|
|
|
cnt = 0;
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
clip_point(pin[i], Left, pout, &cnt, first, s);
|
|
}
|
|
|
|
close_clip(pout, &cnt, first, s);
|
|
|
|
return(cnt);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* Let sound effects (e.g., "Save" sfx) play out before quitting... */
|
|
|
|
void wait_for_sfx(void)
|
|
{
|
|
#ifndef NOSOUND
|
|
if (use_sound)
|
|
{
|
|
while (Mix_Playing(-1))
|
|
SDL_Delay(10);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Determine the current language/locale, and set the language string: */
|
|
|
|
int current_language(void)
|
|
{
|
|
char * loc;
|
|
#ifdef WIN32
|
|
char str[128];
|
|
#endif
|
|
int lang, i;
|
|
|
|
|
|
/* Default... */
|
|
|
|
lang = LANG_EN;
|
|
|
|
|
|
#ifndef WIN32
|
|
loc = setlocale(LC_MESSAGES, NULL);
|
|
if (loc != NULL)
|
|
{
|
|
if (strstr(loc, "LC_MESSAGES") != NULL)
|
|
loc = getenv("LANG");
|
|
}
|
|
#else
|
|
loc = getenv("LANG");
|
|
if (!loc)
|
|
{
|
|
loc = g_win32_getlocale();
|
|
if (loc)
|
|
{
|
|
snprintf(str, sizeof(str), "LANG=%s", loc);
|
|
putenv(str);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
debug(loc);
|
|
|
|
if (loc != NULL)
|
|
{
|
|
/* Which, if any, of the locales is it? */
|
|
|
|
for (i = 0; i < NUM_LANGS; i++)
|
|
{
|
|
/* Case-insensitive */
|
|
/* (so that, e.g. "pt_BR" is recognized as "pt_br") */
|
|
|
|
if (strncasecmp(loc, lang_prefixes[i], strlen(lang_prefixes[i])) == 0)
|
|
{
|
|
lang = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("lang=%d\n\n", lang);
|
|
#endif
|
|
|
|
return lang;
|
|
}
|
|
|
|
|
|
/* XOR-based outline of rubber stamp shapes
|
|
(unused if LOW_QUALITY_STAMP_OUTLINE is #defined) */
|
|
|
|
void stamp_xor(int x, int y)
|
|
{
|
|
int xx, yy;
|
|
Uint8 r, g, b, a, olda, abovea;
|
|
|
|
SDL_LockSurface(img_stamps[cur_stamp]);
|
|
SDL_LockSurface(screen);
|
|
|
|
for (yy = (y % 2) + 1; yy < img_stamps[cur_stamp]->h; yy = yy + 2)
|
|
{
|
|
olda = 0;
|
|
|
|
for (xx = (x % 2); xx < img_stamps[cur_stamp]->w; xx = xx + 2)
|
|
{
|
|
SDL_GetRGBA(getpixel(img_stamps[cur_stamp], xx, yy),
|
|
img_stamps[cur_stamp]->format, &r, &g, &b, &a);
|
|
|
|
SDL_GetRGBA(getpixel(img_stamps[cur_stamp], xx, yy - 1),
|
|
img_stamps[cur_stamp]->format, &r, &g, &b, &abovea);
|
|
|
|
if ((a < 128 && olda >= 128) ||
|
|
(a >= 128 && olda < 128) ||
|
|
(a < 128 && abovea >= 128) ||
|
|
(a >= 128 && abovea < 128))
|
|
{
|
|
clipped_putpixel(screen, x + 96 + xx, y + yy,
|
|
0xFFFFFFFF - getpixel(screen, x + 96 + xx, y + yy));
|
|
}
|
|
|
|
olda = a;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(screen);
|
|
SDL_UnlockSurface(img_stamps[cur_stamp]);
|
|
}
|
|
|
|
|
|
/* Returns whether a particular stamp can be colored: */
|
|
|
|
int stamp_colorable(int stamp)
|
|
{
|
|
if (inf_stamps[stamp] != NULL)
|
|
{
|
|
return inf_stamps[stamp]->colorable;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Returns whether a particular stamp can be tinted: */
|
|
|
|
int stamp_tintable(int stamp)
|
|
{
|
|
if (inf_stamps[stamp] != NULL)
|
|
{
|
|
return inf_stamps[stamp]->tintable;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v)
|
|
{
|
|
float rgb_min, rgb_max, delta, r, g, b;
|
|
|
|
r = (r8 / 255.0);
|
|
g = (g8 / 255.0);
|
|
b = (b8 / 255.0);
|
|
|
|
rgb_min = min(r, min(g, b));
|
|
rgb_max = max(r, max(g, b));
|
|
*v = rgb_max;
|
|
|
|
delta = rgb_max - rgb_min;
|
|
|
|
if (rgb_max == 0)
|
|
{
|
|
/* Black */
|
|
|
|
*s = 0;
|
|
*h = -1;
|
|
}
|
|
else
|
|
{
|
|
*s = delta / rgb_max;
|
|
|
|
if (r == rgb_max)
|
|
*h = (g - b) / delta;
|
|
else if (g == rgb_max)
|
|
*h = 2 + (b - r) / delta; /* between cyan & yellow */
|
|
else
|
|
*h = 4 + (r - g) / delta; /* between magenta & cyan */
|
|
|
|
*h = (*h * 60); /* degrees */
|
|
|
|
if (*h < 0)
|
|
*h = (*h + 360);
|
|
}
|
|
}
|
|
|
|
|
|
void hsvtorgb(float h, float s, float v, Uint8 *r8, Uint8 *g8, Uint8 *b8)
|
|
{
|
|
int i;
|
|
float f, p, q, t, r, g, b;
|
|
|
|
if (s == 0)
|
|
{
|
|
/* Achromatic (grey) */
|
|
|
|
r = v;
|
|
g = v;
|
|
b = v;
|
|
}
|
|
else
|
|
{
|
|
h = h / 60;
|
|
i = floor(h);
|
|
f = h - i;
|
|
p = v * (1 - s);
|
|
q = v * (1 - s * f);
|
|
t = v * (1 - s * (1 - f));
|
|
|
|
if (i == 0)
|
|
{
|
|
r = v;
|
|
g = t;
|
|
b = p;
|
|
}
|
|
else if (i == 1)
|
|
{
|
|
r = q;
|
|
g = v;
|
|
b = p;
|
|
}
|
|
else if (i == 2)
|
|
{
|
|
r = p;
|
|
g = v;
|
|
b = t;
|
|
}
|
|
else if (i == 3)
|
|
{
|
|
r = p;
|
|
g = q;
|
|
b = v;
|
|
}
|
|
else if (i == 4)
|
|
{
|
|
r = t;
|
|
g = p;
|
|
b = v;
|
|
}
|
|
else
|
|
{
|
|
r = v;
|
|
g = p;
|
|
b = q;
|
|
}
|
|
}
|
|
|
|
|
|
*r8 = (Uint8) (r * 255);
|
|
*g8 = (Uint8) (g * 255);
|
|
*b8 = (Uint8) (b * 255);
|
|
}
|
|
|
|
|
|
void show_progress_bar(void)
|
|
{
|
|
SDL_Rect dest, src;
|
|
int x;
|
|
|
|
|
|
for (x = 0; x < WINDOW_WIDTH; x = x + 65)
|
|
{
|
|
src.x = 65 - (prog_bar_ctr % 65);
|
|
src.y = 0;
|
|
src.w = 65;
|
|
src.h = 24;
|
|
|
|
dest.x = x;
|
|
dest.y = WINDOW_HEIGHT - 24;
|
|
|
|
SDL_BlitSurface(img_progress, &src, screen, &dest);
|
|
}
|
|
|
|
prog_bar_ctr++;
|
|
|
|
SDL_UpdateRect(screen, 0, WINDOW_HEIGHT - 24, WINDOW_WIDTH, 24);
|
|
}
|
|
|
|
|
|
void do_print(void)
|
|
{
|
|
#if !defined(WIN32) && !defined(__BEOS__) && !defined(__APPLE__)
|
|
/* Linux, Unix, etc. */
|
|
|
|
FILE * pi;
|
|
|
|
pi = popen(printcommand, "w");
|
|
|
|
if (pi == NULL)
|
|
{
|
|
perror(printcommand);
|
|
}
|
|
else
|
|
{
|
|
if (do_png_save(pi, printcommand, canvas))
|
|
do_prompt(PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "");
|
|
}
|
|
#else
|
|
#ifdef WIN32
|
|
/* Win32 */
|
|
|
|
char f[512];
|
|
|
|
int show = (SDL_GetModState() & KMOD_ALT) && !fullscreen;
|
|
|
|
snprintf(f, sizeof(f), "%s/%s", savedir, "print.cfg");
|
|
SurfacePrint(canvas, use_print_config?f:NULL, show);
|
|
#elif defined(__BEOS__)
|
|
/* BeOS */
|
|
|
|
SurfacePrint(canvas);
|
|
#elif defined(__APPLE__)
|
|
/* Mac OS X */
|
|
|
|
int show = (SDL_GetModState() & KMOD_ALT) && !fullscreen;
|
|
|
|
const char* error = SurfacePrint (canvas, show);
|
|
|
|
if (error)
|
|
fprintf (stderr, "Cannot print: %s\n", error);
|
|
else
|
|
do_prompt (PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "");
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
void do_render_cur_text(int do_blit)
|
|
{
|
|
int w, h;
|
|
SDL_Color color = {color_hexes[cur_color][0],
|
|
color_hexes[cur_color][1],
|
|
color_hexes[cur_color][2],
|
|
0};
|
|
SDL_Surface * tmp_surf;
|
|
SDL_Rect dest, src;
|
|
char * str;
|
|
|
|
|
|
/* Keep cursor on the screen! */
|
|
|
|
if (cursor_y > ((48 * 7 + 40 + HEIGHTOFFSET) -
|
|
TTF_FontHeight(fonts[cur_font])))
|
|
{
|
|
cursor_y = ((48 * 7 + 40 + HEIGHTOFFSET) -
|
|
TTF_FontHeight(fonts[cur_font]));
|
|
}
|
|
|
|
|
|
/* Render the text: */
|
|
|
|
if (texttool_len > 0)
|
|
{
|
|
str = uppercase(texttool_str);
|
|
|
|
tmp_surf = RENDER_TEXT(fonts[cur_font], str, color);
|
|
|
|
w = tmp_surf->w;
|
|
h = tmp_surf->h;
|
|
|
|
cursor_textwidth = w;
|
|
|
|
free(str);
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: Do something different! */
|
|
|
|
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
cursor_textwidth = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
if (!do_blit)
|
|
{
|
|
/* FIXME: Only delete what's changed! */
|
|
|
|
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
|
|
|
|
|
/* Draw outline around text: */
|
|
|
|
dest.x = cursor_x - 2 + 96;
|
|
dest.y = cursor_y - 2;
|
|
dest.w = w + 4;
|
|
dest.h = h + 4;
|
|
|
|
if (dest.x + dest.w > WINDOW_WIDTH - 96)
|
|
dest.w = WINDOW_WIDTH - 96 - dest.x;
|
|
if (dest.y + dest.h > (48 * 7 + 40 + HEIGHTOFFSET))
|
|
dest.h = (48 * 7 + 40 + HEIGHTOFFSET) - dest.y;
|
|
|
|
SDL_FillRect(screen, &dest,
|
|
SDL_MapRGB(canvas->format, 0, 0, 0));
|
|
|
|
|
|
/* FIXME: This would be nice if it were alpha-blended: */
|
|
|
|
dest.x = cursor_x + 96;
|
|
dest.y = cursor_y;
|
|
dest.w = w;
|
|
dest.h = h;
|
|
|
|
if (dest.x + dest.w > WINDOW_WIDTH - 96)
|
|
dest.w = WINDOW_WIDTH - 96 - dest.x;
|
|
if (dest.y + dest.h > (48 * 7 + 40 + HEIGHTOFFSET))
|
|
dest.h = (48 * 7 + 40 + HEIGHTOFFSET) - dest.y;
|
|
|
|
if ((color_hexes[cur_color][0] +
|
|
color_hexes[cur_color][1] +
|
|
color_hexes[cur_color][2]) >= 384)
|
|
{
|
|
/* Grey background if blit is white!... */
|
|
|
|
SDL_FillRect(screen, &dest,
|
|
SDL_MapRGB(canvas->format, 64, 64, 64));
|
|
}
|
|
else
|
|
{
|
|
/* White background, normally... */
|
|
|
|
SDL_FillRect(screen, &dest,
|
|
SDL_MapRGB(canvas->format, 255, 255, 255));
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw the text itself! */
|
|
|
|
if (tmp_surf != NULL)
|
|
{
|
|
dest.x = cursor_x;
|
|
dest.y = cursor_y;
|
|
|
|
src.x = 0;
|
|
src.y = 0;
|
|
src.w = tmp_surf->w;
|
|
src.h = tmp_surf->h;
|
|
|
|
if (dest.x + src.w > WINDOW_WIDTH - 96 - 96)
|
|
src.w = WINDOW_WIDTH - 96 - 96 - dest.x;
|
|
if (dest.y + src.h > (48 * 7 + 40 + HEIGHTOFFSET))
|
|
src.h = (48 * 7 + 40 + HEIGHTOFFSET) - dest.y;
|
|
|
|
if (do_blit)
|
|
{
|
|
SDL_BlitSurface(tmp_surf, &src, canvas, &dest);
|
|
update_canvas(dest.x, dest.y, dest.x + tmp_surf->w, dest.y + tmp_surf->h);
|
|
}
|
|
else
|
|
{
|
|
dest.x = dest.x + 96;
|
|
SDL_BlitSurface(tmp_surf, &src, screen, &dest);
|
|
}
|
|
}
|
|
|
|
|
|
/* FIXME: Only update what's changed! */
|
|
|
|
SDL_Flip(screen);
|
|
|
|
|
|
if (tmp_surf != NULL)
|
|
SDL_FreeSurface(tmp_surf);
|
|
}
|
|
|
|
|
|
void loadfonts(char * dir, int fatal)
|
|
{
|
|
DIR * d;
|
|
struct dirent * f;
|
|
struct stat sbuf;
|
|
char fname[512];
|
|
char * d_names[MAX_FILES];
|
|
int num_files, i;
|
|
|
|
|
|
/* Open the directory: */
|
|
|
|
d = opendir(dir);
|
|
if (d == NULL)
|
|
{
|
|
if (fatal)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: I can't find a directory of fonts\n"
|
|
"%s\n"
|
|
"The system error that occurred was: %s\n",
|
|
dir, strerror(errno));
|
|
|
|
cleanup();
|
|
exit(1);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
|
|
/* Read directory for images: */
|
|
|
|
num_files = 0;
|
|
do
|
|
{
|
|
f = readdir(d);
|
|
|
|
if (f != NULL)
|
|
{
|
|
d_names[num_files] = strdup(f->d_name);
|
|
num_files++;
|
|
}
|
|
}
|
|
while (f != NULL && num_files < MAX_FILES);
|
|
|
|
closedir(d);
|
|
|
|
|
|
qsort(d_names, num_files, sizeof(char *),
|
|
(int(*)(const void *, const void *))compare_strings);
|
|
|
|
|
|
/* Do something with each file (load TTFs): */
|
|
|
|
for (i = 0; i < num_files && num_fonts + 3 < MAX_FONTS; i++)
|
|
{
|
|
/* Ignore things starting with "." (e.g., "." and ".." dirs): */
|
|
|
|
if (strstr(d_names[i], ".") != d_names[i])
|
|
{
|
|
/* If it's a directory, recurse down into it: */
|
|
|
|
snprintf(fname, sizeof(fname), "%s/%s", dir, d_names[i]);
|
|
debug(fname);
|
|
|
|
stat(fname, &sbuf);
|
|
|
|
if (strstr(d_names[i], ".ttf") != NULL)
|
|
{
|
|
/* If it has ".ttf" in the filename, assume we can try to load it: */
|
|
|
|
fonts[num_fonts++] = TTF_OpenFont(fname, 16);
|
|
fonts[num_fonts++] = TTF_OpenFont(fname, 24);
|
|
fonts[num_fonts++] = TTF_OpenFont(fname, 32);
|
|
fonts[num_fonts++] = TTF_OpenFont(fname, 48);
|
|
|
|
show_progress_bar();
|
|
}
|
|
}
|
|
|
|
free(d_names[i]);
|
|
}
|
|
|
|
|
|
/* Give warning if too many files were found (e.g., some not loaded): */
|
|
|
|
if (num_fonts == MAX_FONTS)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Reached maximum fonts (%d) which can be loaded.\n\n",
|
|
MAX_FONTS);
|
|
}
|
|
}
|
|
|
|
|
|
/* Return string as uppercase if that option is set: */
|
|
|
|
char * uppercase(char * str)
|
|
{
|
|
char * ustr;
|
|
int i;
|
|
|
|
ustr = strdup(str);
|
|
|
|
if (only_uppercase)
|
|
{
|
|
for (i = 0; i < strlen(ustr); i++)
|
|
ustr[i] = toupper(ustr[i]);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf(" ORIGINAL: %s\n"
|
|
"UPPERCASE: %s\n\n", str, ustr);
|
|
#endif
|
|
|
|
return (ustr);
|
|
}
|
|
|
|
|
|
/* Return string in right-to-left mode, if necessary: */
|
|
|
|
unsigned char * textdir(unsigned char * str)
|
|
{
|
|
unsigned char * dstr;
|
|
int i, j;
|
|
|
|
#ifdef DEBUG
|
|
printf("ORIG_DIR: %s\n", str);
|
|
#endif
|
|
|
|
dstr = (unsigned char *) malloc((strlen(str) + 5) * sizeof(unsigned char));
|
|
|
|
if (need_right_to_left(language))
|
|
{
|
|
dstr[strlen(str)] = '\0';
|
|
|
|
for (i = 0; i < strlen(str); i++)
|
|
{
|
|
j = (strlen(str) - i - 1);
|
|
|
|
if (str[i] < 128) /* 0xxx xxxx - 1 byte */
|
|
{
|
|
dstr[j] = str[i];
|
|
}
|
|
else if ((str[i] & 0xE0) == 0xC0) /* 110x xxxx - 2 bytes */
|
|
{
|
|
dstr[j - 1] = str[i + 0];
|
|
dstr[j - 0] = str[i + 1];
|
|
i = i + 1;
|
|
}
|
|
else if ((str[i] & 0xF0) == 0xE0) /* 1110 xxxx - 3 bytes */
|
|
{
|
|
dstr[j - 2] = str[i + 0];
|
|
dstr[j - 1] = str[i + 1];
|
|
dstr[j - 0] = str[i + 2];
|
|
i = i + 2;
|
|
}
|
|
else /* 1111 0xxx - 4 bytes */
|
|
{
|
|
dstr[j - 3] = str[i + 0];
|
|
dstr[j - 2] = str[i + 1];
|
|
dstr[j - 1] = str[i + 2];
|
|
dstr[j - 0] = str[i + 3];
|
|
i = i + 3;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strcpy(dstr, str);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("L2R_DIR: %s\n", dstr);
|
|
#endif
|
|
|
|
return (dstr);
|
|
}
|
|
|
|
|
|
/* For flood fill... */
|
|
|
|
int colors_close(Uint32 c1, Uint32 c2)
|
|
{
|
|
#ifdef LOW_QUALITY_FLOOD_FILL
|
|
return (c1 == c2);
|
|
#else
|
|
Uint8 r1, g1, b1,
|
|
r2, g2, b2;
|
|
|
|
if (c1 == c2)
|
|
{
|
|
/* Get it over with quick, if possible! */
|
|
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
SDL_GetRGB(c1, canvas->format, &r1, &g1, &b1);
|
|
SDL_GetRGB(c2, canvas->format, &r2, &g2, &b2);
|
|
|
|
if (abs(r1 - r2) <= 64 &&
|
|
abs(g1 - g2) <= 64 &&
|
|
abs(b1 - b2) <= 64)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Flood fill! */
|
|
|
|
void do_flood_fill(int x, int y, Uint32 cur_colr, Uint32 old_colr)
|
|
{
|
|
int fillL, fillR, i, in_line;
|
|
static unsigned char prog_anim;
|
|
|
|
|
|
if (cur_colr == old_colr ||
|
|
colors_close(cur_colr, old_colr))
|
|
return;
|
|
|
|
|
|
fillL = x;
|
|
fillR = x;
|
|
|
|
prog_anim++;
|
|
if ((prog_anim % 4) == 0)
|
|
{
|
|
show_progress_bar();
|
|
playsound(0, SND_BUBBLE, 0);
|
|
}
|
|
|
|
|
|
/* Find left side, filling along the way */
|
|
|
|
in_line = 1;
|
|
|
|
while (in_line)
|
|
{
|
|
putpixel(canvas, fillL, y, cur_colr);
|
|
fillL--;
|
|
|
|
in_line = (fillL < 0) ? 0 : colors_close(getpixel(canvas, fillL, y),
|
|
old_colr);
|
|
}
|
|
|
|
fillL++;
|
|
|
|
|
|
/* Find right side, filling along the way */
|
|
|
|
in_line = 1;
|
|
while (in_line)
|
|
{
|
|
putpixel(canvas, fillR, y, cur_colr);
|
|
fillR++;
|
|
|
|
in_line = (fillR >= canvas->w) ? 0 : colors_close(getpixel(canvas,
|
|
fillR, y),
|
|
old_colr);
|
|
}
|
|
|
|
fillR--;
|
|
|
|
|
|
/* Search top and bottom */
|
|
|
|
for (i = fillL; i <= fillR; i++)
|
|
{
|
|
if (y > 0 && colors_close(getpixel(canvas, i, y - 1), old_colr))
|
|
do_flood_fill(i, y - 1, cur_colr, old_colr);
|
|
|
|
if (y < canvas->h && colors_close(getpixel(canvas, i, y + 1), old_colr))
|
|
do_flood_fill(i, y + 1, cur_colr, old_colr);
|
|
}
|
|
}
|
|
|
|
|
|
/* Scroll Timer */
|
|
|
|
static Uint32 scrolltimer_callback(Uint32 interval, void *param)
|
|
{
|
|
SDL_PushEvent((SDL_Event*)param);
|
|
|
|
return interval;
|
|
}
|
|
|
|
|
|
/* Controls the Text-Timer - interval == 0 removes the timer */
|
|
|
|
void control_drawtext_timer(Uint32 interval, char* text)
|
|
{
|
|
static int activated = 0;
|
|
static SDL_TimerID TimerID = 0;
|
|
static SDL_Event drawtext_event;
|
|
|
|
|
|
/* Remove old timer if any is running */
|
|
|
|
if (activated)
|
|
{
|
|
SDL_RemoveTimer(TimerID);
|
|
activated = 0;
|
|
TimerID = 0;
|
|
}
|
|
|
|
if (interval == 0)
|
|
return;
|
|
|
|
drawtext_event.type = SDL_USEREVENT;
|
|
drawtext_event.user.code = USEREVENT_TEXT_UPDATE;
|
|
drawtext_event.user.data1 = (void*) text;
|
|
|
|
|
|
/* Add new timer */
|
|
|
|
TimerID = SDL_AddTimer(interval, drawtext_callback, (void*) &drawtext_event);
|
|
activated = 1;
|
|
}
|
|
|
|
|
|
/* Drawtext Timer */
|
|
|
|
static Uint32 drawtext_callback(Uint32 interval, void *param)
|
|
{
|
|
SDL_PushEvent((SDL_Event*)param);
|
|
|
|
return 0; /* Remove timer */
|
|
}
|
|
|
|
|
|
void parse_options(FILE * fi)
|
|
{
|
|
char str[256];
|
|
|
|
do
|
|
{
|
|
fgets(str, sizeof(str), fi);
|
|
|
|
strip_trailing_whitespace(str);
|
|
|
|
if (!feof(fi))
|
|
{
|
|
debug(str);
|
|
|
|
|
|
/* Should "lang=" and "locale=" be here as well???
|
|
Comments welcome ... bill@newbreedsoftware.com */
|
|
|
|
|
|
/* FIXME: This should be handled better! */
|
|
/* (e.g., complain on illegal lines, support comments, blanks, etc.) */
|
|
|
|
if (strcmp(str, "fullscreen=yes") == 0)
|
|
{
|
|
fullscreen = 1;
|
|
}
|
|
else if (strcmp(str, "fullscreen=no") == 0 ||
|
|
strcmp(str, "windowed=yes") == 0)
|
|
{
|
|
fullscreen = 0;
|
|
}
|
|
else if (strcmp(str, "800x600=yes") == 0)
|
|
{
|
|
WINDOW_WIDTH = 800;
|
|
WINDOW_HEIGHT = 600;
|
|
}
|
|
else if (strcmp(str, "800x600=no") == 0 ||
|
|
strcmp(str, "640x480=yes") == 0)
|
|
{
|
|
WINDOW_WIDTH = 640;
|
|
WINDOW_HEIGHT = 480;
|
|
}
|
|
else if (strcmp(str, "nooutlines=yes") == 0)
|
|
{
|
|
dont_do_xor = 1;
|
|
}
|
|
else if (strcmp(str, "nooutlines=no") == 0 ||
|
|
strcmp(str, "outlines=yes") == 0)
|
|
{
|
|
dont_do_xor = 0;
|
|
}
|
|
else if (strcmp(str, "keyboard=yes") == 0)
|
|
{
|
|
keymouse = 1;
|
|
}
|
|
else if (strcmp(str, "keyboard=no") == 0 ||
|
|
strcmp(str, "mouse=yes") == 0)
|
|
{
|
|
keymouse = 0;
|
|
}
|
|
else if (strcmp(str, "nowheelmouse=yes") == 0)
|
|
{
|
|
wheely = 0;
|
|
}
|
|
else if (strcmp(str, "nowheelmouse=no") == 0 ||
|
|
strcmp(str, "wheelmouse=yes") == 0)
|
|
{
|
|
wheely = 1;
|
|
}
|
|
else if (strcmp(str, "grab=yes") == 0)
|
|
{
|
|
grab_input = 1;
|
|
}
|
|
else if (strcmp(str, "grab=no") == 0 ||
|
|
strcmp(str, "nograb=yes") == 0)
|
|
{
|
|
grab_input = 0;
|
|
}
|
|
else if (strcmp(str, "nofancycursors=yes") == 0)
|
|
{
|
|
no_fancy_cursors = 1;
|
|
}
|
|
else if (strcmp(str, "nofancycursors=no") == 0 ||
|
|
strcmp(str, "fancycursors=yes") == 0)
|
|
{
|
|
no_fancy_cursors = 0;
|
|
}
|
|
else if (strcmp(str, "uppercase=yes") == 0)
|
|
{
|
|
only_uppercase = 1;
|
|
}
|
|
else if (strcmp(str, "uppercase=no") == 0 ||
|
|
strcmp(str, "mixedcase=yes") == 0)
|
|
{
|
|
only_uppercase = 0;
|
|
}
|
|
else if (strcmp(str, "noquit=yes") == 0)
|
|
{
|
|
disable_quit = 1;
|
|
}
|
|
else if (strcmp(str, "noquit=no") == 0 ||
|
|
strcmp(str, "quit=yes") == 0)
|
|
{
|
|
disable_quit = 0;
|
|
}
|
|
else if (strcmp(str, "noprint=yes") == 0)
|
|
{
|
|
disable_print = 1;
|
|
}
|
|
else if (strcmp(str, "noprint=no") == 0 ||
|
|
strcmp(str, "print=yes") == 0)
|
|
{
|
|
disable_print = 0;
|
|
}
|
|
else if (strcmp(str, "nostamps=yes") == 0)
|
|
{
|
|
dont_load_stamps = 1;
|
|
}
|
|
else if (strcmp(str, "nostamps=no") == 0 ||
|
|
strcmp(str, "stamps=yes") == 0)
|
|
{
|
|
dont_load_stamps = 0;
|
|
}
|
|
else if (strcmp(str, "nosound=yes") == 0)
|
|
{
|
|
use_sound = 0;
|
|
}
|
|
else if (strcmp(str, "nosound=no") == 0 ||
|
|
strcmp(str, "sound=yes") == 0)
|
|
{
|
|
use_sound = 1;
|
|
}
|
|
else if (strcmp(str, "simpleshapes=yes") == 0)
|
|
{
|
|
simple_shapes = 1;
|
|
}
|
|
else if (strcmp(str, "simpleshapes=no") == 0 ||
|
|
strcmp(str, "complexshapes=yes") == 0)
|
|
{
|
|
simple_shapes = 1;
|
|
}
|
|
else if (strstr(str, "lang=") == str)
|
|
{
|
|
langstr = strdup(str + 5);
|
|
#ifdef DEBUG
|
|
printf("langstr set to: %s\n", langstr);
|
|
#endif
|
|
}
|
|
else if (strstr(str, "printdelay=") == str)
|
|
{
|
|
sscanf(str + 11, "%d", &print_delay);
|
|
#ifdef DEBUG
|
|
printf("Print delay set to %d seconds\n", print_delay);
|
|
#endif
|
|
}
|
|
else if (strcmp(str, "printcfg=yes") == 0)
|
|
{
|
|
#ifndef WIN32
|
|
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
|
#endif
|
|
use_print_config = 1;
|
|
}
|
|
else if (strcmp(str, "printcfg=no") == 0 ||
|
|
strcmp(str, "noprintcfg=yes") == 0)
|
|
{
|
|
#ifndef WIN32
|
|
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
|
#endif
|
|
use_print_config = 0;
|
|
}
|
|
else if (strstr(str, "printcommand=") == str)
|
|
{
|
|
printcommand = strdup(str + 13);
|
|
}
|
|
else if (strcmp(str, "saveover=yes") == 0)
|
|
{
|
|
promptless_save = SAVE_OVER_ALWAYS;
|
|
}
|
|
else if (strcmp(str, "saveover=ask") == 0)
|
|
{
|
|
/* (Default) */
|
|
|
|
promptless_save = SAVE_OVER_PROMPT;
|
|
}
|
|
else if (strcmp(str, "saveover=new") == 0)
|
|
{
|
|
promptless_save = SAVE_OVER_NO;
|
|
}
|
|
else if (strstr(str, "savedir=") == str)
|
|
{
|
|
savedir = strdup(str + 8);
|
|
remove_slash(savedir);
|
|
|
|
#ifdef DEBUG
|
|
printf("savedir set to: %s\n", savedir);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
while (!feof(fi));
|
|
}
|
|
|
|
|
|
char * debug_gettext(const char * str)
|
|
{
|
|
if (strcmp(str, dgettext(NULL, str)) == 0)
|
|
{
|
|
printf("NOTRANS: %s\n", str);
|
|
}
|
|
|
|
return(dgettext(NULL, str));
|
|
}
|
|
|
|
|
|
void do_setcursor(SDL_Cursor * c)
|
|
{
|
|
if (!no_fancy_cursors)
|
|
SDL_SetCursor(c);
|
|
}
|
|
|
|
|
|
char * great_str(void)
|
|
{
|
|
return(great_strs[rand() % (sizeof(great_strs) / sizeof(char *))]);
|
|
}
|
|
|
|
|
|
int charsize(char c)
|
|
{
|
|
Uint16 str[2];
|
|
int w, h;
|
|
|
|
str[0] = c;
|
|
str[1] = '\0';
|
|
|
|
TTF_SizeUNICODE(fonts[cur_font], str, &w, &h);
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
void draw_image_title(int t, int x)
|
|
{
|
|
SDL_Rect dest;
|
|
|
|
dest.x = x;
|
|
dest.y = 0;
|
|
SDL_BlitSurface(img_title_on, NULL, screen, &dest);
|
|
|
|
dest.x = x + (96 - img_title_names[t]->w) / 2;;
|
|
dest.y = (40 - img_title_names[t]->h) / 2;
|
|
SDL_BlitSurface(img_title_names[t], NULL, screen, &dest);
|
|
}
|
|
|
|
|
|
int need_unicode(int l)
|
|
{
|
|
int i, need;
|
|
|
|
need = 0;
|
|
|
|
for (i = 0; lang_use_unicode[i] != -1 && need == 0; i++)
|
|
{
|
|
if (lang_use_unicode[i] == l)
|
|
{
|
|
need = 1;
|
|
}
|
|
}
|
|
|
|
return need;
|
|
}
|
|
|
|
|
|
int need_utf8(int l)
|
|
{
|
|
int i, need;
|
|
|
|
need = 0;
|
|
|
|
for (i = 0; lang_use_utf8[i] != -1 && need == 0; i++)
|
|
{
|
|
if (lang_use_utf8[i] == l)
|
|
{
|
|
need = 1;
|
|
}
|
|
}
|
|
|
|
return need;
|
|
}
|
|
|
|
|
|
int need_own_font(int l)
|
|
{
|
|
if (need_utf8(l) || need_unicode(l))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
int need_right_to_left(int l)
|
|
{
|
|
int i, need;
|
|
|
|
need = 0;
|
|
|
|
for (i = 0; lang_use_right_to_left[i] != -1 && need == 0; i++)
|
|
{
|
|
if (lang_use_right_to_left[i] == l)
|
|
{
|
|
need = 1;
|
|
}
|
|
}
|
|
|
|
return need;
|
|
}
|
|
|
|
|
|
|
|
/* Handle keyboard events to control the mouse: */
|
|
|
|
void handle_keymouse(SDLKey key, Uint8 updown)
|
|
{
|
|
SDL_Event event;
|
|
|
|
if (keymouse)
|
|
{
|
|
if (key == SDLK_LEFT)
|
|
mousekey_left = updown;
|
|
else if (key == SDLK_RIGHT)
|
|
mousekey_right = updown;
|
|
else if (key == SDLK_UP)
|
|
mousekey_up = updown;
|
|
else if (key == SDLK_DOWN)
|
|
mousekey_down = updown;
|
|
else if (key == SDLK_SPACE)
|
|
{
|
|
if (updown == SDL_KEYDOWN)
|
|
event.type = SDL_MOUSEBUTTONDOWN;
|
|
else
|
|
event.type = SDL_MOUSEBUTTONUP;
|
|
|
|
event.button.x = mouse_x;
|
|
event.button.y = mouse_y;
|
|
event.button.button = 1;
|
|
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
if (mousekey_up == SDL_KEYDOWN && mouse_y > 0)
|
|
mouse_y = mouse_y - 8;
|
|
else if (mousekey_down == SDL_KEYDOWN && mouse_y < WINDOW_HEIGHT - 1)
|
|
mouse_y = mouse_y + 8;
|
|
|
|
if (mousekey_left == SDL_KEYDOWN && mouse_x > 0)
|
|
mouse_x = mouse_x - 8;
|
|
if (mousekey_right == SDL_KEYDOWN && mouse_x < WINDOW_WIDTH - 1)
|
|
mouse_x = mouse_x + 8;
|
|
|
|
SDL_WarpMouse(mouse_x, mouse_y);
|
|
}
|
|
}
|
|
|
|
|
|
/* Unblank screen in fullscreen mode, if needed: */
|
|
|
|
void handle_active( SDL_Event *event )
|
|
{
|
|
if (event->active.state & SDL_APPACTIVE)
|
|
{
|
|
if (event->active.gain == 1 )
|
|
{
|
|
if ( fullscreen )
|
|
SDL_Flip(screen);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* removes a single '\' or '/' from end of path */
|
|
|
|
char *remove_slash( char *path )
|
|
{
|
|
int len = strlen(path);
|
|
|
|
if (!len)
|
|
return path;
|
|
|
|
if (path[len-1] == '/' || path[len-1] == '\\')
|
|
path[len-1] = 0;
|
|
|
|
return path;
|
|
}
|
|
|
|
|
|
/* Decode a UTF8 string */
|
|
|
|
unsigned char * utf8_decode(unsigned char * str)
|
|
{
|
|
int i;
|
|
unsigned char utf8_char[4];
|
|
unsigned char utf8_str[1024];
|
|
|
|
|
|
utf8_str[0] = '\0';
|
|
|
|
for (i = 0; i < strlen(str); i++)
|
|
{
|
|
/* How many bytes does this character need? */
|
|
|
|
if (str[i] < 128) /* 0xxx xxxx - 1 byte */
|
|
{
|
|
utf8_char[0] = str[i];
|
|
utf8_char[1] = '\0';
|
|
}
|
|
else if ((str[i] & 0xE0) == 0xC0) /* 110x xxxx - 2 bytes */
|
|
{
|
|
utf8_char[0] = str[i];
|
|
utf8_char[1] = str[i + 1];
|
|
utf8_char[2] = '\0';
|
|
i = i + 1;
|
|
}
|
|
else if ((str[i] & 0xF0) == 0xE0) /* 1110 xxxx - 3 bytes */
|
|
{
|
|
utf8_char[0] = str[i];
|
|
utf8_char[1] = str[i + 1];
|
|
utf8_char[2] = str[i + 2];
|
|
utf8_char[3] = '\0';
|
|
i = i + 2;
|
|
}
|
|
else /* 1111 0xxx - 4 bytes */
|
|
{
|
|
utf8_char[0] = str[i];
|
|
utf8_char[1] = str[i + 1];
|
|
utf8_char[2] = str[i + 2];
|
|
utf8_char[3] = str[i + 3];
|
|
utf8_char[4] = '\0';
|
|
i = i + 3;
|
|
}
|
|
|
|
strcat(utf8_str, utf8_char);
|
|
}
|
|
|
|
return(strdup(utf8_str));
|
|
}
|
|
|
|
|
|
/* Escape codes for HTML-style escaping of stamp description strings: */
|
|
|
|
typedef struct escape_string_type {
|
|
char * str;
|
|
unsigned int chr;
|
|
} escape_string_type;
|
|
|
|
|
|
/* Thanks to http://bushong.net/dawn/links/htmlCodes.shtml
|
|
for the list of escape codes */
|
|
|
|
escape_string_type escape_strings[] = {
|
|
{"quot", '\"'},
|
|
{"amp", '&'},
|
|
{"lt", '<'},
|
|
{"gt", '>'},
|
|
{"iexcl", 161},
|
|
{"cent", 162},
|
|
{"pound", 163},
|
|
{"curren", 164},
|
|
{"yen", 165},
|
|
{"brvbar", 166},
|
|
{"sect", 167},
|
|
{"uml", 168},
|
|
{"copy", 169},
|
|
{"ordf", 170},
|
|
{"laquo", 171},
|
|
{"not", 172},
|
|
{"shy", 173},
|
|
{"reg", 174},
|
|
{"macr", 175},
|
|
{"deg", 176},
|
|
{"plusmn", 177},
|
|
{"sup2", 178},
|
|
{"sup3", 179},
|
|
{"acute", 180},
|
|
{"micro", 181},
|
|
{"para", 182},
|
|
{"middot", 183},
|
|
{"cedil", 184},
|
|
{"sup1", 185},
|
|
{"ordm", 186},
|
|
{"raquo", 187},
|
|
{"frac14", 188},
|
|
{"frac12", 189},
|
|
{"frac34", 190},
|
|
{"iquest", 191},
|
|
{"Agrave", 192},
|
|
{"Aacute", 193},
|
|
{"Acirc", 194},
|
|
{"Atilde", 195},
|
|
{"Auml", 196},
|
|
{"Aring", 197},
|
|
{"AElig", 198},
|
|
{"Ccedil", 199},
|
|
{"Egrave", 200},
|
|
{"Eacute", 201},
|
|
{"Ecirc", 202},
|
|
{"Euml", 203},
|
|
{"Igrave", 204},
|
|
{"Iacute", 205},
|
|
{"Icirc", 206},
|
|
{"Iuml", 207},
|
|
{"ETH", 208},
|
|
{"Ntilde", 209},
|
|
{"Ograve", 210},
|
|
{"Oacute", 211},
|
|
{"Ocirc", 212},
|
|
{"Otilde", 213},
|
|
{"Ouml", 214},
|
|
{"times", 215},
|
|
{"Oslash", 216},
|
|
{"Ugrave", 217},
|
|
{"Uacute", 218},
|
|
{"Ucirc", 219},
|
|
{"Uuml", 220},
|
|
{"Yacute", 221},
|
|
{"THORN", 222},
|
|
{"szlig", 223},
|
|
{"agrave", 224},
|
|
{"aacute", 225},
|
|
{"acirc", 226},
|
|
{"atilde", 227},
|
|
{"auml", 228},
|
|
{"aring", 229},
|
|
{"aelig", 230},
|
|
{"ccedil", 231},
|
|
{"egrave", 232},
|
|
{"eacute", 233},
|
|
{"ecirc", 234},
|
|
{"euml", 235},
|
|
{"igrave", 236},
|
|
{"iacute", 237},
|
|
{"icirc", 238},
|
|
{"iuml", 239},
|
|
{"eth", 240},
|
|
{"ntilde", 241},
|
|
{"ograve", 242},
|
|
{"oacute", 243},
|
|
{"ocirc", 244},
|
|
{"otilde", 245},
|
|
{"ouml", 246},
|
|
{"divide", 247},
|
|
{"oslash", 248},
|
|
{"ugrave", 249},
|
|
{"uacute", 250},
|
|
{"ucirc", 251},
|
|
{"uuml", 252},
|
|
{"yacute", 253},
|
|
{"thorn", 254},
|
|
{"yuml", 255},
|
|
{"OElig", 338},
|
|
{"oelig", 339},
|
|
{"Scaron", 352},
|
|
{"scaron", 353},
|
|
{"Yuml", 376},
|
|
{"circ", 710},
|
|
{"tilde", 732},
|
|
{"ensp", 8194},
|
|
{"emsp", 8195},
|
|
{"thinsp", 8201},
|
|
{"zwnj", 8204},
|
|
{"zwj", 8205},
|
|
{"lrm", 8206},
|
|
{"rlm", 8207},
|
|
{"ndash", 8211},
|
|
{"mdash", 8212},
|
|
{"lsquo", 8216},
|
|
{"rsquo", 8217},
|
|
{"sbquo", 8218},
|
|
{"ldquo", 8220},
|
|
{"rdquo", 8221},
|
|
{"bdquo", 8222},
|
|
{"dagger", 8224},
|
|
{"Dagger", 8225},
|
|
{"permil", 8240},
|
|
{"lsaquo", 8249},
|
|
{"rsaquo", 8250},
|
|
{"euro", 8364},
|
|
{NULL, 0}
|
|
};
|
|
|
|
|
|
/* Unescape an HTML-escaped-style string (e.g., convert "ñ" and such) */
|
|
|
|
unsigned char * unescape(char * str)
|
|
{
|
|
int i, j, len, esclen, inside_escape;
|
|
|
|
#ifndef WIN32
|
|
char outstr[strlen(str + 1)], escapestr[strlen(str + 1)];
|
|
#else
|
|
char *outstr = alloca( strlen(str + 1) );
|
|
char *escapestr = alloca( strlen(str + 1) );
|
|
#endif
|
|
|
|
inside_escape = 0;
|
|
len = 0;
|
|
esclen = 0;
|
|
|
|
|
|
/* For each character in the input string: */
|
|
|
|
for (i = 0; i < strlen(str); i++)
|
|
{
|
|
if (str[i] == '&')
|
|
{
|
|
/* Starting an escape character! */
|
|
|
|
inside_escape = 1;
|
|
esclen = 0;
|
|
}
|
|
else
|
|
{
|
|
if (inside_escape == 0)
|
|
{
|
|
/* Not within an escaped character... simply append this literally: */
|
|
|
|
outstr[len++] = str[i];
|
|
}
|
|
else
|
|
{
|
|
/* Within an escaped character! */
|
|
|
|
if (str[i] == ';')
|
|
{
|
|
/* We're ending it! */
|
|
|
|
inside_escape = 0;
|
|
|
|
escapestr[esclen] = '\0';
|
|
|
|
|
|
/* What string was it!? */
|
|
|
|
for (j = 0; escape_strings[j].str != NULL; j++)
|
|
{
|
|
if (strcmp(escape_strings[j].str, escapestr) == 0)
|
|
{
|
|
if (escape_strings[j].chr <= 255)
|
|
{
|
|
outstr[len++] = escape_strings[j].chr;
|
|
}
|
|
else
|
|
{
|
|
/* Needs to be represented by two bytes: */
|
|
|
|
/* FIXME: How should this be handled!? */
|
|
|
|
outstr[len++] =
|
|
((((escape_strings[j].chr & 0xFF00) >> 8) & 0x1F) | 0xC0);
|
|
outstr[len++] = ((escape_strings[j].chr & 0x00FF) >> 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Collect the escaped character: */
|
|
|
|
escapestr[esclen++] = str[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
outstr[len] = '\0';
|
|
|
|
|
|
return(strdup(outstr));
|
|
}
|
|
|
|
|
|
static iconv_t cd = (iconv_t)(-1);
|
|
|
|
int converts()
|
|
{
|
|
if ( cd == (iconv_t)(-1) )
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void convert_open(const char *from)
|
|
{
|
|
cd = iconv_open ("UTF-8", from);
|
|
if (cd == (iconv_t)(-1))
|
|
{
|
|
/* FIXME: Error! */
|
|
}
|
|
}
|
|
|
|
void convert_close()
|
|
{
|
|
iconv_close(cd);
|
|
}
|
|
|
|
char * convert2utf8(char c)
|
|
{
|
|
char inbuf[1];
|
|
char outbuf[4];
|
|
|
|
char *inptr;
|
|
char *outptr;
|
|
size_t inbytes_left, outbytes_left;
|
|
int count;
|
|
|
|
inbuf[0]=c;
|
|
memset(outbuf, 0, 4);
|
|
inbytes_left = 1;
|
|
outbytes_left = 4;
|
|
inptr = inbuf;
|
|
outptr = (char *) outbuf;
|
|
|
|
count = iconv (cd, &inptr, &inbytes_left, &outptr, &outbytes_left);
|
|
/*
|
|
if (count < 0)
|
|
{
|
|
printf ("Error!\n");
|
|
}
|
|
*/
|
|
|
|
return strdup(outbuf);
|
|
}
|
|
|
|
|
|
/* in:
|
|
char *utf8_str - buffer containing a well-formed utf8 string
|
|
int len - length of the above string in bytes; len >= 1
|
|
out:
|
|
int result - length of the above string in bytes after removing
|
|
last utf8 char
|
|
*/
|
|
|
|
int delete_utf8_char(char *utf8_str, int len)
|
|
{
|
|
/* from man utf-8:
|
|
The first byte of a multi-byte sequence which represents a single
|
|
non-ASCII UCS character is always in the range 0xc0 to 0xfd and indi-
|
|
cates how long this multi-byte sequence is. All further bytes in a
|
|
multi-byte sequence are in the range 0x80 to 0xbf.
|
|
*/
|
|
|
|
unsigned char *current_char_ptr = utf8_str + len - 1;
|
|
unsigned char current_char = *current_char_ptr;
|
|
|
|
while ( (current_char >= 0x80) && (current_char <= 0xbf) )
|
|
{
|
|
/* part of the utf8 multibyte char but not the first byte */
|
|
|
|
len--;
|
|
current_char_ptr--;
|
|
current_char = *current_char_ptr;
|
|
}
|
|
|
|
/* we have 1 char to remove */
|
|
*current_char_ptr = '\0';
|
|
len--;
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/* For right-to-left languages, when word-wrapping, we need to
|
|
make sure the text doesn't end up going from bottom-to-top, too! */
|
|
|
|
void anti_carriage_return(int left, int right, int cur_top, int new_top,
|
|
int cur_bot, int line_width)
|
|
{
|
|
SDL_Rect src, dest;
|
|
|
|
|
|
/* Move current set of text down one line (and right-justify it!): */
|
|
|
|
src.x = left;
|
|
src.y = cur_top;
|
|
src.w = line_width;
|
|
src.h = cur_bot - cur_top;
|
|
|
|
dest.x = right - line_width;
|
|
dest.y = new_top;
|
|
|
|
SDL_BlitSurface(screen, &src, screen, &dest);
|
|
|
|
|
|
/* Clear the top line for new text: */
|
|
|
|
dest.x = left;
|
|
dest.y = cur_top;
|
|
dest.w = right - left;
|
|
dest.h = new_top - cur_top;
|
|
|
|
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
|
}
|
|
|