rnd-19990215-2
[rocksndiamonds.git] / src / buttons.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  buttons.c                                               *
12 ***********************************************************/
13
14 #include <stdarg.h>
15
16 #include "buttons.h"
17 #include "tools.h"
18 #include "misc.h"
19 #include "editor.h"
20 #include "tape.h"
21
22 /* some positions in the video tape control window */
23 #define VIDEO_BUTTON_EJECT_XPOS (VIDEO_CONTROL_XPOS + 0 * VIDEO_BUTTON_XSIZE)
24 #define VIDEO_BUTTON_STOP_XPOS  (VIDEO_CONTROL_XPOS + 1 * VIDEO_BUTTON_XSIZE)
25 #define VIDEO_BUTTON_PAUSE_XPOS (VIDEO_CONTROL_XPOS + 2 * VIDEO_BUTTON_XSIZE)
26 #define VIDEO_BUTTON_REC_XPOS   (VIDEO_CONTROL_XPOS + 3 * VIDEO_BUTTON_XSIZE)
27 #define VIDEO_BUTTON_PLAY_XPOS  (VIDEO_CONTROL_XPOS + 4 * VIDEO_BUTTON_XSIZE)
28 #define VIDEO_BUTTON_ANY_YPOS   (VIDEO_CONTROL_YPOS)
29 #define VIDEO_DATE_LABEL_XPOS   (VIDEO_DISPLAY1_XPOS)
30 #define VIDEO_DATE_LABEL_YPOS   (VIDEO_DISPLAY1_YPOS)
31 #define VIDEO_DATE_LABEL_XSIZE  (VIDEO_DISPLAY_XSIZE)
32 #define VIDEO_DATE_LABEL_YSIZE  (VIDEO_DISPLAY_YSIZE)
33 #define VIDEO_DATE_XPOS         (VIDEO_DISPLAY1_XPOS+1)
34 #define VIDEO_DATE_YPOS         (VIDEO_DISPLAY1_YPOS+14)
35 #define VIDEO_DATE_XSIZE        (VIDEO_DISPLAY_XSIZE)
36 #define VIDEO_DATE_YSIZE        16
37 #define VIDEO_REC_LABEL_XPOS    (VIDEO_DISPLAY2_XPOS)
38 #define VIDEO_REC_LABEL_YPOS    (VIDEO_DISPLAY2_YPOS)
39 #define VIDEO_REC_LABEL_XSIZE   20
40 #define VIDEO_REC_LABEL_YSIZE   12
41 #define VIDEO_REC_SYMBOL_XPOS   (VIDEO_DISPLAY2_XPOS+20)
42 #define VIDEO_REC_SYMBOL_YPOS   (VIDEO_DISPLAY2_YPOS)
43 #define VIDEO_REC_SYMBOL_XSIZE  16
44 #define VIDEO_REC_SYMBOL_YSIZE  16
45 #define VIDEO_PLAY_LABEL_XPOS   (VIDEO_DISPLAY2_XPOS+65)
46 #define VIDEO_PLAY_LABEL_YPOS   (VIDEO_DISPLAY2_YPOS)
47 #define VIDEO_PLAY_LABEL_XSIZE  22
48 #define VIDEO_PLAY_LABEL_YSIZE  12
49 #define VIDEO_PLAY_SYMBOL_XPOS  (VIDEO_DISPLAY2_XPOS+52)
50 #define VIDEO_PLAY_SYMBOL_YPOS  (VIDEO_DISPLAY2_YPOS)
51 #define VIDEO_PLAY_SYMBOL_XSIZE 11
52 #define VIDEO_PLAY_SYMBOL_YSIZE 13
53 #define VIDEO_PAUSE_LABEL_XPOS  (VIDEO_DISPLAY2_XPOS)
54 #define VIDEO_PAUSE_LABEL_YPOS  (VIDEO_DISPLAY2_YPOS+20)
55 #define VIDEO_PAUSE_LABEL_XSIZE 35
56 #define VIDEO_PAUSE_LABEL_YSIZE 8
57 #define VIDEO_PAUSE_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS+35)
58 #define VIDEO_PAUSE_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
59 #define VIDEO_PAUSE_SYMBOL_XSIZE 17
60 #define VIDEO_PAUSE_SYMBOL_YSIZE 13
61 #define VIDEO_TIME_XPOS         (VIDEO_DISPLAY2_XPOS+38)
62 #define VIDEO_TIME_YPOS         (VIDEO_DISPLAY2_YPOS+14)
63 #define VIDEO_TIME_XSIZE        50
64 #define VIDEO_TIME_YSIZE        16
65
66 /* special */
67 #define VIDEO_PBEND_LABEL_XPOS  6
68 #define VIDEO_PBEND_LABEL_YPOS  220
69 #define VIDEO_PBEND_LABEL_XSIZE 35
70 #define VIDEO_PBEND_LABEL_YSIZE 30
71
72 #define ON_VIDEO_BUTTON(x,y)    ((x)>=(VX+VIDEO_CONTROL_XPOS) &&        \
73                                  (x)< (VX+VIDEO_CONTROL_XPOS +          \
74                                        VIDEO_CONTROL_XSIZE) &&          \
75                                  (y)>=(VY+VIDEO_CONTROL_YPOS) &&        \
76                                  (y)< (VY+VIDEO_CONTROL_YPOS +          \
77                                        VIDEO_CONTROL_YSIZE))
78 #define VIDEO_BUTTON(x)         (((x)-(VX+VIDEO_CONTROL_XPOS))/VIDEO_BUTTON_XSIZE)
79
80 #define VIDEO_STATE_OFF         (VIDEO_STATE_PLAY_OFF   |       \
81                                  VIDEO_STATE_REC_OFF    |       \
82                                  VIDEO_STATE_PAUSE_OFF  |       \
83                                  VIDEO_STATE_FFWD_OFF   |       \
84                                  VIDEO_STATE_PBEND_OFF  |       \
85                                  VIDEO_STATE_DATE_OFF   |       \
86                                  VIDEO_STATE_TIME_OFF)
87 #define VIDEO_PRESS_OFF         (VIDEO_PRESS_PLAY_OFF   |       \
88                                  VIDEO_PRESS_REC_OFF    |       \
89                                  VIDEO_PRESS_PAUSE_OFF  |       \
90                                  VIDEO_PRESS_STOP_OFF   |       \
91                                  VIDEO_PRESS_EJECT_OFF)
92 #define VIDEO_ALL_OFF           (VIDEO_STATE_OFF | VIDEO_PRESS_OFF)
93
94 #define VIDEO_STATE_ON          (VIDEO_STATE_PLAY_ON    |       \
95                                  VIDEO_STATE_REC_ON     |       \
96                                  VIDEO_STATE_PAUSE_ON   |       \
97                                  VIDEO_STATE_FFWD_ON    |       \
98                                  VIDEO_STATE_PBEND_ON   |       \
99                                  VIDEO_STATE_DATE_ON    |       \
100                                  VIDEO_STATE_TIME_ON)
101 #define VIDEO_PRESS_ON          (VIDEO_PRESS_PLAY_ON    |       \
102                                  VIDEO_PRESS_REC_ON     |       \
103                                  VIDEO_PRESS_PAUSE_ON   |       \
104                                  VIDEO_PRESS_STOP_ON    |       \
105                                  VIDEO_PRESS_EJECT_ON)
106 #define VIDEO_ALL_ON            (VIDEO_STATE_ON | VIDEO_PRESS_ON)
107
108 #define VIDEO_STATE             (VIDEO_STATE_ON | VIDEO_STATE_OFF)
109 #define VIDEO_PRESS             (VIDEO_PRESS_ON | VIDEO_PRESS_OFF)
110 #define VIDEO_ALL               (VIDEO_ALL_ON | VIDEO_ALL_OFF)
111
112
113 /* some positions in the sound control window */
114 #define SOUND_BUTTON_XSIZE      30
115 #define SOUND_BUTTON_YSIZE      30
116 #define SOUND_CONTROL_XPOS      5
117 #define SOUND_CONTROL_YPOS      245
118 #define SOUND_CONTROL_XSIZE      (3*SOUND_BUTTON_XSIZE)
119 #define SOUND_CONTROL_YSIZE      (1*SOUND_BUTTON_YSIZE)
120 #define SOUND_BUTTON_MUSIC_XPOS  (SOUND_CONTROL_XPOS + 0 * SOUND_BUTTON_XSIZE)
121 #define SOUND_BUTTON_LOOPS_XPOS  (SOUND_CONTROL_XPOS + 1 * SOUND_BUTTON_XSIZE)
122 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_CONTROL_XPOS + 2 * SOUND_BUTTON_XSIZE)
123 #define SOUND_BUTTON_ANY_YPOS    (SOUND_CONTROL_YPOS)
124
125 #define ON_SOUND_BUTTON(x,y)    ((x)>=(DX+SOUND_CONTROL_XPOS) &&        \
126                                  (x)< (DX+SOUND_CONTROL_XPOS +          \
127                                        SOUND_CONTROL_XSIZE) &&          \
128                                  (y)>=(DY+SOUND_CONTROL_YPOS) &&        \
129                                  (y)< (DY+SOUND_CONTROL_YPOS +          \
130                                        SOUND_CONTROL_YSIZE))
131 #define SOUND_BUTTON(x)         (((x)-(DX+SOUND_CONTROL_XPOS))/SOUND_BUTTON_XSIZE)
132
133 /* some positions in the game control window */
134 #define GAME_BUTTON_STOP_XPOS   (GAME_CONTROL_XPOS + 0 * GAME_BUTTON_XSIZE)
135 #define GAME_BUTTON_STOP_YPOS   (GAME_CONTROL_YPOS)
136 #define GAME_BUTTON_PAUSE_XPOS  (GAME_CONTROL_XPOS + 1 * GAME_BUTTON_XSIZE)
137 #define GAME_BUTTON_PAUSE_YPOS  (GAME_CONTROL_YPOS)
138 #define GAME_BUTTON_PLAY_XPOS   (GAME_CONTROL_XPOS + 2 * GAME_BUTTON_XSIZE)
139 #define GAME_BUTTON_PLAY_YPOS   (GAME_CONTROL_YPOS)
140 #define GAME_BUTTON_ANY_YPOS    (GAME_CONTROL_YPOS)
141
142 #define ON_GAME_BUTTON(x,y)     ((x)>=(DX+GAME_CONTROL_XPOS) && \
143                                  (x)< (DX+GAME_CONTROL_XPOS +           \
144                                        GAME_CONTROL_XSIZE) &&           \
145                                  (y)>=(DY+GAME_CONTROL_YPOS) && \
146                                  (y)< (DY+GAME_CONTROL_YPOS +           \
147                                        GAME_CONTROL_YSIZE))
148 #define GAME_BUTTON(x)          (((x)-(DX+GAME_CONTROL_XPOS))/GAME_BUTTON_XSIZE)
149
150 /* some positions in the asking window */
151 #define OK_BUTTON_XPOS          2
152 #define OK_BUTTON_YPOS          250
153 #define OK_BUTTON_GFX_YPOS      0
154 #define OK_BUTTON_XSIZE         46
155 #define OK_BUTTON_YSIZE         28
156 #define NO_BUTTON_XPOS          52
157 #define NO_BUTTON_YPOS          OK_BUTTON_YPOS
158 #define NO_BUTTON_XSIZE         OK_BUTTON_XSIZE
159 #define NO_BUTTON_YSIZE         OK_BUTTON_YSIZE
160 #define CONFIRM_BUTTON_XPOS     2
161 #define CONFIRM_BUTTON_GFX_YPOS 30
162 #define CONFIRM_BUTTON_YPOS     OK_BUTTON_YPOS
163 #define CONFIRM_BUTTON_XSIZE    96
164 #define CONFIRM_BUTTON_YSIZE    OK_BUTTON_YSIZE
165
166 #define ON_YESNO_BUTTON(x,y)    (((x)>=(DX+OK_BUTTON_XPOS) &&           \
167                                   (x)< (DX+OK_BUTTON_XPOS +             \
168                                         OK_BUTTON_XSIZE) &&             \
169                                   (y)>=(DY+OK_BUTTON_YPOS) &&           \
170                                   (y)< (DY+OK_BUTTON_YPOS +             \
171                                         OK_BUTTON_YSIZE)) ||            \
172                                  ((x)>=(DX+NO_BUTTON_XPOS) &&           \
173                                   (x)< (DX+NO_BUTTON_XPOS +             \
174                                         NO_BUTTON_XSIZE) &&             \
175                                   (y)>=(DY+NO_BUTTON_YPOS) &&           \
176                                   (y)< (DY+NO_BUTTON_YPOS +             \
177                                         NO_BUTTON_YSIZE)))
178 #define ON_CONFIRM_BUTTON(x,y)  (((x)>=(DX+CONFIRM_BUTTON_XPOS) &&      \
179                                   (x)< (DX+CONFIRM_BUTTON_XPOS +        \
180                                         CONFIRM_BUTTON_XSIZE) &&        \
181                                   (y)>=(DY+CONFIRM_BUTTON_YPOS) &&      \
182                                   (y)< (DY+CONFIRM_BUTTON_YPOS +        \
183                                         CONFIRM_BUTTON_YSIZE)))
184 #define YESNO_BUTTON(x)         (((x)-(DX+OK_BUTTON_XPOS))/OK_BUTTON_XSIZE)
185
186 /* some positions in the choose player window */
187 #define PLAYER_BUTTON_XSIZE     30
188 #define PLAYER_BUTTON_YSIZE     30
189 #define PLAYER_BUTTON_GFX_XPOS  5
190 #define PLAYER_BUTTON_GFX_YPOS  (215-30)
191 #define PLAYER_CONTROL_XPOS     (5 + PLAYER_BUTTON_XSIZE/2)
192 #define PLAYER_CONTROL_YPOS     (215 - PLAYER_BUTTON_YSIZE/2)
193 #define PLAYER_CONTROL_XSIZE    (2*PLAYER_BUTTON_XSIZE)
194 #define PLAYER_CONTROL_YSIZE    (2*PLAYER_BUTTON_YSIZE)
195 #define PLAYER_BUTTON_1_XPOS    (PLAYER_CONTROL_XPOS + 0 * PLAYER_BUTTON_XSIZE)
196 #define PLAYER_BUTTON_2_XPOS    (PLAYER_CONTROL_XPOS + 1 * PLAYER_BUTTON_XSIZE)
197 #define PLAYER_BUTTON_3_XPOS    (PLAYER_CONTROL_XPOS + 0 * PLAYER_BUTTON_XSIZE)
198 #define PLAYER_BUTTON_4_XPOS    (PLAYER_CONTROL_XPOS + 1 * PLAYER_BUTTON_XSIZE)
199 #define PLAYER_BUTTON_1_YPOS    (PLAYER_CONTROL_YPOS + 0 * PLAYER_BUTTON_YSIZE)
200 #define PLAYER_BUTTON_2_YPOS    (PLAYER_CONTROL_YPOS + 0 * PLAYER_BUTTON_YSIZE)
201 #define PLAYER_BUTTON_3_YPOS    (PLAYER_CONTROL_YPOS + 1 * PLAYER_BUTTON_YSIZE)
202 #define PLAYER_BUTTON_4_YPOS    (PLAYER_CONTROL_YPOS + 1 * PLAYER_BUTTON_YSIZE)
203
204 #define ON_PLAYER_BUTTON(x,y)   ((x)>=(DX+PLAYER_CONTROL_XPOS) &&       \
205                                  (x)< (DX+PLAYER_CONTROL_XPOS +         \
206                                        PLAYER_CONTROL_XSIZE) &&         \
207                                  (y)>=(DY+PLAYER_CONTROL_YPOS) &&       \
208                                  (y)< (DY+PLAYER_CONTROL_YPOS +         \
209                                        PLAYER_CONTROL_YSIZE))
210 #define PLAYER_BUTTON(x,y)      ((((x)-(DX+PLAYER_CONTROL_XPOS)) /      \
211                                   PLAYER_BUTTON_XSIZE) + 2 *            \
212                                  (((y)-(DY+PLAYER_CONTROL_YPOS)) /      \
213                                   PLAYER_BUTTON_YSIZE))
214
215
216 /* some definitions for the editor control window */
217
218 #define ON_EDIT_BUTTON(x,y)     (((x)>=(VX+ED_BUTTON_CTRL_XPOS) &&      \
219                                   (x)< (VX+ED_BUTTON_CTRL_XPOS +        \
220                                         ED_BUTTON_CTRL_XSIZE) &&        \
221                                   (y)>=(VY+ED_BUTTON_CTRL_YPOS) &&      \
222                                   (y)< (VY+ED_BUTTON_CTRL_YPOS +        \
223                                         ED_BUTTON_CTRL_YSIZE +          \
224                                         ED_BUTTON_FILL_YSIZE)) ||       \
225                                  ((x)>=(VX+ED_BUTTON_LEFT_XPOS) &&      \
226                                   (x)< (VX+ED_BUTTON_LEFT_XPOS +        \
227                                         ED_BUTTON_LEFT_XSIZE +          \
228                                         ED_BUTTON_UP_XSIZE +            \
229                                         ED_BUTTON_RIGHT_XSIZE) &&       \
230                                   (y)>=(VY+ED_BUTTON_LEFT_YPOS) &&      \
231                                   (y)< (VY+ED_BUTTON_LEFT_YPOS +        \
232                                         ED_BUTTON_LEFT_YSIZE)) ||       \
233                                  ((x)>=(VX+ED_BUTTON_UP_XPOS) &&        \
234                                   (x)< (VX+ED_BUTTON_UP_XPOS +          \
235                                         ED_BUTTON_UP_XSIZE) &&          \
236                                   (y)>=(VY+ED_BUTTON_UP_YPOS) &&        \
237                                   (y)< (VY+ED_BUTTON_UP_YPOS +          \
238                                         ED_BUTTON_UP_YSIZE +            \
239                                         ED_BUTTON_DOWN_YSIZE)))
240
241 #define ON_CTRL_BUTTON(x,y)     ((x)>=(VX+ED_BUTTON_EDIT_XPOS) &&       \
242                                  (x)< (VX+ED_BUTTON_EDIT_XPOS +         \
243                                        ED_BUTTON_EDIT_XSIZE) &&         \
244                                  (y)>=(VY+ED_BUTTON_EDIT_YPOS) &&       \
245                                  (y)< (VY+ED_BUTTON_EDIT_YPOS +         \
246                                        ED_BUTTON_EDIT_YSIZE +           \
247                                        ED_BUTTON_CLEAR_YSIZE +          \
248                                        ED_BUTTON_UNDO_YSIZE +           \
249                                        ED_BUTTON_EXIT_YSIZE))
250
251 #define ON_ELEM_BUTTON(x,y)     (((x)>=(DX+ED_BUTTON_EUP_XPOS) &&       \
252                                   (x)< (DX+ED_BUTTON_EUP_XPOS +         \
253                                         ED_BUTTON_EUP_XSIZE) &&         \
254                                   (y)>=(DY+ED_BUTTON_EUP_YPOS) &&       \
255                                   (y)< (DY+ED_BUTTON_EUP_YPOS +         \
256                                         ED_BUTTON_EUP_YSIZE)) ||        \
257                                  ((x)>=(DX+ED_BUTTON_EDOWN_XPOS) &&     \
258                                   (x)< (DX+ED_BUTTON_EDOWN_XPOS +       \
259                                         ED_BUTTON_EDOWN_XSIZE) &&       \
260                                   (y)>=(DY+ED_BUTTON_EDOWN_YPOS) &&     \
261                                   (y)< (DY+ED_BUTTON_EDOWN_YPOS +       \
262                                         ED_BUTTON_EDOWN_YSIZE)) ||      \
263                                  ((x)>=(DX+ED_BUTTON_ELEM_XPOS) &&      \
264                                   (x)< (DX+ED_BUTTON_ELEM_XPOS +        \
265                                         MAX_ELEM_X*ED_BUTTON_ELEM_XSIZE) && \
266                                   (y)>=(DY+ED_BUTTON_ELEM_YPOS) &&      \
267                                   (y)< (DY+ED_BUTTON_ELEM_YPOS +        \
268                                         MAX_ELEM_Y*ED_BUTTON_ELEM_YSIZE)))
269
270 #define ON_COUNT_BUTTON(x,y)    (((((x)>=ED_COUNT_GADGET_XPOS &&        \
271                                     (x)<(ED_COUNT_GADGET_XPOS +         \
272                                          ED_BUTTON_MINUS_XSIZE)) ||     \
273                                    ((x)>=(ED_COUNT_GADGET_XPOS +        \
274                                           (ED_BUTTON_PLUS_XPOS -        \
275                                            ED_BUTTON_MINUS_XPOS)) &&    \
276                                     (x)<(ED_COUNT_GADGET_XPOS +         \
277                                          (ED_BUTTON_PLUS_XPOS -         \
278                                           ED_BUTTON_MINUS_XPOS) +       \
279                                          ED_BUTTON_PLUS_XSIZE))) &&     \
280                                   ((y)>=ED_COUNT_GADGET_YPOS &&         \
281                                    (y)<(ED_COUNT_GADGET_YPOS +          \
282                                         16*ED_COUNT_GADGET_YSIZE)) &&   \
283                                   (((y)-ED_COUNT_GADGET_YPOS) %         \
284                                    ED_COUNT_GADGET_YSIZE) <             \
285                                   ED_BUTTON_MINUS_YSIZE) ||             \
286                                  ((((x)>=ED_SIZE_GADGET_XPOS &&         \
287                                     (x)<(ED_SIZE_GADGET_XPOS +          \
288                                          ED_BUTTON_MINUS_XSIZE)) ||     \
289                                    ((x)>=(ED_SIZE_GADGET_XPOS +         \
290                                           (ED_BUTTON_PLUS_XPOS -        \
291                                            ED_BUTTON_MINUS_XPOS)) &&    \
292                                     (x)<(ED_SIZE_GADGET_XPOS +          \
293                                          (ED_BUTTON_PLUS_XPOS -         \
294                                           ED_BUTTON_MINUS_XPOS) +       \
295                                          ED_BUTTON_PLUS_XSIZE))) &&     \
296                                   ((y)>=ED_SIZE_GADGET_YPOS &&          \
297                                    (y)<(ED_SIZE_GADGET_YPOS +           \
298                                         2*ED_SIZE_GADGET_YSIZE)) &&     \
299                                   (((y)-ED_SIZE_GADGET_YPOS) %          \
300                                    ED_SIZE_GADGET_YSIZE) <              \
301                                   ED_BUTTON_MINUS_YSIZE))
302
303 #define EDIT_BUTTON(x,y)        (((y) < (VY + ED_BUTTON_CTRL_YPOS +     \
304                                          ED_BUTTON_CTRL_YSIZE)) ? 0 :   \
305                                  ((y) < (VY + ED_BUTTON_CTRL_YPOS +     \
306                                          ED_BUTTON_CTRL_YSIZE +         \
307                                          ED_BUTTON_FILL_YSIZE)) ? 1 :   \
308                                  ((x) < (VX + ED_BUTTON_LEFT_XPOS +     \
309                                          ED_BUTTON_LEFT_XSIZE) ? 2 :    \
310                                   (x) > (VX + ED_BUTTON_LEFT_XPOS +     \
311                                          ED_BUTTON_LEFT_XSIZE +         \
312                                          ED_BUTTON_UP_XSIZE) ? 5 :      \
313                                   3+(((y)-(VY + ED_BUTTON_CTRL_YPOS +   \
314                                            ED_BUTTON_CTRL_YSIZE +       \
315                                            ED_BUTTON_FILL_YSIZE)) /     \
316                                      ED_BUTTON_UP_YSIZE)))
317
318 #define CTRL_BUTTON(x,y)        (((y) < (VY + ED_BUTTON_EDIT_YPOS +     \
319                                          ED_BUTTON_EDIT_YSIZE)) ? 0 :   \
320                                  1+(((y)-(VY + ED_BUTTON_EDIT_YPOS +    \
321                                          ED_BUTTON_EDIT_YSIZE)) /       \
322                                     ED_BUTTON_CLEAR_YSIZE))
323
324 #define ELEM_BUTTON(x,y)        (((y) < (DY + ED_BUTTON_EUP_YPOS +      \
325                                          ED_BUTTON_EUP_YSIZE)) ? 0 :    \
326                                  ((y) > (DY + ED_BUTTON_EDOWN_YPOS)) ? 1 : \
327                                  2+(((y) - (DY + ED_BUTTON_ELEM_YPOS)) /   \
328                                  ED_BUTTON_ELEM_YSIZE)*MAX_ELEM_X +     \
329                                  ((x) - (DX + ED_BUTTON_ELEM_XPOS)) /   \
330                                  ED_BUTTON_ELEM_XSIZE)
331
332 #define COUNT_BUTTON(x,y)       ((x) < ED_SIZE_GADGET_XPOS ?            \
333                                  ((((y) - ED_COUNT_GADGET_YPOS) /       \
334                                    ED_COUNT_GADGET_YSIZE)*2 +           \
335                                   ((x) < (ED_COUNT_GADGET_XPOS +        \
336                                           ED_BUTTON_MINUS_XSIZE) ? 0 : 1)) : \
337                                  32+((((y) - ED_SIZE_GADGET_YPOS) /     \
338                                       ED_SIZE_GADGET_YSIZE)*2 +         \
339                                      ((x) < (ED_SIZE_GADGET_XPOS +      \
340                                              ED_BUTTON_MINUS_XSIZE) ? 0 : 1)))
341
342 /****************************************************************/
343 /********** drawing buttons and corresponding displays **********/
344 /****************************************************************/
345
346 void OLD_DrawVideoDisplay(unsigned long state, unsigned long value)
347 {
348   int i;
349   int part_label = 0, part_symbol = 1;
350   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
351   static char *monatsname[12] =
352   {
353     "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
354     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
355   };
356   static int video_pos[5][2][4] =
357   {
358     {{ VIDEO_PLAY_LABEL_XPOS, VIDEO_PLAY_LABEL_YPOS,
359        VIDEO_PLAY_LABEL_XSIZE,VIDEO_PLAY_LABEL_YSIZE },
360      { VIDEO_PLAY_SYMBOL_XPOS, VIDEO_PLAY_SYMBOL_YPOS,
361        VIDEO_PLAY_SYMBOL_XSIZE,VIDEO_PLAY_SYMBOL_YSIZE }},
362
363     {{ VIDEO_REC_LABEL_XPOS, VIDEO_REC_LABEL_YPOS,
364        VIDEO_REC_LABEL_XSIZE,VIDEO_REC_LABEL_YSIZE },
365      { VIDEO_REC_SYMBOL_XPOS, VIDEO_REC_SYMBOL_YPOS,
366        VIDEO_REC_SYMBOL_XSIZE,VIDEO_REC_SYMBOL_YSIZE }},
367
368     {{ VIDEO_PAUSE_LABEL_XPOS, VIDEO_PAUSE_LABEL_YPOS,
369        VIDEO_PAUSE_LABEL_XSIZE,VIDEO_PAUSE_LABEL_YSIZE },
370      { VIDEO_PAUSE_SYMBOL_XPOS, VIDEO_PAUSE_SYMBOL_YPOS,
371        VIDEO_PAUSE_SYMBOL_XSIZE,VIDEO_PAUSE_SYMBOL_YSIZE }},
372
373     {{ VIDEO_DATE_LABEL_XPOS, VIDEO_DATE_LABEL_YPOS,
374        VIDEO_DATE_LABEL_XSIZE,VIDEO_DATE_LABEL_YSIZE },
375      { VIDEO_DATE_XPOS, VIDEO_DATE_YPOS,
376        VIDEO_DATE_XSIZE,VIDEO_DATE_YSIZE }},
377
378     {{ 0,0,
379        0,0 },
380      { VIDEO_TIME_XPOS, VIDEO_TIME_YPOS,
381        VIDEO_TIME_XSIZE,VIDEO_TIME_YSIZE }}
382   };
383
384   if (state & VIDEO_STATE_PBEND_OFF)
385   {
386     int cx = DOOR_GFX_PAGEX3, cy = DOOR_GFX_PAGEY2;
387
388     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
389               cx + VIDEO_REC_LABEL_XPOS,
390               cy + VIDEO_REC_LABEL_YPOS,
391               VIDEO_PBEND_LABEL_XSIZE,
392               VIDEO_PBEND_LABEL_YSIZE,
393               VX + VIDEO_REC_LABEL_XPOS,
394               VY + VIDEO_REC_LABEL_YPOS);
395   }
396
397   for(i=0;i<10;i++)
398   {
399     if (state & (1<<i))
400     {
401       int pos = i/2, cx, cy = DOOR_GFX_PAGEY2;
402
403       if (i%2)                  /* i ungerade => STATE_ON / PRESS_OFF */
404         cx = DOOR_GFX_PAGEX4;
405       else
406         cx = DOOR_GFX_PAGEX3;   /* i gerade => STATE_OFF / PRESS_ON */
407
408       if (video_pos[pos][part_label][0] && value != VIDEO_DISPLAY_SYMBOL_ONLY)
409         XCopyArea(display,pix[PIX_DOOR],drawto,gc,
410                   cx + video_pos[pos][part_label][xpos],
411                   cy + video_pos[pos][part_label][ypos],
412                   video_pos[pos][part_label][xsize],
413                   video_pos[pos][part_label][ysize],
414                   VX + video_pos[pos][part_label][xpos],
415                   VY + video_pos[pos][part_label][ypos]);
416       if (video_pos[pos][part_symbol][0] && value != VIDEO_DISPLAY_LABEL_ONLY)
417         XCopyArea(display,pix[PIX_DOOR],drawto,gc,
418                   cx + video_pos[pos][part_symbol][xpos],
419                   cy + video_pos[pos][part_symbol][ypos],
420                   video_pos[pos][part_symbol][xsize],
421                   video_pos[pos][part_symbol][ysize],
422                   VX + video_pos[pos][part_symbol][xpos],
423                   VY + video_pos[pos][part_symbol][ypos]);
424     }
425   }
426
427   if (state & VIDEO_STATE_FFWD_ON)
428   {
429     int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY2;
430
431     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
432               cx + VIDEO_PLAY_SYMBOL_XPOS,
433               cy + VIDEO_PLAY_SYMBOL_YPOS,
434               VIDEO_PLAY_SYMBOL_XSIZE - 2,
435               VIDEO_PLAY_SYMBOL_YSIZE,
436               VX + VIDEO_PLAY_SYMBOL_XPOS - 9,
437               VY + VIDEO_PLAY_SYMBOL_YPOS);
438   }
439
440   if (state & VIDEO_STATE_PBEND_ON)
441   {
442     int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY1;
443
444     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
445               cx + VIDEO_PBEND_LABEL_XPOS,
446               cy + VIDEO_PBEND_LABEL_YPOS,
447               VIDEO_PBEND_LABEL_XSIZE,
448               VIDEO_PBEND_LABEL_YSIZE,
449               VX + VIDEO_REC_LABEL_XPOS,
450               VY + VIDEO_REC_LABEL_YPOS);
451   }
452
453   if (state & VIDEO_STATE_DATE_ON)
454   {
455     int tag = value % 100;
456     int monat = (value/100) % 100;
457     int jahr = (value/10000);
458
459     DrawText(VX+VIDEO_DATE_XPOS,VY+VIDEO_DATE_YPOS,
460              int2str(tag,2),FS_SMALL,FC_SPECIAL1);
461     DrawText(VX+VIDEO_DATE_XPOS+27,VY+VIDEO_DATE_YPOS,
462              monatsname[monat],FS_SMALL,FC_SPECIAL1);
463     DrawText(VX+VIDEO_DATE_XPOS+64,VY+VIDEO_DATE_YPOS,
464              int2str(jahr,2),FS_SMALL,FC_SPECIAL1);
465   }
466
467   if (state & VIDEO_STATE_TIME_ON)
468   {
469     int min = value / 60;
470     int sec = value % 60;
471
472     DrawText(VX+VIDEO_TIME_XPOS,VY+VIDEO_TIME_YPOS,
473              int2str(min,2),FS_SMALL,FC_SPECIAL1);
474     DrawText(VX+VIDEO_TIME_XPOS+27,VY+VIDEO_TIME_YPOS,
475              int2str(sec,2),FS_SMALL,FC_SPECIAL1);
476   }
477
478   if (state & VIDEO_STATE_DATE)
479     redraw_mask |= REDRAW_VIDEO_1;
480   if ((state & ~VIDEO_STATE_DATE) & VIDEO_STATE)
481     redraw_mask |= REDRAW_VIDEO_2;
482   if (state & VIDEO_PRESS)
483     redraw_mask |= REDRAW_VIDEO_3;
484 }
485
486 void DrawVideoDisplay(unsigned long state, unsigned long value)
487 {
488   int i;
489   int part_label = 0, part_symbol = 1;
490   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
491   static char *monatsname[12] =
492   {
493     "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
494     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
495   };
496   static int video_pos[10][2][4] =
497   {
498     {{ VIDEO_PLAY_LABEL_XPOS, VIDEO_PLAY_LABEL_YPOS,
499        VIDEO_PLAY_LABEL_XSIZE,VIDEO_PLAY_LABEL_YSIZE },
500      { VIDEO_PLAY_SYMBOL_XPOS, VIDEO_PLAY_SYMBOL_YPOS,
501        VIDEO_PLAY_SYMBOL_XSIZE,VIDEO_PLAY_SYMBOL_YSIZE }},
502
503     {{ VIDEO_REC_LABEL_XPOS, VIDEO_REC_LABEL_YPOS,
504        VIDEO_REC_LABEL_XSIZE,VIDEO_REC_LABEL_YSIZE },
505      { VIDEO_REC_SYMBOL_XPOS, VIDEO_REC_SYMBOL_YPOS,
506        VIDEO_REC_SYMBOL_XSIZE,VIDEO_REC_SYMBOL_YSIZE }},
507
508     {{ VIDEO_PAUSE_LABEL_XPOS, VIDEO_PAUSE_LABEL_YPOS,
509        VIDEO_PAUSE_LABEL_XSIZE,VIDEO_PAUSE_LABEL_YSIZE },
510      { VIDEO_PAUSE_SYMBOL_XPOS, VIDEO_PAUSE_SYMBOL_YPOS,
511        VIDEO_PAUSE_SYMBOL_XSIZE,VIDEO_PAUSE_SYMBOL_YSIZE }},
512
513     {{ VIDEO_DATE_LABEL_XPOS, VIDEO_DATE_LABEL_YPOS,
514        VIDEO_DATE_LABEL_XSIZE,VIDEO_DATE_LABEL_YSIZE },
515      { VIDEO_DATE_XPOS, VIDEO_DATE_YPOS,
516        VIDEO_DATE_XSIZE,VIDEO_DATE_YSIZE }},
517
518     {{ 0,0,
519        0,0 },
520      { VIDEO_TIME_XPOS, VIDEO_TIME_YPOS,
521        VIDEO_TIME_XSIZE,VIDEO_TIME_YSIZE }},
522
523     {{ VIDEO_BUTTON_PLAY_XPOS, VIDEO_BUTTON_ANY_YPOS,
524        VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
525      { 0,0,
526        0,0 }},
527
528     {{ VIDEO_BUTTON_REC_XPOS, VIDEO_BUTTON_ANY_YPOS,
529        VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
530      { 0,0,
531        0,0 }},
532
533     {{ VIDEO_BUTTON_PAUSE_XPOS, VIDEO_BUTTON_ANY_YPOS,
534        VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
535      { 0,0,
536        0,0 }},
537
538     {{ VIDEO_BUTTON_STOP_XPOS, VIDEO_BUTTON_ANY_YPOS,
539        VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
540      { 0,0,
541        0,0 }},
542
543     {{ VIDEO_BUTTON_EJECT_XPOS, VIDEO_BUTTON_ANY_YPOS,
544        VIDEO_BUTTON_XSIZE,VIDEO_BUTTON_YSIZE },
545      { 0,0,
546        0,0 }}
547   };
548
549   if (state & VIDEO_STATE_PBEND_OFF)
550   {
551     int cx = DOOR_GFX_PAGEX3, cy = DOOR_GFX_PAGEY2;
552
553     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
554               cx + VIDEO_REC_LABEL_XPOS,
555               cy + VIDEO_REC_LABEL_YPOS,
556               VIDEO_PBEND_LABEL_XSIZE,
557               VIDEO_PBEND_LABEL_YSIZE,
558               VX + VIDEO_REC_LABEL_XPOS,
559               VY + VIDEO_REC_LABEL_YPOS);
560   }
561
562   for(i=0;i<20;i++)
563   {
564     if (state & (1<<i))
565     {
566       int pos = i/2, cx, cy = DOOR_GFX_PAGEY2;
567
568       if (i%2)                  /* i ungerade => STATE_ON / PRESS_OFF */
569         cx = DOOR_GFX_PAGEX4;
570       else
571         cx = DOOR_GFX_PAGEX3;   /* i gerade => STATE_OFF / PRESS_ON */
572
573       if (video_pos[pos][part_label][0] && value != VIDEO_DISPLAY_SYMBOL_ONLY)
574         XCopyArea(display,pix[PIX_DOOR],drawto,gc,
575                   cx + video_pos[pos][part_label][xpos],
576                   cy + video_pos[pos][part_label][ypos],
577                   video_pos[pos][part_label][xsize],
578                   video_pos[pos][part_label][ysize],
579                   VX + video_pos[pos][part_label][xpos],
580                   VY + video_pos[pos][part_label][ypos]);
581       if (video_pos[pos][part_symbol][0] && value != VIDEO_DISPLAY_LABEL_ONLY)
582         XCopyArea(display,pix[PIX_DOOR],drawto,gc,
583                   cx + video_pos[pos][part_symbol][xpos],
584                   cy + video_pos[pos][part_symbol][ypos],
585                   video_pos[pos][part_symbol][xsize],
586                   video_pos[pos][part_symbol][ysize],
587                   VX + video_pos[pos][part_symbol][xpos],
588                   VY + video_pos[pos][part_symbol][ypos]);
589     }
590   }
591
592   if (state & VIDEO_STATE_FFWD_ON)
593   {
594     int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY2;
595
596     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
597               cx + VIDEO_PLAY_SYMBOL_XPOS,
598               cy + VIDEO_PLAY_SYMBOL_YPOS,
599               VIDEO_PLAY_SYMBOL_XSIZE - 2,
600               VIDEO_PLAY_SYMBOL_YSIZE,
601               VX + VIDEO_PLAY_SYMBOL_XPOS - 9,
602               VY + VIDEO_PLAY_SYMBOL_YPOS);
603   }
604
605   if (state & VIDEO_STATE_PBEND_ON)
606   {
607     int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY1;
608
609     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
610               cx + VIDEO_PBEND_LABEL_XPOS,
611               cy + VIDEO_PBEND_LABEL_YPOS,
612               VIDEO_PBEND_LABEL_XSIZE,
613               VIDEO_PBEND_LABEL_YSIZE,
614               VX + VIDEO_REC_LABEL_XPOS,
615               VY + VIDEO_REC_LABEL_YPOS);
616   }
617
618   if (state & VIDEO_STATE_DATE_ON)
619   {
620     int tag = value % 100;
621     int monat = (value/100) % 100;
622     int jahr = (value/10000);
623
624     DrawText(VX+VIDEO_DATE_XPOS,VY+VIDEO_DATE_YPOS,
625              int2str(tag,2),FS_SMALL,FC_SPECIAL1);
626     DrawText(VX+VIDEO_DATE_XPOS+27,VY+VIDEO_DATE_YPOS,
627              monatsname[monat],FS_SMALL,FC_SPECIAL1);
628     DrawText(VX+VIDEO_DATE_XPOS+64,VY+VIDEO_DATE_YPOS,
629              int2str(jahr,2),FS_SMALL,FC_SPECIAL1);
630   }
631
632   if (state & VIDEO_STATE_TIME_ON)
633   {
634     int min = value / 60;
635     int sec = value % 60;
636
637     DrawText(VX+VIDEO_TIME_XPOS,VY+VIDEO_TIME_YPOS,
638              int2str(min,2),FS_SMALL,FC_SPECIAL1);
639     DrawText(VX+VIDEO_TIME_XPOS+27,VY+VIDEO_TIME_YPOS,
640              int2str(sec,2),FS_SMALL,FC_SPECIAL1);
641   }
642
643   if (state & VIDEO_STATE_DATE)
644     redraw_mask |= REDRAW_VIDEO_1;
645   if ((state & ~VIDEO_STATE_DATE) & VIDEO_STATE)
646     redraw_mask |= REDRAW_VIDEO_2;
647   if (state & VIDEO_PRESS)
648     redraw_mask |= REDRAW_VIDEO_3;
649 }
650
651 void DrawCompleteVideoDisplay()
652 {
653   XCopyArea(display,pix[PIX_DOOR],drawto,gc,
654             DOOR_GFX_PAGEX3,DOOR_GFX_PAGEY2, VXSIZE,VYSIZE, VX,VY);
655   XCopyArea(display,pix[PIX_DOOR],drawto,gc,
656             DOOR_GFX_PAGEX4+VIDEO_CONTROL_XPOS,
657             DOOR_GFX_PAGEY2+VIDEO_CONTROL_YPOS,
658             VIDEO_CONTROL_XSIZE,VIDEO_CONTROL_YSIZE,
659             VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
660
661   DrawVideoDisplay(VIDEO_ALL_OFF,0);
662   if (tape.date && tape.length)
663   {
664     DrawVideoDisplay(VIDEO_STATE_DATE_ON,tape.date);
665     DrawVideoDisplay(VIDEO_STATE_TIME_ON,tape.length_seconds);
666   }
667
668   XCopyArea(display,drawto,pix[PIX_DB_DOOR],gc,
669             VX,VY, VXSIZE,VYSIZE, DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
670 }
671
672
673 /* NEW GADGET STUFF -------------------------------------------------------- */
674
675
676 /* values for DrawGadget() */
677 #define DG_UNPRESSED            0
678 #define DG_PRESSED              1
679 #define DG_BUFFERED             0
680 #define DG_DIRECT               1
681
682 static struct GadgetInfo *gadget_list_first_entry = NULL;
683 static struct GadgetInfo *gadget_list_last_entry = NULL;
684 static int next_free_gadget_id = 1;
685 static boolean gadget_id_wrapped = FALSE;
686
687 static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
688 {
689   struct GadgetInfo *gi = gadget_list_first_entry;
690
691   while (gi && gi->id != id)
692     gi = gi->next;
693
694   return gi;
695 }
696
697 static int getNewGadgetID()
698 {
699   int id = next_free_gadget_id++;
700
701   if (next_free_gadget_id <= 0)         /* counter overrun */
702   {
703     gadget_id_wrapped = TRUE;           /* now we must check each ID */
704     next_free_gadget_id = 0;
705   }
706
707   if (gadget_id_wrapped)
708   {
709     next_free_gadget_id++;
710     while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
711       next_free_gadget_id++;
712   }
713
714   if (next_free_gadget_id <= 0)         /* cannot get new gadget id */
715     Error(ERR_EXIT, "too much gadgets -- this should not happen");
716
717   return id;
718 }
719
720 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
721 {
722   struct GadgetInfo *gi = gadget_list_first_entry;
723
724   while (gi)
725   {
726     if (gi->mapped &&
727         mx >= gi->x && mx < gi->x + gi->width &&
728         my >= gi->y && my < gi->y + gi->height)
729         break;
730
731     gi = gi->next;
732   }
733
734   return gi;
735 }
736
737 static void default_callback_info(void *ptr)
738 {
739   if (game_status == LEVELED)
740     HandleEditorGadgetInfoText(ptr);
741 }
742
743 static void default_callback_action(void *ptr)
744 {
745   return;
746 }
747
748 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
749 {
750   int state = (pressed ? 1 : 0);
751   struct GadgetDesign *gd = (gi->checked ?
752                              &gi->alt_design[state] :
753                              &gi->design[state]);
754
755   switch (gi->type)
756   {
757     case GD_TYPE_NORMAL_BUTTON:
758     case GD_TYPE_CHECK_BUTTON:
759     case GD_TYPE_RADIO_BUTTON:
760       XCopyArea(display, gd->pixmap, drawto, gc,
761                 gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
762       if (gi->deco.design.pixmap)
763         XCopyArea(display, gi->deco.design.pixmap, drawto, gc,
764                   gi->deco.design.x, gi->deco.design.y,
765                   gi->deco.width, gi->deco.height,
766                   gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
767                   gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
768       break;
769
770     case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
771     case GD_TYPE_TEXTINPUT_NUMERIC:
772       {
773         int i;
774         char cursor_letter;
775         char cursor_string[3];
776         char text[MAX_GADGET_TEXTSIZE + 1];
777         int font_color = FC_YELLOW;
778         int border = gi->design_border;
779         strcpy(text, gi->text.value);
780         strcat(text, " ");
781
782         /* left part of gadget */
783         XCopyArea(display, gd->pixmap, drawto, gc,
784                   gd->x, gd->y, border, gi->height, gi->x, gi->y);
785
786         /* middle part of gadget */
787         for (i=0; i<=gi->text.size; i++)
788           XCopyArea(display, gd->pixmap, drawto, gc,
789                     gd->x + border, gd->y, FONT2_XSIZE, gi->height,
790                     gi->x + border + i * FONT2_XSIZE, gi->y);
791
792         /* right part of gadget */
793         XCopyArea(display, gd->pixmap, drawto, gc,
794                   gd->x + ED_WIN_COUNT_XSIZE - border, gd->y,
795                   border, gi->height, gi->x + gi->width - border, gi->y);
796
797         /* gadget text value */
798         DrawText(gi->x + border, gi->y + border, text, FS_SMALL, font_color);
799
800         cursor_letter = gi->text.value[gi->text.cursor_position];
801         cursor_string[0] = '~';
802         cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
803         cursor_string[2] = '\0';
804
805         /* draw cursor, if active */
806         if (pressed)
807           DrawText(gi->x + border + gi->text.cursor_position * FONT2_XSIZE,
808                    gi->y + border, cursor_string, FS_SMALL, font_color);
809       }
810       break;
811
812     case GD_TYPE_SCROLLBAR_VERTICAL:
813       {
814         int i;
815         int xpos = gi->x;
816         int ypos = gi->y + gi->scrollbar.position;
817         int design_full = gi->width;
818         int design_body = design_full - 2 * gi->design_border;
819         int size_full = gi->scrollbar.size;
820         int size_body = size_full - 2 * gi->design_border;
821         int num_steps = size_body / design_body;
822         int step_size_remain = size_body - num_steps * design_body;
823
824         /* clear scrollbar area */
825         XFillRectangle(display, backbuffer, gc,
826                        gi->x, gi->y, gi->width, gi->height);
827
828         /* upper part of gadget */
829         XCopyArea(display, gd->pixmap, drawto, gc,
830                   gd->x, gd->y,
831                   gi->width, gi->design_border,
832                   xpos, ypos);
833
834         /* middle part of gadget */
835         for (i=0; i<num_steps; i++)
836           XCopyArea(display, gd->pixmap, drawto, gc,
837                     gd->x, gd->y + gi->design_border,
838                     gi->width, design_body,
839                     xpos, ypos + gi->design_border + i * design_body);
840
841         /* remaining middle part of gadget */
842         if (step_size_remain > 0)
843           XCopyArea(display, gd->pixmap, drawto, gc,
844                     gd->x,  gd->y + gi->design_border,
845                     gi->width, step_size_remain,
846                     xpos, ypos + gi->design_border + num_steps * design_body);
847
848         /* lower part of gadget */
849         XCopyArea(display, gd->pixmap, drawto, gc,
850                   gd->x, gd->y + design_full - gi->design_border,
851                   gi->width, gi->design_border,
852                   xpos, ypos + size_full - gi->design_border);
853       }
854       break;
855
856     case GD_TYPE_SCROLLBAR_HORIZONTAL:
857       {
858         int i;
859         int xpos = gi->x + gi->scrollbar.position;
860         int ypos = gi->y;
861         int design_full = gi->height;
862         int design_body = design_full - 2 * gi->design_border;
863         int size_full = gi->scrollbar.size;
864         int size_body = size_full - 2 * gi->design_border;
865         int num_steps = size_body / design_body;
866         int step_size_remain = size_body - num_steps * design_body;
867
868         /* clear scrollbar area */
869         XFillRectangle(display, backbuffer, gc,
870                        gi->x, gi->y, gi->width, gi->height);
871
872         /* left part of gadget */
873         XCopyArea(display, gd->pixmap, drawto, gc,
874                   gd->x, gd->y,
875                   gi->design_border, gi->height,
876                   xpos, ypos);
877
878         /* middle part of gadget */
879         for (i=0; i<num_steps; i++)
880           XCopyArea(display, gd->pixmap, drawto, gc,
881                     gd->x + gi->design_border, gd->y,
882                     design_body, gi->height,
883                     xpos + gi->design_border + i * design_body, ypos);
884
885         /* remaining middle part of gadget */
886         if (step_size_remain > 0)
887           XCopyArea(display, gd->pixmap, drawto, gc,
888                     gd->x + gi->design_border, gd->y,
889                     step_size_remain, gi->height,
890                     xpos + gi->design_border + num_steps * design_body, ypos);
891
892         /* right part of gadget */
893         XCopyArea(display, gd->pixmap, drawto, gc,
894                   gd->x + design_full - gi->design_border, gd->y,
895                   gi->design_border, gi->height,
896                   xpos + size_full - gi->design_border, ypos);
897       }
898       break;
899
900     default:
901       return;
902   }
903
904   if (direct)
905     XCopyArea(display, drawto, window, gc,
906               gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
907   else
908     redraw_mask |= (gi->x < SX + SXSIZE ? REDRAW_FIELD :
909                     gi->y < DY + DYSIZE ? REDRAW_DOOR_1 :
910                     gi->y > VY ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
911 }
912
913 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
914 {
915   int tag = first_tag;
916
917   while (tag != GDI_END)
918   {
919     switch(tag)
920     {
921       case GDI_CUSTOM_ID:
922         gi->custom_id = va_arg(ap, int);
923         break;
924
925       case GDI_INFO_TEXT:
926         {
927           int max_textsize = MAX_INFO_TEXTSIZE - 1;
928
929           strncpy(gi->info_text, va_arg(ap, char *), max_textsize);
930           gi->info_text[max_textsize] = '\0';
931         }
932         break;
933
934       case GDI_X:
935         gi->x = va_arg(ap, int);
936         break;
937
938       case GDI_Y:
939         gi->y = va_arg(ap, int);
940         break;
941
942       case GDI_WIDTH:
943         gi->width = va_arg(ap, int);
944         break;
945
946       case GDI_HEIGHT:
947         gi->height = va_arg(ap, int);
948         break;
949
950       case GDI_TYPE:
951         gi->type = va_arg(ap, unsigned long);
952         break;
953
954       case GDI_STATE:
955         gi->state = va_arg(ap, unsigned long);
956         break;
957
958       case GDI_CHECKED:
959         gi->checked = va_arg(ap, boolean);
960         break;
961
962       case GDI_RADIO_NR:
963         gi->radio_nr = va_arg(ap, unsigned long);
964         break;
965
966       case GDI_NUMBER_VALUE:
967         gi->text.number_value = va_arg(ap, long);
968         sprintf(gi->text.value, "%d", gi->text.number_value);
969         gi->text.cursor_position = strlen(gi->text.value);
970         break;
971
972       case GDI_NUMBER_MIN:
973         gi->text.number_min = va_arg(ap, long);
974         if (gi->text.number_value < gi->text.number_min)
975         {
976           gi->text.number_value = gi->text.number_min;
977           sprintf(gi->text.value, "%d", gi->text.number_value);
978         }
979         break;
980
981       case GDI_NUMBER_MAX:
982         gi->text.number_max = va_arg(ap, long);
983         if (gi->text.number_value > gi->text.number_max)
984         {
985           gi->text.number_value = gi->text.number_max;
986           sprintf(gi->text.value, "%d", gi->text.number_value);
987         }
988         break;
989
990       case GDI_TEXT_VALUE:
991         {
992           int max_textsize = MAX_GADGET_TEXTSIZE;
993
994           if (gi->text.size)
995             max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
996
997           strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
998           gi->text.value[max_textsize] = '\0';
999           gi->text.cursor_position = strlen(gi->text.value);
1000         }
1001         break;
1002
1003       case GDI_TEXT_SIZE:
1004         {
1005           int tag_value = va_arg(ap, int);
1006           int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
1007
1008           gi->text.size = max_textsize;
1009           gi->text.value[max_textsize] = '\0';
1010
1011           if (gi->width == 0 && gi->height == 0)
1012           {
1013             gi->width = (gi->text.size + 1) * FONT2_XSIZE + 6;
1014             gi->height = ED_WIN_COUNT_YSIZE;
1015           }
1016         }
1017         break;
1018
1019       case GDI_DESIGN_UNPRESSED:
1020         gi->design[GD_BUTTON_UNPRESSED].pixmap = va_arg(ap, Pixmap);
1021         gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
1022         gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
1023         break;
1024
1025       case GDI_DESIGN_PRESSED:
1026         gi->design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
1027         gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
1028         gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
1029         break;
1030
1031       case GDI_ALT_DESIGN_UNPRESSED:
1032         gi->alt_design[GD_BUTTON_UNPRESSED].pixmap= va_arg(ap, Pixmap);
1033         gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
1034         gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
1035         break;
1036
1037       case GDI_ALT_DESIGN_PRESSED:
1038         gi->alt_design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
1039         gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
1040         gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
1041         break;
1042
1043       case GDI_DESIGN_BORDER:
1044         gi->design_border = va_arg(ap, int);
1045         break;
1046
1047       case GDI_DECORATION_DESIGN:
1048         gi->deco.design.pixmap = va_arg(ap, Pixmap);
1049         gi->deco.design.x = va_arg(ap, int);
1050         gi->deco.design.y = va_arg(ap, int);
1051         break;
1052
1053       case GDI_DECORATION_POSITION:
1054         gi->deco.x = va_arg(ap, int);
1055         gi->deco.y = va_arg(ap, int);
1056         break;
1057
1058       case GDI_DECORATION_SIZE:
1059         gi->deco.width = va_arg(ap, int);
1060         gi->deco.height = va_arg(ap, int);
1061         break;
1062
1063       case GDI_DECORATION_SHIFTING:
1064         gi->deco.xshift = va_arg(ap, int);
1065         gi->deco.yshift = va_arg(ap, int);
1066         break;
1067
1068       case GDI_EVENT_MASK:
1069         gi->event_mask = va_arg(ap, unsigned long);
1070         break;
1071
1072       case GDI_AREA_SIZE:
1073         gi->drawing.area_xsize = va_arg(ap, int);
1074         gi->drawing.area_ysize = va_arg(ap, int);
1075
1076         /* determine dependent values for drawing area gadget, if needed */
1077         if (gi->width == 0 && gi->height == 0 &&
1078             gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
1079         {
1080           gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
1081           gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
1082         }
1083         else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
1084                  gi->width != 0 && gi->height != 0)
1085         {
1086           gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
1087           gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
1088         }
1089         break;
1090
1091       case GDI_ITEM_SIZE:
1092         gi->drawing.item_xsize = va_arg(ap, int);
1093         gi->drawing.item_ysize = va_arg(ap, int);
1094
1095         /* determine dependent values for drawing area gadget, if needed */
1096         if (gi->width == 0 && gi->height == 0 &&
1097             gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
1098         {
1099           gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
1100           gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
1101         }
1102         else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
1103                  gi->width != 0 && gi->height != 0)
1104         {
1105           gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
1106           gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
1107         }
1108         break;
1109
1110       case GDI_SCROLLBAR_ITEMS_MAX:
1111         gi->scrollbar.items_max = va_arg(ap, int);
1112         break;
1113
1114       case GDI_SCROLLBAR_ITEMS_VISIBLE:
1115         gi->scrollbar.items_visible = va_arg(ap, int);
1116         break;
1117
1118       case GDI_SCROLLBAR_ITEM_POSITION:
1119         gi->scrollbar.item_position = va_arg(ap, int);
1120         break;
1121
1122       case GDI_CALLBACK_INFO:
1123         gi->callback_info = va_arg(ap, gadget_function);
1124         break;
1125
1126       case GDI_CALLBACK_ACTION:
1127         gi->callback_action = va_arg(ap, gadget_function);
1128         break;
1129
1130       default:
1131         Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
1132     }
1133
1134     tag = va_arg(ap, int);      /* read next tag */
1135   }
1136
1137   /* check if gadget complete */
1138   if (gi->type != GD_TYPE_DRAWING_AREA &&
1139       (!gi->design[GD_BUTTON_UNPRESSED].pixmap ||
1140        !gi->design[GD_BUTTON_PRESSED].pixmap))
1141     Error(ERR_EXIT, "gadget incomplete (missing Pixmap)");
1142
1143   /* adjust gadget values in relation to other gadget values */
1144
1145   if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
1146   {
1147     struct GadgetTextInput *text = &gi->text;
1148     int value = text->number_value;
1149
1150     text->number_value = (value < text->number_min ? text->number_min :
1151                           value > text->number_max ? text->number_max :
1152                           value);
1153
1154     sprintf(text->value, "%d", text->number_value);
1155   }
1156
1157   if (gi->type & GD_TYPE_SCROLLBAR)
1158   {
1159     struct GadgetScrollbar *gs = &gi->scrollbar;
1160
1161     if (gi->width == 0 || gi->height == 0 ||
1162         gs->items_max == 0 || gs->items_visible == 0)
1163       Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
1164
1165     /* calculate internal scrollbar values */
1166     gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
1167                     gi->height : gi->width);
1168     gs->size = gs->size_max * gs->items_visible / gs->items_max;
1169     gs->position = gs->size_max * gs->item_position / gs->items_max;
1170     gs->position_max = gs->size_max - gs->size;
1171
1172     /* finetuning for maximal right/bottom position */
1173     if (gs->item_position == gs->items_max - gs->items_visible)
1174       gs->position = gs->position_max;
1175   }
1176 }
1177
1178 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
1179 {
1180   va_list ap;
1181
1182   va_start(ap, first_tag);
1183   HandleGadgetTags(gi, first_tag, ap);
1184   va_end(ap);
1185
1186   RedrawGadget(gi);
1187 }
1188
1189 void RedrawGadget(struct GadgetInfo *gi)
1190 {
1191   if (gi->mapped)
1192     DrawGadget(gi, gi->state, DG_DIRECT);
1193 }
1194
1195 struct GadgetInfo *CreateGadget(int first_tag, ...)
1196 {
1197   struct GadgetInfo *new_gadget = checked_malloc(sizeof(struct GadgetInfo));
1198   va_list ap;
1199
1200   /* always start with reliable default values */
1201   memset(new_gadget, 0, sizeof(struct GadgetInfo));     /* zero all fields */
1202   new_gadget->id = getNewGadgetID();
1203   new_gadget->callback_info = default_callback_info;
1204   new_gadget->callback_action = default_callback_action;
1205
1206   va_start(ap, first_tag);
1207   HandleGadgetTags(new_gadget, first_tag, ap);
1208   va_end(ap);
1209
1210   /* insert new gadget into global gadget list */
1211   if (gadget_list_last_entry)
1212   {
1213     gadget_list_last_entry->next = new_gadget;
1214     gadget_list_last_entry = gadget_list_last_entry->next;
1215   }
1216   else
1217     gadget_list_first_entry = gadget_list_last_entry = new_gadget;
1218
1219   return new_gadget;
1220 }
1221
1222 void FreeGadget(struct GadgetInfo *gi)
1223 {
1224   struct GadgetInfo *gi_previous = gadget_list_first_entry;
1225
1226   while (gi_previous && gi_previous->next != gi)
1227     gi_previous = gi_previous->next;
1228
1229   if (gi == gadget_list_first_entry)
1230     gadget_list_first_entry = gi->next;
1231
1232   if (gi == gadget_list_last_entry)
1233     gadget_list_last_entry = gi_previous;
1234
1235   gi_previous->next = gi->next;
1236   free(gi);
1237 }
1238
1239 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
1240 {
1241   if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
1242     return;
1243
1244   gi->text.number_value = atoi(gi->text.value);
1245
1246   if (gi->text.number_value < gi->text.number_min)
1247     gi->text.number_value = gi->text.number_min;
1248   if (gi->text.number_value > gi->text.number_max)
1249     gi->text.number_value = gi->text.number_max;
1250
1251   sprintf(gi->text.value, "%d", gi->text.number_value);
1252
1253   if (gi->text.cursor_position < 0)
1254     gi->text.cursor_position = 0;
1255   else if (gi->text.cursor_position > strlen(gi->text.value))
1256     gi->text.cursor_position = strlen(gi->text.value);
1257 }
1258
1259 /* global pointer to gadget actually in use (when mouse button pressed) */
1260 static struct GadgetInfo *last_gi = NULL;
1261
1262 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
1263 {
1264   if (gi == NULL || gi->mapped)
1265     return;
1266
1267   gi->mapped = TRUE;
1268
1269   if (redraw)
1270     DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
1271 }
1272
1273 void MapGadget(struct GadgetInfo *gi)
1274 {
1275   MapGadgetExt(gi, TRUE);
1276 }
1277
1278 void UnmapGadget(struct GadgetInfo *gi)
1279 {
1280   if (gi == NULL || !gi->mapped)
1281     return;
1282
1283   gi->mapped = FALSE;
1284
1285   if (gi == last_gi)
1286     last_gi = NULL;
1287 }
1288
1289 #define MAX_NUM_GADGETS         1024
1290 #define MULTIMAP_UNMAP          (1 << 0)
1291 #define MULTIMAP_REMAP          (1 << 1)
1292 #define MULTIMAP_REDRAW         (1 << 2)
1293 #define MULTIMAP_PLAYFIELD      (1 << 3)
1294 #define MULTIMAP_DOOR_1         (1 << 4)
1295 #define MULTIMAP_DOOR_2         (1 << 5)
1296 #define MULTIMAP_ALL            (MULTIMAP_PLAYFIELD | \
1297                                  MULTIMAP_DOOR_1 | \
1298                                  MULTIMAP_DOOR_2)
1299
1300 static void MultiMapGadgets(int mode)
1301 {
1302   struct GadgetInfo *gi = gadget_list_first_entry;
1303   static boolean map_state[MAX_NUM_GADGETS];
1304   int map_count = 0;
1305
1306   while (gi)
1307   {
1308     if ((mode & MULTIMAP_PLAYFIELD && gi->x < SX + SXSIZE) ||
1309         (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y < DY + DYSIZE) ||
1310         (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y > DY + DYSIZE))
1311     {
1312       if (mode & MULTIMAP_UNMAP)
1313       {
1314         map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
1315         UnmapGadget(gi);
1316       }
1317       else
1318       {
1319         if (map_state[map_count++ % MAX_NUM_GADGETS])
1320           MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
1321       }
1322     }
1323
1324     gi = gi->next;
1325   }
1326 }
1327
1328 void UnmapAllGadgets()
1329 {
1330   MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
1331 }
1332
1333 void RemapAllGadgets()
1334 {
1335   MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
1336 }
1337
1338 boolean anyTextGadgetActive()
1339 {
1340   return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
1341 }
1342
1343 void ClickOnGadget(struct GadgetInfo *gi, int button)
1344 {
1345   /* simulate releasing mouse button over last gadget, if still pressed */
1346   if (button_status)
1347     HandleGadgets(-1, -1, 0);
1348
1349   /* simulate pressing mouse button over specified gadget */
1350   HandleGadgets(gi->x, gi->y, button);
1351
1352   /* simulate releasing mouse button over specified gadget */
1353   HandleGadgets(gi->x, gi->y, 0);
1354 }
1355
1356 void HandleGadgets(int mx, int my, int button)
1357 {
1358   static struct GadgetInfo *last_info_gi = NULL;
1359   static unsigned long pressed_delay = 0;
1360   static int last_button = 0;
1361   static int last_mx = 0, last_my = 0;
1362   int scrollbar_mouse_pos = 0;
1363   struct GadgetInfo *new_gi, *gi;
1364   boolean press_event;
1365   boolean release_event;
1366   boolean mouse_moving;
1367   boolean gadget_pressed;
1368   boolean gadget_pressed_repeated;
1369   boolean gadget_moving;
1370   boolean gadget_moving_inside;
1371   boolean gadget_moving_off_borders;
1372   boolean gadget_released;
1373   boolean gadget_released_inside;
1374   boolean gadget_released_off_borders;
1375   boolean changed_position = FALSE;
1376
1377   /* check if there are any gadgets defined */
1378   if (gadget_list_first_entry == NULL)
1379     return;
1380
1381   /* check which gadget is under the mouse pointer */
1382   new_gi = getGadgetInfoFromMousePosition(mx, my);
1383
1384   /* check if button state has changed since last invocation */
1385   press_event = (button != 0 && last_button == 0);
1386   release_event = (button == 0 && last_button != 0);
1387   last_button = button;
1388
1389   /* check if mouse has been moved since last invocation */
1390   mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1391   last_mx = mx;
1392   last_my = my;
1393
1394   /* special treatment for text and number input gadgets */
1395   if (anyTextGadgetActive() && button != 0 && !motion_status)
1396   {
1397     struct GadgetInfo *gi = last_gi;
1398
1399     if (new_gi == last_gi)
1400     {
1401       /* if mouse button pressed inside activated text gadget, set cursor */
1402       gi->text.cursor_position = (mx - gi->x) / FONT2_XSIZE;
1403
1404       if (gi->text.cursor_position < 0)
1405         gi->text.cursor_position = 0;
1406       else if (gi->text.cursor_position > strlen(gi->text.value))
1407         gi->text.cursor_position = strlen(gi->text.value);
1408
1409       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1410     }
1411     else
1412     {
1413       /* if mouse button pressed outside text input gadget, deactivate it */
1414       CheckRangeOfNumericInputGadget(gi);
1415       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1416
1417       if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1418         gi->callback_action(gi);
1419
1420       last_gi = NULL;
1421     }
1422   }
1423
1424   gadget_pressed =
1425     (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1426   gadget_pressed_repeated =
1427     (button != 0 && last_gi != NULL && new_gi == last_gi);
1428
1429   gadget_released =             (release_event && last_gi != NULL);
1430   gadget_released_inside =      (gadget_released && new_gi == last_gi);
1431   gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1432
1433   gadget_moving =             (button != 0 && last_gi != NULL && mouse_moving);
1434   gadget_moving_inside =      (gadget_moving && new_gi == last_gi);
1435   gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1436
1437   /* if new gadget pressed, store this gadget  */
1438   if (gadget_pressed)
1439     last_gi = new_gi;
1440
1441   /* 'gi' is actually handled gadget */
1442   gi = last_gi;
1443
1444   /* if gadget is scrollbar, choose mouse position value */
1445   if (gi && gi->type & GD_TYPE_SCROLLBAR)
1446     scrollbar_mouse_pos =
1447       (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1448
1449   /* if mouse button released, no gadget needs to be handled anymore */
1450   if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
1451     last_gi = NULL;
1452
1453   /* modify event position values even if no gadget is pressed */
1454   if (button == 0 && !release_event)
1455     gi = new_gi;
1456
1457   if (gi)
1458   {
1459     int last_x = gi->event.x;
1460     int last_y = gi->event.y;
1461
1462     gi->event.x = mx - gi->x;
1463     gi->event.y = my - gi->y;
1464
1465     if (gi->type == GD_TYPE_DRAWING_AREA)
1466     {
1467       gi->event.x /= gi->drawing.item_xsize;
1468       gi->event.y /= gi->drawing.item_ysize;
1469
1470       if (last_x != gi->event.x || last_y != gi->event.y)
1471         changed_position = TRUE;
1472     }
1473   }
1474
1475   /* handle gadget popup info text */
1476   if (last_info_gi != new_gi ||
1477       (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1478   {
1479     last_info_gi = new_gi;
1480
1481     if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1482     {
1483       new_gi->event.type = 0;
1484       new_gi->callback_info(new_gi);
1485     }
1486     else
1487       default_callback_info(NULL);
1488   }
1489
1490   if (gadget_pressed)
1491   {
1492     if (gi->type == GD_TYPE_CHECK_BUTTON)
1493     {
1494       gi->checked = !gi->checked;
1495     }
1496     else if (gi->type == GD_TYPE_RADIO_BUTTON)
1497     {
1498       struct GadgetInfo *rgi = gadget_list_first_entry;
1499
1500       while (rgi)
1501       {
1502         if (rgi->mapped &&
1503             rgi->type == GD_TYPE_RADIO_BUTTON &&
1504             rgi->radio_nr == gi->radio_nr &&
1505             rgi != gi)
1506         {
1507           rgi->checked = FALSE;
1508           DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
1509         }
1510
1511         rgi = rgi->next;
1512       }
1513
1514       gi->checked = TRUE;
1515     }
1516     else if (gi->type & GD_TYPE_SCROLLBAR)
1517     {
1518       int mpos, gpos;
1519
1520       if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1521       {
1522         mpos = mx;
1523         gpos = gi->x;
1524       }
1525       else
1526       {
1527         mpos = my;
1528         gpos = gi->y;
1529       }
1530
1531       if (mpos >= gpos + gi->scrollbar.position &&
1532           mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1533       {
1534         /* drag scrollbar */
1535         gi->scrollbar.drag_position =
1536           scrollbar_mouse_pos - gi->scrollbar.position;
1537       }
1538       else
1539       {
1540         /* click scrollbar one scrollbar length up/left or down/right */
1541
1542         struct GadgetScrollbar *gs = &gi->scrollbar;
1543         int old_item_position = gs->item_position;
1544
1545         changed_position = FALSE;
1546
1547         gs->item_position +=
1548           gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1549
1550         if (gs->item_position < 0)
1551           gs->item_position = 0;
1552         if (gs->item_position > gs->items_max - gs->items_visible)
1553           gs->item_position = gs->items_max - gs->items_visible;
1554
1555         if (old_item_position != gs->item_position)
1556         {
1557           gi->event.item_position = gs->item_position;
1558           changed_position = TRUE;
1559         }
1560
1561         ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1562                      GDI_END);
1563
1564         gi->state = GD_BUTTON_UNPRESSED;
1565         gi->event.type = GD_EVENT_MOVING;
1566         gi->event.off_borders = FALSE;
1567
1568         if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1569           gi->callback_action(gi);
1570
1571         /* don't handle this scrollbar anymore while mouse button pressed */
1572         last_gi = NULL;
1573
1574         return;
1575       }
1576     }
1577
1578     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1579
1580     gi->state = GD_BUTTON_PRESSED;
1581     gi->event.type = GD_EVENT_PRESSED;
1582     gi->event.button = button;
1583     gi->event.off_borders = FALSE;
1584
1585     /* initialize delay counter */
1586     DelayReached(&pressed_delay, 0);
1587
1588     if (gi->event_mask & GD_EVENT_PRESSED)
1589       gi->callback_action(gi);
1590   }
1591
1592   if (gadget_pressed_repeated)
1593   {
1594     if (gi->event_mask & GD_EVENT_REPEATED &&
1595         DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1596       gi->callback_action(gi);
1597   }
1598
1599   if (gadget_moving)
1600   {
1601     if (gi->type & GD_TYPE_BUTTON)
1602     {
1603       if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1604         DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1605       else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1606         DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1607     }
1608
1609     if (gi->type & GD_TYPE_SCROLLBAR)
1610     {
1611       struct GadgetScrollbar *gs = &gi->scrollbar;
1612       int old_item_position = gs->item_position;
1613
1614       gs->position = scrollbar_mouse_pos - gs->drag_position;
1615
1616       if (gs->position < 0)
1617         gs->position = 0;
1618       if (gs->position > gs->position_max)
1619         gs->position = gs->position_max;
1620
1621       gs->item_position = gs->items_max * gs->position / gs->size_max;
1622
1623       if (old_item_position != gs->item_position)
1624       {
1625         gi->event.item_position = gs->item_position;
1626         changed_position = TRUE;
1627       }
1628
1629       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1630     }
1631
1632     gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1633                  GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1634     gi->event.type = GD_EVENT_MOVING;
1635     gi->event.off_borders = gadget_moving_off_borders;
1636
1637     if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1638         (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1639       gi->callback_action(gi);
1640   }
1641
1642   if (gadget_released_inside)
1643   {
1644     if (!(gi->type & GD_TYPE_TEXTINPUT))
1645       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1646
1647     gi->state = GD_BUTTON_UNPRESSED;
1648     gi->event.type = GD_EVENT_RELEASED;
1649
1650     if (gi->event_mask & GD_EVENT_RELEASED)
1651       gi->callback_action(gi);
1652   }
1653
1654   if (gadget_released_off_borders)
1655   {
1656     if (gi->type & GD_TYPE_SCROLLBAR)
1657       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1658
1659     gi->event.type = GD_EVENT_RELEASED;
1660
1661     if (gi->event_mask & GD_EVENT_RELEASED &&
1662         gi->event_mask & GD_EVENT_OFF_BORDERS)
1663       gi->callback_action(gi);
1664   }
1665 }
1666
1667 void HandleGadgetsKeyInput(KeySym key)
1668 {
1669   struct GadgetInfo *gi = last_gi;
1670   char text[MAX_GADGET_TEXTSIZE];
1671   int text_length;
1672   int cursor_pos;
1673   char letter;
1674   boolean legal_letter;
1675
1676   if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
1677     return;
1678
1679   text_length = strlen(gi->text.value);
1680   cursor_pos = gi->text.cursor_position;
1681   letter = getCharFromKeySym(key);
1682   legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1683                   letter >= '0' && letter <= '9' :
1684                   letter != 0);
1685
1686   if (legal_letter && text_length < gi->text.size)
1687   {
1688     strcpy(text, gi->text.value);
1689     strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1690     gi->text.value[cursor_pos] = letter;
1691     gi->text.cursor_position++;
1692     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1693   }
1694   else if (key == XK_Left && cursor_pos > 0)
1695   {
1696     gi->text.cursor_position--;
1697     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1698   }
1699   else if (key == XK_Right && cursor_pos < text_length)
1700   {
1701     gi->text.cursor_position++;
1702     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1703   }
1704   else if (key == XK_BackSpace && cursor_pos > 0)
1705   {
1706     strcpy(text, gi->text.value);
1707     strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1708     gi->text.cursor_position--;
1709     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1710   }
1711   else if (key == XK_Delete && cursor_pos < text_length)
1712   {
1713     strcpy(text, gi->text.value);
1714     strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1715     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1716   }
1717   else if (key == XK_Return)
1718   {
1719     CheckRangeOfNumericInputGadget(gi);
1720     DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1721
1722     if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1723       gi->callback_action(gi);
1724
1725     last_gi = NULL;
1726   }
1727 }