rnd-19990205-1
[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 void DrawSoundDisplay(unsigned long state)
673 {
674   int pos, cx = DOOR_GFX_PAGEX4, cy = 0;
675
676   pos = (state & BUTTON_SOUND_MUSIC ? SOUND_BUTTON_MUSIC_XPOS :
677          state & BUTTON_SOUND_LOOPS ? SOUND_BUTTON_LOOPS_XPOS :
678          SOUND_BUTTON_SIMPLE_XPOS);
679
680   if (state & BUTTON_ON)
681     cy -= SOUND_BUTTON_YSIZE;
682
683   if (state & BUTTON_PRESSED)
684     cx = DOOR_GFX_PAGEX3;
685
686   XCopyArea(display,pix[PIX_DOOR],drawto,gc,
687             cx + pos,cy + SOUND_BUTTON_ANY_YPOS,
688             SOUND_BUTTON_XSIZE,SOUND_BUTTON_YSIZE,
689             DX + pos,DY + SOUND_BUTTON_ANY_YPOS);
690
691   redraw_mask |= REDRAW_DOOR_1;
692 }
693
694 void DrawGameButton(unsigned long state)
695 {
696   int pos, cx = DOOR_GFX_PAGEX4, cy = -GAME_BUTTON_YSIZE;
697
698   pos = (state & BUTTON_GAME_STOP ? GAME_BUTTON_STOP_XPOS :
699          state & BUTTON_GAME_PAUSE ? GAME_BUTTON_PAUSE_XPOS :
700          GAME_BUTTON_PLAY_XPOS);
701
702   if (state & BUTTON_PRESSED)
703     cx = DOOR_GFX_PAGEX3;
704
705   XCopyArea(display,pix[PIX_DOOR],drawto,gc,
706             cx + pos,cy + GAME_BUTTON_ANY_YPOS,
707             GAME_BUTTON_XSIZE,GAME_BUTTON_YSIZE,
708             DX + pos,DY + GAME_BUTTON_ANY_YPOS);
709
710   redraw_mask |= REDRAW_DOOR_1;
711 }
712
713 void DrawYesNoButton(unsigned long state, int mode)
714 {
715   Drawable dest_drawto;
716   int dest_xoffset, dest_yoffset;
717   int xpos, cx = DOOR_GFX_PAGEX4;
718
719   if (mode == DB_INIT)
720   {
721     dest_drawto = pix[PIX_DB_DOOR];
722     dest_xoffset = DOOR_GFX_PAGEX1;
723     dest_yoffset = 0;
724   }
725   else
726   {
727     dest_drawto = drawto;
728     dest_xoffset = DX;
729     dest_yoffset = DY;
730   }
731
732   xpos = (state & BUTTON_OK ? OK_BUTTON_XPOS : NO_BUTTON_XPOS);
733
734   if (state & BUTTON_PRESSED)
735     cx = DOOR_GFX_PAGEX3;
736
737   XCopyArea(display, pix[PIX_DOOR], dest_drawto, gc,
738             cx + xpos, OK_BUTTON_GFX_YPOS,
739             OK_BUTTON_XSIZE, OK_BUTTON_YSIZE,
740             dest_xoffset + xpos, dest_yoffset + OK_BUTTON_YPOS);
741
742   redraw_mask |= REDRAW_DOOR_1;
743 }
744
745 void DrawConfirmButton(unsigned long state, int mode)
746 {
747   Drawable dest_drawto;
748   int dest_xoffset, dest_yoffset;
749   int cx = DOOR_GFX_PAGEX4;
750
751   if (mode == DB_INIT)
752   {
753     dest_drawto = pix[PIX_DB_DOOR];
754     dest_xoffset = DOOR_GFX_PAGEX1;
755     dest_yoffset = 0;
756   }
757   else
758   {
759     dest_drawto = drawto;
760     dest_xoffset = DX;
761     dest_yoffset = DY;
762   }
763
764   if (state & BUTTON_PRESSED)
765     cx = DOOR_GFX_PAGEX3;
766
767   XCopyArea(display, pix[PIX_DOOR], dest_drawto, gc,
768             cx + CONFIRM_BUTTON_XPOS, CONFIRM_BUTTON_GFX_YPOS,
769             CONFIRM_BUTTON_XSIZE, CONFIRM_BUTTON_YSIZE,
770             dest_xoffset + CONFIRM_BUTTON_XPOS,
771             dest_yoffset + CONFIRM_BUTTON_YPOS);
772
773   redraw_mask |= REDRAW_DOOR_1;
774 }
775
776 void DrawPlayerButton(unsigned long state, int mode)
777 {
778   Drawable dest_drawto;
779   int dest_xoffset, dest_yoffset;
780   int graphic = GFX_SPIELER1;   /* default */
781   int graphic_offset = (PLAYER_BUTTON_XSIZE - TILEX/2)/2;
782   int xpos, ypos;
783   int cx = DOOR_GFX_PAGEX4, cy = 0;
784
785   if (mode == DB_INIT)
786   {
787     dest_drawto = pix[PIX_DB_DOOR];
788     dest_xoffset = DOOR_GFX_PAGEX1;
789     dest_yoffset = 0;
790   }
791   else
792   {
793     dest_drawto = drawto;
794     dest_xoffset = DX;
795     dest_yoffset = DY;
796   }
797
798   if (state & BUTTON_PLAYER_1)
799     graphic = GFX_SPIELER1;
800   else if (state & BUTTON_PLAYER_2)
801     graphic = GFX_SPIELER2;
802   else if (state & BUTTON_PLAYER_3)
803     graphic = GFX_SPIELER3;
804   else if (state & BUTTON_PLAYER_4)
805     graphic = GFX_SPIELER4;
806
807   xpos = (state & BUTTON_PLAYER_1 || state & BUTTON_PLAYER_3 ?
808           PLAYER_BUTTON_1_XPOS : PLAYER_BUTTON_2_XPOS);
809   ypos = (state & BUTTON_PLAYER_1 || state & BUTTON_PLAYER_2 ?
810           PLAYER_BUTTON_1_YPOS : PLAYER_BUTTON_3_YPOS);
811
812   if (state & BUTTON_PRESSED)
813   {
814     cx = DOOR_GFX_PAGEX3;
815     graphic_offset += 1;
816   }
817
818   XCopyArea(display, pix[PIX_DOOR], dest_drawto, gc,
819             cx + PLAYER_BUTTON_GFX_XPOS, cy + PLAYER_BUTTON_GFX_YPOS,
820             PLAYER_BUTTON_XSIZE, PLAYER_BUTTON_YSIZE,
821             dest_xoffset + xpos, dest_yoffset + ypos);
822   DrawMiniGraphicExt(dest_drawto,gc,
823                      dest_xoffset + xpos + graphic_offset,
824                      dest_yoffset + ypos + graphic_offset,
825                      graphic);
826
827   redraw_mask |= REDRAW_DOOR_1;
828 }
829
830 /* several buttons in the level editor */
831
832 void DrawEditButton(unsigned long state)
833 {
834   int i;
835   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
836   int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY2;
837   static int edit_pos[6][4] =
838   {
839    {ED_BUTTON_CTRL_XPOS,ED_BUTTON_CTRL_YPOS,
840     ED_BUTTON_CTRL_XSIZE,ED_BUTTON_CTRL_YSIZE},
841
842    {ED_BUTTON_FILL_XPOS,ED_BUTTON_FILL_YPOS,
843     ED_BUTTON_FILL_XSIZE,ED_BUTTON_FILL_YSIZE},
844
845    {ED_BUTTON_LEFT_XPOS,ED_BUTTON_LEFT_YPOS,
846     ED_BUTTON_LEFT_XSIZE,ED_BUTTON_LEFT_YSIZE},
847
848    {ED_BUTTON_UP_XPOS,ED_BUTTON_UP_YPOS,
849     ED_BUTTON_UP_XSIZE,ED_BUTTON_UP_YSIZE},
850
851    {ED_BUTTON_DOWN_XPOS,ED_BUTTON_DOWN_YPOS,
852     ED_BUTTON_DOWN_XSIZE,ED_BUTTON_DOWN_YSIZE},
853
854    {ED_BUTTON_RIGHT_XPOS,ED_BUTTON_RIGHT_YPOS,
855     ED_BUTTON_RIGHT_XSIZE,ED_BUTTON_RIGHT_YSIZE}
856   };
857
858   if (state & ED_BUTTON_PRESSED)
859     cx = DOOR_GFX_PAGEX5;
860
861   for(i=0;i<6;i++)
862   {
863     if (state & (1<<i))
864       XCopyArea(display,pix[PIX_DOOR],drawto,gc,
865                 cx + edit_pos[i][xpos],
866                 cy + edit_pos[i][ypos],
867                 edit_pos[i][xsize],
868                 edit_pos[i][ysize],
869                 VX + edit_pos[i][xpos],
870                 VY + edit_pos[i][ypos]);
871   }
872
873   redraw_mask |= REDRAW_DOOR_2;
874 }
875
876 void DrawCtrlButton(unsigned long state)
877 {
878   int i;
879   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
880   int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY1+80;
881   static int edit_pos[4][4] =
882   {
883    {ED_BUTTON_EDIT_XPOS,ED_BUTTON_EDIT_YPOS,
884     ED_BUTTON_EDIT_XSIZE,ED_BUTTON_EDIT_YSIZE},
885
886    {ED_BUTTON_CLEAR_XPOS,ED_BUTTON_CLEAR_YPOS,
887     ED_BUTTON_CLEAR_XSIZE,ED_BUTTON_CLEAR_YSIZE},
888
889    {ED_BUTTON_UNDO_XPOS,ED_BUTTON_UNDO_YPOS,
890     ED_BUTTON_UNDO_XSIZE,ED_BUTTON_UNDO_YSIZE},
891
892    {ED_BUTTON_EXIT_XPOS,ED_BUTTON_EXIT_YPOS,
893     ED_BUTTON_EXIT_XSIZE,ED_BUTTON_EXIT_YSIZE}
894   };
895
896   if (state & ED_BUTTON_PRESSED)
897     cx = DOOR_GFX_PAGEX3;
898
899   for(i=0;i<4;i++)
900   {
901     if (state & (1<<(i+6)))
902       XCopyArea(display,pix[PIX_DOOR],drawto,gc,
903                 cx + edit_pos[i][xpos],
904                 cy + edit_pos[i][ypos],
905                 edit_pos[i][xsize],
906                 edit_pos[i][ysize],
907                 VX + edit_pos[i][xpos],
908                 VY + edit_pos[i][ypos]);
909   }
910
911   redraw_mask |= REDRAW_DOOR_2;
912 }
913
914 void DrawElemButton(int button_nr, int button_state)
915 {
916   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
917   int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY1;
918   int from_x, from_y, to_x,to_y, size_x, size_y;
919   static int edit_pos[3][4] =
920   {
921    {ED_BUTTON_EUP_XPOS,ED_BUTTON_EUP_YPOS,
922     ED_BUTTON_EUP_XSIZE,ED_BUTTON_EUP_YSIZE},
923
924    {ED_BUTTON_EDOWN_XPOS,ED_BUTTON_EDOWN_YPOS,
925     ED_BUTTON_EDOWN_XSIZE,ED_BUTTON_EDOWN_YSIZE},
926
927    {ED_BUTTON_ELEM_XPOS,ED_BUTTON_ELEM_YPOS,
928     ED_BUTTON_ELEM_XSIZE,ED_BUTTON_ELEM_YSIZE}
929   };
930
931   if (button_nr<ED_BUTTON_ELEM)
932   {
933     int pos = button_nr;
934
935     from_x = cx + edit_pos[pos][xpos];
936     from_y = cy + edit_pos[pos][ypos];
937     size_x = edit_pos[pos][xsize];
938     size_y = edit_pos[pos][ysize];
939     to_x   = DX + edit_pos[pos][xpos];
940     to_y   = DY + edit_pos[pos][ypos];
941
942     if (button_state & ED_BUTTON_PRESSED)
943     {
944       if (button_nr==ED_BUTTON_EUP)
945         from_y = cy + ED_BUTTON_EUP_Y2POS;
946       else
947         from_y = cy + ED_BUTTON_EDOWN_Y2POS;
948     }
949
950     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
951               from_x,from_y, size_x,size_y, to_x,to_y);
952   }
953   else
954   {
955     int pos = ED_BUTTON_ELEM;
956     int elem_pos = button_nr-ED_BUTTON_ELEM;
957     int x = elem_pos % MAX_ELEM_X;
958     int y = elem_pos / MAX_ELEM_X;
959     int graphic;
960     int shift = 0;
961
962     if (elem_pos+element_shift < elements_in_list)
963       graphic = el2gfx(editor_element[elem_pos+element_shift]);
964     else
965       graphic = GFX_LEERRAUM;
966
967     from_x = cx + edit_pos[pos][xpos];
968     from_y = cy + edit_pos[pos][ypos];
969     size_x = edit_pos[pos][xsize];
970     size_y = edit_pos[pos][ysize];
971     to_x   = DX + edit_pos[pos][xpos] + x * ED_BUTTON_ELEM_XSIZE;
972     to_y   = DY + edit_pos[pos][ypos] + y * ED_BUTTON_ELEM_YSIZE;
973
974     if (button_state & ED_BUTTON_PRESSED)
975     {
976       from_y = ED_BUTTON_ELEM_Y2POS;
977       shift = 1;
978     }
979
980     XCopyArea(display,pix[PIX_DOOR],drawto,gc,
981               from_x,from_y, size_x,size_y, to_x,to_y);
982
983     DrawMiniGraphicExt(drawto,gc,
984                        DX+ED_BUTTON_ELEM_XPOS+3+shift + 
985                        (elem_pos % MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
986                        DY+ED_BUTTON_ELEM_YPOS+3+shift +
987                        (elem_pos / MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
988                        graphic);
989   }
990
991   redraw_mask |= REDRAW_DOOR_1;
992 }
993
994 void DrawCountButton(int button_nr, int button_state)
995 {
996   int from_x, from_y, to_x,to_y, size_x, size_y;
997
998   from_x =
999     DOOR_GFX_PAGEX4+(button_nr%2 ? ED_BUTTON_PLUS_XPOS : ED_BUTTON_MINUS_XPOS);
1000   from_y = DOOR_GFX_PAGEY1 + ED_BUTTON_MINUS_YPOS;
1001   size_x = ED_BUTTON_MINUS_XSIZE;
1002   size_y = ED_BUTTON_MINUS_YSIZE;
1003   to_x = (button_nr<32 ? ED_COUNT_GADGET_XPOS : ED_SIZE_GADGET_XPOS);
1004   if (button_nr % 2)
1005     to_x += (ED_BUTTON_PLUS_XPOS - ED_BUTTON_MINUS_XPOS);
1006   to_y = (button_nr<32 ? ED_COUNT_GADGET_YPOS : ED_SIZE_GADGET_YPOS) +
1007     ((button_nr<32 ? button_nr : button_nr-32)/2)*ED_COUNT_GADGET_YSIZE;
1008
1009   if (button_state & ED_BUTTON_PRESSED)
1010     from_x -= DXSIZE;
1011
1012   XCopyArea(display,pix[PIX_DOOR],drawto,gc,
1013             from_x,from_y, size_x,size_y, to_x,to_y);
1014   XCopyArea(display,pix[PIX_DOOR],window,gc,
1015             from_x,from_y, size_x,size_y, to_x,to_y);
1016 }
1017
1018 /**********************************************************************/
1019 /********** checking buttons (and redrawing them, if needed) **********/
1020 /**********************************************************************/
1021
1022 int CheckVideoButtons(int mx, int my, int button)
1023 {
1024   int return_code = 0;
1025   static int choice = -1;
1026   static boolean pressed = FALSE;
1027   static int video_button[5] =
1028   {
1029     VIDEO_PRESS_EJECT_ON,
1030     VIDEO_PRESS_STOP_ON,
1031     VIDEO_PRESS_PAUSE_ON,
1032     VIDEO_PRESS_REC_ON,
1033     VIDEO_PRESS_PLAY_ON
1034   };
1035
1036   if (button)
1037   {
1038     if (!motion_status)         /* Maustaste neu gedrückt */
1039     {
1040       if (ON_VIDEO_BUTTON(mx,my))
1041       {
1042         choice = VIDEO_BUTTON(mx);
1043         pressed = TRUE;
1044         DrawVideoDisplay(video_button[choice],0);
1045       }
1046     }
1047     else                        /* Mausbewegung bei gedrückter Maustaste */
1048     {
1049       if ((!ON_VIDEO_BUTTON(mx,my) || VIDEO_BUTTON(mx)!=choice) &&
1050           choice>=0 && pressed)
1051       {
1052         pressed = FALSE;
1053         DrawVideoDisplay(video_button[choice]<<1,0);
1054       }
1055       else if (ON_VIDEO_BUTTON(mx,my) && VIDEO_BUTTON(mx)==choice && !pressed)
1056       {
1057         pressed = TRUE;
1058         DrawVideoDisplay(video_button[choice],0);
1059       }
1060     }
1061   }
1062   else                          /* Maustaste wieder losgelassen */
1063   {
1064     if (ON_VIDEO_BUTTON(mx,my) && VIDEO_BUTTON(mx)==choice && pressed)
1065     {
1066       DrawVideoDisplay(video_button[choice]<<1,0);
1067       return_code = choice+1;
1068       choice = -1;
1069       pressed = FALSE;
1070     }
1071     else
1072     {
1073       choice = -1;
1074       pressed = FALSE;
1075     }
1076   }
1077
1078   BackToFront();
1079   return(return_code);
1080 }
1081
1082 int CheckSoundButtons(int mx, int my, int button)
1083 {
1084   int return_code = 0;
1085   static int choice = -1;
1086   static boolean pressed = FALSE;
1087   int sound_state[3];
1088
1089   sound_state[0] = BUTTON_SOUND_MUSIC  | (BUTTON_ON * setup.sound_music);
1090   sound_state[1] = BUTTON_SOUND_LOOPS  | (BUTTON_ON * setup.sound_loops);
1091   sound_state[2] = BUTTON_SOUND_SIMPLE | (BUTTON_ON * setup.sound_simple);
1092
1093   if (button)
1094   {
1095     if (!motion_status)         /* Maustaste neu gedrückt */
1096     {
1097       if (ON_SOUND_BUTTON(mx,my))
1098       {
1099         choice = SOUND_BUTTON(mx);
1100         pressed = TRUE;
1101         DrawSoundDisplay(sound_state[choice] | BUTTON_PRESSED);
1102       }
1103     }
1104     else                        /* Mausbewegung bei gedrückter Maustaste */
1105     {
1106       if ((!ON_SOUND_BUTTON(mx,my) || SOUND_BUTTON(mx)!=choice) &&
1107           choice>=0 && pressed)
1108       {
1109         pressed = FALSE;
1110         DrawSoundDisplay(sound_state[choice] | BUTTON_RELEASED);
1111       }
1112       else if (ON_SOUND_BUTTON(mx,my) && SOUND_BUTTON(mx)==choice && !pressed)
1113       {
1114         pressed = TRUE;
1115         DrawSoundDisplay(sound_state[choice] | BUTTON_PRESSED);
1116       }
1117     }
1118   }
1119   else                          /* Maustaste wieder losgelassen */
1120   {
1121     if (ON_SOUND_BUTTON(mx,my) && SOUND_BUTTON(mx)==choice && pressed)
1122     {
1123       DrawSoundDisplay(sound_state[choice] | BUTTON_RELEASED);
1124       return_code = 1<<choice;
1125       choice = -1;
1126       pressed = FALSE;
1127     }
1128     else
1129     {
1130       choice = -1;
1131       pressed = FALSE;
1132     }
1133   }
1134
1135   BackToFront();
1136   return(return_code);
1137 }
1138
1139 int CheckGameButtons(int mx, int my, int button)
1140 {
1141   int return_code = 0;
1142   static int choice = -1;
1143   static boolean pressed = FALSE;
1144   int game_state[3] =
1145   {
1146     BUTTON_GAME_STOP,
1147     BUTTON_GAME_PAUSE,
1148     BUTTON_GAME_PLAY
1149   };
1150
1151   if (button)
1152   {
1153     if (!motion_status)         /* Maustaste neu gedrückt */
1154     {
1155       if (ON_GAME_BUTTON(mx,my))
1156       {
1157         choice = GAME_BUTTON(mx);
1158         pressed = TRUE;
1159         DrawGameButton(game_state[choice] | BUTTON_PRESSED);
1160       }
1161     }
1162     else                        /* Mausbewegung bei gedrückter Maustaste */
1163     {
1164       if ((!ON_GAME_BUTTON(mx,my) || GAME_BUTTON(mx)!=choice) &&
1165           choice>=0 && pressed)
1166       {
1167         pressed = FALSE;
1168         DrawGameButton(game_state[choice] | BUTTON_RELEASED);
1169       }
1170       else if (ON_GAME_BUTTON(mx,my) && GAME_BUTTON(mx)==choice && !pressed)
1171       {
1172         pressed = TRUE;
1173         DrawGameButton(game_state[choice] | BUTTON_PRESSED);
1174       }
1175     }
1176   }
1177   else                          /* Maustaste wieder losgelassen */
1178   {
1179     if (ON_GAME_BUTTON(mx,my) && GAME_BUTTON(mx)==choice && pressed)
1180     {
1181       DrawGameButton(game_state[choice] | BUTTON_RELEASED);
1182       return_code = 1<<choice;
1183       choice = -1;
1184       pressed = FALSE;
1185     }
1186     else
1187     {
1188       choice = -1;
1189       pressed = FALSE;
1190     }
1191   }
1192
1193   BackToFront();
1194   return(return_code);
1195 }
1196
1197 int CheckYesNoButtons(int mx, int my, int button)
1198 {
1199   int return_code = 0;
1200   static int choice = -1;
1201   static boolean pressed = FALSE;
1202   static int yesno_button[5] =
1203   {
1204     BUTTON_OK,
1205     BUTTON_NO
1206   };
1207
1208   if (button)
1209   {
1210     if (!motion_status)         /* Maustaste neu gedrückt */
1211     {
1212       if (ON_YESNO_BUTTON(mx,my))
1213       {
1214         choice = YESNO_BUTTON(mx);
1215         pressed = TRUE;
1216         DrawYesNoButton(yesno_button[choice] | BUTTON_PRESSED, DB_NORMAL);
1217       }
1218     }
1219     else                        /* Mausbewegung bei gedrückter Maustaste */
1220     {
1221       if ((!ON_YESNO_BUTTON(mx,my) || YESNO_BUTTON(mx)!=choice) &&
1222           choice>=0 && pressed)
1223       {
1224         pressed = FALSE;
1225         DrawYesNoButton(yesno_button[choice] | BUTTON_RELEASED, DB_NORMAL);
1226       }
1227       else if (ON_YESNO_BUTTON(mx,my) && YESNO_BUTTON(mx)==choice && !pressed)
1228       {
1229         pressed = TRUE;
1230         DrawYesNoButton(yesno_button[choice] | BUTTON_PRESSED, DB_NORMAL);
1231       }
1232     }
1233   }
1234   else                          /* Maustaste wieder losgelassen */
1235   {
1236     if (ON_YESNO_BUTTON(mx,my) && YESNO_BUTTON(mx)==choice && pressed)
1237     {
1238       DrawYesNoButton(yesno_button[choice] | BUTTON_RELEASED, DB_NORMAL);
1239       return_code = choice+1;
1240       choice = -1;
1241       pressed = FALSE;
1242     }
1243     else
1244     {
1245       choice = -1;
1246       pressed = FALSE;
1247     }
1248   }
1249
1250   BackToFront();
1251   return(return_code);
1252 }
1253
1254 int CheckConfirmButton(int mx, int my, int button)
1255 {
1256   int return_code = 0;
1257   static int choice = -1;
1258   static boolean pressed = FALSE;
1259
1260   if (button)
1261   {
1262     if (!motion_status)         /* Maustaste neu gedrückt */
1263     {
1264       if (ON_CONFIRM_BUTTON(mx,my))
1265       {
1266         choice = 0;
1267         pressed = TRUE;
1268         DrawConfirmButton(BUTTON_PRESSED, DB_NORMAL);
1269       }
1270     }
1271     else                        /* Mausbewegung bei gedrückter Maustaste */
1272     {
1273       if (!ON_CONFIRM_BUTTON(mx,my) && choice>=0 && pressed)
1274       {
1275         pressed = FALSE;
1276         DrawConfirmButton(BUTTON_RELEASED, DB_NORMAL);
1277       }
1278       else if (ON_CONFIRM_BUTTON(mx,my) && !pressed)
1279       {
1280         pressed = TRUE;
1281         DrawConfirmButton(BUTTON_PRESSED, DB_NORMAL);
1282       }
1283     }
1284   }
1285   else                          /* Maustaste wieder losgelassen */
1286   {
1287     if (ON_CONFIRM_BUTTON(mx,my) && pressed)
1288     {
1289       DrawConfirmButton(BUTTON_RELEASED, DB_NORMAL);
1290       return_code = BUTTON_CONFIRM;
1291       choice = -1;
1292       pressed = FALSE;
1293     }
1294     else
1295     {
1296       choice = -1;
1297       pressed = FALSE;
1298     }
1299   }
1300
1301   BackToFront();
1302   return(return_code);
1303 }
1304
1305 int CheckPlayerButtons(int mx, int my, int button)
1306 {
1307   int return_code = 0;
1308   static int choice = -1;
1309   static boolean pressed = FALSE;
1310   int player_state[4] =
1311   {
1312     BUTTON_PLAYER_1,
1313     BUTTON_PLAYER_2,
1314     BUTTON_PLAYER_3,
1315     BUTTON_PLAYER_4
1316   };
1317
1318   if (button)
1319   {
1320     if (!motion_status)         /* Maustaste neu gedrückt */
1321     {
1322       if (ON_PLAYER_BUTTON(mx,my))
1323       {
1324         choice = PLAYER_BUTTON(mx,my);
1325         pressed = TRUE;
1326         DrawPlayerButton(player_state[choice] | BUTTON_PRESSED, DB_NORMAL);
1327       }
1328     }
1329     else                        /* Mausbewegung bei gedrückter Maustaste */
1330     {
1331       if ((!ON_PLAYER_BUTTON(mx,my) || PLAYER_BUTTON(mx,my)!=choice) &&
1332           choice>=0 && pressed)
1333       {
1334         pressed = FALSE;
1335         DrawPlayerButton(player_state[choice] | BUTTON_RELEASED, DB_NORMAL);
1336       }
1337       else if (ON_PLAYER_BUTTON(mx,my) && PLAYER_BUTTON(mx,my)==choice && !pressed)
1338       {
1339         pressed = TRUE;
1340         DrawPlayerButton(player_state[choice] | BUTTON_PRESSED, DB_NORMAL);
1341       }
1342     }
1343   }
1344   else                          /* Maustaste wieder losgelassen */
1345   {
1346     if (ON_PLAYER_BUTTON(mx,my) && PLAYER_BUTTON(mx,my)==choice && pressed)
1347     {
1348       DrawPlayerButton(player_state[choice] | BUTTON_RELEASED, DB_NORMAL);
1349       return_code = player_state[choice];
1350       choice = -1;
1351       pressed = FALSE;
1352     }
1353     else
1354     {
1355       choice = -1;
1356       pressed = FALSE;
1357     }
1358   }
1359
1360   BackToFront();
1361   return(return_code);
1362 }
1363
1364 /* several buttons in the level editor */
1365
1366 int CheckEditButtons(int mx, int my, int button)
1367 {
1368   int return_code = 0;
1369   static int choice = -1;
1370   static boolean pressed = FALSE;
1371   static int edit_button[6] =
1372   {
1373     ED_BUTTON_CTRL,
1374     ED_BUTTON_FILL,
1375     ED_BUTTON_LEFT,
1376     ED_BUTTON_UP,
1377     ED_BUTTON_DOWN,
1378     ED_BUTTON_RIGHT
1379   };
1380
1381   if (button)
1382   {
1383     if (!motion_status)         /* Maustaste neu gedrückt */
1384     {
1385       if (ON_EDIT_BUTTON(mx,my))
1386       {
1387         choice = EDIT_BUTTON(mx,my);
1388         pressed = TRUE;
1389         DrawEditButton(edit_button[choice] | ED_BUTTON_PRESSED);
1390         if (edit_button[choice]!=ED_BUTTON_CTRL &&
1391             edit_button[choice]!=ED_BUTTON_FILL)
1392           return_code = 1<<choice;
1393       }
1394     }
1395     else                        /* Mausbewegung bei gedrückter Maustaste */
1396     {
1397       if ((!ON_EDIT_BUTTON(mx,my) || EDIT_BUTTON(mx,my)!=choice) &&
1398           choice>=0 && pressed)
1399       {
1400         pressed = FALSE;
1401         DrawEditButton(edit_button[choice] | ED_BUTTON_RELEASED);
1402       }
1403       else if (ON_EDIT_BUTTON(mx,my) && EDIT_BUTTON(mx,my)==choice)
1404       {
1405         if (!pressed)
1406           DrawEditButton(edit_button[choice] | ED_BUTTON_PRESSED);
1407         pressed = TRUE;
1408         if (edit_button[choice]!=ED_BUTTON_CTRL &&
1409             edit_button[choice]!=ED_BUTTON_FILL)
1410           return_code = 1<<choice;
1411       }
1412     }
1413   }
1414   else                          /* Maustaste wieder losgelassen */
1415   {
1416     if (ON_EDIT_BUTTON(mx,my) && EDIT_BUTTON(mx,my)==choice && pressed)
1417     {
1418       DrawEditButton(edit_button[choice] | ED_BUTTON_RELEASED);
1419       if (edit_button[choice]==ED_BUTTON_CTRL ||
1420           edit_button[choice]==ED_BUTTON_FILL)
1421         return_code = 1<<choice;
1422       choice = -1;
1423       pressed = FALSE;
1424     }
1425     else
1426     {
1427       choice = -1;
1428       pressed = FALSE;
1429     }
1430   }
1431
1432   BackToFront();
1433   return(return_code);
1434 }
1435
1436 int CheckCtrlButtons(int mx, int my, int button)
1437 {
1438   int return_code = 0;
1439   static int choice = -1;
1440   static boolean pressed = FALSE;
1441   static int ctrl_button[4] =
1442   {
1443     ED_BUTTON_EDIT,
1444     ED_BUTTON_CLEAR,
1445     ED_BUTTON_UNDO,
1446     ED_BUTTON_EXIT
1447   };
1448
1449   if (button)
1450   {
1451     if (!motion_status)         /* Maustaste neu gedrückt */
1452     {
1453       if (ON_CTRL_BUTTON(mx,my))
1454       {
1455         choice = CTRL_BUTTON(mx,my);
1456         pressed = TRUE;
1457         DrawCtrlButton(ctrl_button[choice] | ED_BUTTON_PRESSED);
1458       }
1459     }
1460     else                        /* Mausbewegung bei gedrückter Maustaste */
1461     {
1462       if ((!ON_CTRL_BUTTON(mx,my) || CTRL_BUTTON(mx,my)!=choice) &&
1463           choice>=0 && pressed)
1464       {
1465         pressed = FALSE;
1466         DrawCtrlButton(ctrl_button[choice] | ED_BUTTON_RELEASED);
1467       }
1468       else if (ON_CTRL_BUTTON(mx,my) && CTRL_BUTTON(mx,my)==choice && !pressed)
1469       {
1470         pressed = TRUE;
1471         DrawCtrlButton(ctrl_button[choice] | ED_BUTTON_PRESSED);
1472       }
1473     }
1474   }
1475   else                          /* Maustaste wieder losgelassen */
1476   {
1477     if (ON_CTRL_BUTTON(mx,my) && CTRL_BUTTON(mx,my)==choice && pressed)
1478     {
1479       DrawCtrlButton(ctrl_button[choice] | ED_BUTTON_RELEASED);
1480       return_code = 1<<(choice+6);
1481       choice = -1;
1482       pressed = FALSE;
1483     }
1484     else
1485     {
1486       choice = -1;
1487       pressed = FALSE;
1488     }
1489   }
1490
1491   BackToFront();
1492   return(return_code);
1493 }
1494
1495 int CheckElemButtons(int mx, int my, int button)
1496 {
1497   int return_code = -1;
1498   static int choice = -1;
1499   static boolean pressed = FALSE;
1500
1501   if (button)
1502   {
1503     if (!motion_status)         /* Maustaste neu gedrückt */
1504     {
1505       if (ON_ELEM_BUTTON(mx,my))
1506       {
1507         choice = ELEM_BUTTON(mx,my);
1508         pressed = TRUE;
1509         DrawElemButton(choice,ED_BUTTON_PRESSED);
1510         if (choice==ED_BUTTON_EUP ||
1511             choice==ED_BUTTON_EDOWN)
1512           return_code = choice;
1513       }
1514     }
1515     else                        /* Mausbewegung bei gedrückter Maustaste */
1516     {
1517       if ((!ON_ELEM_BUTTON(mx,my) || ELEM_BUTTON(mx,my)!=choice) &&
1518           choice>=0 && pressed)
1519       {
1520         pressed = FALSE;
1521         DrawElemButton(choice,ED_BUTTON_RELEASED);
1522       }
1523       else if (ON_ELEM_BUTTON(mx,my) && ELEM_BUTTON(mx,my)==choice)
1524       {
1525         if (!pressed)
1526           DrawElemButton(choice,ED_BUTTON_PRESSED);
1527         pressed = TRUE;
1528         if (choice==ED_BUTTON_EUP ||
1529             choice==ED_BUTTON_EDOWN)
1530           return_code = choice;
1531       }
1532     }
1533   }
1534   else                          /* Maustaste wieder losgelassen */
1535   {
1536     if (ON_ELEM_BUTTON(mx,my) && ELEM_BUTTON(mx,my)==choice && pressed)
1537     {
1538       DrawElemButton(choice,ED_BUTTON_RELEASED);
1539       if (choice!=ED_BUTTON_EUP &&
1540           choice!=ED_BUTTON_EDOWN)
1541         return_code = choice;
1542       choice = -1;
1543       pressed = FALSE;
1544     }
1545     else
1546     {
1547       choice = -1;
1548       pressed = FALSE;
1549     }
1550   }
1551
1552   BackToFront();
1553   return(return_code);
1554 }
1555
1556 int CheckCountButtons(int mx, int my, int button)
1557 {
1558   int return_code = -1;
1559   static int choice = -1;
1560   static boolean pressed = FALSE;
1561
1562   if (button)
1563   {
1564     if (!motion_status)         /* Maustaste neu gedrückt */
1565     {
1566       if (ON_COUNT_BUTTON(mx,my))
1567       {
1568         choice = COUNT_BUTTON(mx,my);
1569         pressed = TRUE;
1570         DrawCountButton(choice,ED_BUTTON_PRESSED);
1571         return_code = choice;
1572       }
1573     }
1574     else                        /* Mausbewegung bei gedrückter Maustaste */
1575     {
1576       if ((!ON_COUNT_BUTTON(mx,my) || COUNT_BUTTON(mx,my)!=choice) &&
1577           choice>=0 && pressed)
1578       {
1579         pressed = FALSE;
1580         DrawCountButton(choice,ED_BUTTON_RELEASED);
1581       }
1582       else if (ON_COUNT_BUTTON(mx,my) && COUNT_BUTTON(mx,my)==choice)
1583       {
1584         if (!pressed)
1585           DrawCountButton(choice,ED_BUTTON_PRESSED);
1586         pressed = TRUE;
1587         return_code = choice;
1588       }
1589     }
1590   }
1591   else                          /* Maustaste wieder losgelassen */
1592   {
1593     if (ON_COUNT_BUTTON(mx,my) && COUNT_BUTTON(mx,my)==choice && pressed)
1594     {
1595       DrawCountButton(choice,ED_BUTTON_RELEASED);
1596       choice = -1;
1597       pressed = FALSE;
1598     }
1599     else
1600     {
1601       choice = -1;
1602       pressed = FALSE;
1603     }
1604   }
1605
1606   BackToFront();
1607   return(return_code);
1608 }
1609
1610
1611 /* NEW GADGET STUFF -------------------------------------------------------- */
1612
1613
1614 /* values for DrawGadget() */
1615 #define DG_UNPRESSED            0
1616 #define DG_PRESSED              1
1617 #define DG_BUFFERED             0
1618 #define DG_DIRECT               1
1619
1620 static struct GadgetInfo *gadget_list_first_entry = NULL;
1621 static struct GadgetInfo *gadget_list_last_entry = NULL;
1622 static int next_free_gadget_id = 1;
1623 static boolean gadget_id_wrapped = FALSE;
1624
1625 static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
1626 {
1627   struct GadgetInfo *gi = gadget_list_first_entry;
1628
1629   while (gi && gi->id != id)
1630     gi = gi->next;
1631
1632   return gi;
1633 }
1634
1635 static int getNewGadgetID()
1636 {
1637   int id = next_free_gadget_id++;
1638
1639   if (next_free_gadget_id <= 0)         /* counter overrun */
1640   {
1641     gadget_id_wrapped = TRUE;           /* now we must check each ID */
1642     next_free_gadget_id = 0;
1643   }
1644
1645   if (gadget_id_wrapped)
1646   {
1647     next_free_gadget_id++;
1648     while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
1649       next_free_gadget_id++;
1650   }
1651
1652   if (next_free_gadget_id <= 0)         /* cannot get new gadget id */
1653     Error(ERR_EXIT, "too much gadgets -- this should not happen");
1654
1655   return id;
1656 }
1657
1658 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
1659 {
1660   struct GadgetInfo *gi = gadget_list_first_entry;
1661
1662   while (gi)
1663   {
1664     if (gi->mapped &&
1665         mx >= gi->x && mx < gi->x + gi->width &&
1666         my >= gi->y && my < gi->y + gi->height)
1667         break;
1668
1669     gi = gi->next;
1670   }
1671
1672   return gi;
1673 }
1674
1675 static void default_callback_info(void *ptr)
1676 {
1677   if (game_status == LEVELED)
1678     HandleEditorGadgetInfoText(ptr);
1679 }
1680
1681 static void default_callback_action(void *ptr)
1682 {
1683   return;
1684 }
1685
1686 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
1687 {
1688   int state = (pressed ? 1 : 0);
1689   struct GadgetDesign *gd = (gi->checked ?
1690                              &gi->alt_design[state] :
1691                              &gi->design[state]);
1692
1693   switch (gi->type)
1694   {
1695     case GD_TYPE_NORMAL_BUTTON:
1696     case GD_TYPE_CHECK_BUTTON:
1697     case GD_TYPE_RADIO_BUTTON:
1698       XCopyArea(display, gd->pixmap, drawto, gc,
1699                 gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
1700       if (gi->deco.design.pixmap)
1701         XCopyArea(display, gi->deco.design.pixmap, drawto, gc,
1702                   gi->deco.design.x, gi->deco.design.y,
1703                   gi->deco.width, gi->deco.height,
1704                   gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
1705                   gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
1706       break;
1707
1708     case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
1709     case GD_TYPE_TEXTINPUT_NUMERIC:
1710       {
1711         int i;
1712         char cursor_letter;
1713         char cursor_string[3];
1714         char text[MAX_GADGET_TEXTSIZE + 1];
1715         int font_color = FC_YELLOW;
1716         int border = gi->design_border;
1717         strcpy(text, gi->text.value);
1718         strcat(text, " ");
1719
1720         /* left part of gadget */
1721         XCopyArea(display, gd->pixmap, drawto, gc,
1722                   gd->x, gd->y, border, gi->height, gi->x, gi->y);
1723
1724         /* middle part of gadget */
1725         for (i=0; i<=gi->text.size; i++)
1726           XCopyArea(display, gd->pixmap, drawto, gc,
1727                     gd->x + border, gd->y, FONT2_XSIZE, gi->height,
1728                     gi->x + border + i * FONT2_XSIZE, gi->y);
1729
1730         /* right part of gadget */
1731         XCopyArea(display, gd->pixmap, drawto, gc,
1732                   gd->x + ED_WIN_COUNT_XSIZE - border, gd->y,
1733                   border, gi->height, gi->x + gi->width - border, gi->y);
1734
1735         /* gadget text value */
1736         DrawText(gi->x + border, gi->y + border, text, FS_SMALL, font_color);
1737
1738         cursor_letter = gi->text.value[gi->text.cursor_position];
1739         cursor_string[0] = '~';
1740         cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
1741         cursor_string[2] = '\0';
1742
1743         /* draw cursor, if active */
1744         if (pressed)
1745           DrawText(gi->x + border + gi->text.cursor_position * FONT2_XSIZE,
1746                    gi->y + border, cursor_string, FS_SMALL, font_color);
1747       }
1748       break;
1749
1750     case GD_TYPE_SCROLLBAR_VERTICAL:
1751       {
1752         int i;
1753         int xpos = gi->x;
1754         int ypos = gi->y + gi->scrollbar.position;
1755         int design_full = gi->width;
1756         int design_body = design_full - 2 * gi->design_border;
1757         int size_full = gi->scrollbar.size;
1758         int size_body = size_full - 2 * gi->design_border;
1759         int num_steps = size_body / design_body;
1760         int step_size_remain = size_body - num_steps * design_body;
1761
1762         /* clear scrollbar area */
1763         XFillRectangle(display, backbuffer, gc,
1764                        gi->x, gi->y, gi->width, gi->height);
1765
1766         /* upper part of gadget */
1767         XCopyArea(display, gd->pixmap, drawto, gc,
1768                   gd->x, gd->y,
1769                   gi->width, gi->design_border,
1770                   xpos, ypos);
1771
1772         /* middle part of gadget */
1773         for (i=0; i<num_steps; i++)
1774           XCopyArea(display, gd->pixmap, drawto, gc,
1775                     gd->x, gd->y + gi->design_border,
1776                     gi->width, design_body,
1777                     xpos, ypos + gi->design_border + i * design_body);
1778
1779         /* remaining middle part of gadget */
1780         if (step_size_remain > 0)
1781           XCopyArea(display, gd->pixmap, drawto, gc,
1782                     gd->x,  gd->y + gi->design_border,
1783                     gi->width, step_size_remain,
1784                     xpos, ypos + gi->design_border + num_steps * design_body);
1785
1786         /* lower part of gadget */
1787         XCopyArea(display, gd->pixmap, drawto, gc,
1788                   gd->x, gd->y + design_full - gi->design_border,
1789                   gi->width, gi->design_border,
1790                   xpos, ypos + size_full - gi->design_border);
1791       }
1792       break;
1793
1794     case GD_TYPE_SCROLLBAR_HORIZONTAL:
1795       {
1796         int i;
1797         int xpos = gi->x + gi->scrollbar.position;
1798         int ypos = gi->y;
1799         int design_full = gi->height;
1800         int design_body = design_full - 2 * gi->design_border;
1801         int size_full = gi->scrollbar.size;
1802         int size_body = size_full - 2 * gi->design_border;
1803         int num_steps = size_body / design_body;
1804         int step_size_remain = size_body - num_steps * design_body;
1805
1806         /* clear scrollbar area */
1807         XFillRectangle(display, backbuffer, gc,
1808                        gi->x, gi->y, gi->width, gi->height);
1809
1810         /* left part of gadget */
1811         XCopyArea(display, gd->pixmap, drawto, gc,
1812                   gd->x, gd->y,
1813                   gi->design_border, gi->height,
1814                   xpos, ypos);
1815
1816         /* middle part of gadget */
1817         for (i=0; i<num_steps; i++)
1818           XCopyArea(display, gd->pixmap, drawto, gc,
1819                     gd->x + gi->design_border, gd->y,
1820                     design_body, gi->height,
1821                     xpos + gi->design_border + i * design_body, ypos);
1822
1823         /* remaining middle part of gadget */
1824         if (step_size_remain > 0)
1825           XCopyArea(display, gd->pixmap, drawto, gc,
1826                     gd->x + gi->design_border, gd->y,
1827                     step_size_remain, gi->height,
1828                     xpos + gi->design_border + num_steps * design_body, ypos);
1829
1830         /* right part of gadget */
1831         XCopyArea(display, gd->pixmap, drawto, gc,
1832                   gd->x + design_full - gi->design_border, gd->y,
1833                   gi->design_border, gi->height,
1834                   xpos + size_full - gi->design_border, ypos);
1835       }
1836       break;
1837
1838     default:
1839       return;
1840   }
1841
1842   if (direct)
1843     XCopyArea(display, drawto, window, gc,
1844               gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
1845   else
1846     redraw_mask |= (gi->x < SX + SXSIZE ? REDRAW_FIELD :
1847                     gi->y < DY + DYSIZE ? REDRAW_DOOR_1 :
1848                     gi->y > VY ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
1849 }
1850
1851 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
1852 {
1853   int tag = first_tag;
1854
1855   while (tag != GDI_END)
1856   {
1857     switch(tag)
1858     {
1859       case GDI_CUSTOM_ID:
1860         gi->custom_id = va_arg(ap, int);
1861         break;
1862
1863       case GDI_INFO_TEXT:
1864         {
1865           int max_textsize = MAX_INFO_TEXTSIZE - 1;
1866
1867           strncpy(gi->info_text, va_arg(ap, char *), max_textsize);
1868           gi->info_text[max_textsize] = '\0';
1869         }
1870         break;
1871
1872       case GDI_X:
1873         gi->x = va_arg(ap, int);
1874         break;
1875
1876       case GDI_Y:
1877         gi->y = va_arg(ap, int);
1878         break;
1879
1880       case GDI_WIDTH:
1881         gi->width = va_arg(ap, int);
1882         break;
1883
1884       case GDI_HEIGHT:
1885         gi->height = va_arg(ap, int);
1886         break;
1887
1888       case GDI_TYPE:
1889         gi->type = va_arg(ap, unsigned long);
1890         break;
1891
1892       case GDI_STATE:
1893         gi->state = va_arg(ap, unsigned long);
1894         break;
1895
1896       case GDI_CHECKED:
1897         gi->checked = va_arg(ap, boolean);
1898         break;
1899
1900       case GDI_RADIO_NR:
1901         gi->radio_nr = va_arg(ap, unsigned long);
1902         break;
1903
1904       case GDI_NUMBER_VALUE:
1905         gi->text.number_value = va_arg(ap, long);
1906         sprintf(gi->text.value, "%d", gi->text.number_value);
1907         gi->text.cursor_position = strlen(gi->text.value);
1908         break;
1909
1910       case GDI_NUMBER_MIN:
1911         gi->text.number_min = va_arg(ap, long);
1912         if (gi->text.number_value < gi->text.number_min)
1913         {
1914           gi->text.number_value = gi->text.number_min;
1915           sprintf(gi->text.value, "%d", gi->text.number_value);
1916         }
1917         break;
1918
1919       case GDI_NUMBER_MAX:
1920         gi->text.number_max = va_arg(ap, long);
1921         if (gi->text.number_value > gi->text.number_max)
1922         {
1923           gi->text.number_value = gi->text.number_max;
1924           sprintf(gi->text.value, "%d", gi->text.number_value);
1925         }
1926         break;
1927
1928       case GDI_TEXT_VALUE:
1929         {
1930           int max_textsize = MAX_GADGET_TEXTSIZE;
1931
1932           if (gi->text.size)
1933             max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
1934
1935           strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
1936           gi->text.value[max_textsize] = '\0';
1937           gi->text.cursor_position = strlen(gi->text.value);
1938         }
1939         break;
1940
1941       case GDI_TEXT_SIZE:
1942         {
1943           int tag_value = va_arg(ap, int);
1944           int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
1945
1946           gi->text.size = max_textsize;
1947           gi->text.value[max_textsize] = '\0';
1948
1949           if (gi->width == 0 && gi->height == 0)
1950           {
1951             gi->width = (gi->text.size + 1) * FONT2_XSIZE + 6;
1952             gi->height = ED_WIN_COUNT_YSIZE;
1953           }
1954         }
1955         break;
1956
1957       case GDI_DESIGN_UNPRESSED:
1958         gi->design[GD_BUTTON_UNPRESSED].pixmap = va_arg(ap, Pixmap);
1959         gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
1960         gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
1961         break;
1962
1963       case GDI_DESIGN_PRESSED:
1964         gi->design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
1965         gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
1966         gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
1967         break;
1968
1969       case GDI_ALT_DESIGN_UNPRESSED:
1970         gi->alt_design[GD_BUTTON_UNPRESSED].pixmap= va_arg(ap, Pixmap);
1971         gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
1972         gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
1973         break;
1974
1975       case GDI_ALT_DESIGN_PRESSED:
1976         gi->alt_design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
1977         gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
1978         gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
1979         break;
1980
1981       case GDI_DESIGN_BORDER:
1982         gi->design_border = va_arg(ap, int);
1983         break;
1984
1985       case GDI_DECORATION_DESIGN:
1986         gi->deco.design.pixmap = va_arg(ap, Pixmap);
1987         gi->deco.design.x = va_arg(ap, int);
1988         gi->deco.design.y = va_arg(ap, int);
1989         break;
1990
1991       case GDI_DECORATION_POSITION:
1992         gi->deco.x = va_arg(ap, int);
1993         gi->deco.y = va_arg(ap, int);
1994         break;
1995
1996       case GDI_DECORATION_SIZE:
1997         gi->deco.width = va_arg(ap, int);
1998         gi->deco.height = va_arg(ap, int);
1999         break;
2000
2001       case GDI_DECORATION_SHIFTING:
2002         gi->deco.xshift = va_arg(ap, int);
2003         gi->deco.yshift = va_arg(ap, int);
2004         break;
2005
2006       case GDI_EVENT_MASK:
2007         gi->event_mask = va_arg(ap, unsigned long);
2008         break;
2009
2010       case GDI_AREA_SIZE:
2011         gi->drawing.area_xsize = va_arg(ap, int);
2012         gi->drawing.area_ysize = va_arg(ap, int);
2013
2014         /* determine dependent values for drawing area gadget, if needed */
2015         if (gi->width == 0 && gi->height == 0 &&
2016             gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
2017         {
2018           gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
2019           gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
2020         }
2021         else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
2022                  gi->width != 0 && gi->height != 0)
2023         {
2024           gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
2025           gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
2026         }
2027         break;
2028
2029       case GDI_ITEM_SIZE:
2030         gi->drawing.item_xsize = va_arg(ap, int);
2031         gi->drawing.item_ysize = va_arg(ap, int);
2032
2033         /* determine dependent values for drawing area gadget, if needed */
2034         if (gi->width == 0 && gi->height == 0 &&
2035             gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
2036         {
2037           gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
2038           gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
2039         }
2040         else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
2041                  gi->width != 0 && gi->height != 0)
2042         {
2043           gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
2044           gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
2045         }
2046         break;
2047
2048       case GDI_SCROLLBAR_ITEMS_MAX:
2049         gi->scrollbar.items_max = va_arg(ap, int);
2050         break;
2051
2052       case GDI_SCROLLBAR_ITEMS_VISIBLE:
2053         gi->scrollbar.items_visible = va_arg(ap, int);
2054         break;
2055
2056       case GDI_SCROLLBAR_ITEM_POSITION:
2057         gi->scrollbar.item_position = va_arg(ap, int);
2058         break;
2059
2060       case GDI_CALLBACK_INFO:
2061         gi->callback_info = va_arg(ap, gadget_function);
2062         break;
2063
2064       case GDI_CALLBACK_ACTION:
2065         gi->callback_action = va_arg(ap, gadget_function);
2066         break;
2067
2068       default:
2069         Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
2070     }
2071
2072     tag = va_arg(ap, int);      /* read next tag */
2073   }
2074
2075   /* check if gadget complete */
2076   if (gi->type != GD_TYPE_DRAWING_AREA &&
2077       (!gi->design[GD_BUTTON_UNPRESSED].pixmap ||
2078        !gi->design[GD_BUTTON_PRESSED].pixmap))
2079     Error(ERR_EXIT, "gadget incomplete (missing Pixmap)");
2080
2081   /* adjust gadget values in relation to other gadget values */
2082
2083   if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
2084   {
2085     struct GadgetTextInput *text = &gi->text;
2086     int value = text->number_value;
2087
2088     text->number_value = (value < text->number_min ? text->number_min :
2089                           value > text->number_max ? text->number_max :
2090                           value);
2091
2092     sprintf(text->value, "%d", text->number_value);
2093   }
2094
2095   if (gi->type & GD_TYPE_SCROLLBAR)
2096   {
2097     struct GadgetScrollbar *gs = &gi->scrollbar;
2098
2099     if (gi->width == 0 || gi->height == 0 ||
2100         gs->items_max == 0 || gs->items_visible == 0)
2101       Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
2102
2103     /* calculate internal scrollbar values */
2104     gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
2105                     gi->height : gi->width);
2106     gs->size = gs->size_max * gs->items_visible / gs->items_max;
2107     gs->position = gs->size_max * gs->item_position / gs->items_max;
2108     gs->position_max = gs->size_max - gs->size;
2109
2110     /* finetuning for maximal right/bottom position */
2111     if (gs->item_position == gs->items_max - gs->items_visible)
2112       gs->position = gs->position_max;
2113   }
2114 }
2115
2116 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
2117 {
2118   va_list ap;
2119
2120   va_start(ap, first_tag);
2121   HandleGadgetTags(gi, first_tag, ap);
2122   va_end(ap);
2123
2124   RedrawGadget(gi);
2125 }
2126
2127 void RedrawGadget(struct GadgetInfo *gi)
2128 {
2129   if (gi->mapped)
2130     DrawGadget(gi, gi->state, DG_DIRECT);
2131 }
2132
2133 struct GadgetInfo *CreateGadget(int first_tag, ...)
2134 {
2135   struct GadgetInfo *new_gadget = checked_malloc(sizeof(struct GadgetInfo));
2136   va_list ap;
2137
2138   /* always start with reliable default values */
2139   memset(new_gadget, 0, sizeof(struct GadgetInfo));     /* zero all fields */
2140   new_gadget->id = getNewGadgetID();
2141   new_gadget->callback_info = default_callback_info;
2142   new_gadget->callback_action = default_callback_action;
2143
2144   va_start(ap, first_tag);
2145   HandleGadgetTags(new_gadget, first_tag, ap);
2146   va_end(ap);
2147
2148   /* insert new gadget into global gadget list */
2149   if (gadget_list_last_entry)
2150   {
2151     gadget_list_last_entry->next = new_gadget;
2152     gadget_list_last_entry = gadget_list_last_entry->next;
2153   }
2154   else
2155     gadget_list_first_entry = gadget_list_last_entry = new_gadget;
2156
2157   return new_gadget;
2158 }
2159
2160 void FreeGadget(struct GadgetInfo *gi)
2161 {
2162   struct GadgetInfo *gi_previous = gadget_list_first_entry;
2163
2164   while (gi_previous && gi_previous->next != gi)
2165     gi_previous = gi_previous->next;
2166
2167   if (gi == gadget_list_first_entry)
2168     gadget_list_first_entry = gi->next;
2169
2170   if (gi == gadget_list_last_entry)
2171     gadget_list_last_entry = gi_previous;
2172
2173   gi_previous->next = gi->next;
2174   free(gi);
2175 }
2176
2177 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
2178 {
2179   if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
2180     return;
2181
2182   gi->text.number_value = atoi(gi->text.value);
2183
2184   if (gi->text.number_value < gi->text.number_min)
2185     gi->text.number_value = gi->text.number_min;
2186   if (gi->text.number_value > gi->text.number_max)
2187     gi->text.number_value = gi->text.number_max;
2188
2189   sprintf(gi->text.value, "%d", gi->text.number_value);
2190
2191   if (gi->text.cursor_position < 0)
2192     gi->text.cursor_position = 0;
2193   else if (gi->text.cursor_position > strlen(gi->text.value))
2194     gi->text.cursor_position = strlen(gi->text.value);
2195 }
2196
2197 /* global pointer to gadget actually in use (when mouse button pressed) */
2198 static struct GadgetInfo *last_gi = NULL;
2199
2200 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
2201 {
2202   if (gi == NULL || gi->mapped)
2203     return;
2204
2205   gi->mapped = TRUE;
2206
2207   if (redraw)
2208     DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
2209 }
2210
2211 void MapGadget(struct GadgetInfo *gi)
2212 {
2213   MapGadgetExt(gi, TRUE);
2214 }
2215
2216 void UnmapGadget(struct GadgetInfo *gi)
2217 {
2218   if (gi == NULL || !gi->mapped)
2219     return;
2220
2221   gi->mapped = FALSE;
2222
2223   if (gi == last_gi)
2224     last_gi = NULL;
2225 }
2226
2227 #define MAX_NUM_GADGETS         1024
2228 #define MULTIMAP_UNMAP          (1 << 0)
2229 #define MULTIMAP_REMAP          (1 << 1)
2230 #define MULTIMAP_REDRAW         (1 << 2)
2231 #define MULTIMAP_PLAYFIELD      (1 << 3)
2232 #define MULTIMAP_DOOR_1         (1 << 4)
2233 #define MULTIMAP_DOOR_2         (1 << 5)
2234 #define MULTIMAP_ALL            (MULTIMAP_PLAYFIELD | \
2235                                  MULTIMAP_DOOR_1 | \
2236                                  MULTIMAP_DOOR_2)
2237
2238 static void MultiMapGadgets(int mode)
2239 {
2240   struct GadgetInfo *gi = gadget_list_first_entry;
2241   static boolean map_state[MAX_NUM_GADGETS];
2242   int map_count = 0;
2243
2244   while (gi)
2245   {
2246     if ((mode & MULTIMAP_PLAYFIELD && gi->x < SX + SXSIZE) ||
2247         (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y < DY + DYSIZE) ||
2248         (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y > DY + DYSIZE))
2249     {
2250       if (mode & MULTIMAP_UNMAP)
2251       {
2252         map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
2253         UnmapGadget(gi);
2254       }
2255       else
2256       {
2257         if (map_state[map_count++ % MAX_NUM_GADGETS])
2258           MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
2259       }
2260     }
2261
2262     gi = gi->next;
2263   }
2264 }
2265
2266 void UnmapAllGadgets()
2267 {
2268   MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
2269 }
2270
2271 void RemapAllGadgets()
2272 {
2273   MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
2274 }
2275
2276 boolean anyTextGadgetActive()
2277 {
2278   return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
2279 }
2280
2281 void ClickOnGadget(struct GadgetInfo *gi, int button)
2282 {
2283   /* simulate releasing mouse button over last gadget, if still pressed */
2284   if (button_status)
2285     HandleGadgets(-1, -1, 0);
2286
2287   /* simulate pressing mouse button over specified gadget */
2288   HandleGadgets(gi->x, gi->y, button);
2289
2290   /* simulate releasing mouse button over specified gadget */
2291   HandleGadgets(gi->x, gi->y, 0);
2292 }
2293
2294 void HandleGadgets(int mx, int my, int button)
2295 {
2296   static struct GadgetInfo *last_info_gi = NULL;
2297   static unsigned long pressed_delay = 0;
2298   static int last_button = 0;
2299   static int last_mx = 0, last_my = 0;
2300   int scrollbar_mouse_pos = 0;
2301   struct GadgetInfo *new_gi, *gi;
2302   boolean press_event;
2303   boolean release_event;
2304   boolean mouse_moving;
2305   boolean gadget_pressed;
2306   boolean gadget_pressed_repeated;
2307   boolean gadget_moving;
2308   boolean gadget_moving_inside;
2309   boolean gadget_moving_off_borders;
2310   boolean gadget_released;
2311   boolean gadget_released_inside;
2312   boolean gadget_released_off_borders;
2313   boolean changed_position = FALSE;
2314
2315   /* check if there are any gadgets defined */
2316   if (gadget_list_first_entry == NULL)
2317     return;
2318
2319   /* check which gadget is under the mouse pointer */
2320   new_gi = getGadgetInfoFromMousePosition(mx, my);
2321
2322   /* check if button state has changed since last invocation */
2323   press_event = (button != 0 && last_button == 0);
2324   release_event = (button == 0 && last_button != 0);
2325   last_button = button;
2326
2327   /* check if mouse has been moved since last invocation */
2328   mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
2329   last_mx = mx;
2330   last_my = my;
2331
2332   /* special treatment for text and number input gadgets */
2333   if (anyTextGadgetActive() && button != 0 && !motion_status)
2334   {
2335     struct GadgetInfo *gi = last_gi;
2336
2337     if (new_gi == last_gi)
2338     {
2339       /* if mouse button pressed inside activated text gadget, set cursor */
2340       gi->text.cursor_position = (mx - gi->x) / FONT2_XSIZE;
2341
2342       if (gi->text.cursor_position < 0)
2343         gi->text.cursor_position = 0;
2344       else if (gi->text.cursor_position > strlen(gi->text.value))
2345         gi->text.cursor_position = strlen(gi->text.value);
2346
2347       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2348     }
2349     else
2350     {
2351       /* if mouse button pressed outside text input gadget, deactivate it */
2352       CheckRangeOfNumericInputGadget(gi);
2353       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
2354
2355       if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
2356         gi->callback_action(gi);
2357
2358       last_gi = NULL;
2359     }
2360   }
2361
2362   gadget_pressed =
2363     (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
2364   gadget_pressed_repeated =
2365     (button != 0 && last_gi != NULL && new_gi == last_gi);
2366
2367   gadget_released =             (release_event && last_gi != NULL);
2368   gadget_released_inside =      (gadget_released && new_gi == last_gi);
2369   gadget_released_off_borders = (gadget_released && new_gi != last_gi);
2370
2371   gadget_moving =             (button != 0 && last_gi != NULL && mouse_moving);
2372   gadget_moving_inside =      (gadget_moving && new_gi == last_gi);
2373   gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
2374
2375   /* if new gadget pressed, store this gadget  */
2376   if (gadget_pressed)
2377     last_gi = new_gi;
2378
2379   /* 'gi' is actually handled gadget */
2380   gi = last_gi;
2381
2382   /* if gadget is scrollbar, choose mouse position value */
2383   if (gi && gi->type & GD_TYPE_SCROLLBAR)
2384     scrollbar_mouse_pos =
2385       (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
2386
2387   /* if mouse button released, no gadget needs to be handled anymore */
2388   if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
2389     last_gi = NULL;
2390
2391   /* modify event position values even if no gadget is pressed */
2392   if (button == 0 && !release_event)
2393     gi = new_gi;
2394
2395   if (gi)
2396   {
2397     int last_x = gi->event.x;
2398     int last_y = gi->event.y;
2399
2400     gi->event.x = mx - gi->x;
2401     gi->event.y = my - gi->y;
2402
2403     if (gi->type == GD_TYPE_DRAWING_AREA)
2404     {
2405       gi->event.x /= gi->drawing.item_xsize;
2406       gi->event.y /= gi->drawing.item_ysize;
2407
2408       if (last_x != gi->event.x || last_y != gi->event.y)
2409         changed_position = TRUE;
2410     }
2411   }
2412
2413   /* handle gadget popup info text */
2414   if (last_info_gi != new_gi ||
2415       (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
2416   {
2417     last_info_gi = new_gi;
2418
2419     if (new_gi != NULL && (button == 0 || new_gi == last_gi))
2420     {
2421       new_gi->event.type = 0;
2422       new_gi->callback_info(new_gi);
2423     }
2424     else
2425       default_callback_info(NULL);
2426   }
2427
2428   if (gadget_pressed)
2429   {
2430     if (gi->type == GD_TYPE_CHECK_BUTTON)
2431     {
2432       gi->checked = !gi->checked;
2433     }
2434     else if (gi->type == GD_TYPE_RADIO_BUTTON)
2435     {
2436       struct GadgetInfo *rgi = gadget_list_first_entry;
2437
2438       while (rgi)
2439       {
2440         if (rgi->mapped &&
2441             rgi->type == GD_TYPE_RADIO_BUTTON &&
2442             rgi->radio_nr == gi->radio_nr &&
2443             rgi != gi)
2444         {
2445           rgi->checked = FALSE;
2446           DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
2447         }
2448
2449         rgi = rgi->next;
2450       }
2451
2452       gi->checked = TRUE;
2453     }
2454     else if (gi->type & GD_TYPE_SCROLLBAR)
2455     {
2456       int mpos, gpos;
2457
2458       if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
2459       {
2460         mpos = mx;
2461         gpos = gi->x;
2462       }
2463       else
2464       {
2465         mpos = my;
2466         gpos = gi->y;
2467       }
2468
2469       if (mpos >= gpos + gi->scrollbar.position &&
2470           mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
2471       {
2472         /* drag scrollbar */
2473         gi->scrollbar.drag_position =
2474           scrollbar_mouse_pos - gi->scrollbar.position;
2475       }
2476       else
2477       {
2478         /* click scrollbar one scrollbar length up/left or down/right */
2479
2480         struct GadgetScrollbar *gs = &gi->scrollbar;
2481         int old_item_position = gs->item_position;
2482
2483         changed_position = FALSE;
2484
2485         gs->item_position +=
2486           gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
2487
2488         if (gs->item_position < 0)
2489           gs->item_position = 0;
2490         if (gs->item_position > gs->items_max - gs->items_visible)
2491           gs->item_position = gs->items_max - gs->items_visible;
2492
2493         if (old_item_position != gs->item_position)
2494         {
2495           gi->event.item_position = gs->item_position;
2496           changed_position = TRUE;
2497         }
2498
2499         ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
2500                      GDI_END);
2501
2502         gi->state = GD_BUTTON_UNPRESSED;
2503         gi->event.type = GD_EVENT_MOVING;
2504         gi->event.off_borders = FALSE;
2505
2506         if (gi->event_mask & GD_EVENT_MOVING && changed_position)
2507           gi->callback_action(gi);
2508
2509         /* don't handle this scrollbar anymore while mouse button pressed */
2510         last_gi = NULL;
2511
2512         return;
2513       }
2514     }
2515
2516     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2517
2518     gi->state = GD_BUTTON_PRESSED;
2519     gi->event.type = GD_EVENT_PRESSED;
2520     gi->event.button = button;
2521     gi->event.off_borders = FALSE;
2522
2523     /* initialize delay counter */
2524     DelayReached(&pressed_delay, 0);
2525
2526     if (gi->event_mask & GD_EVENT_PRESSED)
2527       gi->callback_action(gi);
2528   }
2529
2530   if (gadget_pressed_repeated)
2531   {
2532     if (gi->event_mask & GD_EVENT_REPEATED &&
2533         DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
2534       gi->callback_action(gi);
2535   }
2536
2537   if (gadget_moving)
2538   {
2539     if (gi->type & GD_TYPE_BUTTON)
2540     {
2541       if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
2542         DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2543       else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
2544         DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
2545     }
2546
2547     if (gi->type & GD_TYPE_SCROLLBAR)
2548     {
2549       struct GadgetScrollbar *gs = &gi->scrollbar;
2550       int old_item_position = gs->item_position;
2551
2552       gs->position = scrollbar_mouse_pos - gs->drag_position;
2553
2554       if (gs->position < 0)
2555         gs->position = 0;
2556       if (gs->position > gs->position_max)
2557         gs->position = gs->position_max;
2558
2559       gs->item_position = gs->items_max * gs->position / gs->size_max;
2560
2561       if (old_item_position != gs->item_position)
2562       {
2563         gi->event.item_position = gs->item_position;
2564         changed_position = TRUE;
2565       }
2566
2567       DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2568     }
2569
2570     gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
2571                  GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
2572     gi->event.type = GD_EVENT_MOVING;
2573     gi->event.off_borders = gadget_moving_off_borders;
2574
2575     if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
2576         (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
2577       gi->callback_action(gi);
2578   }
2579
2580   if (gadget_released_inside)
2581   {
2582     if (!(gi->type & GD_TYPE_TEXTINPUT))
2583       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
2584
2585     gi->state = GD_BUTTON_UNPRESSED;
2586     gi->event.type = GD_EVENT_RELEASED;
2587
2588     if (gi->event_mask & GD_EVENT_RELEASED)
2589       gi->callback_action(gi);
2590   }
2591
2592   if (gadget_released_off_borders)
2593   {
2594     if (gi->type & GD_TYPE_SCROLLBAR)
2595       DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
2596
2597     gi->event.type = GD_EVENT_RELEASED;
2598
2599     if (gi->event_mask & GD_EVENT_RELEASED &&
2600         gi->event_mask & GD_EVENT_OFF_BORDERS)
2601       gi->callback_action(gi);
2602   }
2603 }
2604
2605 void HandleGadgetsKeyInput(KeySym key)
2606 {
2607   struct GadgetInfo *gi = last_gi;
2608   char text[MAX_GADGET_TEXTSIZE];
2609   int text_length;
2610   int cursor_pos;
2611   char letter;
2612   boolean legal_letter;
2613
2614   if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
2615     return;
2616
2617   text_length = strlen(gi->text.value);
2618   cursor_pos = gi->text.cursor_position;
2619   letter = getCharFromKeySym(key);
2620   legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
2621                   letter >= '0' && letter <= '9' :
2622                   letter != 0);
2623
2624   if (legal_letter && text_length < gi->text.size)
2625   {
2626     strcpy(text, gi->text.value);
2627     strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
2628     gi->text.value[cursor_pos] = letter;
2629     gi->text.cursor_position++;
2630     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2631   }
2632   else if (key == XK_Left && cursor_pos > 0)
2633   {
2634     gi->text.cursor_position--;
2635     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2636   }
2637   else if (key == XK_Right && cursor_pos < text_length)
2638   {
2639     gi->text.cursor_position++;
2640     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2641   }
2642   else if (key == XK_BackSpace && cursor_pos > 0)
2643   {
2644     strcpy(text, gi->text.value);
2645     strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
2646     gi->text.cursor_position--;
2647     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2648   }
2649   else if (key == XK_Delete && cursor_pos < text_length)
2650   {
2651     strcpy(text, gi->text.value);
2652     strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
2653     DrawGadget(gi, DG_PRESSED, DG_DIRECT);
2654   }
2655   else if (key == XK_Return)
2656   {
2657     CheckRangeOfNumericInputGadget(gi);
2658     DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
2659
2660     if (gi->event_mask & GD_EVENT_TEXT_RETURN)
2661       gi->callback_action(gi);
2662
2663     last_gi = NULL;
2664   }
2665 }