rnd-20030610-1-src
[rocksndiamonds.git] / src / editor.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * editor.c                                                 *
12 ***********************************************************/
13
14 #include <math.h>
15
16 #include "libgame/libgame.h"
17
18 #include "editor.h"
19 #include "screens.h"
20 #include "tools.h"
21 #include "files.h"
22 #include "game.h"
23 #include "tape.h"
24
25
26 /*
27   -----------------------------------------------------------------------------
28   screen and artwork graphic pixel position definitions
29   -----------------------------------------------------------------------------
30 */
31
32 /* positions in the level editor */
33 #define ED_WIN_MB_LEFT_XPOS             6
34 #define ED_WIN_MB_LEFT_YPOS             258
35 #define ED_WIN_MB_MIDDLE_XPOS           42
36 #define ED_WIN_MB_MIDDLE_YPOS           ED_WIN_MB_LEFT_YPOS
37 #define ED_WIN_MB_RIGHT_XPOS            78
38 #define ED_WIN_MB_RIGHT_YPOS            ED_WIN_MB_LEFT_YPOS
39
40 /* values for the control window */
41 #define ED_CTRL_BUTTONS_GFX_YPOS        236
42 #define ED_CTRL_BUTTONS_ALT_GFX_YPOS    142
43
44 #define ED_CTRL1_BUTTONS_HORIZ          4
45 #define ED_CTRL1_BUTTONS_VERT           4
46 #define ED_CTRL1_BUTTON_XSIZE           22
47 #define ED_CTRL1_BUTTON_YSIZE           22
48 #define ED_CTRL1_BUTTONS_XPOS           6
49 #define ED_CTRL1_BUTTONS_YPOS           6
50 #define ED_CTRL2_BUTTONS_HORIZ          3
51 #define ED_CTRL2_BUTTONS_VERT           2
52 #define ED_CTRL2_BUTTON_XSIZE           30
53 #define ED_CTRL2_BUTTON_YSIZE           20
54 #define ED_CTRL2_BUTTONS_XPOS           5
55 #define ED_CTRL2_BUTTONS_YPOS           99
56 #define ED_NUM_CTRL1_BUTTONS   (ED_CTRL1_BUTTONS_HORIZ * ED_CTRL1_BUTTONS_VERT)
57 #define ED_NUM_CTRL2_BUTTONS   (ED_CTRL2_BUTTONS_HORIZ * ED_CTRL2_BUTTONS_VERT)
58 #define ED_NUM_CTRL_BUTTONS    (ED_NUM_CTRL1_BUTTONS + ED_NUM_CTRL2_BUTTONS)
59
60 /* values for the element list */
61 #define ED_ELEMENTLIST_XPOS             5
62 #define ED_ELEMENTLIST_YPOS             30
63 #define ED_ELEMENTLIST_XSIZE            20
64 #define ED_ELEMENTLIST_YSIZE            20
65 #define ED_ELEMENTLIST_BUTTONS_HORIZ    4
66 #define ED_ELEMENTLIST_BUTTONS_VERT     11
67 #define ED_NUM_ELEMENTLIST_BUTTONS      (ED_ELEMENTLIST_BUTTONS_HORIZ * \
68                                          ED_ELEMENTLIST_BUTTONS_VERT)
69
70 /* standard distances */
71 #define ED_BORDER_SIZE                  3
72 #define ED_BORDER2_SIZE                 5
73 #define ED_GADGET_DISTANCE              2
74 #define ED_GADGET_TEXT_DISTANCE         (2 * ED_GADGET_DISTANCE)
75
76 /* values for the setting windows */
77 #define ED_SETTINGS_XSTART              (3 * MINI_TILEX / 2)
78 #define ED_SETTINGS_YSTART              (MINI_TILEY * 10)
79
80 #define ED_XOFFSET_CHECKBOX             (ED_CHECKBUTTON_XSIZE + \
81                                          2 * ED_GADGET_DISTANCE)
82
83 #define ED_SETTINGS_XOFFSET             ED_XOFFSET_CHECKBOX
84 #define ED_SETTINGS_YOFFSET             (3 * MINI_TILEY / 2)
85
86 #define ED_SETTINGS_XPOS(n)             (ED_SETTINGS_XSTART + \
87                                          n * ED_SETTINGS_XOFFSET)
88 #define ED_SETTINGS_YPOS(n)             (ED_SETTINGS_YSTART + \
89                                          n * ED_SETTINGS_YOFFSET)
90
91 #define ED_SETTINGS1_YPOS               MINI_TILEY
92 #define ED_SETTINGS2_XPOS               MINI_TILEX
93 #define ED_SETTINGS2_YPOS               (ED_SETTINGS1_YPOS + 12 * TILEY - 2)
94
95 /* values for counter gadgets */
96 #define ED_COUNT_PUSH_DELAY_RND_XPOS    (ED_SETTINGS_XPOS(1) + 16 * MINI_TILEX)
97 #define ED_COUNT_MOVE_DELAY_RND_XPOS    ED_COUNT_PUSH_DELAY_RND_XPOS
98 #define ED_COUNT_CHANGE_DELAY_RND_XPOS  (ED_SETTINGS_XPOS(1) + 13 * MINI_TILEX)
99
100 #define ED_COUNTER_YSTART               (ED_SETTINGS1_YPOS + 2 * TILEY)
101 #define ED_COUNTER_YDISTANCE            (3 * MINI_TILEY)
102 #define ED_COUNTER_YPOS(n)              (ED_COUNTER_YSTART + \
103                                          n * ED_COUNTER_YDISTANCE)
104 #define ED_COUNTER2_YPOS(n)             (ED_COUNTER_YSTART + \
105                                          n * ED_COUNTER_YDISTANCE - 2)
106
107 /* values for element content drawing areas */
108 #define ED_AREA_ELEM_CONTENT_XPOS       ( 2 * MINI_TILEX)
109 #define ED_AREA_ELEM_CONTENT_YPOS       (22 * MINI_TILEY)
110
111 #define ED_AREA_ELEM_CONTENT2_XPOS      (20 * MINI_TILEX)
112 #define ED_AREA_ELEM_CONTENT2_YPOS      (ED_SETTINGS_YPOS(2) + \
113                                          ED_GADGET_DISTANCE)
114
115 #define ED_AREA_ELEM_CONTENT3_XPOS      (24 * MINI_TILEX)
116 #define ED_AREA_ELEM_CONTENT3_YPOS      (ED_SETTINGS_YPOS(1) + \
117                                          ED_GADGET_DISTANCE)
118
119 #if 1
120 #define ED_AREA_ELEM_CONTENT4_XPOS      (29 * MINI_TILEX)
121 #define ED_AREA_ELEM_CONTENT4_YPOS      (ED_SETTINGS_YPOS(2) + \
122                                          ED_GADGET_DISTANCE - MINI_TILEY)
123 #else
124 #define ED_AREA_ELEM_CONTENT4_XPOS      (17 * MINI_TILEX)
125 #define ED_AREA_ELEM_CONTENT4_YPOS      (ED_SETTINGS_YPOS(11) + \
126                                          ED_GADGET_DISTANCE - MINI_TILEY)
127 #endif
128
129 /* values for random placement background drawing area */
130 #define ED_AREA_RANDOM_BACKGROUND_XPOS  (29 * MINI_TILEX)
131 #define ED_AREA_RANDOM_BACKGROUND_YPOS  (31 * MINI_TILEY)
132
133 /* values for scrolling gadgets for drawing area */
134 #define ED_SCROLLBUTTON_XPOS            24
135 #define ED_SCROLLBUTTON_YPOS            0
136 #define ED_SCROLLBAR_XPOS               24
137 #define ED_SCROLLBAR_YPOS               64
138
139 #define ED_SCROLLBUTTON_XSIZE           16
140 #define ED_SCROLLBUTTON_YSIZE           16
141
142 #define ED_SCROLL_UP_XPOS               (SXSIZE - ED_SCROLLBUTTON_XSIZE)
143 #define ED_SCROLL_UP_YPOS               (0)
144 #define ED_SCROLL_DOWN_XPOS             ED_SCROLL_UP_XPOS
145 #define ED_SCROLL_DOWN_YPOS             (SYSIZE - 3 * ED_SCROLLBUTTON_YSIZE)
146 #define ED_SCROLL_LEFT_XPOS             (0)
147 #define ED_SCROLL_LEFT_YPOS             (SYSIZE - 2 * ED_SCROLLBUTTON_YSIZE)
148 #define ED_SCROLL_RIGHT_XPOS            (SXSIZE - 2 * ED_SCROLLBUTTON_XSIZE)
149 #define ED_SCROLL_RIGHT_YPOS            ED_SCROLL_LEFT_YPOS
150 #define ED_SCROLL_HORIZONTAL_XPOS (ED_SCROLL_LEFT_XPOS + ED_SCROLLBUTTON_XSIZE)
151 #define ED_SCROLL_HORIZONTAL_YPOS       ED_SCROLL_LEFT_YPOS
152 #define ED_SCROLL_HORIZONTAL_XSIZE      (SXSIZE - 3 * ED_SCROLLBUTTON_XSIZE)
153 #define ED_SCROLL_HORIZONTAL_YSIZE      ED_SCROLLBUTTON_YSIZE
154 #define ED_SCROLL_VERTICAL_XPOS         ED_SCROLL_UP_XPOS
155 #define ED_SCROLL_VERTICAL_YPOS   (ED_SCROLL_UP_YPOS + ED_SCROLLBUTTON_YSIZE)
156 #define ED_SCROLL_VERTICAL_XSIZE        ED_SCROLLBUTTON_XSIZE
157 #define ED_SCROLL_VERTICAL_YSIZE        (SYSIZE - 4 * ED_SCROLLBUTTON_YSIZE)
158
159 /* values for scrolling gadgets for element list */
160 #define ED_SCROLLBUTTON2_XPOS           50
161 #define ED_SCROLLBUTTON2_YPOS           0
162 #define ED_SCROLLBAR2_XPOS              50
163 #define ED_SCROLLBAR2_YPOS              20
164
165 #define ED_SCROLLBUTTON2_XSIZE          10
166 #define ED_SCROLLBUTTON2_YSIZE          10
167
168 #define ED_SCROLL2_UP_XPOS              85
169 #define ED_SCROLL2_UP_YPOS              30
170 #define ED_SCROLL2_DOWN_XPOS            ED_SCROLL2_UP_XPOS
171 #define ED_SCROLL2_DOWN_YPOS            (ED_SCROLL2_UP_YPOS + \
172                                          ED_ELEMENTLIST_BUTTONS_VERT * \
173                                          ED_ELEMENTLIST_YSIZE - \
174                                          ED_SCROLLBUTTON2_YSIZE)
175 #define ED_SCROLL2_VERTICAL_XPOS        ED_SCROLL2_UP_XPOS
176 #define ED_SCROLL2_VERTICAL_YPOS        (ED_SCROLL2_UP_YPOS + \
177                                          ED_SCROLLBUTTON2_YSIZE)
178 #define ED_SCROLL2_VERTICAL_XSIZE       ED_SCROLLBUTTON2_XSIZE
179 #define ED_SCROLL2_VERTICAL_YSIZE       (ED_ELEMENTLIST_BUTTONS_VERT * \
180                                          ED_ELEMENTLIST_YSIZE - \
181                                          2 * ED_SCROLLBUTTON2_YSIZE)
182
183 /* values for checkbutton gadgets */
184 #define ED_CHECKBUTTON_XSIZE            ED_BUTTON_COUNT_XSIZE
185 #define ED_CHECKBUTTON_YSIZE            ED_BUTTON_COUNT_YSIZE
186 #define ED_CHECKBUTTON_UNCHECKED_XPOS   ED_BUTTON_MINUS_XPOS
187 #define ED_CHECKBUTTON_CHECKED_XPOS     ED_BUTTON_PLUS_XPOS
188 #define ED_CHECKBUTTON_YPOS             (ED_BUTTON_MINUS_YPOS + 22)
189 #define ED_RADIOBUTTON_YPOS             (ED_BUTTON_MINUS_YPOS + 44)
190 #define ED_STICKYBUTTON_YPOS            (ED_BUTTON_MINUS_YPOS + 66)
191
192 /* some positions in the editor control window */
193 #define ED_BUTTON_ELEM_XPOS             6
194 #define ED_BUTTON_ELEM_YPOS             30
195 #define ED_BUTTON_ELEM_XSIZE            22
196 #define ED_BUTTON_ELEM_YSIZE            22
197
198 /* some values for text input, selectbox and counter gadgets */
199 #define ED_BUTTON_COUNT_YPOS            60
200 #define ED_BUTTON_COUNT_XSIZE           20
201 #define ED_BUTTON_COUNT_YSIZE           20
202 #define ED_WIN_COUNT_XPOS               (2 + ED_BUTTON_COUNT_XSIZE + 2)
203 #define ED_WIN_COUNT_YPOS               ED_BUTTON_COUNT_YPOS
204 #define ED_WIN_COUNT_XSIZE              52
205 #define ED_WIN_COUNT_YSIZE              ED_BUTTON_COUNT_YSIZE
206 #define ED_WIN_COUNT2_XPOS              27
207 #define ED_WIN_COUNT2_YPOS              3
208 #define ED_WIN_COUNT2_XSIZE             46
209 #define ED_WIN_COUNT2_YSIZE             ED_BUTTON_COUNT_YSIZE
210
211 #define ED_BUTTON_MINUS_XPOS            2
212 #define ED_BUTTON_MINUS_YPOS            ED_BUTTON_COUNT_YPOS
213 #define ED_BUTTON_MINUS_XSIZE           ED_BUTTON_COUNT_XSIZE
214 #define ED_BUTTON_MINUS_YSIZE           ED_BUTTON_COUNT_YSIZE
215 #define ED_BUTTON_PLUS_XPOS             (ED_WIN_COUNT_XPOS + \
216                                          ED_WIN_COUNT_XSIZE + 2)
217 #define ED_BUTTON_PLUS_YPOS             ED_BUTTON_COUNT_YPOS
218 #define ED_BUTTON_PLUS_XSIZE            ED_BUTTON_COUNT_XSIZE
219 #define ED_BUTTON_PLUS_YSIZE            ED_BUTTON_COUNT_YSIZE
220
221 #define ED_SELECTBOX_XPOS               ED_WIN_COUNT_XPOS
222 #define ED_SELECTBOX_YPOS               (ED_WIN_COUNT_YPOS + \
223                                          2 + ED_WIN_COUNT_YSIZE)
224 #define ED_SELECTBOX_XSIZE              ED_WIN_COUNT_XSIZE
225 #define ED_SELECTBOX_YSIZE              ED_WIN_COUNT_YSIZE
226
227 #define ED_TEXTBUTTON_XPOS              ED_WIN_COUNT_XPOS
228 #define ED_TEXTBUTTON_YPOS              (ED_WIN_COUNT_YPOS + \
229                                          4 * (2 + ED_WIN_COUNT_YSIZE))
230 #define ED_TEXTBUTTON_INACTIVE_YPOS     ED_TEXTBUTTON_YPOS
231
232 #define ED_TEXTBUTTON_TAB_XPOS          ED_WIN_COUNT_XPOS
233 #define ED_TEXTBUTTON_TAB_YPOS          (ED_WIN_COUNT_YPOS + \
234                                          2 * (2 + ED_WIN_COUNT_YSIZE))
235 #define ED_TEXTBUTTON_TAB_INACTIVE_YPOS (ED_WIN_COUNT_YPOS + \
236                                          3 * (2 + ED_WIN_COUNT_YSIZE))
237
238 #define ED_TEXTBUTTON_XSIZE             ED_WIN_COUNT_XSIZE
239 #define ED_TEXTBUTTON_YSIZE             ED_WIN_COUNT_YSIZE
240
241 /* values for ClearEditorGadgetInfoText() and HandleGadgetInfoText() */
242 #define INFOTEXT_XPOS                   SX
243 #define INFOTEXT_YPOS                   (SY + SYSIZE - MINI_TILEX + 2)
244 #define INFOTEXT_XSIZE                  SXSIZE
245 #define INFOTEXT_YSIZE                  MINI_TILEX
246
247
248 /*
249   -----------------------------------------------------------------------------
250   editor gadget definitions
251   -----------------------------------------------------------------------------
252 */
253
254 /* drawing toolbox buttons */
255 #define GADGET_ID_NONE                  -1
256 #define GADGET_ID_TOOLBOX_FIRST         0
257
258 #define GADGET_ID_SINGLE_ITEMS          (GADGET_ID_TOOLBOX_FIRST + 0)
259 #define GADGET_ID_CONNECTED_ITEMS       (GADGET_ID_TOOLBOX_FIRST + 1)
260 #define GADGET_ID_LINE                  (GADGET_ID_TOOLBOX_FIRST + 2)
261 #define GADGET_ID_ARC                   (GADGET_ID_TOOLBOX_FIRST + 3)
262 #define GADGET_ID_RECTANGLE             (GADGET_ID_TOOLBOX_FIRST + 4)
263 #define GADGET_ID_FILLED_BOX            (GADGET_ID_TOOLBOX_FIRST + 5)
264 #define GADGET_ID_WRAP_UP               (GADGET_ID_TOOLBOX_FIRST + 6)
265 #define GADGET_ID_TEXT                  (GADGET_ID_TOOLBOX_FIRST + 7)
266 #define GADGET_ID_FLOOD_FILL            (GADGET_ID_TOOLBOX_FIRST + 8)
267 #define GADGET_ID_WRAP_LEFT             (GADGET_ID_TOOLBOX_FIRST + 9)
268 #define GADGET_ID_PROPERTIES            (GADGET_ID_TOOLBOX_FIRST + 10)
269 #define GADGET_ID_WRAP_RIGHT            (GADGET_ID_TOOLBOX_FIRST + 11)
270 #define GADGET_ID_RANDOM_PLACEMENT      (GADGET_ID_TOOLBOX_FIRST + 12)
271 #define GADGET_ID_GRAB_BRUSH            (GADGET_ID_TOOLBOX_FIRST + 13)
272 #define GADGET_ID_WRAP_DOWN             (GADGET_ID_TOOLBOX_FIRST + 14)
273 #define GADGET_ID_PICK_ELEMENT          (GADGET_ID_TOOLBOX_FIRST + 15)
274 #define GADGET_ID_UNDO                  (GADGET_ID_TOOLBOX_FIRST + 16)
275 #define GADGET_ID_INFO                  (GADGET_ID_TOOLBOX_FIRST + 17)
276 #define GADGET_ID_SAVE                  (GADGET_ID_TOOLBOX_FIRST + 18)
277 #define GADGET_ID_CLEAR                 (GADGET_ID_TOOLBOX_FIRST + 19)
278 #define GADGET_ID_TEST                  (GADGET_ID_TOOLBOX_FIRST + 20)
279 #define GADGET_ID_EXIT                  (GADGET_ID_TOOLBOX_FIRST + 21)
280
281 /* counter button identifiers */
282 #define GADGET_ID_COUNTER_FIRST         (GADGET_ID_TOOLBOX_FIRST + 22)
283
284 #define GADGET_ID_SELECT_LEVEL_DOWN     (GADGET_ID_COUNTER_FIRST + 0)
285 #define GADGET_ID_SELECT_LEVEL_TEXT     (GADGET_ID_COUNTER_FIRST + 1)
286 #define GADGET_ID_SELECT_LEVEL_UP       (GADGET_ID_COUNTER_FIRST + 2)
287 #define GADGET_ID_LEVEL_XSIZE_DOWN      (GADGET_ID_COUNTER_FIRST + 3)
288 #define GADGET_ID_LEVEL_XSIZE_TEXT      (GADGET_ID_COUNTER_FIRST + 4)
289 #define GADGET_ID_LEVEL_XSIZE_UP        (GADGET_ID_COUNTER_FIRST + 5)
290 #define GADGET_ID_LEVEL_YSIZE_DOWN      (GADGET_ID_COUNTER_FIRST + 6)
291 #define GADGET_ID_LEVEL_YSIZE_TEXT      (GADGET_ID_COUNTER_FIRST + 7)
292 #define GADGET_ID_LEVEL_YSIZE_UP        (GADGET_ID_COUNTER_FIRST + 8)
293 #define GADGET_ID_LEVEL_RANDOM_DOWN     (GADGET_ID_COUNTER_FIRST + 9)
294 #define GADGET_ID_LEVEL_RANDOM_TEXT     (GADGET_ID_COUNTER_FIRST + 10)
295 #define GADGET_ID_LEVEL_RANDOM_UP       (GADGET_ID_COUNTER_FIRST + 11)
296 #define GADGET_ID_LEVEL_COLLECT_DOWN    (GADGET_ID_COUNTER_FIRST + 12)
297 #define GADGET_ID_LEVEL_COLLECT_TEXT    (GADGET_ID_COUNTER_FIRST + 13)
298 #define GADGET_ID_LEVEL_COLLECT_UP      (GADGET_ID_COUNTER_FIRST + 14)
299 #define GADGET_ID_LEVEL_TIMELIMIT_DOWN  (GADGET_ID_COUNTER_FIRST + 15)
300 #define GADGET_ID_LEVEL_TIMELIMIT_TEXT  (GADGET_ID_COUNTER_FIRST + 16)
301 #define GADGET_ID_LEVEL_TIMELIMIT_UP    (GADGET_ID_COUNTER_FIRST + 17)
302 #define GADGET_ID_LEVEL_TIMESCORE_DOWN  (GADGET_ID_COUNTER_FIRST + 18)
303 #define GADGET_ID_LEVEL_TIMESCORE_TEXT  (GADGET_ID_COUNTER_FIRST + 19)
304 #define GADGET_ID_LEVEL_TIMESCORE_UP    (GADGET_ID_COUNTER_FIRST + 20)
305 #define GADGET_ID_ELEMENT_SCORE_DOWN    (GADGET_ID_COUNTER_FIRST + 21)
306 #define GADGET_ID_ELEMENT_SCORE_TEXT    (GADGET_ID_COUNTER_FIRST + 22)
307 #define GADGET_ID_ELEMENT_SCORE_UP      (GADGET_ID_COUNTER_FIRST + 23)
308 #define GADGET_ID_ELEMENT_CONTENT_DOWN  (GADGET_ID_COUNTER_FIRST + 24)
309 #define GADGET_ID_ELEMENT_CONTENT_TEXT  (GADGET_ID_COUNTER_FIRST + 25)
310 #define GADGET_ID_ELEMENT_CONTENT_UP    (GADGET_ID_COUNTER_FIRST + 26)
311 #define GADGET_ID_CUSTOM_SCORE_DOWN     (GADGET_ID_COUNTER_FIRST + 27)
312 #define GADGET_ID_CUSTOM_SCORE_TEXT     (GADGET_ID_COUNTER_FIRST + 28)
313 #define GADGET_ID_CUSTOM_SCORE_UP       (GADGET_ID_COUNTER_FIRST + 29)
314 #define GADGET_ID_PUSH_DELAY_FIX_DOWN   (GADGET_ID_COUNTER_FIRST + 30)
315 #define GADGET_ID_PUSH_DELAY_FIX_TEXT   (GADGET_ID_COUNTER_FIRST + 31)
316 #define GADGET_ID_PUSH_DELAY_FIX_UP     (GADGET_ID_COUNTER_FIRST + 32)
317 #define GADGET_ID_PUSH_DELAY_RND_DOWN   (GADGET_ID_COUNTER_FIRST + 33)
318 #define GADGET_ID_PUSH_DELAY_RND_TEXT   (GADGET_ID_COUNTER_FIRST + 34)
319 #define GADGET_ID_PUSH_DELAY_RND_UP     (GADGET_ID_COUNTER_FIRST + 35)
320 #define GADGET_ID_MOVE_DELAY_FIX_DOWN   (GADGET_ID_COUNTER_FIRST + 36)
321 #define GADGET_ID_MOVE_DELAY_FIX_TEXT   (GADGET_ID_COUNTER_FIRST + 37)
322 #define GADGET_ID_MOVE_DELAY_FIX_UP     (GADGET_ID_COUNTER_FIRST + 38)
323 #define GADGET_ID_MOVE_DELAY_RND_DOWN   (GADGET_ID_COUNTER_FIRST + 39)
324 #define GADGET_ID_MOVE_DELAY_RND_TEXT   (GADGET_ID_COUNTER_FIRST + 40)
325 #define GADGET_ID_MOVE_DELAY_RND_UP     (GADGET_ID_COUNTER_FIRST + 41)
326 #define GADGET_ID_CHANGE_DELAY_FIX_DOWN (GADGET_ID_COUNTER_FIRST + 42)
327 #define GADGET_ID_CHANGE_DELAY_FIX_TEXT (GADGET_ID_COUNTER_FIRST + 43)
328 #define GADGET_ID_CHANGE_DELAY_FIX_UP   (GADGET_ID_COUNTER_FIRST + 44)
329 #define GADGET_ID_CHANGE_DELAY_RND_DOWN (GADGET_ID_COUNTER_FIRST + 45)
330 #define GADGET_ID_CHANGE_DELAY_RND_TEXT (GADGET_ID_COUNTER_FIRST + 46)
331 #define GADGET_ID_CHANGE_DELAY_RND_UP   (GADGET_ID_COUNTER_FIRST + 47)
332
333 /* drawing area identifiers */
334 #define GADGET_ID_DRAWING_AREA_FIRST    (GADGET_ID_COUNTER_FIRST + 48)
335
336 #define GADGET_ID_DRAWING_LEVEL         (GADGET_ID_DRAWING_AREA_FIRST + 0)
337 #define GADGET_ID_ELEMENT_CONTENT_0     (GADGET_ID_DRAWING_AREA_FIRST + 1)
338 #define GADGET_ID_ELEMENT_CONTENT_1     (GADGET_ID_DRAWING_AREA_FIRST + 2)
339 #define GADGET_ID_ELEMENT_CONTENT_2     (GADGET_ID_DRAWING_AREA_FIRST + 3)
340 #define GADGET_ID_ELEMENT_CONTENT_3     (GADGET_ID_DRAWING_AREA_FIRST + 4)
341 #define GADGET_ID_ELEMENT_CONTENT_4     (GADGET_ID_DRAWING_AREA_FIRST + 5)
342 #define GADGET_ID_ELEMENT_CONTENT_5     (GADGET_ID_DRAWING_AREA_FIRST + 6)
343 #define GADGET_ID_ELEMENT_CONTENT_6     (GADGET_ID_DRAWING_AREA_FIRST + 7)
344 #define GADGET_ID_ELEMENT_CONTENT_7     (GADGET_ID_DRAWING_AREA_FIRST + 8)
345 #define GADGET_ID_AMOEBA_CONTENT        (GADGET_ID_DRAWING_AREA_FIRST + 9)
346 #define GADGET_ID_CUSTOM_GRAPHIC        (GADGET_ID_DRAWING_AREA_FIRST + 10)
347 #define GADGET_ID_CUSTOM_CONTENT        (GADGET_ID_DRAWING_AREA_FIRST + 11)
348 #define GADGET_ID_CUSTOM_CHANGED        (GADGET_ID_DRAWING_AREA_FIRST + 12)
349 #define GADGET_ID_RANDOM_BACKGROUND     (GADGET_ID_DRAWING_AREA_FIRST + 13)
350
351 /* text input identifiers */
352 #define GADGET_ID_TEXT_INPUT_FIRST      (GADGET_ID_DRAWING_AREA_FIRST + 14)
353
354 #define GADGET_ID_LEVEL_NAME            (GADGET_ID_TEXT_INPUT_FIRST + 0)
355 #define GADGET_ID_LEVEL_AUTHOR          (GADGET_ID_TEXT_INPUT_FIRST + 1)
356
357 /* selectbox identifiers */
358 #define GADGET_ID_SELECTBOX_FIRST       (GADGET_ID_TEXT_INPUT_FIRST + 2)
359
360 #define GADGET_ID_CUSTOM_WALK_TO_ACTION (GADGET_ID_SELECTBOX_FIRST + 0)
361 #define GADGET_ID_CUSTOM_CONSISTENCY    (GADGET_ID_SELECTBOX_FIRST + 1)
362 #define GADGET_ID_CUSTOM_DEADLINESS     (GADGET_ID_SELECTBOX_FIRST + 2)
363 #define GADGET_ID_CUSTOM_MOVE_PATTERN   (GADGET_ID_SELECTBOX_FIRST + 3)
364 #define GADGET_ID_CUSTOM_MOVE_DIRECTION (GADGET_ID_SELECTBOX_FIRST + 4)
365 #define GADGET_ID_CUSTOM_MOVE_STEPSIZE  (GADGET_ID_SELECTBOX_FIRST + 5)
366 #define GADGET_ID_CUSTOM_SMASH_TARGETS  (GADGET_ID_SELECTBOX_FIRST + 6)
367 #define GADGET_ID_CUSTOM_WALKABLE_LAYER (GADGET_ID_SELECTBOX_FIRST + 7)
368 #define GADGET_ID_CHANGE_TIME_UNITS     (GADGET_ID_SELECTBOX_FIRST + 8)
369 #define GADGET_ID_CHANGE_PLAYER_ACTION  (GADGET_ID_SELECTBOX_FIRST + 9)
370 #define GADGET_ID_CHANGE_CAUSE          (GADGET_ID_SELECTBOX_FIRST + 10)
371
372 /* textbutton identifiers */
373 #define GADGET_ID_TEXTBUTTON_FIRST      (GADGET_ID_SELECTBOX_FIRST + 11)
374
375 #define GADGET_ID_PROPERTIES_INFO       (GADGET_ID_TEXTBUTTON_FIRST + 0)
376 #define GADGET_ID_PROPERTIES_CONFIG     (GADGET_ID_TEXTBUTTON_FIRST + 1)
377 #define GADGET_ID_PROPERTIES_ADVANCED   (GADGET_ID_TEXTBUTTON_FIRST + 2)
378 #define GADGET_ID_SAVE_AS_TEMPLATE      (GADGET_ID_TEXTBUTTON_FIRST + 3)
379
380 /* gadgets for scrolling of drawing area */
381 #define GADGET_ID_SCROLLING_FIRST       (GADGET_ID_TEXTBUTTON_FIRST + 4)
382
383 #define GADGET_ID_SCROLL_UP             (GADGET_ID_SCROLLING_FIRST + 0)
384 #define GADGET_ID_SCROLL_DOWN           (GADGET_ID_SCROLLING_FIRST + 1)
385 #define GADGET_ID_SCROLL_LEFT           (GADGET_ID_SCROLLING_FIRST + 2)
386 #define GADGET_ID_SCROLL_RIGHT          (GADGET_ID_SCROLLING_FIRST + 3)
387 #define GADGET_ID_SCROLL_HORIZONTAL     (GADGET_ID_SCROLLING_FIRST + 4)
388 #define GADGET_ID_SCROLL_VERTICAL       (GADGET_ID_SCROLLING_FIRST + 5)
389
390 /* gadgets for scrolling element list */
391 #define GADGET_ID_SCROLLING_LIST_FIRST  (GADGET_ID_SCROLLING_FIRST + 6)
392
393 #define GADGET_ID_SCROLL_LIST_UP        (GADGET_ID_SCROLLING_LIST_FIRST + 0)
394 #define GADGET_ID_SCROLL_LIST_DOWN      (GADGET_ID_SCROLLING_LIST_FIRST + 1)
395 #define GADGET_ID_SCROLL_LIST_VERTICAL  (GADGET_ID_SCROLLING_LIST_FIRST + 2)
396
397 /* checkbuttons for level/element properties */
398 #define GADGET_ID_CHECKBUTTON_FIRST     (GADGET_ID_SCROLLING_LIST_FIRST + 3)
399
400 #define GADGET_ID_RANDOM_PERCENTAGE     (GADGET_ID_CHECKBUTTON_FIRST + 0)
401 #define GADGET_ID_RANDOM_QUANTITY       (GADGET_ID_CHECKBUTTON_FIRST + 1)
402 #define GADGET_ID_RANDOM_RESTRICTED     (GADGET_ID_CHECKBUTTON_FIRST + 2)
403 #define GADGET_ID_DOUBLE_SPEED          (GADGET_ID_CHECKBUTTON_FIRST + 3)
404 #define GADGET_ID_GRAVITY               (GADGET_ID_CHECKBUTTON_FIRST + 4)
405 #define GADGET_ID_STICK_ELEMENT         (GADGET_ID_CHECKBUTTON_FIRST + 5)
406 #define GADGET_ID_EM_SLIPPERY_GEMS      (GADGET_ID_CHECKBUTTON_FIRST + 6)
407 #define GADGET_ID_CUSTOM_EXPLODE_RESULT (GADGET_ID_CHECKBUTTON_FIRST + 7)
408 #define GADGET_ID_CUSTOM_EXPLODE_FIRE   (GADGET_ID_CHECKBUTTON_FIRST + 8)
409 #define GADGET_ID_CUSTOM_EXPLODE_SMASH  (GADGET_ID_CHECKBUTTON_FIRST + 9)
410 #define GADGET_ID_CUSTOM_EXPLODE_IMPACT (GADGET_ID_CHECKBUTTON_FIRST + 10)
411 #define GADGET_ID_CUSTOM_WALK_TO_OBJECT (GADGET_ID_CHECKBUTTON_FIRST + 11)
412 #define GADGET_ID_CUSTOM_DEADLY         (GADGET_ID_CHECKBUTTON_FIRST + 12)
413 #define GADGET_ID_CUSTOM_CAN_MOVE       (GADGET_ID_CHECKBUTTON_FIRST + 13)
414 #define GADGET_ID_CUSTOM_CAN_FALL       (GADGET_ID_CHECKBUTTON_FIRST + 14)
415 #define GADGET_ID_CUSTOM_CAN_SMASH      (GADGET_ID_CHECKBUTTON_FIRST + 15)
416 #define GADGET_ID_CUSTOM_SLIPPERY       (GADGET_ID_CHECKBUTTON_FIRST + 16)
417 #define GADGET_ID_CUSTOM_WALKABLE       (GADGET_ID_CHECKBUTTON_FIRST + 17)
418 #define GADGET_ID_CUSTOM_USE_GRAPHIC    (GADGET_ID_CHECKBUTTON_FIRST + 18)
419 #define GADGET_ID_CUSTOM_USE_TEMPLATE   (GADGET_ID_CHECKBUTTON_FIRST + 19)
420 #define GADGET_ID_CUSTOM_CAN_CHANGE     (GADGET_ID_CHECKBUTTON_FIRST + 20)
421 #define GADGET_ID_CHANGE_DELAY          (GADGET_ID_CHECKBUTTON_FIRST + 21)
422 #define GADGET_ID_CHANGE_BY_PLAYER      (GADGET_ID_CHECKBUTTON_FIRST + 22)
423
424 /* gadgets for buttons in element list */
425 #define GADGET_ID_ELEMENTLIST_FIRST     (GADGET_ID_CHECKBUTTON_FIRST + 23)
426 #define GADGET_ID_ELEMENTLIST_LAST      (GADGET_ID_ELEMENTLIST_FIRST +  \
427                                         ED_NUM_ELEMENTLIST_BUTTONS - 1)
428
429 #define NUM_EDITOR_GADGETS              (GADGET_ID_ELEMENTLIST_LAST + 1)
430
431 /* radio button numbers */
432 #define RADIO_NR_NONE                   0
433 #define RADIO_NR_DRAWING_TOOLBOX        1
434 #define RADIO_NR_RANDOM_ELEMENTS        2
435
436 /* values for counter gadgets */
437 #define ED_COUNTER_ID_SELECT_LEVEL      0
438 #define ED_COUNTER_ID_LEVEL_XSIZE       1
439 #define ED_COUNTER_ID_LEVEL_YSIZE       2
440 #define ED_COUNTER_ID_LEVEL_COLLECT     3
441 #define ED_COUNTER_ID_LEVEL_TIMELIMIT   4
442 #define ED_COUNTER_ID_LEVEL_TIMESCORE   5
443 #define ED_COUNTER_ID_LEVEL_RANDOM      6
444 #define ED_COUNTER_ID_ELEMENT_SCORE     7
445 #define ED_COUNTER_ID_ELEMENT_CONTENT   8
446 #define ED_COUNTER_ID_CUSTOM_SCORE      9
447 #define ED_COUNTER_ID_PUSH_DELAY_FIX    10
448 #define ED_COUNTER_ID_PUSH_DELAY_RND    11
449 #define ED_COUNTER_ID_MOVE_DELAY_FIX    12
450 #define ED_COUNTER_ID_MOVE_DELAY_RND    13
451 #define ED_COUNTER_ID_CHANGE_DELAY_FIX  14
452 #define ED_COUNTER_ID_CHANGE_DELAY_RND  15
453
454 #define ED_NUM_COUNTERBUTTONS           16
455
456 #define ED_COUNTER_ID_LEVEL_FIRST       ED_COUNTER_ID_LEVEL_XSIZE
457 #define ED_COUNTER_ID_LEVEL_LAST        ED_COUNTER_ID_LEVEL_RANDOM
458
459 #define ED_COUNTER_ID_CUSTOM_FIRST      ED_COUNTER_ID_CUSTOM_SCORE
460 #define ED_COUNTER_ID_CUSTOM_LAST       ED_COUNTER_ID_MOVE_DELAY_RND
461
462 #define ED_COUNTER_ID_CHANGE_FIRST      ED_COUNTER_ID_CHANGE_DELAY_FIX
463 #define ED_COUNTER_ID_CHANGE_LAST       ED_COUNTER_ID_CHANGE_DELAY_RND
464
465 /* values for scrollbutton gadgets */
466 #define ED_SCROLLBUTTON_ID_AREA_UP      0
467 #define ED_SCROLLBUTTON_ID_AREA_DOWN    1
468 #define ED_SCROLLBUTTON_ID_AREA_LEFT    2
469 #define ED_SCROLLBUTTON_ID_AREA_RIGHT   3
470 #define ED_SCROLLBUTTON_ID_LIST_UP      4
471 #define ED_SCROLLBUTTON_ID_LIST_DOWN    5
472
473 #define ED_NUM_SCROLLBUTTONS            6
474
475 #define ED_SCROLLBUTTON_ID_AREA_FIRST   ED_SCROLLBUTTON_ID_AREA_UP
476 #define ED_SCROLLBUTTON_ID_AREA_LAST    ED_SCROLLBUTTON_ID_AREA_RIGHT
477
478 /* values for scrollbar gadgets */
479 #define ED_SCROLLBAR_ID_AREA_HORIZONTAL 0
480 #define ED_SCROLLBAR_ID_AREA_VERTICAL   1
481 #define ED_SCROLLBAR_ID_LIST_VERTICAL   2
482
483 #define ED_NUM_SCROLLBARS               3
484
485 #define ED_SCROLLBAR_ID_AREA_FIRST      ED_SCROLLBAR_ID_AREA_HORIZONTAL
486 #define ED_SCROLLBAR_ID_AREA_LAST       ED_SCROLLBAR_ID_AREA_VERTICAL
487
488 /* values for text input gadgets */
489 #define ED_TEXTINPUT_ID_LEVEL_NAME      0
490 #define ED_TEXTINPUT_ID_LEVEL_AUTHOR    1
491
492 #define ED_NUM_TEXTINPUT                2
493
494 #define ED_TEXTINPUT_ID_LEVEL_FIRST     ED_TEXTINPUT_ID_LEVEL_NAME
495 #define ED_TEXTINPUT_ID_LEVEL_LAST      ED_TEXTINPUT_ID_LEVEL_AUTHOR
496
497 /* values for selectbox gadgets */
498 #define ED_SELECTBOX_ID_CUSTOM_WALK_TO_ACTION   0
499 #define ED_SELECTBOX_ID_CUSTOM_CONSISTENCY      1
500 #define ED_SELECTBOX_ID_CUSTOM_DEADLINESS       2
501 #define ED_SELECTBOX_ID_CUSTOM_MOVE_PATTERN     3
502 #define ED_SELECTBOX_ID_CUSTOM_MOVE_DIRECTION   4
503 #define ED_SELECTBOX_ID_CUSTOM_MOVE_STEPSIZE    5
504 #define ED_SELECTBOX_ID_CUSTOM_SMASH_TARGETS    6
505 #define ED_SELECTBOX_ID_CUSTOM_WALKABLE_LAYER   7
506 #define ED_SELECTBOX_ID_CHANGE_TIME_UNITS       8
507 #define ED_SELECTBOX_ID_CHANGE_PLAYER_ACTION    9
508 #define ED_SELECTBOX_ID_CHANGE_CAUSE            10
509
510 #define ED_NUM_SELECTBOX                        11
511
512 #define ED_SELECTBOX_ID_CUSTOM_FIRST    ED_SELECTBOX_ID_CUSTOM_WALK_TO_ACTION
513 #define ED_SELECTBOX_ID_CUSTOM_LAST     ED_SELECTBOX_ID_CUSTOM_WALKABLE_LAYER
514
515 #define ED_SELECTBOX_ID_CHANGE_FIRST    ED_SELECTBOX_ID_CHANGE_TIME_UNITS
516 #define ED_SELECTBOX_ID_CHANGE_LAST     ED_SELECTBOX_ID_CHANGE_CAUSE
517
518 /* values for textbutton gadgets */
519 #define ED_TEXTBUTTON_ID_PROPERTIES_INFO        0
520 #define ED_TEXTBUTTON_ID_PROPERTIES_CONFIG      1
521 #define ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED    2
522 #define ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE       3
523
524 #define ED_NUM_TEXTBUTTON                       4
525
526 /* values for checkbutton gadgets */
527 #define ED_CHECKBUTTON_ID_DOUBLE_SPEED          0
528 #define ED_CHECKBUTTON_ID_GRAVITY               1
529 #define ED_CHECKBUTTON_ID_RANDOM_RESTRICTED     2
530 #define ED_CHECKBUTTON_ID_STICK_ELEMENT         3
531 #define ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS      4
532 #define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_RESULT 5
533 #define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_FIRE   6
534 #define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_SMASH  7
535 #define ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_IMPACT 8
536 #define ED_CHECKBUTTON_ID_CUSTOM_WALK_TO_OBJECT 9
537 #define ED_CHECKBUTTON_ID_CUSTOM_DEADLY         10
538 #define ED_CHECKBUTTON_ID_CUSTOM_CAN_MOVE       11
539 #define ED_CHECKBUTTON_ID_CUSTOM_CAN_FALL       12
540 #define ED_CHECKBUTTON_ID_CUSTOM_CAN_SMASH      13
541 #define ED_CHECKBUTTON_ID_CUSTOM_SLIPPERY       14
542 #define ED_CHECKBUTTON_ID_CUSTOM_WALKABLE       15
543 #define ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC    16
544 #define ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE   17
545 #define ED_CHECKBUTTON_ID_CUSTOM_CAN_CHANGE     18
546 #define ED_CHECKBUTTON_ID_CHANGE_DELAY          19
547 #define ED_CHECKBUTTON_ID_CHANGE_BY_PLAYER      20
548
549 #define ED_NUM_CHECKBUTTONS                     21
550
551 #define ED_CHECKBUTTON_ID_LEVEL_FIRST   ED_CHECKBUTTON_ID_DOUBLE_SPEED
552 #define ED_CHECKBUTTON_ID_LEVEL_LAST    ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
553
554 #define ED_CHECKBUTTON_ID_CUSTOM_FIRST  ED_CHECKBUTTON_ID_CUSTOM_EXPLODE_RESULT
555 #define ED_CHECKBUTTON_ID_CUSTOM_LAST   ED_CHECKBUTTON_ID_CUSTOM_WALKABLE
556
557 #define ED_CHECKBUTTON_ID_CHANGE_FIRST  ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC
558 #define ED_CHECKBUTTON_ID_CHANGE_LAST   ED_CHECKBUTTON_ID_CHANGE_BY_PLAYER
559
560 /* values for radiobutton gadgets */
561 #define ED_RADIOBUTTON_ID_PERCENTAGE    0
562 #define ED_RADIOBUTTON_ID_QUANTITY      1
563
564 #define ED_NUM_RADIOBUTTONS             2
565
566 #define ED_RADIOBUTTON_ID_LEVEL_FIRST   ED_RADIOBUTTON_ID_PERCENTAGE
567 #define ED_RADIOBUTTON_ID_LEVEL_LAST    ED_RADIOBUTTON_ID_QUANTITY
568
569
570 /*
571   -----------------------------------------------------------------------------
572   some internally used definitions
573   -----------------------------------------------------------------------------
574 */
575
576 /* values for CopyLevelToUndoBuffer() */
577 #define UNDO_IMMEDIATE                  0
578 #define UNDO_ACCUMULATE                 1
579
580 /* values for scrollbars */
581 #define ED_SCROLL_NO                    0
582 #define ED_SCROLL_LEFT                  1
583 #define ED_SCROLL_RIGHT                 2
584 #define ED_SCROLL_UP                    4
585 #define ED_SCROLL_DOWN                  8
586
587 /* screens in the level editor */
588 #define ED_MODE_DRAWING                 0
589 #define ED_MODE_INFO                    1
590 #define ED_MODE_PROPERTIES              2
591
592 /* sub-screens in the element properties section */
593 #define ED_MODE_PROPERTIES_INFO         ED_TEXTBUTTON_ID_PROPERTIES_INFO
594 #define ED_MODE_PROPERTIES_CONFIG       ED_TEXTBUTTON_ID_PROPERTIES_CONFIG
595 #define ED_MODE_PROPERTIES_ADVANCED     ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED
596
597 /* how many steps can be cancelled */
598 #define NUM_UNDO_STEPS                  (10 + 1)
599
600 /* values for elements with score */
601 #define MIN_SCORE                       0
602 #define MAX_SCORE                       255
603
604 /* values for random placement */
605 #define RANDOM_USE_PERCENTAGE           0
606 #define RANDOM_USE_QUANTITY             1
607
608 /* maximal size of level editor drawing area */
609 #define MAX_ED_FIELDX           (2 * SCR_FIELDX)
610 #define MAX_ED_FIELDY           (2 * SCR_FIELDY - 1)
611
612
613 /*
614   -----------------------------------------------------------------------------
615   some internally used data structure definitions
616   -----------------------------------------------------------------------------
617 */
618
619 static struct
620 {
621   char shortcut;
622   char *text;
623 } control_info[ED_NUM_CTRL_BUTTONS] =
624 {
625   { 's',        "draw single items"             },
626   { 'd',        "draw connected items"          },
627   { 'l',        "draw lines"                    },
628   { 'a',        "draw arcs"                     },
629   { 'r',        "draw outline rectangles"       },
630   { 'R',        "draw filled rectangles"        },
631   { '\0',       "wrap (rotate) level up"        },
632   { 't',        "enter text elements"           },
633   { 'f',        "flood fill"                    },
634   { '\0',       "wrap (rotate) level left"      },
635   { '?',        "properties of drawing element" },
636   { '\0',       "wrap (rotate) level right"     },
637   { '\0',       "random element placement"      },
638   { 'b',        "grab brush"                    },
639   { '\0',       "wrap (rotate) level down"      },
640   { ',',        "pick drawing element"          },
641   { 'U',        "undo last operation"           },
642   { 'I',        "level properties"              },
643   { 'S',        "save level"                    },
644   { 'C',        "clear level"                   },
645   { 'T',        "test level"                    },
646   { 'E',        "exit level editor"             }
647 };
648
649 static int random_placement_value = 10;
650 static int random_placement_method = RANDOM_USE_QUANTITY;
651 static int random_placement_background_element = EL_SAND;
652 static boolean random_placement_background_restricted = FALSE;
653 static boolean stick_element_properties_window = FALSE;
654 static boolean custom_element_properties[NUM_ELEMENT_PROPERTIES];
655 static boolean custom_element_change_events[NUM_CHANGE_EVENTS];
656 static struct ElementInfo custom_element;
657
658 static struct
659 {
660   int x, y;
661   int min_value, max_value;
662   int gadget_id_down, gadget_id_up;
663   int gadget_id_text;
664   int *value;
665   char *text_above, *text_left, *text_right;
666 } counterbutton_info[ED_NUM_COUNTERBUTTONS] =
667 {
668   {
669     DX + 5 - SX,                        DY + 3 - SY,
670     1,                                  100,
671     GADGET_ID_SELECT_LEVEL_DOWN,        GADGET_ID_SELECT_LEVEL_UP,
672     GADGET_ID_SELECT_LEVEL_TEXT,
673     &level_nr,
674     NULL,                               NULL, NULL
675   },
676   {
677     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(2),
678     MIN_LEV_FIELDX,                     MAX_LEV_FIELDX,
679     GADGET_ID_LEVEL_XSIZE_DOWN,         GADGET_ID_LEVEL_XSIZE_UP,
680     GADGET_ID_LEVEL_XSIZE_TEXT,
681     &level.fieldx,
682     "playfield size",                   NULL, "width",
683   },
684   {
685     ED_SETTINGS_XPOS(0) + 2 * DXSIZE,   ED_COUNTER_YPOS(2),
686     MIN_LEV_FIELDY,                     MAX_LEV_FIELDY,
687     GADGET_ID_LEVEL_YSIZE_DOWN,         GADGET_ID_LEVEL_YSIZE_UP,
688     GADGET_ID_LEVEL_YSIZE_TEXT,
689     &level.fieldy,
690     NULL,                               NULL, "height",
691   },
692   {
693     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(3),
694     0,                                  999,
695     GADGET_ID_LEVEL_COLLECT_DOWN,       GADGET_ID_LEVEL_COLLECT_UP,
696     GADGET_ID_LEVEL_COLLECT_TEXT,
697     &level.gems_needed,
698     "number of emeralds to collect",    NULL, NULL
699   },
700   {
701     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(4),
702     0,                                  999,
703     GADGET_ID_LEVEL_TIMELIMIT_DOWN,     GADGET_ID_LEVEL_TIMELIMIT_UP,
704     GADGET_ID_LEVEL_TIMELIMIT_TEXT,
705     &level.time,
706     "time available to solve level",    NULL, "(0 => no time limit)"
707   },
708   {
709     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(5),
710     0,                                  255,
711     GADGET_ID_LEVEL_TIMESCORE_DOWN,     GADGET_ID_LEVEL_TIMESCORE_UP,
712     GADGET_ID_LEVEL_TIMESCORE_TEXT,
713     &level.score[SC_TIME_BONUS],
714     "score for each 10 seconds left",   NULL, NULL
715   },
716   {
717     ED_SETTINGS_XPOS(0),                ED_COUNTER2_YPOS(8),
718     1,                                  100,
719     GADGET_ID_LEVEL_RANDOM_DOWN,        GADGET_ID_LEVEL_RANDOM_UP,
720     GADGET_ID_LEVEL_RANDOM_TEXT,
721     &random_placement_value,
722     "random element placement",         NULL, "in"
723   },
724   {
725     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(0),
726     MIN_SCORE,                          MAX_SCORE,
727     GADGET_ID_ELEMENT_SCORE_DOWN,       GADGET_ID_ELEMENT_SCORE_UP,
728     GADGET_ID_ELEMENT_SCORE_TEXT,
729     NULL,                               /* will be set when used */
730     NULL,                               NULL, NULL
731   },
732   {
733     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(6),
734     MIN_ELEMENT_CONTENTS,               MAX_ELEMENT_CONTENTS,
735     GADGET_ID_ELEMENT_CONTENT_DOWN,     GADGET_ID_ELEMENT_CONTENT_UP,
736     GADGET_ID_ELEMENT_CONTENT_TEXT,
737     &level.num_yamyam_contents,
738     NULL,                               NULL, "number of content areas"
739   },
740   {
741     ED_SETTINGS_XPOS(9),                ED_SETTINGS_YPOS(4),
742     MIN_SCORE,                          MAX_SCORE,
743     GADGET_ID_CUSTOM_SCORE_DOWN,        GADGET_ID_CUSTOM_SCORE_UP,
744     GADGET_ID_CUSTOM_SCORE_TEXT,
745     &custom_element.score,
746     NULL,                               "score", NULL
747   },
748   {
749     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(5),
750     0,                                  999,
751     GADGET_ID_PUSH_DELAY_FIX_DOWN,      GADGET_ID_PUSH_DELAY_FIX_UP,
752     GADGET_ID_PUSH_DELAY_FIX_TEXT,
753     &custom_element.push_delay_fixed,
754     NULL,                               "push delay", NULL
755   },
756   {
757     ED_COUNT_PUSH_DELAY_RND_XPOS,       ED_SETTINGS_YPOS(5),
758     0,                                  999,
759     GADGET_ID_PUSH_DELAY_RND_DOWN,      GADGET_ID_PUSH_DELAY_RND_UP,
760     GADGET_ID_PUSH_DELAY_RND_TEXT,
761     &custom_element.push_delay_random,
762     NULL,                               "+random", NULL
763   },
764   {
765     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(8),
766     0,                                  999,
767     GADGET_ID_MOVE_DELAY_FIX_DOWN,      GADGET_ID_MOVE_DELAY_FIX_UP,
768     GADGET_ID_MOVE_DELAY_FIX_TEXT,
769     &custom_element.move_delay_fixed,
770     NULL,                               "move delay", NULL
771   },
772   {
773     ED_COUNT_MOVE_DELAY_RND_XPOS,       ED_SETTINGS_YPOS(8),
774     0,                                  999,
775     GADGET_ID_MOVE_DELAY_RND_DOWN,      GADGET_ID_MOVE_DELAY_RND_UP,
776     GADGET_ID_MOVE_DELAY_RND_TEXT,
777     &custom_element.move_delay_random,
778     NULL,                               "+random", NULL
779   },
780   {
781     ED_SETTINGS_XPOS(2),                ED_SETTINGS_YPOS(3),
782     0,                                  999,
783     GADGET_ID_CHANGE_DELAY_FIX_DOWN,    GADGET_ID_CHANGE_DELAY_FIX_UP,
784     GADGET_ID_CHANGE_DELAY_FIX_TEXT,
785     &custom_element.change.delay_fixed,
786     NULL,                               "delay", NULL,
787   },
788   {
789     ED_COUNT_CHANGE_DELAY_RND_XPOS,     ED_SETTINGS_YPOS(3),
790     0,                                  999,
791     GADGET_ID_CHANGE_DELAY_RND_DOWN,    GADGET_ID_CHANGE_DELAY_RND_UP,
792     GADGET_ID_CHANGE_DELAY_RND_TEXT,
793     &custom_element.change.delay_random,
794     NULL,                               "+random", NULL
795   }
796 };
797
798 static struct
799 {
800   int x, y;
801   int gadget_id;
802   int size;
803   char *value;
804   char *infotext;
805 } textinput_info[ED_NUM_TEXTINPUT] =
806 {
807   {
808     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(0),
809     GADGET_ID_LEVEL_NAME,
810     MAX_LEVEL_NAME_LEN,
811     level.name,
812     "Title"
813   },
814   {
815     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(1),
816     GADGET_ID_LEVEL_AUTHOR,
817     MAX_LEVEL_AUTHOR_LEN,
818     level.author,
819     "Author"
820   }
821 };
822
823 static struct ValueTextInfo options_walk_to_action[] =
824 {
825   { EP_DIGGABLE,                "diggable"                      },
826   { EP_COLLECTIBLE,             "collectible"                   },
827   { EP_PUSHABLE,                "pushable"                      },
828   { -1,                         NULL                            }
829 };
830 static int value_walk_to_action = 0;
831
832 static struct ValueTextInfo options_move_pattern[] =
833 {
834   { MV_LEFT,                    "left"                          },
835   { MV_RIGHT,                   "right"                         },
836   { MV_UP,                      "up"                            },
837   { MV_DOWN,                    "down"                          },
838   { MV_HORIZONTAL,              "horizontal"                    },
839   { MV_VERTICAL,                "vertical"                      },
840   { MV_ALL_DIRECTIONS,          "all directions"                },
841   { MV_TOWARDS_PLAYER,          "towards player"                },
842   { MV_AWAY_FROM_PLAYER,        "away from player"              },
843   { MV_ALONG_LEFT_SIDE,         "along left side"               },
844   { MV_ALONG_RIGHT_SIDE,        "along right side"              },
845   { -1,                         NULL                            }
846 };
847
848 static struct ValueTextInfo options_move_direction[] =
849 {
850   { MV_NO_MOVING,               "automatic"                     },
851   { MV_LEFT,                    "left"                          },
852   { MV_RIGHT,                   "right"                         },
853   { MV_UP,                      "up"                            },
854   { MV_DOWN,                    "down"                          },
855   { -1,                         NULL                            }
856 };
857
858 static struct ValueTextInfo options_move_stepsize[] =
859 {
860   { 1,                          "very slow"                     },
861   { 2,                          "slow"                          },
862   { 4,                          "normal"                        },
863   { 8,                          "fast"                          },
864   { 16,                         "very fast"                     },
865   { -1,                         NULL                            }
866 };
867
868 static struct ValueTextInfo options_consistency[] =
869 {
870   { EP_CAN_EXPLODE,             "can explode"                   },
871   { EP_INDESTRUCTIBLE,          "indestructible"                },
872   { -1,                         NULL                            }
873 };
874 static int value_consistency = 0;
875
876 static struct ValueTextInfo options_deadliness[] =
877 {
878   { EP_DONT_RUN_INTO,           "running into"                  },
879   { EP_DONT_COLLIDE_WITH,       "colliding with"                },
880   { EP_DONT_TOUCH,              "touching"                      },
881   { -1,                         NULL                            }
882 };
883 static int value_deadliness = 0;
884
885 static struct ValueTextInfo options_smash_targets[] =
886 {
887   { EP_CAN_SMASH_FRIENDS,       "friends"                       },
888   { EP_CAN_SMASH_ENEMIES,       "enemies"                       },
889   { EP_CAN_SMASH_EXPLOSIVES,    "explosives"                    },
890   { -1,                         NULL                            }
891 };
892 static int value_smash_targets = 0;
893
894 static struct ValueTextInfo options_walkable_layer[] =
895 {
896   { EP_WALKABLE_OVER,           "over"                          },
897   { EP_WALKABLE_INSIDE,         "inside"                        },
898   { EP_WALKABLE_UNDER,          "under"                         },
899   { -1,                         NULL                            }
900 };
901 static int value_walkable_layer = 0;
902
903 static struct ValueTextInfo options_time_units[] =
904 {
905   { 50,                         "seconds"                       },
906   { 1,                          "frames"                        },
907   { -1,                         NULL                            }
908 };
909
910 static struct ValueTextInfo options_change_player_action[] =
911 {
912   { CE_PRESSED_BY_PLAYER,       "pressed"                       },
913   { CE_TOUCHED_BY_PLAYER,       "touched"                       },
914   { -1,                         NULL                            }
915 };
916 static int value_change_player_action = 0;
917
918 static struct ValueTextInfo options_change_cause[] =
919 {
920   { 1,                          "specified delay"               },
921   { 2,                          "impact (active)"               },
922   { 3,                          "impact (passive)"              },
923   { 4,                          "touched by player"             },
924   { 5,                          "pressed by player"             },
925   { -1,                         NULL                            }
926 };
927 static int value_change_cause = 0;
928
929 static struct
930 {
931   int x, y;
932   int gadget_id;
933   int size;     /* char size of selectbox or '-1' (dynamically determined) */
934   struct ValueTextInfo *options;
935   int *value;
936   char *text_left, *text_right, *infotext;
937 } selectbox_info[ED_NUM_SELECTBOX] =
938 {
939   {
940     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(4),
941     GADGET_ID_CUSTOM_WALK_TO_ACTION,
942     -1,
943     options_walk_to_action,
944     &value_walk_to_action,
945     NULL, NULL, "diggable/collectible/pushable"
946   },
947   {
948     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(2),
949     GADGET_ID_CUSTOM_CONSISTENCY,
950     -1,
951     options_consistency,
952     &value_consistency,
953     NULL, "explodes to:", "consistency/destructibility"
954   },
955   {
956     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(1),
957     GADGET_ID_CUSTOM_DEADLINESS,
958     -1,
959     options_deadliness,
960     &value_deadliness,
961     "deadly when", NULL, "deadliness of element"
962   },
963   {
964     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(6),
965     GADGET_ID_CUSTOM_MOVE_PATTERN,
966     -1,
967     options_move_pattern,
968     &custom_element.move_pattern,
969     "can move", NULL, "element move direction"
970   },
971   {
972     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(7),
973     GADGET_ID_CUSTOM_MOVE_DIRECTION,
974     -1,
975     options_move_direction,
976     &custom_element.move_direction_initial,
977     "starts moving", NULL, "initial element move direction"
978   },
979   {
980     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(9),
981     GADGET_ID_CUSTOM_MOVE_STEPSIZE,
982     -1,
983     options_move_stepsize,
984     &custom_element.move_stepsize,
985     "move speed", NULL, "speed of element movement"
986   },
987   {
988     ED_SETTINGS_XPOS(7),                ED_SETTINGS_YPOS(10),
989     GADGET_ID_CUSTOM_SMASH_TARGETS,
990     -1,
991     options_smash_targets,
992     &value_smash_targets,
993     "can smash", NULL, "elements that can be smashed"
994   },
995   {
996     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(12),
997     GADGET_ID_CUSTOM_WALKABLE_LAYER,
998     -1,
999     options_walkable_layer,
1000     &value_walkable_layer,
1001     "player can walk", NULL, "layer where player can walk"
1002   },
1003   {
1004     ED_SETTINGS_XPOS(2),                ED_SETTINGS_YPOS(4),
1005     GADGET_ID_CHANGE_TIME_UNITS,
1006     -1,
1007     options_time_units,
1008     &custom_element.change.delay_frames,
1009     "delay time given in", NULL, "delay time units for change"
1010   },
1011   {
1012     ED_SETTINGS_XPOS(2),                ED_SETTINGS_YPOS(5),
1013     GADGET_ID_CHANGE_PLAYER_ACTION,
1014     -1,
1015     options_change_player_action,
1016     &value_change_player_action,
1017     NULL, "by player", "type of player contact"
1018   },
1019   {
1020     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(10),
1021     GADGET_ID_CHANGE_CAUSE,
1022     -1,
1023     options_change_cause,
1024     &value_change_cause,
1025     "test:", NULL, "test-selectbox entry"
1026   },
1027 };
1028
1029 static struct
1030 {
1031   int x, y;
1032   int gadget_id;
1033   int size;
1034   char *text, *infotext;
1035 } textbutton_info[ED_NUM_TEXTBUTTON] =
1036 {
1037   {
1038     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(1),
1039     GADGET_ID_PROPERTIES_INFO,
1040     11, "Information",                  "Show information about element"
1041   },
1042   {
1043     ED_SETTINGS_XPOS(0) + 166,          ED_COUNTER_YPOS(1),
1044     GADGET_ID_PROPERTIES_CONFIG,
1045     11, "Configure",                    "Configure element properties"
1046   },
1047   {
1048     ED_SETTINGS_XPOS(0) + 332,          ED_COUNTER_YPOS(1),
1049     GADGET_ID_PROPERTIES_ADVANCED,
1050     11, "Advanced",                     "Advanced element configuration"
1051   },
1052   {
1053     ED_SETTINGS_XPOS(0) + 262,          ED_SETTINGS_YPOS(12),
1054     GADGET_ID_SAVE_AS_TEMPLATE,
1055     -1, "Save as template",             "Save current settings as new template"
1056   },
1057 };
1058
1059 static struct
1060 {
1061   int gd_x, gd_y;
1062   int x, y;
1063   int gadget_id;
1064   char *infotext;
1065 } scrollbutton_info[ED_NUM_SCROLLBUTTONS] =
1066 {
1067   {
1068     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 0 * ED_SCROLLBUTTON_YSIZE,
1069     ED_SCROLL_UP_XPOS,      ED_SCROLL_UP_YPOS,
1070     GADGET_ID_SCROLL_UP,
1071     "scroll level editing area up"
1072   },
1073   {
1074     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 1 * ED_SCROLLBUTTON_YSIZE,
1075     ED_SCROLL_DOWN_XPOS,    ED_SCROLL_DOWN_YPOS,
1076     GADGET_ID_SCROLL_DOWN,
1077     "scroll level editing area down"
1078   },
1079   {
1080     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 2 * ED_SCROLLBUTTON_YSIZE,
1081     ED_SCROLL_LEFT_XPOS,    ED_SCROLL_LEFT_YPOS,
1082     GADGET_ID_SCROLL_LEFT,
1083     "scroll level editing area left"
1084   },
1085   {
1086     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 3 * ED_SCROLLBUTTON_YSIZE,
1087     ED_SCROLL_RIGHT_XPOS,   ED_SCROLL_RIGHT_YPOS,
1088     GADGET_ID_SCROLL_RIGHT,
1089     "scroll level editing area right"
1090   },
1091   {
1092     ED_SCROLLBUTTON2_XPOS,  ED_SCROLLBUTTON2_YPOS + 0 * ED_SCROLLBUTTON2_YSIZE,
1093     ED_SCROLL2_UP_XPOS,     ED_SCROLL2_UP_YPOS,
1094     GADGET_ID_SCROLL_LIST_UP,
1095     "scroll element list up ('Page Up')"
1096   },
1097   {
1098     ED_SCROLLBUTTON2_XPOS,  ED_SCROLLBUTTON2_YPOS + 1 * ED_SCROLLBUTTON2_YSIZE,
1099     ED_SCROLL2_DOWN_XPOS,    ED_SCROLL2_DOWN_YPOS,
1100     GADGET_ID_SCROLL_LIST_DOWN,
1101     "scroll element list down ('Page Down')"
1102   }
1103 };
1104
1105 static struct
1106 {
1107   int gd_x, gd_y;
1108   int x, y;
1109   int width, height;
1110   int type;
1111   int gadget_id;
1112   char *infotext;
1113 } scrollbar_info[ED_NUM_SCROLLBARS] =
1114 {
1115   {
1116     ED_SCROLLBAR_XPOS,                  ED_SCROLLBAR_YPOS,
1117     SX + ED_SCROLL_HORIZONTAL_XPOS,     SY + ED_SCROLL_HORIZONTAL_YPOS,
1118     ED_SCROLL_HORIZONTAL_XSIZE,         ED_SCROLL_HORIZONTAL_YSIZE,
1119     GD_TYPE_SCROLLBAR_HORIZONTAL,
1120     GADGET_ID_SCROLL_HORIZONTAL,
1121     "scroll level editing area horizontally"
1122   },
1123   {
1124     ED_SCROLLBAR_XPOS,                  ED_SCROLLBAR_YPOS,
1125     SX + ED_SCROLL_VERTICAL_XPOS,       SY + ED_SCROLL_VERTICAL_YPOS,
1126     ED_SCROLL_VERTICAL_XSIZE,           ED_SCROLL_VERTICAL_YSIZE,
1127     GD_TYPE_SCROLLBAR_VERTICAL,
1128     GADGET_ID_SCROLL_VERTICAL,
1129     "scroll level editing area vertically"
1130   },
1131   {
1132     ED_SCROLLBAR2_XPOS,                 ED_SCROLLBAR2_YPOS,
1133     DX + ED_SCROLL2_VERTICAL_XPOS,      DY + ED_SCROLL2_VERTICAL_YPOS,
1134     ED_SCROLL2_VERTICAL_XSIZE,          ED_SCROLL2_VERTICAL_YSIZE,
1135     GD_TYPE_SCROLLBAR_VERTICAL,
1136     GADGET_ID_SCROLL_LIST_VERTICAL,
1137     "scroll element list vertically"
1138   }
1139 };
1140
1141 static struct
1142 {
1143   int x, y;
1144   int gadget_id;
1145   int radio_button_nr;
1146   int *value;
1147   int checked_value;
1148   char *text_right, *infotext;
1149 } radiobutton_info[ED_NUM_RADIOBUTTONS] =
1150 {
1151   {
1152     ED_SETTINGS_XPOS(0) + 160,          ED_COUNTER2_YPOS(8),
1153     GADGET_ID_RANDOM_PERCENTAGE,
1154     RADIO_NR_RANDOM_ELEMENTS,
1155     &random_placement_method,           RANDOM_USE_PERCENTAGE,
1156     "percentage",                       "use percentage for random elements"
1157   },
1158   {
1159     ED_SETTINGS_XPOS(0) + 340,          ED_COUNTER2_YPOS(8),
1160     GADGET_ID_RANDOM_QUANTITY,
1161     RADIO_NR_RANDOM_ELEMENTS,
1162     &random_placement_method,           RANDOM_USE_QUANTITY,
1163     "quantity",                         "use quantity for random elements"
1164   }
1165 };
1166
1167 static struct
1168 {
1169   int x, y;
1170   int gadget_id;
1171   boolean *value;
1172   char *text_right, *infotext;
1173 } checkbutton_info[ED_NUM_CHECKBUTTONS] =
1174 {
1175   {
1176     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(6) - MINI_TILEY,
1177     GADGET_ID_DOUBLE_SPEED,
1178     &level.double_speed,
1179     "double speed movement",            "set movement speed of player"
1180   },
1181   {
1182     ED_SETTINGS_XPOS(0) + 340,          ED_COUNTER_YPOS(6) - MINI_TILEY,
1183     GADGET_ID_GRAVITY,
1184     &level.gravity,
1185     "gravity",                          "set level gravity"
1186   },
1187   {
1188     ED_SETTINGS_XPOS(0),                ED_COUNTER2_YPOS(9) - MINI_TILEY,
1189     GADGET_ID_RANDOM_RESTRICTED,
1190     &random_placement_background_restricted,
1191     "restrict random placement to",     "set random placement restriction"
1192   },
1193   {
1194     ED_SETTINGS_XPOS(0),                0,      /* set at runtime */
1195     GADGET_ID_STICK_ELEMENT,
1196     &stick_element_properties_window,
1197     "stick this screen to edit content","stick this screen to edit content"
1198   },
1199   {
1200     ED_SETTINGS_XPOS(0),                ED_COUNTER_YPOS(4),
1201     GADGET_ID_EM_SLIPPERY_GEMS,
1202     &level.em_slippery_gems,
1203     "slip down from certain flat walls","use EM style slipping behaviour"
1204   },
1205   {
1206     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(2),
1207     GADGET_ID_CUSTOM_EXPLODE_RESULT,
1208     &custom_element_properties[EP_EXPLODE_RESULT],
1209     NULL,                               "set consistency/destructibility"
1210   },
1211   {
1212     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(3),
1213     GADGET_ID_CUSTOM_EXPLODE_FIRE,
1214     &custom_element_properties[EP_CAN_EXPLODE_BY_FIRE],
1215     "by fire",                          "element can explode by fire/explosion"
1216   },
1217   {
1218     ED_SETTINGS_XPOS(7),                ED_SETTINGS_YPOS(3),
1219     GADGET_ID_CUSTOM_EXPLODE_SMASH,
1220     &custom_element_properties[EP_CAN_EXPLODE_SMASHED],
1221     "smashed",                          "element can explode when smashed"
1222   },
1223   {
1224     ED_SETTINGS_XPOS(13),               ED_SETTINGS_YPOS(3),
1225     GADGET_ID_CUSTOM_EXPLODE_IMPACT,
1226     &custom_element_properties[EP_CAN_EXPLODE_IMPACT],
1227     "impact",                           "element can explode on impact"
1228   },
1229   {
1230     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(4),
1231     GADGET_ID_CUSTOM_WALK_TO_OBJECT,
1232     &custom_element_properties[EP_WALK_TO_OBJECT],
1233     NULL,                               "player can dig/collect/push element"
1234   },
1235   {
1236     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(1),
1237     GADGET_ID_CUSTOM_DEADLY,
1238     &custom_element_properties[EP_DEADLY],
1239     NULL,                               "element can kill the player"
1240   },
1241   {
1242     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(6),
1243     GADGET_ID_CUSTOM_CAN_MOVE,
1244     &custom_element_properties[EP_CAN_MOVE],
1245     NULL,                               "element can move in some direction"
1246   },
1247   {
1248     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(10),
1249     GADGET_ID_CUSTOM_CAN_FALL,
1250     &custom_element_properties[EP_CAN_FALL],
1251     "can fall",                         "element can fall down"
1252   },
1253   {
1254     ED_SETTINGS_XPOS(6),                ED_SETTINGS_YPOS(10),
1255     GADGET_ID_CUSTOM_CAN_SMASH,
1256     &custom_element_properties[EP_CAN_SMASH],
1257     NULL,                               "element can smash other elements"
1258   },
1259   {
1260     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(11),
1261     GADGET_ID_CUSTOM_SLIPPERY,
1262     &custom_element_properties[EP_SLIPPERY],
1263     "slippery",                         "other elements can fall down from it"
1264   },
1265   {
1266     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(12),
1267     GADGET_ID_CUSTOM_WALKABLE,
1268     &custom_element_properties[EP_WALKABLE],
1269     NULL,                               "player can walk on the same field"
1270   },
1271   {
1272     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(1),
1273     GADGET_ID_CUSTOM_USE_GRAPHIC,
1274     &custom_element.use_gfx_element,
1275     "use graphic of element:",          "use graphic for custom element"
1276   },
1277   {
1278     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(12),
1279     GADGET_ID_CUSTOM_USE_TEMPLATE,
1280     &custom_element.use_template,
1281     "use template",                     "use template for custom properties"
1282   },
1283   {
1284     ED_SETTINGS_XPOS(0),                ED_SETTINGS_YPOS(2),
1285     GADGET_ID_CUSTOM_CAN_CHANGE,
1286     &custom_element_properties[EP_CAN_CHANGE],
1287     "element changes to    after/when:","element can change to other element"
1288   },
1289   {
1290     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(3),
1291     GADGET_ID_CHANGE_DELAY,
1292     &custom_element_change_events[CE_DELAY],
1293     NULL,                               "element changes after delay"
1294   },
1295   {
1296     ED_SETTINGS_XPOS(1),                ED_SETTINGS_YPOS(5),
1297     GADGET_ID_CHANGE_BY_PLAYER,
1298     &custom_element_change_events[CE_BY_PLAYER],
1299     NULL,                               "element changes by player contact"
1300   },
1301 };
1302
1303
1304 /*
1305   -----------------------------------------------------------------------------
1306   some internally used variables
1307   -----------------------------------------------------------------------------
1308 */
1309
1310 /* actual size of level editor drawing area */
1311 static int ed_fieldx = MAX_ED_FIELDX - 1, ed_fieldy = MAX_ED_FIELDY - 1;
1312
1313 /* actual position of level editor drawing area in level playfield */
1314 static int level_xpos = -1, level_ypos = -1;
1315
1316 #define IN_ED_FIELD(x,y)  ((x)>=0 && (x)<ed_fieldx && (y)>=0 &&(y)<ed_fieldy)
1317
1318 /* drawing elements on the three mouse buttons */
1319 static int new_element1 = EL_WALL;
1320 static int new_element2 = EL_EMPTY;
1321 static int new_element3 = EL_SAND;
1322
1323 #define BUTTON_ELEMENT(button) ((button) == 1 ? new_element1 : \
1324                                 (button) == 2 ? new_element2 : \
1325                                 (button) == 3 ? new_element3 : EL_EMPTY)
1326 #define BUTTON_STEPSIZE(button) ((button) == 1 ? 1 : (button) == 2 ? 5 : 10)
1327
1328 /* forward declaration for internal use */
1329 static void ModifyEditorCounter(int, int);
1330 static void ModifyEditorCounterLimits(int, int, int);
1331 static void ModifyEditorSelectbox(int, int);
1332 static void ModifyEditorElementList();
1333 static void DrawDrawingWindow();
1334 static void DrawLevelInfoWindow();
1335 static void DrawPropertiesWindow();
1336 static boolean checkPropertiesConfig();
1337 static void CopyLevelToUndoBuffer(int);
1338 static void HandleDrawingAreas(struct GadgetInfo *);
1339 static void HandleCounterButtons(struct GadgetInfo *);
1340 static void HandleTextInputGadgets(struct GadgetInfo *);
1341 static void HandleSelectboxGadgets(struct GadgetInfo *);
1342 static void HandleTextbuttonGadgets(struct GadgetInfo *);
1343 static void HandleRadiobuttons(struct GadgetInfo *);
1344 static void HandleCheckbuttons(struct GadgetInfo *);
1345 static void HandleControlButtons(struct GadgetInfo *);
1346 static void HandleDrawingAreaInfo(struct GadgetInfo *);
1347
1348 static struct GadgetInfo *level_editor_gadget[NUM_EDITOR_GADGETS];
1349
1350 static int drawing_function = GADGET_ID_SINGLE_ITEMS;
1351 static int last_drawing_function = GADGET_ID_SINGLE_ITEMS;
1352 static boolean draw_with_brush = FALSE;
1353 static int properties_element = 0;
1354
1355 static short FieldBackup[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
1356 static short UndoBuffer[NUM_UNDO_STEPS][MAX_LEV_FIELDX][MAX_LEV_FIELDY];
1357 static int undo_buffer_position = 0;
1358 static int undo_buffer_steps = 0;
1359
1360 static int edit_mode;
1361 static int edit_mode_properties;
1362
1363 static int element_shift = 0;
1364
1365 static int editor_el_boulderdash[] =
1366 {
1367   EL_CHAR('B'),
1368   EL_CHAR('O'),
1369   EL_CHAR('U'),
1370   EL_CHAR('L'),
1371
1372   EL_CHAR('-'),
1373   EL_CHAR('D'),
1374   EL_CHAR('E'),
1375   EL_CHAR('R'),
1376
1377   EL_CHAR('D'),
1378   EL_CHAR('A'),
1379   EL_CHAR('S'),
1380   EL_CHAR('H'),
1381
1382   EL_PLAYER_1,
1383   EL_EMPTY,
1384   EL_SAND,
1385   EL_STEELWALL,
1386
1387   EL_BD_WALL,
1388   EL_BD_MAGIC_WALL,
1389   EL_EXIT_CLOSED,
1390   EL_EXIT_OPEN,
1391
1392   EL_BD_DIAMOND,
1393   EL_BD_BUTTERFLY_UP,
1394   EL_BD_FIREFLY_UP,
1395   EL_BD_ROCK,
1396
1397   EL_BD_BUTTERFLY_LEFT,
1398   EL_BD_FIREFLY_LEFT,
1399   EL_BD_BUTTERFLY_RIGHT,
1400   EL_BD_FIREFLY_RIGHT,
1401
1402   EL_BD_AMOEBA,
1403   EL_BD_BUTTERFLY_DOWN,
1404   EL_BD_FIREFLY_DOWN,
1405   EL_EMPTY,
1406 };
1407 static int num_editor_el_boulderdash = SIZEOF_ARRAY_INT(editor_el_boulderdash);
1408
1409 static int editor_el_emerald_mine[] =
1410 {
1411   EL_CHAR('E'),
1412   EL_CHAR('M'),
1413   EL_CHAR('E'),
1414   EL_CHAR('-'),
1415
1416   EL_CHAR('R'),
1417   EL_CHAR('A'),
1418   EL_CHAR('L'),
1419   EL_CHAR('D'),
1420
1421   EL_CHAR('M'),
1422   EL_CHAR('I'),
1423   EL_CHAR('N'),
1424   EL_CHAR('E'),
1425
1426   EL_PLAYER_1,
1427   EL_PLAYER_2,
1428   EL_PLAYER_3,
1429   EL_PLAYER_4,
1430
1431   EL_PLAYER_1,
1432   EL_EMPTY,
1433   EL_SAND,
1434   EL_ROCK,
1435
1436   EL_STEELWALL,
1437   EL_WALL,
1438   EL_WALL_CRUMBLED,
1439   EL_MAGIC_WALL,
1440
1441   EL_EMERALD,
1442   EL_DIAMOND,
1443   EL_NUT,
1444   EL_BOMB,
1445
1446   EL_WALL_EMERALD,
1447   EL_WALL_DIAMOND,
1448   EL_QUICKSAND_EMPTY,
1449   EL_QUICKSAND_FULL,
1450
1451   EL_DYNAMITE,
1452   EL_DYNAMITE_ACTIVE,
1453   EL_EXIT_CLOSED,
1454   EL_EXIT_OPEN,
1455
1456   EL_YAMYAM,
1457   EL_BUG_UP,
1458   EL_SPACESHIP_UP,
1459   EL_ROBOT,
1460
1461   EL_BUG_LEFT,
1462   EL_SPACESHIP_LEFT,
1463   EL_BUG_RIGHT,
1464   EL_SPACESHIP_RIGHT,
1465
1466   EL_ROBOT_WHEEL,
1467   EL_BUG_DOWN,
1468   EL_SPACESHIP_DOWN,
1469   EL_INVISIBLE_WALL,
1470
1471   EL_ACID_POOL_TOPLEFT,
1472   EL_ACID,
1473   EL_ACID_POOL_TOPRIGHT,
1474   EL_EMPTY,
1475
1476   EL_ACID_POOL_BOTTOMLEFT,
1477   EL_ACID_POOL_BOTTOM,
1478   EL_ACID_POOL_BOTTOMRIGHT,
1479   EL_EMPTY,
1480
1481   EL_AMOEBA_DROP,
1482   EL_AMOEBA_DEAD,
1483   EL_AMOEBA_WET,
1484   EL_AMOEBA_DRY,
1485
1486   EL_EM_KEY_1_FILE,
1487   EL_EM_KEY_2_FILE,
1488   EL_EM_KEY_3_FILE,
1489   EL_EM_KEY_4_FILE,
1490
1491   EL_EM_GATE_1,
1492   EL_EM_GATE_2,
1493   EL_EM_GATE_3,
1494   EL_EM_GATE_4,
1495
1496   EL_EM_GATE_1_GRAY,
1497   EL_EM_GATE_2_GRAY,
1498   EL_EM_GATE_3_GRAY,
1499   EL_EM_GATE_4_GRAY,
1500 };
1501 static int num_editor_el_emerald_mine = SIZEOF_ARRAY_INT(editor_el_emerald_mine);
1502
1503 static int editor_el_more[] =
1504 {
1505   EL_CHAR('M'),
1506   EL_CHAR('O'),
1507   EL_CHAR('R'),
1508   EL_CHAR('E'),
1509
1510   EL_KEY_1,
1511   EL_KEY_2,
1512   EL_KEY_3,
1513   EL_KEY_4,
1514
1515   EL_GATE_1,
1516   EL_GATE_2,
1517   EL_GATE_3,
1518   EL_GATE_4,
1519
1520   EL_GATE_1_GRAY,
1521   EL_GATE_2_GRAY,
1522   EL_GATE_3_GRAY,
1523   EL_GATE_4_GRAY,
1524
1525   EL_ARROW_LEFT,
1526   EL_ARROW_RIGHT,
1527   EL_ARROW_UP,
1528   EL_ARROW_DOWN,
1529
1530   EL_AMOEBA_FULL,
1531   EL_EMERALD_YELLOW,
1532   EL_EMERALD_RED,
1533   EL_EMERALD_PURPLE,
1534
1535   EL_WALL_BD_DIAMOND,
1536   EL_WALL_EMERALD_YELLOW,
1537   EL_WALL_EMERALD_RED,
1538   EL_WALL_EMERALD_PURPLE,
1539
1540   EL_GAME_OF_LIFE,
1541   EL_PACMAN_UP,
1542   EL_TIME_ORB_FULL,
1543   EL_TIME_ORB_EMPTY,
1544
1545   EL_PACMAN_LEFT,
1546   EL_DARK_YAMYAM,
1547   EL_PACMAN_RIGHT,
1548   EL_EXPANDABLE_WALL,
1549
1550   EL_BIOMAZE,
1551   EL_PACMAN_DOWN,
1552   EL_LAMP,
1553   EL_LAMP_ACTIVE,
1554
1555   EL_DYNABOMB_INCREASE_NUMBER,
1556   EL_DYNABOMB_INCREASE_SIZE,
1557   EL_DYNABOMB_INCREASE_POWER,
1558   EL_STONEBLOCK,
1559
1560   EL_MOLE,
1561   EL_PENGUIN,
1562   EL_PIG,
1563   EL_DRAGON,
1564
1565   EL_EMPTY,
1566   EL_MOLE_UP,
1567   EL_EMPTY,
1568   EL_EMPTY,
1569
1570   EL_MOLE_LEFT,
1571   EL_EMPTY,
1572   EL_MOLE_RIGHT,
1573   EL_EMPTY,
1574
1575   EL_EMPTY,
1576   EL_MOLE_DOWN,
1577   EL_BALLOON,
1578   EL_BALLOON_SWITCH_ANY,
1579
1580   EL_BALLOON_SWITCH_LEFT,
1581   EL_BALLOON_SWITCH_RIGHT,
1582   EL_BALLOON_SWITCH_UP,
1583   EL_BALLOON_SWITCH_DOWN,
1584
1585   EL_SATELLITE,
1586   EL_EXPANDABLE_WALL_HORIZONTAL,
1587   EL_EXPANDABLE_WALL_VERTICAL,
1588   EL_EXPANDABLE_WALL_ANY,
1589
1590   EL_INVISIBLE_STEELWALL,
1591   EL_INVISIBLE_WALL,
1592   EL_SPEED_PILL,
1593   EL_BLACK_ORB,
1594
1595   EL_EMC_STEELWALL_1,
1596   EL_EMC_WALL_1,
1597   EL_EMC_WALL_2,
1598   EL_EMC_WALL_3,
1599
1600   EL_EMC_WALL_4,
1601   EL_EMC_WALL_5,
1602   EL_EMC_WALL_6,
1603   EL_EMC_WALL_7,
1604 };
1605 static int num_editor_el_more = SIZEOF_ARRAY_INT(editor_el_more);
1606
1607 static int editor_el_sokoban[] =
1608 {
1609   EL_CHAR('S'),
1610   EL_CHAR('O'),
1611   EL_CHAR('K'),
1612   EL_CHAR('O'),
1613
1614   EL_CHAR('-'),
1615   EL_CHAR('B'),
1616   EL_CHAR('A'),
1617   EL_CHAR('N'),
1618
1619   EL_SOKOBAN_OBJECT,
1620   EL_SOKOBAN_FIELD_EMPTY,
1621   EL_SOKOBAN_FIELD_FULL,
1622   EL_STEELWALL,
1623 };
1624 static int num_editor_el_sokoban = SIZEOF_ARRAY_INT(editor_el_sokoban);
1625
1626 static int editor_el_supaplex[] =
1627 {
1628   EL_CHAR('S'),
1629   EL_CHAR('U'),
1630   EL_CHAR('P'),
1631   EL_CHAR('A'),
1632
1633   EL_CHAR('P'),
1634   EL_CHAR('L'),
1635   EL_CHAR('E'),
1636   EL_CHAR('X'),
1637
1638   EL_SP_EMPTY,
1639   EL_SP_ZONK,
1640   EL_SP_BASE,
1641   EL_SP_MURPHY,
1642
1643   EL_SP_INFOTRON,
1644   EL_SP_CHIP_SINGLE,
1645   EL_SP_HARDWARE_GRAY,
1646   EL_SP_EXIT_CLOSED,
1647
1648   EL_SP_DISK_ORANGE,
1649   EL_SP_PORT_RIGHT,
1650   EL_SP_PORT_DOWN,
1651   EL_SP_PORT_LEFT,
1652
1653   EL_SP_PORT_UP,
1654   EL_SP_GRAVITY_PORT_RIGHT,
1655   EL_SP_GRAVITY_PORT_DOWN,
1656   EL_SP_GRAVITY_PORT_LEFT,
1657
1658   EL_SP_GRAVITY_PORT_UP,
1659   EL_SP_SNIKSNAK,
1660   EL_SP_DISK_YELLOW,
1661   EL_SP_TERMINAL,
1662
1663   EL_SP_DISK_RED,
1664   EL_SP_PORT_VERTICAL,
1665   EL_SP_PORT_HORIZONTAL,
1666   EL_SP_PORT_ANY,
1667
1668   EL_SP_ELECTRON,
1669   EL_SP_BUGGY_BASE,
1670   EL_SP_CHIP_LEFT,
1671   EL_SP_CHIP_RIGHT,
1672
1673   EL_SP_HARDWARE_BASE_1,
1674   EL_SP_HARDWARE_GREEN,
1675   EL_SP_HARDWARE_BLUE,
1676   EL_SP_HARDWARE_RED,
1677
1678   EL_SP_HARDWARE_YELLOW,
1679   EL_SP_HARDWARE_BASE_2,
1680   EL_SP_HARDWARE_BASE_3,
1681   EL_SP_HARDWARE_BASE_4,
1682
1683   EL_SP_HARDWARE_BASE_5,
1684   EL_SP_HARDWARE_BASE_6,
1685   EL_SP_CHIP_TOP,
1686   EL_SP_CHIP_BOTTOM,
1687 };
1688 static int num_editor_el_supaplex = SIZEOF_ARRAY_INT(editor_el_supaplex);
1689
1690 static int editor_el_diamond_caves[] =
1691 {
1692   EL_CHAR('D'),
1693   EL_CHAR('I'),
1694   EL_CHAR('A'),
1695   EL_CHAR('-'),
1696
1697   EL_CHAR('M'),
1698   EL_CHAR('O'),
1699   EL_CHAR('N'),
1700   EL_CHAR('D'),
1701
1702   EL_CHAR('C'),
1703   EL_CHAR('A'),
1704   EL_CHAR('V'),
1705   EL_CHAR('E'),
1706
1707   EL_CHAR('S'),
1708   EL_CHAR(' '),
1709   EL_CHAR('I'),
1710   EL_CHAR('I'),
1711
1712   EL_PEARL,
1713   EL_CRYSTAL,
1714   EL_WALL_PEARL,
1715   EL_WALL_CRYSTAL,
1716
1717   EL_CONVEYOR_BELT_1_LEFT,
1718   EL_CONVEYOR_BELT_1_MIDDLE,
1719   EL_CONVEYOR_BELT_1_RIGHT,
1720   EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
1721
1722   EL_CONVEYOR_BELT_2_LEFT,
1723   EL_CONVEYOR_BELT_2_MIDDLE,
1724   EL_CONVEYOR_BELT_2_RIGHT,
1725   EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
1726
1727   EL_CONVEYOR_BELT_3_LEFT,
1728   EL_CONVEYOR_BELT_3_MIDDLE,
1729   EL_CONVEYOR_BELT_3_RIGHT,
1730   EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
1731
1732   EL_CONVEYOR_BELT_4_LEFT,
1733   EL_CONVEYOR_BELT_4_MIDDLE,
1734   EL_CONVEYOR_BELT_4_RIGHT,
1735   EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
1736
1737   EL_CONVEYOR_BELT_1_SWITCH_LEFT,
1738   EL_CONVEYOR_BELT_2_SWITCH_LEFT,
1739   EL_CONVEYOR_BELT_3_SWITCH_LEFT,
1740   EL_CONVEYOR_BELT_4_SWITCH_LEFT,
1741
1742   EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
1743   EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
1744   EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
1745   EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
1746
1747   EL_SWITCHGATE_OPEN,
1748   EL_SWITCHGATE_CLOSED,
1749   EL_SWITCHGATE_SWITCH_UP,
1750   EL_ENVELOPE,
1751
1752   EL_TIMEGATE_CLOSED,
1753   EL_TIMEGATE_OPEN,
1754   EL_TIMEGATE_SWITCH,
1755   EL_EMPTY,
1756
1757   EL_LANDMINE,
1758   EL_INVISIBLE_SAND,
1759   EL_STEELWALL_SLANTED,
1760   EL_EMPTY,
1761
1762   EL_SIGN_EXCLAMATION,
1763   EL_SIGN_STOP,
1764   EL_LIGHT_SWITCH,
1765   EL_LIGHT_SWITCH_ACTIVE,
1766
1767   EL_SHIELD_NORMAL,
1768   EL_SHIELD_DEADLY,
1769   EL_EXTRA_TIME,
1770   EL_EMPTY,
1771 };
1772 static int num_editor_el_diamond_caves = SIZEOF_ARRAY_INT(editor_el_diamond_caves);
1773
1774 static int editor_el_dx_boulderdash[] =
1775 {
1776   EL_CHAR('D'),
1777   EL_CHAR('X'),
1778   EL_CHAR('-'),
1779   EL_CHAR(' '),
1780
1781   EL_CHAR('B'),
1782   EL_CHAR('O'),
1783   EL_CHAR('U'),
1784   EL_CHAR('L'),
1785
1786   EL_CHAR('-'),
1787   EL_CHAR('D'),
1788   EL_CHAR('E'),
1789   EL_CHAR('R'),
1790
1791   EL_CHAR('D'),
1792   EL_CHAR('A'),
1793   EL_CHAR('S'),
1794   EL_CHAR('H'),
1795
1796   EL_SPRING,
1797   EL_TUBE_RIGHT_DOWN,
1798   EL_TUBE_HORIZONTAL_DOWN,
1799   EL_TUBE_LEFT_DOWN,
1800
1801   EL_TUBE_HORIZONTAL,
1802   EL_TUBE_VERTICAL_RIGHT,
1803   EL_TUBE_ANY,
1804   EL_TUBE_VERTICAL_LEFT,
1805
1806   EL_TUBE_VERTICAL,
1807   EL_TUBE_RIGHT_UP,
1808   EL_TUBE_HORIZONTAL_UP,
1809   EL_TUBE_LEFT_UP,
1810
1811   EL_TRAP,
1812   EL_DX_SUPABOMB,
1813   EL_EMPTY,
1814   EL_EMPTY
1815 };
1816 static int num_editor_el_dx_boulderdash = SIZEOF_ARRAY_INT(editor_el_dx_boulderdash);
1817
1818 static int editor_el_chars[] =
1819 {
1820   EL_CHAR('T'),
1821   EL_CHAR('E'),
1822   EL_CHAR('X'),
1823   EL_CHAR('T'),
1824
1825   EL_CHAR(' '),
1826   EL_CHAR('!'),
1827   EL_CHAR('"'),
1828   EL_CHAR('#'),
1829
1830   EL_CHAR('$'),
1831   EL_CHAR('%'),
1832   EL_CHAR('&'),
1833   EL_CHAR('\''),
1834
1835   EL_CHAR('('),
1836   EL_CHAR(')'),
1837   EL_CHAR('*'),
1838   EL_CHAR('+'),
1839
1840   EL_CHAR(','),
1841   EL_CHAR('-'),
1842   EL_CHAR('.'),
1843   EL_CHAR('/'),
1844
1845   EL_CHAR('0'),
1846   EL_CHAR('1'),
1847   EL_CHAR('2'),
1848   EL_CHAR('3'),
1849
1850   EL_CHAR('4'),
1851   EL_CHAR('5'),
1852   EL_CHAR('6'),
1853   EL_CHAR('7'),
1854
1855   EL_CHAR('8'),
1856   EL_CHAR('9'),
1857   EL_CHAR(':'),
1858   EL_CHAR(';'),
1859
1860   EL_CHAR('<'),
1861   EL_CHAR('='),
1862   EL_CHAR('>'),
1863   EL_CHAR('?'),
1864
1865   EL_CHAR('@'),
1866   EL_CHAR('A'),
1867   EL_CHAR('B'),
1868   EL_CHAR('C'),
1869
1870   EL_CHAR('D'),
1871   EL_CHAR('E'),
1872   EL_CHAR('F'),
1873   EL_CHAR('G'),
1874
1875   EL_CHAR('H'),
1876   EL_CHAR('I'),
1877   EL_CHAR('J'),
1878   EL_CHAR('K'),
1879
1880   EL_CHAR('L'),
1881   EL_CHAR('M'),
1882   EL_CHAR('N'),
1883   EL_CHAR('O'),
1884
1885   EL_CHAR('P'),
1886   EL_CHAR('Q'),
1887   EL_CHAR('R'),
1888   EL_CHAR('S'),
1889
1890   EL_CHAR('T'),
1891   EL_CHAR('U'),
1892   EL_CHAR('V'),
1893   EL_CHAR('W'),
1894
1895   EL_CHAR('X'),
1896   EL_CHAR('Y'),
1897   EL_CHAR('Z'),
1898   EL_CHAR('['),
1899
1900   EL_CHAR('\\'),
1901   EL_CHAR(']'),
1902   EL_CHAR('^'),
1903   EL_CHAR('_'),
1904
1905   EL_CHAR('©'),
1906   EL_CHAR('Ä'),
1907   EL_CHAR('Ö'),
1908   EL_CHAR('Ãœ'),
1909
1910   EL_CHAR('°'),
1911   EL_CHAR('®'),
1912   EL_CHAR(FONT_ASCII_CURSOR),
1913   EL_CHAR(' ')
1914 };
1915 static int num_editor_el_chars = SIZEOF_ARRAY_INT(editor_el_chars);
1916
1917 static int editor_el_custom[] =
1918 {
1919   EL_CHAR('C'),
1920   EL_CHAR('U'),
1921   EL_CHAR('S'),
1922   EL_CHAR('-'),
1923
1924   EL_CHAR('T'),
1925   EL_CHAR('O'),
1926   EL_CHAR('M'),
1927   EL_CHAR(' '),
1928
1929   EL_CHAR('E'),
1930   EL_CHAR('L'),
1931   EL_CHAR('E'),
1932   EL_CHAR('M'),
1933
1934   EL_CHAR('E'),
1935   EL_CHAR('N'),
1936   EL_CHAR('T'),
1937   EL_CHAR('S'),
1938
1939   EL_CUSTOM_START + 0,
1940   EL_CUSTOM_START + 1,
1941   EL_CUSTOM_START + 2,
1942   EL_CUSTOM_START + 3,
1943
1944   EL_CUSTOM_START + 4,
1945   EL_CUSTOM_START + 5,
1946   EL_CUSTOM_START + 6,
1947   EL_CUSTOM_START + 7,
1948
1949   EL_CUSTOM_START + 8,
1950   EL_CUSTOM_START + 9,
1951   EL_CUSTOM_START + 10,
1952   EL_CUSTOM_START + 11,
1953
1954   EL_CUSTOM_START + 12,
1955   EL_CUSTOM_START + 13,
1956   EL_CUSTOM_START + 14,
1957   EL_CUSTOM_START + 15,
1958
1959   EL_CUSTOM_START + 16,
1960   EL_CUSTOM_START + 17,
1961   EL_CUSTOM_START + 18,
1962   EL_CUSTOM_START + 19,
1963
1964   EL_CUSTOM_START + 20,
1965   EL_CUSTOM_START + 21,
1966   EL_CUSTOM_START + 22,
1967   EL_CUSTOM_START + 23,
1968
1969   EL_CUSTOM_START + 24,
1970   EL_CUSTOM_START + 25,
1971   EL_CUSTOM_START + 26,
1972   EL_CUSTOM_START + 27,
1973
1974   EL_CUSTOM_START + 28,
1975   EL_CUSTOM_START + 29,
1976   EL_CUSTOM_START + 30,
1977   EL_CUSTOM_START + 31,
1978
1979   EL_CUSTOM_START + 32,
1980   EL_CUSTOM_START + 33,
1981   EL_CUSTOM_START + 34,
1982   EL_CUSTOM_START + 35,
1983
1984   EL_CUSTOM_START + 36,
1985   EL_CUSTOM_START + 37,
1986   EL_CUSTOM_START + 38,
1987   EL_CUSTOM_START + 39,
1988
1989   EL_CUSTOM_START + 40,
1990   EL_CUSTOM_START + 41,
1991   EL_CUSTOM_START + 42,
1992   EL_CUSTOM_START + 43,
1993
1994   EL_CUSTOM_START + 44,
1995   EL_CUSTOM_START + 45,
1996   EL_CUSTOM_START + 46,
1997   EL_CUSTOM_START + 47,
1998
1999   EL_CUSTOM_START + 48,
2000   EL_CUSTOM_START + 49,
2001   EL_CUSTOM_START + 50,
2002   EL_CUSTOM_START + 51,
2003
2004   EL_CUSTOM_START + 52,
2005   EL_CUSTOM_START + 53,
2006   EL_CUSTOM_START + 54,
2007   EL_CUSTOM_START + 55,
2008
2009   EL_CUSTOM_START + 56,
2010   EL_CUSTOM_START + 57,
2011   EL_CUSTOM_START + 58,
2012   EL_CUSTOM_START + 59,
2013
2014   EL_CUSTOM_START + 60,
2015   EL_CUSTOM_START + 61,
2016   EL_CUSTOM_START + 62,
2017   EL_CUSTOM_START + 63,
2018
2019   EL_CUSTOM_START + 64,
2020   EL_CUSTOM_START + 65,
2021   EL_CUSTOM_START + 66,
2022   EL_CUSTOM_START + 67,
2023
2024   EL_CUSTOM_START + 68,
2025   EL_CUSTOM_START + 69,
2026   EL_CUSTOM_START + 70,
2027   EL_CUSTOM_START + 71,
2028
2029   EL_CUSTOM_START + 72,
2030   EL_CUSTOM_START + 73,
2031   EL_CUSTOM_START + 74,
2032   EL_CUSTOM_START + 75,
2033
2034   EL_CUSTOM_START + 76,
2035   EL_CUSTOM_START + 77,
2036   EL_CUSTOM_START + 78,
2037   EL_CUSTOM_START + 79,
2038
2039   EL_CUSTOM_START + 80,
2040   EL_CUSTOM_START + 81,
2041   EL_CUSTOM_START + 82,
2042   EL_CUSTOM_START + 83,
2043
2044   EL_CUSTOM_START + 84,
2045   EL_CUSTOM_START + 85,
2046   EL_CUSTOM_START + 86,
2047   EL_CUSTOM_START + 87,
2048
2049   EL_CUSTOM_START + 88,
2050   EL_CUSTOM_START + 89,
2051   EL_CUSTOM_START + 90,
2052   EL_CUSTOM_START + 91,
2053
2054   EL_CUSTOM_START + 92,
2055   EL_CUSTOM_START + 93,
2056   EL_CUSTOM_START + 94,
2057   EL_CUSTOM_START + 95,
2058
2059   EL_CUSTOM_START + 96,
2060   EL_CUSTOM_START + 97,
2061   EL_CUSTOM_START + 98,
2062   EL_CUSTOM_START + 99,
2063
2064   EL_CUSTOM_START + 100,
2065   EL_CUSTOM_START + 101,
2066   EL_CUSTOM_START + 102,
2067   EL_CUSTOM_START + 103,
2068
2069   EL_CUSTOM_START + 104,
2070   EL_CUSTOM_START + 105,
2071   EL_CUSTOM_START + 106,
2072   EL_CUSTOM_START + 107,
2073
2074   EL_CUSTOM_START + 108,
2075   EL_CUSTOM_START + 109,
2076   EL_CUSTOM_START + 110,
2077   EL_CUSTOM_START + 111,
2078
2079   EL_CUSTOM_START + 112,
2080   EL_CUSTOM_START + 113,
2081   EL_CUSTOM_START + 114,
2082   EL_CUSTOM_START + 115,
2083
2084   EL_CUSTOM_START + 116,
2085   EL_CUSTOM_START + 117,
2086   EL_CUSTOM_START + 118,
2087   EL_CUSTOM_START + 119,
2088
2089   EL_CUSTOM_START + 120,
2090   EL_CUSTOM_START + 121,
2091   EL_CUSTOM_START + 122,
2092   EL_CUSTOM_START + 123,
2093
2094   EL_CUSTOM_START + 124,
2095   EL_CUSTOM_START + 125,
2096   EL_CUSTOM_START + 126,
2097   EL_CUSTOM_START + 127
2098 };
2099 static int num_editor_el_custom = SIZEOF_ARRAY_INT(editor_el_custom);
2100
2101 static int *editor_elements = NULL;     /* dynamically allocated */
2102 static int num_editor_elements = 0;     /* dynamically determined */
2103
2104 static struct
2105 {
2106   boolean *setup_value;
2107   int *element_list;
2108   int *element_list_size;
2109
2110   boolean last_setup_value;
2111 }
2112 editor_elements_info[] =
2113 {
2114   { &setup.editor.el_boulderdash,       editor_el_boulderdash,
2115     &num_editor_el_boulderdash                                          },
2116   { &setup.editor.el_emerald_mine,      editor_el_emerald_mine,
2117     &num_editor_el_emerald_mine                                         },
2118   { &setup.editor.el_more,              editor_el_more,
2119     &num_editor_el_more                                                 },
2120   { &setup.editor.el_sokoban,           editor_el_sokoban,
2121     &num_editor_el_sokoban                                              },
2122   { &setup.editor.el_supaplex,          editor_el_supaplex,
2123     &num_editor_el_supaplex                                             },
2124   { &setup.editor.el_diamond_caves,     editor_el_diamond_caves,
2125     &num_editor_el_diamond_caves                                        },
2126   { &setup.editor.el_dx_boulderdash,    editor_el_dx_boulderdash,
2127     &num_editor_el_dx_boulderdash                                       },
2128   { &setup.editor.el_chars,             editor_el_chars,
2129     &num_editor_el_chars                                                },
2130   { &setup.editor.el_custom,            editor_el_custom,
2131     &num_editor_el_custom                                               },
2132   { NULL,                               NULL,
2133     NULL                                                                }
2134 };
2135
2136
2137 /*
2138   -----------------------------------------------------------------------------
2139   functions
2140   -----------------------------------------------------------------------------
2141 */
2142
2143 static void ReinitializeElementList()
2144 {
2145   int pos = 0;
2146   int i, j;
2147
2148   if (editor_elements != NULL)
2149     free(editor_elements);
2150
2151   num_editor_elements = 0;
2152
2153   /* determine size of element list */
2154   for (i=0; editor_elements_info[i].setup_value != NULL; i++)
2155     if (*editor_elements_info[i].setup_value)
2156       num_editor_elements += *editor_elements_info[i].element_list_size;
2157
2158   if (num_editor_elements < ED_NUM_ELEMENTLIST_BUTTONS)
2159   {
2160     /* workaround: offer at least as many elements as element buttons exist */
2161     int list_nr = 1;    /* see above: editor_elements_info for Emerald Mine */
2162
2163     *editor_elements_info[list_nr].setup_value = TRUE;
2164     num_editor_elements += *editor_elements_info[list_nr].element_list_size;
2165   }
2166
2167   editor_elements = checked_malloc(num_editor_elements * sizeof(int));
2168
2169   /* fill element list */
2170   for (i=0; editor_elements_info[i].setup_value != NULL; i++)
2171     if (*editor_elements_info[i].setup_value)
2172       for (j=0; j<*editor_elements_info[i].element_list_size; j++)
2173         editor_elements[pos++] = editor_elements_info[i].element_list[j];
2174 }
2175
2176 static void ReinitializeElementListButtons()
2177 {
2178   static boolean initialization_needed = TRUE;
2179   int i;
2180
2181   if (!initialization_needed)   /* check if editor element setup has changed */
2182     for (i=0; editor_elements_info[i].setup_value != NULL; i++)
2183       if (editor_elements_info[i].last_setup_value !=
2184           *editor_elements_info[i].setup_value)
2185         initialization_needed = TRUE;
2186
2187   if (!initialization_needed)
2188     return;
2189
2190   FreeLevelEditorGadgets();
2191   CreateLevelEditorGadgets();
2192
2193   /* store current setup values for next invocation of this function */
2194   for (i=0; editor_elements_info[i].setup_value != NULL; i++)
2195     editor_elements_info[i].last_setup_value =
2196       *editor_elements_info[i].setup_value;
2197
2198   initialization_needed = FALSE;
2199 }
2200
2201 static int getCounterGadgetWidth()
2202 {
2203   return (DXSIZE + getFontWidth(FONT_INPUT_1) - 2 * ED_GADGET_DISTANCE);
2204 }
2205
2206 static int getMaxInfoTextLength()
2207 {
2208   return (SXSIZE / getFontWidth(FONT_TEXT_2));
2209 }
2210
2211 static char *getElementInfoText(int element)
2212 {
2213   char *info_text = NULL;
2214
2215   if (element < NUM_FILE_ELEMENTS)
2216   {
2217     if (element_info[element].custom_description != NULL)
2218       info_text = element_info[element].custom_description;
2219     else if (element_info[element].editor_description != NULL)
2220       info_text = element_info[element].editor_description;
2221   }
2222
2223   if (info_text == NULL)
2224   {
2225     info_text = "unknown";
2226
2227     Error(ERR_WARN, "no element description for element %d", element);
2228   }
2229
2230   return info_text;
2231 }
2232
2233 static void ScrollMiniLevel(int from_x, int from_y, int scroll)
2234 {
2235   int x,y;
2236   int dx = (scroll == ED_SCROLL_LEFT ? -1 : scroll == ED_SCROLL_RIGHT ? 1 : 0);
2237   int dy = (scroll == ED_SCROLL_UP   ? -1 : scroll == ED_SCROLL_DOWN  ? 1 : 0);
2238
2239   BlitBitmap(drawto, drawto,
2240              SX + (dx == -1 ? MINI_TILEX : 0),
2241              SY + (dy == -1 ? MINI_TILEY : 0),
2242              (ed_fieldx * MINI_TILEX) - (dx != 0 ? MINI_TILEX : 0),
2243              (ed_fieldy * MINI_TILEY) - (dy != 0 ? MINI_TILEY : 0),
2244              SX + (dx == +1 ? MINI_TILEX : 0),
2245              SY + (dy == +1 ? MINI_TILEY : 0));
2246   if (dx)
2247   {
2248     x = (dx == 1 ? 0 : ed_fieldx - 1);
2249     for(y=0; y<ed_fieldy; y++)
2250       DrawMiniElementOrWall(x, y, from_x, from_y);
2251   }
2252   else if (dy)
2253   {
2254     y = (dy == 1 ? 0 : ed_fieldy - 1);
2255     for(x=0; x<ed_fieldx; x++)
2256       DrawMiniElementOrWall(x, y, from_x, from_y);
2257   }
2258
2259   redraw_mask |= REDRAW_FIELD;
2260   BackToFront();
2261 }
2262
2263 static void CreateControlButtons()
2264 {
2265   Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2266   struct GadgetInfo *gi;
2267   unsigned long event_mask;
2268   int i;
2269
2270   /* create toolbox buttons */
2271   for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
2272   {
2273     int id = i;
2274     int width, height;
2275     int gd_xoffset, gd_yoffset;
2276     int gd_x1, gd_x2, gd_y1, gd_y2;
2277     int button_type;
2278     int radio_button_nr;
2279     boolean checked;
2280
2281     if (id == GADGET_ID_SINGLE_ITEMS ||
2282         id == GADGET_ID_CONNECTED_ITEMS ||
2283         id == GADGET_ID_LINE ||
2284         id == GADGET_ID_ARC ||
2285         id == GADGET_ID_TEXT ||
2286         id == GADGET_ID_RECTANGLE ||
2287         id == GADGET_ID_FILLED_BOX ||
2288         id == GADGET_ID_FLOOD_FILL ||
2289         id == GADGET_ID_GRAB_BRUSH ||
2290         id == GADGET_ID_PICK_ELEMENT)
2291     {
2292       button_type = GD_TYPE_RADIO_BUTTON;
2293       radio_button_nr = RADIO_NR_DRAWING_TOOLBOX;
2294       checked = (id == drawing_function ? TRUE : FALSE);
2295       event_mask = GD_EVENT_PRESSED;
2296     }
2297     else
2298     {
2299       button_type = GD_TYPE_NORMAL_BUTTON;
2300       radio_button_nr = RADIO_NR_NONE;
2301       checked = FALSE;
2302
2303       if (id == GADGET_ID_WRAP_LEFT ||
2304           id == GADGET_ID_WRAP_RIGHT ||
2305           id == GADGET_ID_WRAP_UP ||
2306           id == GADGET_ID_WRAP_DOWN)
2307         event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
2308       else
2309         event_mask = GD_EVENT_RELEASED;
2310     }
2311
2312     if (id < ED_NUM_CTRL1_BUTTONS)
2313     {
2314       int x = i % ED_CTRL1_BUTTONS_HORIZ;
2315       int y = i / ED_CTRL1_BUTTONS_HORIZ;
2316
2317       gd_xoffset = ED_CTRL1_BUTTONS_XPOS + x * ED_CTRL1_BUTTON_XSIZE;
2318       gd_yoffset = ED_CTRL1_BUTTONS_YPOS + y * ED_CTRL1_BUTTON_YSIZE;
2319       width = ED_CTRL1_BUTTON_XSIZE;
2320       height = ED_CTRL1_BUTTON_YSIZE;
2321     }
2322     else
2323     {
2324       int x = (i - ED_NUM_CTRL1_BUTTONS) % ED_CTRL2_BUTTONS_HORIZ;
2325       int y = (i - ED_NUM_CTRL1_BUTTONS) / ED_CTRL2_BUTTONS_HORIZ;
2326
2327       gd_xoffset = ED_CTRL2_BUTTONS_XPOS + x * ED_CTRL2_BUTTON_XSIZE;
2328       gd_yoffset = ED_CTRL2_BUTTONS_YPOS + y * ED_CTRL2_BUTTON_YSIZE;
2329       width = ED_CTRL2_BUTTON_XSIZE;
2330       height = ED_CTRL2_BUTTON_YSIZE;
2331     }
2332
2333     gd_x1 = DOOR_GFX_PAGEX8 + gd_xoffset;
2334     gd_x2 = DOOR_GFX_PAGEX7 + gd_xoffset;
2335     gd_y1  = DOOR_GFX_PAGEY1 + ED_CTRL_BUTTONS_GFX_YPOS + gd_yoffset;
2336     gd_y2  = DOOR_GFX_PAGEY1 + ED_CTRL_BUTTONS_ALT_GFX_YPOS + gd_yoffset;
2337
2338     gi = CreateGadget(GDI_CUSTOM_ID, id,
2339                       GDI_CUSTOM_TYPE_ID, i,
2340                       GDI_INFO_TEXT, control_info[i].text,
2341                       GDI_X, EX + gd_xoffset,
2342                       GDI_Y, EY + gd_yoffset,
2343                       GDI_WIDTH, width,
2344                       GDI_HEIGHT, height,
2345                       GDI_TYPE, button_type,
2346                       GDI_STATE, GD_BUTTON_UNPRESSED,
2347                       GDI_RADIO_NR, radio_button_nr,
2348                       GDI_CHECKED, checked,
2349                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2350                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
2351                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
2352                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
2353                       GDI_EVENT_MASK, event_mask,
2354                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2355                       GDI_CALLBACK_ACTION, HandleControlButtons,
2356                       GDI_END);
2357
2358     if (gi == NULL)
2359       Error(ERR_EXIT, "cannot create gadget");
2360
2361     level_editor_gadget[id] = gi;
2362   }
2363
2364   /* create buttons for scrolling of drawing area and element list */
2365   for (i=0; i<ED_NUM_SCROLLBUTTONS; i++)
2366   {
2367     int id = scrollbutton_info[i].gadget_id;
2368     int x, y, width, height;
2369     int gd_x1, gd_x2, gd_y1, gd_y2;
2370
2371     x = scrollbutton_info[i].x;
2372     y = scrollbutton_info[i].y;
2373
2374     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
2375
2376     if (id == GADGET_ID_SCROLL_LIST_UP ||
2377         id == GADGET_ID_SCROLL_LIST_DOWN)
2378     {
2379       x += DX;
2380       y += DY;
2381       width = ED_SCROLLBUTTON2_XSIZE;
2382       height = ED_SCROLLBUTTON2_YSIZE;
2383       gd_x1 = DOOR_GFX_PAGEX8 + scrollbutton_info[i].gd_x;
2384       gd_y1 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].gd_y;
2385       gd_x2 = gd_x1 - ED_SCROLLBUTTON2_XSIZE;
2386       gd_y2 = gd_y1;
2387     }
2388     else
2389     {
2390       x += SX;
2391       y += SY;
2392       width = ED_SCROLLBUTTON_XSIZE;
2393       height = ED_SCROLLBUTTON_YSIZE;
2394       gd_x1 = DOOR_GFX_PAGEX8 + scrollbutton_info[i].gd_x;
2395       gd_y1 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].gd_y;
2396       gd_x2 = gd_x1 - ED_SCROLLBUTTON_XSIZE;
2397       gd_y2 = gd_y1;
2398     }
2399
2400     gi = CreateGadget(GDI_CUSTOM_ID, id,
2401                       GDI_CUSTOM_TYPE_ID, i,
2402                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
2403                       GDI_X, x,
2404                       GDI_Y, y,
2405                       GDI_WIDTH, width,
2406                       GDI_HEIGHT, height,
2407                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2408                       GDI_STATE, GD_BUTTON_UNPRESSED,
2409                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2410                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
2411                       GDI_EVENT_MASK, event_mask,
2412                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2413                       GDI_CALLBACK_ACTION, HandleControlButtons,
2414                       GDI_END);
2415
2416     if (gi == NULL)
2417       Error(ERR_EXIT, "cannot create gadget");
2418
2419     level_editor_gadget[id] = gi;
2420   }
2421
2422   /* create buttons for element list */
2423   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
2424   {
2425     Bitmap *deco_bitmap;
2426     int deco_x, deco_y, deco_xpos, deco_ypos;
2427     int gd_xoffset, gd_yoffset;
2428     int gd_x1, gd_x2, gd_y;
2429     int x = i % ED_ELEMENTLIST_BUTTONS_HORIZ;
2430     int y = i / ED_ELEMENTLIST_BUTTONS_HORIZ;
2431     int id = GADGET_ID_ELEMENTLIST_FIRST + i;
2432     int element = editor_elements[i];
2433
2434     event_mask = GD_EVENT_RELEASED;
2435
2436     gd_xoffset = ED_ELEMENTLIST_XPOS + x * ED_ELEMENTLIST_XSIZE;
2437     gd_yoffset = ED_ELEMENTLIST_YPOS + y * ED_ELEMENTLIST_YSIZE;
2438
2439     gd_x1 = DOOR_GFX_PAGEX6 + ED_ELEMENTLIST_XPOS + ED_ELEMENTLIST_XSIZE;
2440     gd_x2 = DOOR_GFX_PAGEX6 + ED_ELEMENTLIST_XPOS;
2441     gd_y  = DOOR_GFX_PAGEY1 + ED_ELEMENTLIST_YPOS;
2442
2443     getMiniGraphicSource(el2edimg(element), &deco_bitmap, &deco_x, &deco_y);
2444     deco_xpos = (ED_ELEMENTLIST_XSIZE - MINI_TILEX) / 2;
2445     deco_ypos = (ED_ELEMENTLIST_YSIZE - MINI_TILEY) / 2;
2446
2447     gi = CreateGadget(GDI_CUSTOM_ID, id,
2448                       GDI_CUSTOM_TYPE_ID, i,
2449                       GDI_INFO_TEXT, getElementInfoText(element),
2450                       GDI_X, DX + gd_xoffset,
2451                       GDI_Y, DY + gd_yoffset,
2452                       GDI_WIDTH, ED_ELEMENTLIST_XSIZE,
2453                       GDI_HEIGHT, ED_ELEMENTLIST_YSIZE,
2454                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2455                       GDI_STATE, GD_BUTTON_UNPRESSED,
2456                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2457                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2458                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2459                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2460                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2461                       GDI_DECORATION_SHIFTING, 1, 1,
2462                       GDI_EVENT_MASK, event_mask,
2463                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2464                       GDI_CALLBACK_ACTION, HandleControlButtons,
2465                       GDI_END);
2466
2467     if (gi == NULL)
2468       Error(ERR_EXIT, "cannot create gadget");
2469
2470     level_editor_gadget[id] = gi;
2471   }
2472 }
2473
2474 static void CreateCounterButtons()
2475 {
2476   int max_infotext_len = getMaxInfoTextLength();
2477   int i;
2478
2479   for (i=0; i<ED_NUM_COUNTERBUTTONS; i++)
2480   {
2481     int j;
2482     int xpos = SX + counterbutton_info[i].x;    /* xpos of down count button */
2483     int ypos = SY + counterbutton_info[i].y;
2484     int xoffset = 0;
2485
2486     /* determine horizontal offset for leading counter text */
2487     if (counterbutton_info[i].text_left != NULL)
2488       xoffset = (getFontWidth(FONT_TEXT_1) *
2489                  strlen(counterbutton_info[i].text_left) +
2490                  ED_GADGET_TEXT_DISTANCE);
2491
2492     for (j=0; j<2; j++)
2493     {
2494       Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2495       struct GadgetInfo *gi;
2496       int id = (j == 0 ?
2497                 counterbutton_info[i].gadget_id_down :
2498                 counterbutton_info[i].gadget_id_up);
2499       int gd_xoffset;
2500       int gd_x, gd_x1, gd_x2, gd_y;
2501       int x_size, y_size;
2502       unsigned long event_mask;
2503       char infotext[max_infotext_len + 1];
2504
2505       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
2506
2507       if (i == ED_COUNTER_ID_SELECT_LEVEL)
2508       {
2509         int sid = (j == 0 ?
2510                    ED_SCROLLBUTTON_ID_AREA_LEFT :
2511                    ED_SCROLLBUTTON_ID_AREA_RIGHT);
2512
2513         event_mask |= GD_EVENT_RELEASED;
2514
2515         if (j == 1)
2516           xpos += 2 * ED_GADGET_DISTANCE;
2517         ypos += ED_GADGET_DISTANCE;
2518
2519         gd_x1 = DOOR_GFX_PAGEX8 + scrollbutton_info[sid].gd_x;
2520         gd_x2 = gd_x1 - ED_SCROLLBUTTON_XSIZE;
2521         gd_y  = DOOR_GFX_PAGEY1 + scrollbutton_info[sid].gd_y;
2522         x_size = ED_SCROLLBUTTON_XSIZE;
2523         y_size = ED_SCROLLBUTTON_YSIZE;
2524       }
2525       else
2526       {
2527         gd_xoffset = (j == 0 ? ED_BUTTON_MINUS_XPOS : ED_BUTTON_PLUS_XPOS);
2528         gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2529         gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2530         gd_y  = DOOR_GFX_PAGEY1 + ED_BUTTON_COUNT_YPOS;
2531         x_size = ED_BUTTON_COUNT_XSIZE;
2532         y_size = ED_BUTTON_COUNT_YSIZE;
2533       }
2534
2535       sprintf(infotext, "%s counter value by 1, 5 or 10",
2536               (j == 0 ? "decrease" : "increase"));
2537
2538       gi = CreateGadget(GDI_CUSTOM_ID, id,
2539                         GDI_CUSTOM_TYPE_ID, i,
2540                         GDI_INFO_TEXT, infotext,
2541                         GDI_X, xpos + xoffset,
2542                         GDI_Y, ypos,
2543                         GDI_WIDTH, x_size,
2544                         GDI_HEIGHT, y_size,
2545                         GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2546                         GDI_STATE, GD_BUTTON_UNPRESSED,
2547                         GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2548                         GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2549                         GDI_EVENT_MASK, event_mask,
2550                         GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2551                         GDI_CALLBACK_ACTION, HandleCounterButtons,
2552                         GDI_END);
2553
2554       if (gi == NULL)
2555         Error(ERR_EXIT, "cannot create gadget");
2556
2557       level_editor_gadget[id] = gi;
2558       xpos += gi->width + ED_GADGET_DISTANCE;   /* xpos of text count button */
2559
2560       if (j == 0)
2561       {
2562         int font_type = FONT_INPUT_1;
2563         int font_type_active = FONT_INPUT_1_ACTIVE;
2564         int gd_width = ED_WIN_COUNT_XSIZE;
2565
2566         id = counterbutton_info[i].gadget_id_text;
2567         event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
2568
2569         if (i == ED_COUNTER_ID_SELECT_LEVEL)
2570         {
2571           font_type = FONT_LEVEL_NUMBER;
2572           font_type_active = FONT_LEVEL_NUMBER;
2573
2574           xpos += 2 * ED_GADGET_DISTANCE;
2575           ypos -= ED_GADGET_DISTANCE;
2576
2577           gd_x = DOOR_GFX_PAGEX6 + ED_WIN_COUNT2_XPOS;
2578           gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT2_YPOS;
2579           gd_width = ED_WIN_COUNT2_XSIZE;
2580         }
2581         else
2582         {
2583           gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
2584           gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
2585         }
2586
2587         gi = CreateGadget(GDI_CUSTOM_ID, id,
2588                           GDI_CUSTOM_TYPE_ID, i,
2589                           GDI_INFO_TEXT, "enter counter value",
2590                           GDI_X, xpos + xoffset,
2591                           GDI_Y, ypos,
2592                           GDI_TYPE, GD_TYPE_TEXTINPUT_NUMERIC,
2593                           GDI_NUMBER_VALUE, 0,
2594                           GDI_NUMBER_MIN, counterbutton_info[i].min_value,
2595                           GDI_NUMBER_MAX, counterbutton_info[i].max_value,
2596                           GDI_TEXT_SIZE, 3,
2597                           GDI_TEXT_FONT, font_type,
2598                           GDI_TEXT_FONT_ACTIVE, font_type_active,
2599                           GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x, gd_y,
2600                           GDI_DESIGN_PRESSED, gd_bitmap, gd_x, gd_y,
2601                           GDI_BORDER_SIZE, ED_BORDER_SIZE, ED_BORDER_SIZE,
2602                           GDI_DESIGN_WIDTH, gd_width,
2603                           GDI_EVENT_MASK, event_mask,
2604                           GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2605                           GDI_CALLBACK_ACTION, HandleCounterButtons,
2606                           GDI_END);
2607
2608         if (gi == NULL)
2609           Error(ERR_EXIT, "cannot create gadget");
2610
2611         level_editor_gadget[id] = gi;
2612         xpos += gi->width + ED_GADGET_DISTANCE; /* xpos of up count button */
2613       }
2614     }
2615   }
2616 }
2617
2618 static void CreateDrawingAreas()
2619 {
2620   struct GadgetInfo *gi;
2621   unsigned long event_mask;
2622   int id;
2623   int i;
2624
2625   event_mask =
2626     GD_EVENT_PRESSED | GD_EVENT_RELEASED | GD_EVENT_MOVING |
2627     GD_EVENT_OFF_BORDERS;
2628
2629   /* one for the level drawing area ... */
2630   id = GADGET_ID_DRAWING_LEVEL;
2631   gi = CreateGadget(GDI_CUSTOM_ID, id,
2632                     GDI_X, SX,
2633                     GDI_Y, SY,
2634                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
2635                     GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
2636                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2637                     GDI_EVENT_MASK, event_mask,
2638                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2639                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
2640                     GDI_END);
2641
2642   if (gi == NULL)
2643     Error(ERR_EXIT, "cannot create gadget");
2644
2645   level_editor_gadget[id] = gi;
2646
2647   /* ... up to eight areas for element content ... */
2648   for (i=0; i<MAX_ELEMENT_CONTENTS; i++)
2649   {
2650     int gx = SX + ED_AREA_ELEM_CONTENT_XPOS + 5 * (i % 4) * MINI_TILEX;
2651     int gy = SX + ED_AREA_ELEM_CONTENT_YPOS + 6 * (i / 4) * MINI_TILEY;
2652
2653     id = GADGET_ID_ELEMENT_CONTENT_0 + i;
2654     gi = CreateGadget(GDI_CUSTOM_ID, id,
2655                       GDI_CUSTOM_TYPE_ID, i,
2656                       GDI_X, gx,
2657                       GDI_Y, gy,
2658                       GDI_WIDTH, 3 * MINI_TILEX,
2659                       GDI_HEIGHT, 3 * MINI_TILEY,
2660                       GDI_TYPE, GD_TYPE_DRAWING_AREA,
2661                       GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2662                       GDI_EVENT_MASK, event_mask,
2663                       GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2664                       GDI_CALLBACK_ACTION, HandleDrawingAreas,
2665                       GDI_END);
2666
2667     if (gi == NULL)
2668       Error(ERR_EXIT, "cannot create gadget");
2669
2670     level_editor_gadget[id] = gi;
2671   }
2672
2673   /* ... one for the amoeba content ... */
2674   id = GADGET_ID_AMOEBA_CONTENT;
2675   gi = CreateGadget(GDI_CUSTOM_ID, id,
2676                     GDI_X, SX + ED_AREA_ELEM_CONTENT_XPOS,
2677                     GDI_Y, SY + ED_AREA_ELEM_CONTENT_YPOS,
2678                     GDI_WIDTH, MINI_TILEX,
2679                     GDI_HEIGHT, MINI_TILEY,
2680                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
2681                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2682                     GDI_EVENT_MASK, event_mask,
2683                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2684                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
2685                     GDI_END);
2686
2687   if (gi == NULL)
2688     Error(ERR_EXIT, "cannot create gadget");
2689
2690   level_editor_gadget[id] = gi;
2691
2692   /* ... one for each custom element optional graphic element ... */
2693   id = GADGET_ID_CUSTOM_GRAPHIC;
2694   gi = CreateGadget(GDI_CUSTOM_ID, id,
2695                     GDI_X, SX + ED_AREA_ELEM_CONTENT3_XPOS,
2696                     GDI_Y, SY + ED_AREA_ELEM_CONTENT3_YPOS,
2697                     GDI_WIDTH, MINI_TILEX,
2698                     GDI_HEIGHT, MINI_TILEY,
2699                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
2700                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2701                     GDI_EVENT_MASK, event_mask,
2702                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2703                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
2704                     GDI_END);
2705
2706   if (gi == NULL)
2707     Error(ERR_EXIT, "cannot create gadget");
2708
2709   level_editor_gadget[id] = gi;
2710
2711   /* ... one areas for custom element explosion content ... */
2712   id = GADGET_ID_CUSTOM_CONTENT;
2713   gi = CreateGadget(GDI_CUSTOM_ID, id,
2714                     GDI_CUSTOM_TYPE_ID, i,
2715                     GDI_X, SX + ED_AREA_ELEM_CONTENT4_XPOS,
2716                     GDI_Y, SX + ED_AREA_ELEM_CONTENT4_YPOS,
2717                     GDI_WIDTH, 3 * MINI_TILEX,
2718                     GDI_HEIGHT, 3 * MINI_TILEY,
2719                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
2720                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2721                     GDI_EVENT_MASK, event_mask,
2722                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2723                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
2724                     GDI_END);
2725
2726   if (gi == NULL)
2727     Error(ERR_EXIT, "cannot create gadget");
2728
2729   level_editor_gadget[id] = gi;
2730
2731   /* ... one for each custom element change target element ... */
2732   id = GADGET_ID_CUSTOM_CHANGED;
2733   gi = CreateGadget(GDI_CUSTOM_ID, id,
2734                     GDI_X, SX + ED_AREA_ELEM_CONTENT2_XPOS,
2735                     GDI_Y, SY + ED_AREA_ELEM_CONTENT2_YPOS,
2736                     GDI_WIDTH, MINI_TILEX,
2737                     GDI_HEIGHT, MINI_TILEY,
2738                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
2739                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2740                     GDI_EVENT_MASK, event_mask,
2741                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2742                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
2743                     GDI_END);
2744
2745   if (gi == NULL)
2746     Error(ERR_EXIT, "cannot create gadget");
2747
2748   level_editor_gadget[id] = gi;
2749
2750   /* ... and one for random placement background restrictions */
2751
2752   id = GADGET_ID_RANDOM_BACKGROUND;
2753   gi = CreateGadget(GDI_CUSTOM_ID, id,
2754                     GDI_X, SX + ED_AREA_RANDOM_BACKGROUND_XPOS,
2755                     GDI_Y, SY + ED_AREA_RANDOM_BACKGROUND_YPOS,
2756                     GDI_WIDTH, MINI_TILEX,
2757                     GDI_HEIGHT, MINI_TILEY,
2758                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
2759                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
2760                     GDI_EVENT_MASK, event_mask,
2761                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
2762                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
2763                     GDI_END);
2764
2765   if (gi == NULL)
2766     Error(ERR_EXIT, "cannot create gadget");
2767
2768   level_editor_gadget[id] = gi;
2769 }
2770
2771 static void CreateTextInputGadgets()
2772 {
2773   int max_infotext_len = getMaxInfoTextLength();
2774   int i;
2775
2776   for (i=0; i<ED_NUM_TEXTINPUT; i++)
2777   {
2778     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2779     int gd_x, gd_y;
2780     struct GadgetInfo *gi;
2781     unsigned long event_mask;
2782     char infotext[MAX_OUTPUT_LINESIZE + 1];
2783     int id = textinput_info[i].gadget_id;
2784
2785     event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
2786
2787     gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
2788     gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
2789
2790     sprintf(infotext, "Enter %s", textinput_info[i].infotext);
2791     infotext[max_infotext_len] = '\0';
2792
2793     gi = CreateGadget(GDI_CUSTOM_ID, id,
2794                       GDI_CUSTOM_TYPE_ID, i,
2795                       GDI_INFO_TEXT, infotext,
2796                       GDI_X, SX + textinput_info[i].x,
2797                       GDI_Y, SY + textinput_info[i].y,
2798                       GDI_TYPE, GD_TYPE_TEXTINPUT_ALPHANUMERIC,
2799                       GDI_TEXT_VALUE, textinput_info[i].value,
2800                       GDI_TEXT_SIZE, textinput_info[i].size,
2801                       GDI_TEXT_FONT, FONT_INPUT_1,
2802                       GDI_TEXT_FONT_ACTIVE, FONT_INPUT_1_ACTIVE,
2803                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x, gd_y,
2804                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x, gd_y,
2805                       GDI_BORDER_SIZE, ED_BORDER_SIZE, ED_BORDER_SIZE,
2806                       GDI_DESIGN_WIDTH, ED_WIN_COUNT_XSIZE,
2807                       GDI_EVENT_MASK, event_mask,
2808                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2809                       GDI_CALLBACK_ACTION, HandleTextInputGadgets,
2810                       GDI_END);
2811
2812     if (gi == NULL)
2813       Error(ERR_EXIT, "cannot create gadget");
2814
2815     level_editor_gadget[id] = gi;
2816   }
2817 }
2818
2819 static void CreateSelectboxGadgets()
2820 {
2821   int max_infotext_len = getMaxInfoTextLength();
2822   int i, j;
2823
2824   for (i=0; i<ED_NUM_SELECTBOX; i++)
2825   {
2826     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2827     int gd_x, gd_y;
2828     int xoffset = 0;
2829     struct GadgetInfo *gi;
2830     unsigned long event_mask;
2831     char infotext[MAX_OUTPUT_LINESIZE + 1];
2832     int id = selectbox_info[i].gadget_id;
2833
2834     if (selectbox_info[i].size == -1)   /* dynamically determine size */
2835     {
2836       /* (we cannot use -1 for uninitialized values if we directly compare
2837          with results from strlen(), because the '<' and '>' operation will
2838          implicitely cast -1 to an unsigned integer value!) */
2839       selectbox_info[i].size = 0;
2840
2841       for (j=0; selectbox_info[i].options[j].text != NULL; j++)
2842         if (strlen(selectbox_info[i].options[j].text) > selectbox_info[i].size)
2843           selectbox_info[i].size = strlen(selectbox_info[i].options[j].text);
2844
2845       selectbox_info[i].size++;         /* add one character empty space */
2846     }
2847
2848     event_mask = GD_EVENT_RELEASED |
2849       GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
2850
2851     gd_x = DOOR_GFX_PAGEX4 + ED_SELECTBOX_XPOS;
2852     gd_y = DOOR_GFX_PAGEY1 + ED_SELECTBOX_YPOS;
2853
2854     /* determine horizontal offset for leading selectbox text */
2855     if (selectbox_info[i].text_left != NULL)
2856       xoffset = (getFontWidth(FONT_TEXT_1) *
2857                  strlen(selectbox_info[i].text_left) +
2858                  ED_GADGET_TEXT_DISTANCE);
2859
2860     sprintf(infotext, "Select %s", selectbox_info[i].infotext);
2861     infotext[max_infotext_len] = '\0';
2862
2863     gi = CreateGadget(GDI_CUSTOM_ID, id,
2864                       GDI_CUSTOM_TYPE_ID, i,
2865                       GDI_INFO_TEXT, infotext,
2866                       GDI_X, SX + selectbox_info[i].x + xoffset,
2867                       GDI_Y, SY + selectbox_info[i].y,
2868                       GDI_TYPE, GD_TYPE_SELECTBOX,
2869                       GDI_SELECTBOX_OPTIONS, selectbox_info[i].options,
2870                       GDI_TEXT_SIZE, selectbox_info[i].size,
2871                       GDI_TEXT_FONT, FONT_INPUT_1,
2872                       GDI_TEXT_FONT_ACTIVE, FONT_INPUT_1_ACTIVE,
2873                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x, gd_y,
2874                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x, gd_y,
2875                       GDI_BORDER_SIZE, ED_BORDER_SIZE, ED_BORDER_SIZE,
2876                       GDI_BORDER_SIZE_SELECTBUTTON, getFontWidth(FONT_INPUT_1),
2877                       GDI_DESIGN_WIDTH, ED_WIN_COUNT_XSIZE,
2878                       GDI_EVENT_MASK, event_mask,
2879                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2880                       GDI_CALLBACK_ACTION, HandleSelectboxGadgets,
2881                       GDI_END);
2882
2883     if (gi == NULL)
2884       Error(ERR_EXIT, "cannot create gadget");
2885
2886     level_editor_gadget[id] = gi;
2887   }
2888 }
2889
2890 static void CreateTextbuttonGadgets()
2891 {
2892   int max_infotext_len = getMaxInfoTextLength();
2893   int i;
2894
2895   for (i=0; i<ED_NUM_TEXTBUTTON; i++)
2896   {
2897     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2898     int gd_x1, gd_x2, gd_y1, gd_y2;
2899     struct GadgetInfo *gi;
2900     unsigned long event_mask;
2901     char infotext[MAX_OUTPUT_LINESIZE + 1];
2902     int id = textbutton_info[i].gadget_id;
2903
2904     if (textbutton_info[i].size == -1)  /* dynamically determine size */
2905       textbutton_info[i].size = strlen(textbutton_info[i].text);
2906
2907     event_mask = GD_EVENT_RELEASED;
2908
2909     if (id >= GADGET_ID_PROPERTIES_INFO && id <= GADGET_ID_PROPERTIES_ADVANCED)
2910     {
2911       gd_x1 = DOOR_GFX_PAGEX4 + ED_TEXTBUTTON_TAB_XPOS;
2912       gd_x2 = DOOR_GFX_PAGEX3 + ED_TEXTBUTTON_TAB_XPOS;
2913       gd_y1 = DOOR_GFX_PAGEY1 + ED_TEXTBUTTON_TAB_YPOS;
2914       gd_y2 = DOOR_GFX_PAGEY1 + ED_TEXTBUTTON_TAB_INACTIVE_YPOS;
2915     }
2916     else
2917     {
2918       gd_x1 = DOOR_GFX_PAGEX4 + ED_TEXTBUTTON_XPOS;
2919       gd_x2 = DOOR_GFX_PAGEX3 + ED_TEXTBUTTON_XPOS;
2920       gd_y1 = DOOR_GFX_PAGEY1 + ED_TEXTBUTTON_YPOS;
2921       gd_y2 = DOOR_GFX_PAGEY1 + ED_TEXTBUTTON_INACTIVE_YPOS;
2922     }
2923
2924     sprintf(infotext, "%s", textbutton_info[i].infotext);
2925     infotext[max_infotext_len] = '\0';
2926
2927     gi = CreateGadget(GDI_CUSTOM_ID, id,
2928                       GDI_CUSTOM_TYPE_ID, i,
2929                       GDI_INFO_TEXT, infotext,
2930                       GDI_X, SX + textbutton_info[i].x,
2931                       GDI_Y, SY + textbutton_info[i].y,
2932                       GDI_TYPE, GD_TYPE_TEXT_BUTTON,
2933                       GDI_TEXT_VALUE, textbutton_info[i].text,
2934                       GDI_TEXT_SIZE, textbutton_info[i].size,
2935                       GDI_TEXT_FONT, FONT_INPUT_2_ACTIVE,
2936                       GDI_TEXT_FONT_ACTIVE, FONT_INPUT_2,
2937                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2938                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
2939                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
2940                       GDI_BORDER_SIZE, ED_BORDER2_SIZE, ED_BORDER_SIZE,
2941                       GDI_DESIGN_WIDTH, ED_WIN_COUNT_XSIZE,
2942                       GDI_DECORATION_SHIFTING, 1, 1,
2943                       GDI_EVENT_MASK, event_mask,
2944                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
2945                       GDI_CALLBACK_ACTION, HandleTextbuttonGadgets,
2946                       GDI_END);
2947
2948     if (gi == NULL)
2949       Error(ERR_EXIT, "cannot create gadget");
2950
2951     level_editor_gadget[id] = gi;
2952   }
2953 }
2954
2955 static void CreateScrollbarGadgets()
2956 {
2957   int i;
2958
2959   for (i=0; i<ED_NUM_SCROLLBARS; i++)
2960   {
2961     int id = scrollbar_info[i].gadget_id;
2962     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2963     int gd_x1, gd_x2, gd_y1, gd_y2;
2964     struct GadgetInfo *gi;
2965     int items_max, items_visible, item_position;
2966     unsigned long event_mask;
2967
2968     if (i == ED_SCROLLBAR_ID_LIST_VERTICAL)
2969     {
2970       items_max = num_editor_elements / ED_ELEMENTLIST_BUTTONS_HORIZ;
2971       items_visible = ED_ELEMENTLIST_BUTTONS_VERT;
2972       item_position = 0;
2973     }
2974     else        /* drawing area scrollbars */
2975     {
2976       if (scrollbar_info[i].type == GD_TYPE_SCROLLBAR_HORIZONTAL)
2977       {
2978         items_max = MAX(lev_fieldx + 2, ed_fieldx);
2979         items_visible = ed_fieldx;
2980         item_position = 0;
2981       }
2982       else
2983       {
2984         items_max = MAX(lev_fieldy + 2, ed_fieldy);
2985         items_visible = ed_fieldy;
2986         item_position = 0;
2987       }
2988     }
2989
2990     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
2991
2992     gd_x1 = DOOR_GFX_PAGEX8 + scrollbar_info[i].gd_x;
2993     gd_x2 = (gd_x1 - (scrollbar_info[i].type == GD_TYPE_SCROLLBAR_HORIZONTAL ?
2994                       scrollbar_info[i].height : scrollbar_info[i].width));
2995     gd_y1 = DOOR_GFX_PAGEY1 + scrollbar_info[i].gd_y;
2996     gd_y2 = DOOR_GFX_PAGEY1 + scrollbar_info[i].gd_y;
2997
2998     gi = CreateGadget(GDI_CUSTOM_ID, id,
2999                       GDI_CUSTOM_TYPE_ID, i,
3000                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
3001                       GDI_X, scrollbar_info[i].x,
3002                       GDI_Y, scrollbar_info[i].y,
3003                       GDI_WIDTH, scrollbar_info[i].width,
3004                       GDI_HEIGHT, scrollbar_info[i].height,
3005                       GDI_TYPE, scrollbar_info[i].type,
3006                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
3007                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
3008                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
3009                       GDI_STATE, GD_BUTTON_UNPRESSED,
3010                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
3011                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
3012                       GDI_BORDER_SIZE, ED_BORDER_SIZE, ED_BORDER_SIZE,
3013                       GDI_EVENT_MASK, event_mask,
3014                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
3015                       GDI_CALLBACK_ACTION, HandleControlButtons,
3016                       GDI_END);
3017
3018     if (gi == NULL)
3019       Error(ERR_EXIT, "cannot create gadget");
3020
3021     level_editor_gadget[id] = gi;
3022   }
3023 }
3024
3025 static void CreateCheckbuttonGadgets()
3026 {
3027   Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
3028   struct GadgetInfo *gi;
3029   unsigned long event_mask;
3030   int gd_x1, gd_x2, gd_x3, gd_x4, gd_y;
3031   boolean checked;
3032   int i;
3033
3034   event_mask = GD_EVENT_PRESSED;
3035
3036   gd_x1 = DOOR_GFX_PAGEX4 + ED_CHECKBUTTON_UNCHECKED_XPOS;
3037   gd_x2 = DOOR_GFX_PAGEX3 + ED_CHECKBUTTON_UNCHECKED_XPOS;
3038   gd_x3 = DOOR_GFX_PAGEX4 + ED_CHECKBUTTON_CHECKED_XPOS;
3039   gd_x4 = DOOR_GFX_PAGEX3 + ED_CHECKBUTTON_CHECKED_XPOS;
3040   gd_y  = DOOR_GFX_PAGEY1 + ED_RADIOBUTTON_YPOS;
3041
3042   for (i=0; i<ED_NUM_RADIOBUTTONS; i++)
3043   {
3044     int id = radiobutton_info[i].gadget_id;
3045
3046     checked =
3047       (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
3048
3049     gi = CreateGadget(GDI_CUSTOM_ID, id,
3050                       GDI_CUSTOM_TYPE_ID, i,
3051                       GDI_INFO_TEXT, radiobutton_info[i].infotext,
3052                       GDI_X, SX + radiobutton_info[i].x,
3053                       GDI_Y, SY + radiobutton_info[i].y,
3054                       GDI_WIDTH, ED_CHECKBUTTON_XSIZE,
3055                       GDI_HEIGHT, ED_CHECKBUTTON_YSIZE,
3056                       GDI_TYPE, GD_TYPE_RADIO_BUTTON,
3057                       GDI_RADIO_NR, radiobutton_info[i].radio_button_nr,
3058                       GDI_CHECKED, checked,
3059                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
3060                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
3061                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x3, gd_y,
3062                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x4, gd_y,
3063                       GDI_EVENT_MASK, event_mask,
3064                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
3065                       GDI_CALLBACK_ACTION, HandleRadiobuttons,
3066                       GDI_END);
3067
3068     if (gi == NULL)
3069       Error(ERR_EXIT, "cannot create gadget");
3070
3071     level_editor_gadget[id] = gi;
3072   }
3073
3074   for (i=0; i<ED_NUM_CHECKBUTTONS; i++)
3075   {
3076     int id = checkbutton_info[i].gadget_id;
3077
3078     if (id == GADGET_ID_STICK_ELEMENT)
3079       gd_y  = DOOR_GFX_PAGEY1 + ED_STICKYBUTTON_YPOS;
3080     else
3081       gd_y  = DOOR_GFX_PAGEY1 + ED_CHECKBUTTON_YPOS;
3082
3083     gi = CreateGadget(GDI_CUSTOM_ID, id,
3084                       GDI_CUSTOM_TYPE_ID, i,
3085                       GDI_INFO_TEXT, checkbutton_info[i].infotext,
3086                       GDI_X, SX + checkbutton_info[i].x,
3087                       GDI_Y, SY + checkbutton_info[i].y,
3088                       GDI_WIDTH, ED_CHECKBUTTON_XSIZE,
3089                       GDI_HEIGHT, ED_CHECKBUTTON_YSIZE,
3090                       GDI_TYPE, GD_TYPE_CHECK_BUTTON,
3091                       GDI_CHECKED, *checkbutton_info[i].value,
3092                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
3093                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
3094                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x3, gd_y,
3095                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x4, gd_y,
3096                       GDI_EVENT_MASK, event_mask,
3097                       GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
3098                       GDI_CALLBACK_ACTION, HandleCheckbuttons,
3099                       GDI_END);
3100
3101     if (gi == NULL)
3102       Error(ERR_EXIT, "cannot create gadget");
3103
3104     level_editor_gadget[id] = gi;
3105   }
3106 }
3107
3108 void CreateLevelEditorGadgets()
3109 {
3110   int old_game_status = game_status;
3111
3112   /* setting 'game_status' is needed to get the right fonts for the editor */
3113   game_status = GAME_MODE_EDITOR;
3114
3115   ReinitializeElementList();
3116
3117   CreateControlButtons();
3118   CreateCounterButtons();
3119   CreateDrawingAreas();
3120   CreateTextInputGadgets();
3121   CreateSelectboxGadgets();
3122   CreateTextbuttonGadgets();
3123   CreateScrollbarGadgets();
3124   CreateCheckbuttonGadgets();
3125
3126   game_status = old_game_status;
3127 }
3128
3129 void FreeLevelEditorGadgets()
3130 {
3131   int i;
3132
3133   for (i=0; i<NUM_EDITOR_GADGETS; i++)
3134     FreeGadget(level_editor_gadget[i]);
3135 }
3136
3137 static void MapCounterButtons(int id)
3138 {
3139   char infotext[MAX_OUTPUT_LINESIZE + 1];
3140   int max_infotext_len = getMaxInfoTextLength();
3141   int xoffset_above = 0;
3142   int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
3143   int xoffset_left = 0;
3144   int yoffset_left = ED_BORDER_SIZE;
3145   int xoffset_right = getCounterGadgetWidth();
3146   int yoffset_right = ED_BORDER_SIZE;
3147   int x, y;
3148
3149   if (counterbutton_info[id].text_above)
3150   {
3151     x = SX + counterbutton_info[id].x + xoffset_above;
3152     y = SY + counterbutton_info[id].y + yoffset_above;
3153
3154     sprintf(infotext, "%s:", counterbutton_info[id].text_above);
3155     infotext[max_infotext_len] = '\0';
3156     DrawText(x, y, infotext, FONT_TEXT_1);
3157   }
3158
3159   if (counterbutton_info[id].text_left)
3160   {
3161     x = SX + counterbutton_info[id].x + xoffset_left;
3162     y = SY + counterbutton_info[id].y + yoffset_left;
3163
3164     sprintf(infotext, "%s", counterbutton_info[id].text_left);
3165     infotext[max_infotext_len] = '\0';
3166     DrawText(x, y, infotext, FONT_TEXT_1);
3167   }
3168
3169   if (counterbutton_info[id].text_right)
3170   {
3171     int gadget_id = counterbutton_info[id].gadget_id_down;
3172
3173     x = level_editor_gadget[gadget_id]->x + xoffset_right;
3174     y = SY + counterbutton_info[id].y + yoffset_right;
3175
3176     sprintf(infotext, "%s", counterbutton_info[id].text_right);
3177     infotext[max_infotext_len] = '\0';
3178     DrawText(x, y, infotext, FONT_TEXT_1);
3179   }
3180
3181   ModifyEditorCounter(id, *counterbutton_info[id].value);
3182
3183   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_down]);
3184   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_text]);
3185   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_up]);
3186 }
3187
3188 static void MapControlButtons()
3189 {
3190   int counter_id;
3191   int i;
3192
3193   /* map toolbox buttons */
3194   for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
3195     MapGadget(level_editor_gadget[i]);
3196
3197   /* map buttons to select elements */
3198   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
3199     MapGadget(level_editor_gadget[GADGET_ID_ELEMENTLIST_FIRST + i]);
3200   MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL]);
3201   MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_UP]);
3202   MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_DOWN]);
3203
3204   /* map buttons to select level */
3205   counter_id = ED_COUNTER_ID_SELECT_LEVEL;
3206   ModifyEditorCounterLimits(counter_id,
3207                             leveldir_current->first_level,
3208                             leveldir_current->last_level);
3209   MapCounterButtons(counter_id);
3210 }
3211
3212 static void MapDrawingArea(int id)
3213 {
3214   MapGadget(level_editor_gadget[id]);
3215 }
3216
3217 static void MapTextInputGadget(int id)
3218 {
3219   char infotext[MAX_OUTPUT_LINESIZE + 1];
3220   int max_infotext_len = getMaxInfoTextLength();
3221   int xoffset_above = 0;
3222   int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
3223   int x = textinput_info[id].x + xoffset_above;
3224   int y = textinput_info[id].y + yoffset_above;
3225
3226   sprintf(infotext, "%s:", textinput_info[id].infotext);
3227   infotext[max_infotext_len] = '\0';
3228   DrawTextF(x, y, FONT_TEXT_1, infotext);
3229
3230   ModifyGadget(level_editor_gadget[textinput_info[id].gadget_id],
3231                GDI_TEXT_VALUE, textinput_info[id].value, GDI_END);
3232
3233   MapGadget(level_editor_gadget[textinput_info[id].gadget_id]);
3234 }
3235
3236 static void MapSelectboxGadget(int id)
3237 {
3238   int xoffset_left = 0;
3239   int yoffset_left = ED_BORDER_SIZE;
3240   int xoffset_right = ED_GADGET_TEXT_DISTANCE;
3241   int yoffset_right = ED_BORDER_SIZE;
3242   int x = selectbox_info[id].x + xoffset_left;
3243   int y = selectbox_info[id].y + yoffset_left;
3244
3245   DrawTextF(x, y, FONT_TEXT_1, selectbox_info[id].text_left);
3246
3247   if (selectbox_info[id].text_right)
3248   {
3249     struct GadgetInfo *gi = level_editor_gadget[selectbox_info[id].gadget_id];
3250
3251     x = gi->x + gi->width + xoffset_right;
3252     y = SY + selectbox_info[id].y + yoffset_right;
3253
3254     DrawText(x, y, selectbox_info[id].text_right, FONT_TEXT_1);
3255   }
3256
3257   ModifyEditorSelectbox(id, *selectbox_info[id].value);
3258
3259   MapGadget(level_editor_gadget[selectbox_info[id].gadget_id]);
3260 }
3261
3262 static void MapTextbuttonGadget(int id)
3263 {
3264   MapGadget(level_editor_gadget[textbutton_info[id].gadget_id]);
3265 }
3266
3267 static void MapRadiobuttonGadget(int id)
3268 {
3269   int xoffset_right = ED_XOFFSET_CHECKBOX;
3270   int yoffset_right = ED_BORDER_SIZE;
3271   int x = radiobutton_info[id].x + xoffset_right;
3272   int y = radiobutton_info[id].y + yoffset_right;
3273   boolean checked =
3274     (*radiobutton_info[id].value == radiobutton_info[id].checked_value);
3275
3276   DrawTextF(x, y, FONT_TEXT_1, radiobutton_info[id].text_right);
3277   ModifyGadget(level_editor_gadget[radiobutton_info[id].gadget_id],
3278                GDI_CHECKED, checked, GDI_END);
3279
3280   MapGadget(level_editor_gadget[radiobutton_info[id].gadget_id]);
3281 }
3282
3283 static void MapCheckbuttonGadget(int id)
3284 {
3285   int xoffset_right = ED_XOFFSET_CHECKBOX;
3286   int yoffset_right = ED_BORDER_SIZE;
3287   int x = checkbutton_info[id].x + xoffset_right;
3288   int y = checkbutton_info[id].y + yoffset_right;
3289
3290   DrawTextF(x, y, FONT_TEXT_1, checkbutton_info[id].text_right);
3291   ModifyGadget(level_editor_gadget[checkbutton_info[id].gadget_id],
3292                GDI_CHECKED, *checkbutton_info[id].value,
3293                GDI_Y, SY + checkbutton_info[id].y, GDI_END);
3294
3295   MapGadget(level_editor_gadget[checkbutton_info[id].gadget_id]);
3296 }
3297
3298 static void MapMainDrawingArea()
3299 {
3300   boolean no_horizontal_scrollbar = (lev_fieldx + 2 <= ed_fieldx);
3301   boolean no_vertical_scrollbar = (lev_fieldy + 2 <= ed_fieldy);
3302   int i;
3303
3304   for (i=ED_SCROLLBUTTON_ID_AREA_FIRST; i<=ED_SCROLLBUTTON_ID_AREA_LAST; i++)
3305   {
3306     if (((i == ED_SCROLLBUTTON_ID_AREA_LEFT ||
3307           i == ED_SCROLLBUTTON_ID_AREA_RIGHT) &&
3308          no_horizontal_scrollbar) ||
3309         ((i == ED_SCROLLBUTTON_ID_AREA_UP ||
3310           i == ED_SCROLLBUTTON_ID_AREA_DOWN) &&
3311          no_vertical_scrollbar))
3312       continue;
3313
3314     MapGadget(level_editor_gadget[scrollbutton_info[i].gadget_id]);
3315   }
3316
3317   for (i=ED_SCROLLBAR_ID_AREA_FIRST; i<=ED_SCROLLBAR_ID_AREA_LAST; i++)
3318   {
3319     if ((i == ED_SCROLLBAR_ID_AREA_HORIZONTAL && no_horizontal_scrollbar) ||
3320         (i == ED_SCROLLBAR_ID_AREA_VERTICAL && no_vertical_scrollbar))
3321       continue;
3322
3323     MapGadget(level_editor_gadget[scrollbar_info[i].gadget_id]);
3324   }
3325
3326   MapDrawingArea(GADGET_ID_DRAWING_LEVEL);
3327 }
3328
3329 static void UnmapDrawingArea(int id)
3330 {
3331   UnmapGadget(level_editor_gadget[id]);
3332 }
3333
3334 void UnmapLevelEditorWindowGadgets()
3335 {
3336   int i;
3337
3338   for (i=0; i<NUM_EDITOR_GADGETS; i++)
3339     if (level_editor_gadget[i]->x < SX + SXSIZE)
3340       UnmapGadget(level_editor_gadget[i]);
3341 }
3342
3343 void UnmapLevelEditorGadgets()
3344 {
3345   int i;
3346
3347   for (i=0; i<NUM_EDITOR_GADGETS; i++)
3348     UnmapGadget(level_editor_gadget[i]);
3349 }
3350
3351 static void ResetUndoBuffer()
3352 {
3353   undo_buffer_position = -1;
3354   undo_buffer_steps = -1;
3355   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3356 }
3357
3358 static void DrawEditModeWindow()
3359 {
3360   if (edit_mode == ED_MODE_INFO)
3361     DrawLevelInfoWindow();
3362   else if (edit_mode == ED_MODE_PROPERTIES)
3363     DrawPropertiesWindow();
3364   else  /* edit_mode == ED_MODE_DRAWING */
3365     DrawDrawingWindow();
3366 }
3367
3368 static boolean LevelChanged()
3369 {
3370   boolean level_changed = FALSE;
3371   int x, y;
3372
3373   for(y=0; y<lev_fieldy; y++) 
3374     for(x=0; x<lev_fieldx; x++)
3375       if (Feld[x][y] != Ur[x][y])
3376         level_changed = TRUE;
3377
3378   return level_changed;
3379 }
3380
3381 static boolean LevelContainsPlayer()
3382 {
3383   boolean player_found = FALSE;
3384   int x, y;
3385
3386   for(y=0; y<lev_fieldy; y++) 
3387     for(x=0; x<lev_fieldx; x++)
3388       if (Feld[x][y] == EL_PLAYER_1 ||
3389           Feld[x][y] == EL_SP_MURPHY) 
3390         player_found = TRUE;
3391
3392   return player_found;
3393 }
3394
3395 static void CopyPlayfield(short src[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
3396                           short dst[MAX_LEV_FIELDX][MAX_LEV_FIELDY])
3397 {
3398   int x, y;
3399
3400   for(x=0; x<lev_fieldx; x++)
3401     for(y=0; y<lev_fieldy; y++) 
3402       dst[x][y] = src[x][y];
3403 }
3404
3405 static void CopyCustomElementPropertiesToEditor(int element)
3406 {
3407   int i;
3408
3409   custom_element = element_info[properties_element];
3410
3411   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
3412     custom_element_properties[i] = HAS_PROPERTY(element, i);
3413
3414   for (i=0; i < NUM_CHANGE_EVENTS; i++)
3415     custom_element_change_events[i] = HAS_CHANGE_EVENT(element, i);
3416
3417   /* set walk-to-object action selectbox help value */
3418   value_walk_to_action =
3419     (IS_DIGGABLE(element) ? EP_DIGGABLE :
3420      IS_COLLECTIBLE(element) ? EP_COLLECTIBLE :
3421      IS_PUSHABLE(element) ? EP_PUSHABLE :
3422      EP_DIGGABLE);
3423   custom_element_properties[EP_WALK_TO_OBJECT] =
3424     (IS_DIGGABLE(element) ||
3425      IS_COLLECTIBLE(element) ||
3426      IS_PUSHABLE(element));
3427
3428   /* set consistency selectbox help value */
3429   value_consistency =
3430     (CAN_EXPLODE(element) ? EP_CAN_EXPLODE :
3431      IS_INDESTRUCTIBLE(element) ? EP_INDESTRUCTIBLE :
3432      EP_CAN_EXPLODE);
3433   custom_element_properties[EP_EXPLODE_RESULT] =
3434     (CAN_EXPLODE(element) ||
3435      IS_INDESTRUCTIBLE(element));
3436
3437   /* set deadliness selectbox help value */
3438   value_deadliness =
3439     (DONT_TOUCH(element) ? EP_DONT_TOUCH :
3440      DONT_COLLIDE_WITH(element) ? EP_DONT_COLLIDE_WITH :
3441      DONT_RUN_INTO(element) ? EP_DONT_RUN_INTO :
3442      EP_DONT_RUN_INTO);
3443   custom_element_properties[EP_DEADLY] =
3444     (DONT_TOUCH(element) ||
3445      DONT_COLLIDE_WITH(element) ||
3446      DONT_RUN_INTO(element));
3447
3448   /* set smash targets selectbox help value */
3449   value_smash_targets =
3450     (CAN_SMASH_EXPLOSIVES(element) ? EP_CAN_SMASH_EXPLOSIVES :
3451      CAN_SMASH_ENEMIES(element) ? EP_CAN_SMASH_ENEMIES :
3452      CAN_SMASH_FRIENDS(element) ? EP_CAN_SMASH_FRIENDS :
3453      EP_CAN_SMASH_FRIENDS);
3454   custom_element_properties[EP_CAN_SMASH] =
3455     (CAN_SMASH_EXPLOSIVES(element) ||
3456      CAN_SMASH_ENEMIES(element) ||
3457      CAN_SMASH_FRIENDS(element));
3458
3459   /* set walkable layer selectbox help value */
3460   value_walkable_layer =
3461     (IS_WALKABLE_OVER(element) ? EP_WALKABLE_OVER :
3462      IS_WALKABLE_INSIDE(element) ? EP_WALKABLE_INSIDE :
3463      IS_WALKABLE_UNDER(element) ? EP_WALKABLE_UNDER :
3464      EP_WALKABLE_OVER);
3465   custom_element_properties[EP_WALKABLE] =
3466     (IS_WALKABLE_OVER(element) ||
3467      IS_WALKABLE_INSIDE(element) ||
3468      IS_WALKABLE_UNDER(element));
3469
3470   /* set change by player selectbox help value */
3471   value_change_player_action =
3472     (HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
3473      HAS_CHANGE_EVENT(element, CE_PRESSED_BY_PLAYER) ? CE_PRESSED_BY_PLAYER :
3474      CE_PRESSED_BY_PLAYER);
3475 }
3476
3477 static void CopyCustomElementPropertiesToGame(int element)
3478 {
3479   int i;
3480
3481   element_info[properties_element] = custom_element;
3482
3483   /* set walk-to-object property from checkbox and selectbox */
3484   custom_element_properties[EP_DIGGABLE] = FALSE;
3485   custom_element_properties[EP_COLLECTIBLE] = FALSE;
3486   custom_element_properties[EP_PUSHABLE] = FALSE;
3487   custom_element_properties[value_walk_to_action] =
3488     custom_element_properties[EP_WALK_TO_OBJECT];
3489
3490   /* set consistency property from checkbox and selectbox */
3491   custom_element_properties[EP_CAN_EXPLODE] = FALSE;
3492   custom_element_properties[EP_INDESTRUCTIBLE] = FALSE;
3493   custom_element_properties[value_consistency] =
3494     custom_element_properties[EP_EXPLODE_RESULT];
3495
3496   /* set deadliness property from checkbox and selectbox */
3497   custom_element_properties[EP_DONT_RUN_INTO] = FALSE;
3498   custom_element_properties[EP_DONT_COLLIDE_WITH] = FALSE;
3499   custom_element_properties[EP_DONT_TOUCH] = FALSE;
3500   custom_element_properties[value_deadliness] =
3501     custom_element_properties[EP_DEADLY];
3502
3503   /* set smash property from checkbox and selectbox */
3504   custom_element_properties[EP_CAN_SMASH_FRIENDS] = FALSE;
3505   custom_element_properties[EP_CAN_SMASH_ENEMIES] = FALSE;
3506   custom_element_properties[EP_CAN_SMASH_EXPLOSIVES] = FALSE;
3507   custom_element_properties[value_smash_targets] =
3508     custom_element_properties[EP_CAN_SMASH];
3509
3510   /* set walkable property from checkbox and selectbox */
3511   custom_element_properties[EP_WALKABLE_OVER] = FALSE;
3512   custom_element_properties[EP_WALKABLE_INSIDE] = FALSE;
3513   custom_element_properties[EP_WALKABLE_UNDER] = FALSE;
3514   custom_element_properties[value_walkable_layer] =
3515     custom_element_properties[EP_WALKABLE];
3516
3517   /* set player change event from checkbox and selectbox */
3518   custom_element.change.events &= ~CE_PRESSED_BY_PLAYER;
3519   custom_element.change.events &= ~CE_TOUCHED_BY_PLAYER;
3520   custom_element.change.events |= value_change_player_action;
3521
3522   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
3523     SET_PROPERTY(element, i, custom_element_properties[i]);
3524
3525   for (i=0; i < NUM_CHANGE_EVENTS; i++)
3526     SET_CHANGE_EVENT(element, i, custom_element_change_events[i]);
3527
3528   /* copy change events also to special level editor variable */
3529   custom_element = element_info[properties_element];
3530 }
3531
3532 void DrawLevelEd()
3533 {
3534   CloseDoor(DOOR_CLOSE_ALL);
3535   OpenDoor(DOOR_OPEN_2 | DOOR_NO_DELAY);
3536
3537   if (level_editor_test_game)
3538   {
3539     CopyPlayfield(Ur, Feld);
3540     CopyPlayfield(FieldBackup, Ur);
3541
3542     level_editor_test_game = FALSE;
3543   }
3544   else
3545   {
3546     edit_mode = ED_MODE_DRAWING;
3547     edit_mode_properties = ED_MODE_PROPERTIES_INFO;
3548
3549     ResetUndoBuffer();
3550
3551     level_xpos = -1;
3552     level_ypos = -1;
3553   }
3554
3555   /* copy default editor door content to main double buffer */
3556   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3557              DOOR_GFX_PAGEX6, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3558
3559   /* draw mouse button brush elements */
3560   DrawMiniGraphicExt(drawto,
3561                      DX + ED_WIN_MB_LEFT_XPOS, DY + ED_WIN_MB_LEFT_YPOS,
3562                      el2edimg(new_element1));
3563   DrawMiniGraphicExt(drawto,
3564                      DX + ED_WIN_MB_MIDDLE_XPOS, DY + ED_WIN_MB_MIDDLE_YPOS,
3565                      el2edimg(new_element2));
3566   DrawMiniGraphicExt(drawto,
3567                      DX + ED_WIN_MB_RIGHT_XPOS, DY + ED_WIN_MB_RIGHT_YPOS,
3568                      el2edimg(new_element3));
3569
3570   /* draw bigger door */
3571   DrawSpecialEditorDoor();
3572
3573   /* draw new control window */
3574   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3575              DOOR_GFX_PAGEX8, 236, EXSIZE, EYSIZE, EX, EY);
3576
3577   redraw_mask |= REDRAW_ALL;
3578
3579   ReinitializeElementListButtons();     /* only needed after setup changes */
3580
3581   UnmapTapeButtons();
3582   MapControlButtons();
3583
3584   /* copy actual editor door content to door double buffer for OpenDoor() */
3585   BlitBitmap(drawto, bitmap_db_door,
3586              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3587
3588   DrawEditModeWindow();
3589
3590   OpenDoor(DOOR_OPEN_1);
3591 }
3592
3593 static void AdjustDrawingAreaGadgets()
3594 {
3595   int ed_xsize = lev_fieldx + 2;
3596   int ed_ysize = lev_fieldy + 2;
3597   int max_ed_fieldx = MAX_ED_FIELDX;
3598   int max_ed_fieldy = MAX_ED_FIELDY;
3599   boolean horizontal_scrollbar_needed;
3600   boolean vertical_scrollbar_needed;
3601   int x, y, width, height;
3602   int xoffset, yoffset;
3603
3604   /* check if we need any scrollbars */
3605   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
3606   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
3607
3608   /* check if we have a smaller editor field because of scrollbars */
3609   if (horizontal_scrollbar_needed)
3610     max_ed_fieldy = MAX_ED_FIELDY - 1;
3611   if (vertical_scrollbar_needed)
3612     max_ed_fieldx = MAX_ED_FIELDX - 1;
3613
3614   /* check again if we now need more scrollbars because of less space */
3615   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
3616   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
3617
3618   /* check if editor field gets even smaller after adding new scrollbars */
3619   if (horizontal_scrollbar_needed)
3620     max_ed_fieldy = MAX_ED_FIELDY - 1;
3621   if (vertical_scrollbar_needed)
3622     max_ed_fieldx = MAX_ED_FIELDX - 1;
3623
3624   ed_fieldx = (ed_xsize < MAX_ED_FIELDX ? ed_xsize : max_ed_fieldx);
3625   ed_fieldy = (ed_ysize < MAX_ED_FIELDY ? ed_ysize : max_ed_fieldy);
3626
3627   ModifyGadget(level_editor_gadget[GADGET_ID_DRAWING_LEVEL],
3628                GDI_WIDTH, ed_fieldx * MINI_TILEX,
3629                GDI_HEIGHT, ed_fieldy * MINI_TILEY,
3630                GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
3631                GDI_END);
3632
3633   xoffset = (ed_fieldx == MAX_ED_FIELDX ? ED_SCROLLBUTTON_XSIZE : 0);
3634   yoffset = (ed_fieldy == MAX_ED_FIELDY ? ED_SCROLLBUTTON_YSIZE : 0);
3635
3636   x = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_RIGHT].x + xoffset;
3637   y = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_DOWN].y + yoffset;
3638
3639   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_RIGHT], GDI_X, x, GDI_END);
3640   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_DOWN], GDI_Y, y, GDI_END);
3641
3642   width = scrollbar_info[ED_SCROLLBAR_ID_AREA_HORIZONTAL].width + xoffset;
3643   height = scrollbar_info[ED_SCROLLBAR_ID_AREA_VERTICAL].height + yoffset;
3644
3645   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
3646                GDI_WIDTH, width,
3647                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldx,
3648                GDI_END);
3649   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
3650                GDI_HEIGHT, height,
3651                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldy,
3652                GDI_END);
3653 }
3654
3655 static void AdjustLevelScrollPosition()
3656 {
3657   if (level_xpos < -1)
3658     level_xpos = -1;
3659   if (level_xpos > lev_fieldx - ed_fieldx + 1)
3660     level_xpos = lev_fieldx - ed_fieldx + 1;
3661   if (lev_fieldx < ed_fieldx - 2)
3662     level_xpos = -1;
3663
3664   if (level_ypos < -1)
3665     level_ypos = -1;
3666   if (level_ypos > lev_fieldy - ed_fieldy + 1)
3667     level_ypos = lev_fieldy - ed_fieldy + 1;
3668   if (lev_fieldy < ed_fieldy - 2)
3669     level_ypos = -1;
3670 }
3671
3672 static void AdjustEditorScrollbar(int id)
3673 {
3674   struct GadgetInfo *gi = level_editor_gadget[id];
3675   int items_max, items_visible, item_position;
3676
3677   if (id == GADGET_ID_SCROLL_HORIZONTAL)
3678   {
3679     items_max = MAX(lev_fieldx + 2, ed_fieldx);
3680     items_visible = ed_fieldx;
3681     item_position = level_xpos + 1;
3682   }
3683   else
3684   {
3685     items_max = MAX(lev_fieldy + 2, ed_fieldy);
3686     items_visible = ed_fieldy;
3687     item_position = level_ypos + 1;
3688   }
3689
3690   if (item_position > items_max - items_visible)
3691     item_position = items_max - items_visible;
3692
3693   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
3694                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
3695 }
3696
3697 static void ModifyEditorCounter(int counter_id, int new_value)
3698 {
3699   int *counter_value = counterbutton_info[counter_id].value;
3700   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
3701   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3702
3703   ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
3704
3705   if (counter_value != NULL)
3706     *counter_value = gi->text.number_value;
3707 }
3708
3709 static void ModifyEditorCounterLimits(int counter_id, int min, int max)
3710 {
3711   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
3712   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3713
3714   ModifyGadget(gi, GDI_NUMBER_MIN, min, GDI_NUMBER_MAX, max, GDI_END);
3715 }
3716
3717 static void ModifyEditorSelectbox(int selectbox_id, int new_value)
3718 {
3719   int gadget_id = selectbox_info[selectbox_id].gadget_id;
3720   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3721   int new_index_value = 0;
3722   int i;
3723
3724   for(i=0; selectbox_info[selectbox_id].options[i].text != NULL; i++)
3725     if (selectbox_info[selectbox_id].options[i].value == new_value)
3726       new_index_value = i;
3727
3728   *selectbox_info[selectbox_id].value =
3729     selectbox_info[selectbox_id].options[new_index_value].value;
3730
3731   ModifyGadget(gi, GDI_SELECTBOX_INDEX, new_index_value, GDI_END);
3732 }
3733
3734 static void ModifyEditorElementList()
3735 {
3736   int i;
3737
3738   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
3739   {
3740     int gadget_id = GADGET_ID_ELEMENTLIST_FIRST + i;
3741     struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3742     struct GadgetDesign *gd = &gi->deco.design;
3743     int element = editor_elements[element_shift + i];
3744
3745     UnmapGadget(gi);
3746     getMiniGraphicSource(el2edimg(element), &gd->bitmap, &gd->x, &gd->y);
3747     ModifyGadget(gi, GDI_INFO_TEXT, getElementInfoText(element), GDI_END);
3748     MapGadget(gi);
3749   }
3750 }
3751
3752 static void PickDrawingElement(int button, int element)
3753 {
3754   if (button < 1 || button > 3)
3755     return;
3756
3757   if (button == 1)
3758   {
3759     new_element1 = element;
3760     DrawMiniGraphicExt(drawto,
3761                        DX + ED_WIN_MB_LEFT_XPOS, DY + ED_WIN_MB_LEFT_YPOS,
3762                        el2edimg(new_element1));
3763   }
3764   else if (button == 2)
3765   {
3766     new_element2 = element;
3767     DrawMiniGraphicExt(drawto,
3768                        DX + ED_WIN_MB_MIDDLE_XPOS, DY + ED_WIN_MB_MIDDLE_YPOS,
3769                        el2edimg(new_element2));
3770   }
3771   else
3772   {
3773     new_element3 = element;
3774     DrawMiniGraphicExt(drawto,
3775                        DX + ED_WIN_MB_RIGHT_XPOS, DY + ED_WIN_MB_RIGHT_YPOS,
3776                        el2edimg(new_element3));
3777   }
3778
3779   redraw_mask |= REDRAW_DOOR_1;
3780 }
3781
3782 static void RedrawDrawingElements()
3783 {
3784   PickDrawingElement(1, new_element1);
3785   PickDrawingElement(2, new_element2);
3786   PickDrawingElement(3, new_element3);
3787 }
3788
3789 static void DrawDrawingWindow()
3790 {
3791   stick_element_properties_window = FALSE;
3792
3793   SetMainBackgroundImage(IMG_UNDEFINED);
3794   ClearWindow();
3795   UnmapLevelEditorWindowGadgets();
3796
3797   AdjustDrawingAreaGadgets();
3798   AdjustLevelScrollPosition();
3799   AdjustEditorScrollbar(GADGET_ID_SCROLL_HORIZONTAL);
3800   AdjustEditorScrollbar(GADGET_ID_SCROLL_VERTICAL);
3801
3802   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3803   MapMainDrawingArea();
3804 }
3805
3806 static void DrawElementBorder(int dest_x, int dest_y, int width, int height,
3807                               boolean input)
3808 {
3809   int border_graphic =
3810     (input ? IMG_EDITOR_ELEMENT_BORDER_INPUT : IMG_EDITOR_ELEMENT_BORDER);
3811   Bitmap *src_bitmap;
3812   int src_x, src_y;
3813   int num_mini_tilex = width / MINI_TILEX + 1;
3814   int num_mini_tiley = width / MINI_TILEY + 1;
3815   int x, y;
3816
3817   getMiniGraphicSource(border_graphic, &src_bitmap, &src_x, &src_y);
3818
3819   for (y=0; y < num_mini_tiley; y++)
3820     for (x=0; x < num_mini_tilex; x++)
3821       BlitBitmap(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3822                  dest_x - MINI_TILEX / 2 + x * MINI_TILEX,
3823                  dest_y - MINI_TILEY / 2 + y * MINI_TILEY);
3824
3825   ClearRectangle(drawto, dest_x - 1, dest_y - 1, width + 2, height + 2);
3826 }
3827
3828 static void DrawRandomPlacementBackgroundArea()
3829 {
3830   int area_x = ED_AREA_RANDOM_BACKGROUND_XPOS / MINI_TILEX;
3831   int area_y = ED_AREA_RANDOM_BACKGROUND_YPOS / MINI_TILEY;
3832   int area_sx = SX + ED_AREA_RANDOM_BACKGROUND_XPOS;
3833   int area_sy = SY + ED_AREA_RANDOM_BACKGROUND_YPOS;
3834
3835   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
3836   DrawMiniElement(area_x, area_y, random_placement_background_element);
3837
3838   MapDrawingArea(GADGET_ID_RANDOM_BACKGROUND);
3839 }
3840
3841 static void DrawLevelInfoWindow()
3842 {
3843   int i;
3844
3845   stick_element_properties_window = FALSE;
3846
3847   SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
3848   ClearWindow();
3849   UnmapLevelEditorWindowGadgets();
3850
3851   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS1_YPOS,
3852            "Level Settings", FONT_TITLE_1);
3853   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS2_YPOS,
3854            "Editor Settings", FONT_TITLE_1);
3855
3856   /* draw counter gadgets */
3857   for (i=ED_COUNTER_ID_LEVEL_FIRST; i<=ED_COUNTER_ID_LEVEL_LAST; i++)
3858     MapCounterButtons(i);
3859
3860   /* draw checkbutton gadgets */
3861   for (i=ED_CHECKBUTTON_ID_LEVEL_FIRST; i<=ED_CHECKBUTTON_ID_LEVEL_LAST; i++)
3862     MapCheckbuttonGadget(i);
3863
3864   /* draw radiobutton gadgets */
3865   for (i=ED_RADIOBUTTON_ID_LEVEL_FIRST; i<=ED_RADIOBUTTON_ID_LEVEL_LAST; i++)
3866     MapRadiobuttonGadget(i);
3867
3868   /* draw text input gadgets */
3869   for (i=ED_TEXTINPUT_ID_LEVEL_FIRST; i<=ED_TEXTINPUT_ID_LEVEL_LAST; i++)
3870     MapTextInputGadget(i);
3871
3872   /* draw drawing area */
3873   DrawRandomPlacementBackgroundArea();
3874 }
3875
3876 static void DrawAmoebaContentArea()
3877 {
3878   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
3879   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
3880   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
3881   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
3882
3883   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
3884   DrawMiniElement(area_x, area_y, level.amoeba_content);
3885
3886   DrawText(area_sx + TILEX, area_sy + 1, "Content of amoeba", FONT_TEXT_1);
3887
3888   MapDrawingArea(GADGET_ID_AMOEBA_CONTENT);
3889 }
3890
3891 static void DrawCustomGraphicElementArea()
3892 {
3893   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_GRAPHIC];
3894   int xpos = ED_AREA_ELEM_CONTENT3_XPOS;
3895   int ypos = ED_AREA_ELEM_CONTENT3_YPOS;
3896   int area_sx = SX + xpos;
3897   int area_sy = SY + ypos;
3898
3899   if (!IS_CUSTOM_ELEMENT(properties_element))
3900   {
3901     /* this should never happen */
3902     Error(ERR_WARN, "element %d is no custom element", properties_element);
3903
3904     return;
3905   }
3906
3907   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
3908   DrawMiniGraphicExt(drawto, gi->x, gi->y,
3909                      el2edimg(custom_element.gfx_element));
3910
3911   MapDrawingArea(GADGET_ID_CUSTOM_GRAPHIC);
3912 }
3913
3914 static void DrawCustomContentArea()
3915 {
3916   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_CONTENT];
3917   int area_sx = SX + ED_AREA_ELEM_CONTENT4_XPOS;
3918   int area_sy = SY + ED_AREA_ELEM_CONTENT4_YPOS;
3919   int x, y;
3920
3921   if (!IS_CUSTOM_ELEMENT(properties_element))
3922   {
3923     /* this should never happen */
3924     Error(ERR_WARN, "element %d is no custom element", properties_element);
3925
3926     return;
3927   }
3928
3929   DrawElementBorder(area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY, TRUE);
3930
3931   for (y=0; y<3; y++)
3932     for (x=0; x<3; x++)
3933       DrawMiniGraphicExt(drawto, gi->x + x * MINI_TILEX,gi->y + y * MINI_TILEY,
3934                          el2edimg(custom_element.content[x][y]));
3935
3936   MapDrawingArea(GADGET_ID_CUSTOM_CONTENT);
3937 }
3938
3939 static void DrawCustomChangedArea()
3940 {
3941   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_CHANGED];
3942   int xpos = ED_AREA_ELEM_CONTENT2_XPOS;
3943   int ypos = ED_AREA_ELEM_CONTENT2_YPOS;
3944   int area_sx = SX + xpos;
3945   int area_sy = SY + ypos;
3946
3947   if (!IS_CUSTOM_ELEMENT(properties_element))
3948   {
3949     /* this should never happen */
3950     Error(ERR_WARN, "element %d is no custom element", properties_element);
3951
3952     return;
3953   }
3954
3955   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
3956   DrawMiniGraphicExt(drawto, gi->x, gi->y,
3957                      el2edimg(custom_element.change.successor));
3958
3959   MapDrawingArea(GADGET_ID_CUSTOM_CHANGED);
3960 }
3961
3962 static void DrawElementContentAreas()
3963 {
3964   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
3965   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
3966   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
3967   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
3968   int i, x, y;
3969
3970   for (i=0; i<MAX_ELEMENT_CONTENTS; i++)
3971     UnmapDrawingArea(GADGET_ID_ELEMENT_CONTENT_0 + i);
3972
3973   /* display counter to choose number of element content areas */
3974   MapCounterButtons(ED_COUNTER_ID_ELEMENT_CONTENT);
3975
3976   /* delete content areas in case of reducing number of them */
3977   DrawBackground(SX, area_sy - MINI_TILEX, SXSIZE, 12 * MINI_TILEY);
3978
3979   for (i=0; i<level.num_yamyam_contents; i++)
3980     DrawElementBorder(area_sx + 5 * (i % 4) * MINI_TILEX,
3981                       area_sy + 6 * (i / 4) * MINI_TILEY,
3982                       3 * MINI_TILEX, 3 * MINI_TILEY, TRUE);
3983
3984   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 0 * MINI_TILEY + 1,
3985            "Content", FONT_TEXT_1);
3986   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 1 * MINI_TILEY + 1,
3987            "when",    FONT_TEXT_1);
3988   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 2 * MINI_TILEY + 1,
3989            "smashed", FONT_TEXT_1);
3990
3991   for (i=0; i<level.num_yamyam_contents; i++)
3992   {
3993     for (y=0; y<3; y++)
3994       for (x=0; x<3; x++)
3995         DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
3996                         level.yamyam_content[i][x][y]);
3997
3998     DrawTextF(area_sx - SX + 5 * (i % 4) * MINI_TILEX + MINI_TILEX + 1,
3999               area_sy - SY + 6 * (i / 4) * MINI_TILEY + 4 * MINI_TILEY - 4,
4000               FONT_TEXT_1, "%d", i + 1);
4001   }
4002
4003   for (i=0; i<level.num_yamyam_contents; i++)
4004     MapDrawingArea(GADGET_ID_ELEMENT_CONTENT_0 + i);
4005 }
4006
4007 char *getElementDescriptionFilename(int element)
4008 {
4009   char *docs_dir = options.docs_directory;
4010   char *elements_subdir = "elements";
4011   static char *filename = NULL;
4012   char basename[MAX_FILENAME_LEN];
4013
4014   if (filename != NULL)
4015     free(filename);
4016
4017   /* 1st try: look for element description file for exactly this element */
4018   sprintf(basename, "%s.txt", element_info[element].token_name);
4019   filename = getPath3(docs_dir, elements_subdir, basename);
4020   if (fileExists(filename))
4021     return filename;
4022
4023   free(filename);
4024
4025   /* 2nd try: look for element description file for this element's class */
4026   sprintf(basename, "%s.txt", element_info[element].class_name);
4027   filename = getPath3(docs_dir, elements_subdir, basename);
4028   if (fileExists(filename))
4029     return filename;
4030
4031   return NULL;
4032 }
4033
4034 static boolean PrintInfoText(char *text, int font_nr, int screen_line)
4035 {
4036   int font_height = getFontHeight(font_nr);
4037   int pad_x = ED_SETTINGS_XPOS(0);
4038   int pad_y = ED_SETTINGS_YPOS(0) + ED_BORDER_SIZE;
4039   int sx = SX + pad_x;
4040   int sy = SY + pad_y;
4041   int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
4042
4043   if (screen_line >= max_lines_per_screen)
4044     return FALSE;
4045
4046   DrawText(sx, sy + screen_line * font_height, text, font_nr);
4047
4048   return TRUE;
4049 }
4050
4051 static int PrintElementDescriptionFromFile(char *filename, int screen_line)
4052 {
4053   int font_nr = FONT_TEXT_2;
4054   int font_width = getFontWidth(font_nr);
4055   int pad_x = ED_SETTINGS_XPOS(0);
4056   int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
4057   char line[MAX_LINE_LEN];
4058   char buffer[max_chars_per_line + 1];
4059   int buffer_len;
4060   int lines_printed = 0;
4061   FILE *file;
4062
4063   if (filename == NULL)
4064     return 0;
4065
4066   if (!(file = fopen(filename, MODE_READ)))
4067     return 0;
4068
4069   buffer[0] = '\0';
4070   buffer_len = 0;
4071
4072   while(!feof(file))
4073   {
4074     char *line_ptr, *word_ptr;
4075     boolean last_line_was_empty = TRUE;
4076
4077     /* read next line of input file */
4078     if (!fgets(line, MAX_LINE_LEN, file))
4079       break;
4080
4081     /* skip comments (lines directly beginning with '#') */
4082     if (line[0] == '#')
4083       continue;
4084
4085     /* cut trailing newline from input line */
4086     for (line_ptr = line; *line_ptr; line_ptr++)
4087     {
4088       if (*line_ptr == '\n' || *line_ptr == '\r')
4089       {
4090         *line_ptr = '\0';
4091         break;
4092       }
4093     }
4094
4095     if (strlen(line) == 0)              /* special case: force empty line */
4096       strcpy(line, "\n");
4097
4098     word_ptr = line;
4099
4100     while (*word_ptr)
4101     {
4102       boolean print_buffer = FALSE;
4103       int word_len;
4104
4105       /* skip leading whitespaces */
4106       while (*word_ptr == ' ' || *word_ptr == '\t')
4107         word_ptr++;
4108
4109       line_ptr = word_ptr;
4110       word_len = 0;
4111
4112       /* look for end of next word */
4113       while (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4114       {
4115         line_ptr++;
4116         word_len++;
4117       }
4118
4119       if (word_len == 0)
4120       {
4121         continue;
4122       }
4123       else if (*word_ptr == '\n')       /* special case: force empty line */
4124       {
4125         if (buffer_len == 0)
4126           word_ptr++;
4127
4128         /* prevent printing of multiple empty lines */
4129         if (buffer_len > 0 || !last_line_was_empty)
4130           print_buffer = TRUE;
4131       }
4132       else if (word_len < max_chars_per_line - buffer_len)
4133       {
4134         /* word fits into text buffer -- add word */
4135
4136         if (buffer_len > 0)
4137           buffer[buffer_len++] = ' ';
4138
4139         strncpy(&buffer[buffer_len], word_ptr, word_len);
4140         buffer_len += word_len;
4141         buffer[buffer_len] = '\0';
4142         word_ptr += word_len;
4143       }
4144       else if (buffer_len > 0)
4145       {
4146         /* not enough space left for word in text buffer -- print buffer */
4147
4148         print_buffer = TRUE;
4149       }
4150       else
4151       {
4152         /* word does not fit at all into empty text buffer -- cut word */
4153
4154         strncpy(buffer, word_ptr, max_chars_per_line);
4155         buffer[max_chars_per_line] = '\0';
4156         word_ptr += max_chars_per_line;
4157         print_buffer = TRUE;
4158       }
4159
4160       if (print_buffer)
4161       {
4162         if (!PrintInfoText(buffer, font_nr, screen_line + lines_printed))
4163           return lines_printed;
4164
4165         last_line_was_empty = (buffer_len == 0);
4166         lines_printed++;
4167
4168         buffer[0] = '\0';
4169         buffer_len = 0;
4170         print_buffer = FALSE;
4171       }
4172     }
4173   }
4174
4175   fclose(file);
4176
4177   if (buffer_len > 0)
4178     if (PrintInfoText(buffer, font_nr, screen_line + lines_printed))
4179       lines_printed++;
4180
4181   return lines_printed;
4182 }
4183
4184 static void DrawPropertiesTabulatorGadgets()
4185 {
4186   struct GadgetInfo *gd_gi = level_editor_gadget[GADGET_ID_PROPERTIES_INFO];
4187   struct GadgetDesign *gd = &gd_gi->alt_design[GD_BUTTON_UNPRESSED];
4188   int gd_x = gd->x + gd_gi->border.width / 2;
4189   int gd_y = gd->y + gd_gi->height - 1;
4190   Pixel tab_color = GetPixel(gd->bitmap, gd_x, gd_y);
4191   int id_first = ED_TEXTBUTTON_ID_PROPERTIES_INFO;
4192 #if 1
4193   int id_last  = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG;
4194 #else
4195   int id_last  = ED_TEXTBUTTON_ID_PROPERTIES_INFO;
4196 #endif
4197   int i;
4198
4199 #if 0
4200   /* draw additional "configure" tabulator for configurable elements */
4201   if (checkPropertiesConfig())
4202     id_last = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG;
4203 #endif
4204
4205   /* draw additional "advanced" tabulator for custom elements */
4206   if (IS_CUSTOM_ELEMENT(properties_element))
4207     id_last = ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED;
4208
4209   for (i=id_first; i <= id_last; i++)
4210   {
4211     int gadget_id = textbutton_info[i].gadget_id;
4212     struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4213     boolean active = (i != edit_mode_properties);
4214
4215     /* draw background line below tabulator button */
4216     ClearRectangleOnBackground(drawto, gi->x, gi->y + gi->height, gi->width,1);
4217
4218     /* draw solid line below inactive tabulator buttons */
4219     if (!active && tab_color != BLACK_PIXEL)    /* black => transparent */
4220       FillRectangle(drawto, gi->x, gi->y + gi->height, gi->width,1, tab_color);
4221
4222     ModifyGadget(gi, GDI_ACTIVE, active, GDI_END);
4223     MapTextbuttonGadget(i);
4224   }
4225
4226   /* draw little border line below tabulator buttons */
4227   if (tab_color != BLACK_PIXEL)                 /* black => transparent */
4228     FillRectangle(drawto, gd_gi->x, gd_gi->y + gd_gi->height + 1,
4229                   3 * gd_gi->width + 2 * ED_GADGET_DISTANCE,
4230                   ED_GADGET_DISTANCE, tab_color);
4231 }
4232
4233 static void DrawPropertiesInfo()
4234 {
4235   static struct
4236   {
4237     int value;
4238     char *text;
4239   }
4240   properties[] =
4241   {
4242     /* configurable properties */
4243     { EP_INDESTRUCTIBLE,        "- undestructible"                      },
4244     { EP_SLIPPERY,              "- slippery for falling objects"        },
4245     { EP_EM_SLIPPERY_WALL,      "- slippery for some gems (EM style)"   },
4246
4247     { EP_DIGGABLE,              "- diggable"                            },
4248     { EP_COLLECTIBLE,           "- collectible"                         },
4249     { EP_PUSHABLE,              "- pushable"                            },
4250
4251     { EP_CAN_MOVE,              "- can move"                            },
4252     { EP_CAN_FALL,              "- can fall"                            },
4253 #if 0
4254     { EP_CAN_SMASH,             "- can smash"                           },
4255 #endif
4256     { EP_CAN_SMASH_FRIENDS,     "- can smash player and friends"        },
4257     { EP_CAN_SMASH_ENEMIES,     "- can smash good and bad guys"         },
4258     { EP_CAN_SMASH_EXPLOSIVES,  "- can smash everything smashable"      },
4259     { EP_CAN_EXPLODE,           "- can explode"                         },
4260     { EP_CAN_EXPLODE_BY_FIRE,   "  - by fire or explosions"             },
4261     { EP_CAN_EXPLODE_SMASHED,   "  - when smashed"                      },
4262     { EP_CAN_EXPLODE_IMPACT,    "  - on impact"                         },
4263
4264     { EP_DONT_RUN_INTO,         "- deadly when running into"            },
4265     { EP_DONT_COLLIDE_WITH,     "- deadly when colliding with"          },
4266     { EP_DONT_TOUCH,            "- deadly when touching"                },
4267
4268     { EP_WALKABLE_OVER,         "- player can walk over it"             },
4269     { EP_WALKABLE_INSIDE,       "- player can walk inside it"           },
4270     { EP_WALKABLE_UNDER,        "- player can walk under it"            },
4271     { EP_PASSABLE_OVER,         "- player can pass over it"             },
4272     { EP_PASSABLE_INSIDE,       "- player can pass through it"          },
4273     { EP_PASSABLE_UNDER,        "- player can pass under it"            },
4274
4275     /* pre-defined properties */
4276     { EP_CAN_PASS_MAGIC_WALL,   "- can pass magic walls"                },
4277     { EP_HAS_CONTENT,           "- can contain other elements"          },
4278
4279     { -1,                       NULL                                    }
4280   };
4281   char *filename = getElementDescriptionFilename(properties_element);
4282   char *percentage_text = "In this level:";
4283   char *properties_text = "Standard properties:";
4284   float percentage;
4285   int num_elements_in_level;
4286   int num_standard_properties = 0;
4287   int font1_nr = FONT_TEXT_1;
4288   int font2_nr = FONT_TEXT_2;
4289   int font1_width = getFontWidth(font1_nr);
4290   int font2_height = getFontHeight(font2_nr);
4291   int pad_x = ED_SETTINGS_XPOS(0);
4292   int pad_y = ED_SETTINGS_YPOS(0) + ED_BORDER_SIZE;
4293   int screen_line = 0;
4294   int i, x, y;
4295
4296   /* ----- print number of elements / percentage of this element in level */
4297
4298   num_elements_in_level = 0;
4299   for (y=0; y<lev_fieldy; y++) 
4300     for (x=0; x<lev_fieldx; x++)
4301       if (Feld[x][y] == properties_element)
4302         num_elements_in_level++;
4303   percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
4304
4305   DrawTextF(pad_x, pad_y + screen_line * font2_height, font1_nr,
4306             percentage_text);
4307   DrawTextF(pad_x + strlen(percentage_text) * font1_width,
4308             pad_y + screen_line++ * font2_height, font2_nr,
4309             "%d (%.2f%%)", num_elements_in_level, percentage);
4310
4311   screen_line++;
4312
4313   /* ----- print standard properties of this element */
4314
4315   DrawTextF(pad_x, pad_y + screen_line++ * font2_height, font1_nr,
4316             properties_text);
4317
4318   for (i=0; properties[i].value != -1; i++)
4319   {
4320     if (!HAS_PROPERTY(properties_element, properties[i].value))
4321       continue;
4322
4323     DrawTextF(pad_x, pad_y + screen_line++ * font2_height, font2_nr,
4324               properties[i].text);
4325     num_standard_properties++;
4326   }
4327
4328   if (num_standard_properties == 0)
4329     DrawTextF(pad_x + strlen(properties_text) * font1_width,
4330               pad_y + (screen_line - 1) * font2_height, font2_nr, "none");
4331
4332   screen_line++;
4333
4334   /* ----- print special description of this element */
4335
4336   PrintInfoText("Description:", FONT_TEXT_1, screen_line);
4337   if (PrintElementDescriptionFromFile(filename, screen_line + 1) == 0)
4338     PrintInfoText("No description available.", FONT_TEXT_1, screen_line);
4339 }
4340
4341 #define TEXT_COLLECTING         "Score for collecting"
4342 #define TEXT_SMASHING           "Score for smashing"
4343 #define TEXT_CRACKING           "Score for cracking"
4344 #define TEXT_SPEED              "Speed of amoeba growth"
4345 #define TEXT_DURATION           "Duration when activated"
4346
4347 static struct
4348 {
4349   int element;
4350   int *value;
4351   char *text;
4352 } elements_with_counter[] =
4353 {
4354   { EL_EMERALD,         &level.score[SC_EMERALD],       TEXT_COLLECTING },
4355   { EL_BD_DIAMOND,      &level.score[SC_EMERALD],       TEXT_COLLECTING },
4356   { EL_EMERALD_YELLOW,  &level.score[SC_EMERALD],       TEXT_COLLECTING },
4357   { EL_EMERALD_RED,     &level.score[SC_EMERALD],       TEXT_COLLECTING },
4358   { EL_EMERALD_PURPLE,  &level.score[SC_EMERALD],       TEXT_COLLECTING },
4359   { EL_SP_INFOTRON,     &level.score[SC_EMERALD],       TEXT_COLLECTING },
4360   { EL_DIAMOND,         &level.score[SC_DIAMOND],       TEXT_COLLECTING },
4361   { EL_CRYSTAL,         &level.score[SC_CRYSTAL],       TEXT_COLLECTING },
4362   { EL_PEARL,           &level.score[SC_PEARL],         TEXT_COLLECTING },
4363   { EL_BUG_RIGHT,       &level.score[SC_BUG],           TEXT_SMASHING   },
4364   { EL_BUG_UP,          &level.score[SC_BUG],           TEXT_SMASHING   },
4365   { EL_BUG_LEFT,        &level.score[SC_BUG],           TEXT_SMASHING   },
4366   { EL_BUG_DOWN,        &level.score[SC_BUG],           TEXT_SMASHING   },
4367   { EL_BD_BUTTERFLY_RIGHT,&level.score[SC_BUG],         TEXT_SMASHING   },
4368   { EL_BD_BUTTERFLY_UP,   &level.score[SC_BUG],         TEXT_SMASHING   },
4369   { EL_BD_BUTTERFLY_LEFT, &level.score[SC_BUG],         TEXT_SMASHING   },
4370   { EL_BD_BUTTERFLY_DOWN, &level.score[SC_BUG],         TEXT_SMASHING   },
4371   { EL_SP_ELECTRON,     &level.score[SC_BUG],           TEXT_SMASHING   },
4372   { EL_SPACESHIP_RIGHT, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4373   { EL_SPACESHIP_UP,    &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4374   { EL_SPACESHIP_LEFT,  &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4375   { EL_SPACESHIP_DOWN,  &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4376   { EL_BD_FIREFLY_RIGHT,&level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4377   { EL_BD_FIREFLY_UP,   &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4378   { EL_BD_FIREFLY_LEFT, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4379   { EL_BD_FIREFLY_DOWN, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4380   { EL_SP_SNIKSNAK,     &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4381   { EL_YAMYAM,          &level.score[SC_YAMYAM],        TEXT_SMASHING   },
4382   { EL_DARK_YAMYAM,     &level.score[SC_YAMYAM],        TEXT_SMASHING   },
4383   { EL_ROBOT,           &level.score[SC_ROBOT],         TEXT_SMASHING   },
4384   { EL_PACMAN_RIGHT,    &level.score[SC_PACMAN],        TEXT_SMASHING   },
4385   { EL_PACMAN_UP,       &level.score[SC_PACMAN],        TEXT_SMASHING   },
4386   { EL_PACMAN_LEFT,     &level.score[SC_PACMAN],        TEXT_SMASHING   },
4387   { EL_PACMAN_DOWN,     &level.score[SC_PACMAN],        TEXT_SMASHING   },
4388   { EL_NUT,             &level.score[SC_NUT],           TEXT_CRACKING   },
4389   { EL_DYNAMITE,        &level.score[SC_DYNAMITE],      TEXT_COLLECTING },
4390   { EL_DYNABOMB_INCREASE_NUMBER,&level.score[SC_DYNAMITE],TEXT_COLLECTING },
4391   { EL_DYNABOMB_INCREASE_SIZE,  &level.score[SC_DYNAMITE],TEXT_COLLECTING },
4392   { EL_DYNABOMB_INCREASE_POWER, &level.score[SC_DYNAMITE],TEXT_COLLECTING },
4393   { EL_SHIELD_NORMAL,   &level.score[SC_SHIELD],        TEXT_COLLECTING },
4394   { EL_SHIELD_DEADLY,   &level.score[SC_SHIELD],        TEXT_COLLECTING },
4395   { EL_EXTRA_TIME,      &level.score[SC_TIME_BONUS],    TEXT_COLLECTING },
4396   { EL_KEY_1,           &level.score[SC_KEY],           TEXT_COLLECTING },
4397   { EL_KEY_2,           &level.score[SC_KEY],           TEXT_COLLECTING },
4398   { EL_KEY_3,           &level.score[SC_KEY],           TEXT_COLLECTING },
4399   { EL_KEY_4,           &level.score[SC_KEY],           TEXT_COLLECTING },
4400   { EL_EM_KEY_1_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4401   { EL_EM_KEY_2_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4402   { EL_EM_KEY_3_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4403   { EL_EM_KEY_4_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4404   { EL_AMOEBA_WET,      &level.amoeba_speed,            TEXT_SPEED      },
4405   { EL_AMOEBA_DRY,      &level.amoeba_speed,            TEXT_SPEED      },
4406   { EL_AMOEBA_FULL,     &level.amoeba_speed,            TEXT_SPEED      },
4407   { EL_BD_AMOEBA,       &level.amoeba_speed,            TEXT_SPEED      },
4408   { EL_MAGIC_WALL,      &level.time_magic_wall,         TEXT_DURATION   },
4409   { EL_ROBOT_WHEEL,     &level.time_wheel,              TEXT_DURATION   },
4410   { -1,                 NULL,                           NULL            }
4411 };
4412
4413 static boolean checkPropertiesConfig()
4414 {
4415   int i;
4416
4417   if (IS_GEM(properties_element) ||
4418       IS_CUSTOM_ELEMENT(properties_element) ||
4419       HAS_CONTENT(properties_element))
4420     return TRUE;
4421   else
4422     for (i=0; elements_with_counter[i].element != -1; i++)
4423       if (elements_with_counter[i].element == properties_element)
4424         return TRUE;
4425
4426   return FALSE;
4427 }
4428
4429 static void DrawPropertiesConfig()
4430 {
4431   int i;
4432
4433   if (!checkPropertiesConfig())
4434   {
4435     PrintInfoText("No configuration options available.", FONT_TEXT_1, 0);
4436
4437     return;
4438   }
4439
4440   /* check if there are elements where a score can be chosen for */
4441   for (i=0; elements_with_counter[i].element != -1; i++)
4442   {
4443     if (elements_with_counter[i].element == properties_element)
4444     {
4445       int counter_id = ED_COUNTER_ID_ELEMENT_SCORE;
4446
4447       counterbutton_info[counter_id].value = elements_with_counter[i].value;
4448       counterbutton_info[counter_id].text_right= elements_with_counter[i].text;
4449       MapCounterButtons(counter_id);
4450
4451       break;
4452     }
4453   }
4454
4455   if (HAS_CONTENT(properties_element))
4456   {
4457     /* draw stickybutton gadget */
4458     i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
4459     checkbutton_info[i].y = ED_COUNTER_YPOS(4);
4460     MapCheckbuttonGadget(i);
4461
4462     if (IS_AMOEBOID(properties_element))
4463       DrawAmoebaContentArea();
4464     else
4465       DrawElementContentAreas();
4466   }
4467
4468   if (IS_GEM(properties_element))
4469     MapCheckbuttonGadget(ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS);
4470
4471   if (IS_CUSTOM_ELEMENT(properties_element))
4472   {
4473     /* draw stickybutton gadget */
4474     i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
4475     checkbutton_info[i].y = ED_SETTINGS_YPOS(0);
4476     MapCheckbuttonGadget(i);
4477
4478     /* draw checkbutton gadgets */
4479     for (i =  ED_CHECKBUTTON_ID_CUSTOM_FIRST;
4480          i <= ED_CHECKBUTTON_ID_CUSTOM_LAST; i++)
4481       MapCheckbuttonGadget(i);
4482
4483     /* draw counter gadgets */
4484     for (i=ED_COUNTER_ID_CUSTOM_FIRST; i<=ED_COUNTER_ID_CUSTOM_LAST; i++)
4485       MapCounterButtons(i);
4486
4487     /* draw selectbox gadgets */
4488     for (i=ED_SELECTBOX_ID_CUSTOM_FIRST; i <= ED_SELECTBOX_ID_CUSTOM_LAST; i++)
4489       MapSelectboxGadget(i);
4490
4491     /* draw drawing area gadgets */
4492     DrawCustomContentArea();
4493   }
4494 }
4495
4496 static void DrawPropertiesAdvanced()
4497 {
4498   int i;
4499
4500   /* draw stickybutton gadget */
4501   i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
4502   checkbutton_info[i].y = ED_SETTINGS_YPOS(0);
4503   MapCheckbuttonGadget(i);
4504
4505   /* draw checkbutton gadgets */
4506   for (i =  ED_CHECKBUTTON_ID_CHANGE_FIRST;
4507        i <= ED_CHECKBUTTON_ID_CHANGE_LAST; i++)
4508     MapCheckbuttonGadget(i);
4509
4510   /* draw counter gadgets */
4511   for (i=ED_COUNTER_ID_CHANGE_FIRST; i<=ED_COUNTER_ID_CHANGE_LAST; i++)
4512     MapCounterButtons(i);
4513
4514   /* draw selectbox gadgets */
4515   for (i=ED_SELECTBOX_ID_CHANGE_FIRST; i<=ED_SELECTBOX_ID_CHANGE_LAST; i++)
4516     MapSelectboxGadget(i);
4517
4518   /* draw textbutton gadgets */
4519   MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE);
4520
4521   /* draw drawing area gadgets */
4522   DrawCustomGraphicElementArea();
4523   DrawCustomChangedArea();
4524 }
4525
4526 static void DrawElementName(int x, int y, int element)
4527 {
4528   char *element_name = getElementInfoText(element);
4529   int font_nr = FONT_TEXT_1;
4530   int font_width = getFontWidth(font_nr);
4531   int font_height = getFontHeight(font_nr);
4532   int max_text_width = SXSIZE - x - ED_SETTINGS_XPOS(0);
4533   int max_chars_per_line = max_text_width / font_width;
4534   char buffer[max_chars_per_line + 1];
4535
4536   if (strlen(element_name) <= max_chars_per_line)
4537     DrawTextF(x, y, font_nr, element_name);
4538   else
4539   {
4540     int next_pos = max_chars_per_line;
4541
4542     strncpy(buffer, element_name, max_chars_per_line);
4543     buffer[max_chars_per_line] = '\0';
4544
4545     if (element_name[max_chars_per_line] == ' ')
4546       next_pos++;
4547     else
4548     {
4549       int i;
4550
4551       for (i = max_chars_per_line - 1; i >= 0; i--)
4552         if (buffer[i] == ' ')
4553           break;
4554
4555       if (strlen(&element_name[i + 1]) <= max_chars_per_line)
4556       {
4557         buffer[i] = '\0';
4558         next_pos = i + 1;
4559       }
4560     }
4561
4562     DrawTextF(x, y - font_height / 2, font_nr, buffer);
4563
4564     strncpy(buffer, &element_name[next_pos], max_chars_per_line);
4565     buffer[max_chars_per_line] = '\0';
4566
4567     DrawTextF(x, y + font_height / 2, font_nr, buffer);
4568   }
4569 }
4570
4571 static void DrawPropertiesWindow()
4572 {
4573   int xstart = 2;
4574   int ystart = 4;
4575
4576   stick_element_properties_window = FALSE;
4577
4578   /* make sure that previous properties edit mode exists for this element */
4579   if (edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED &&
4580       !IS_CUSTOM_ELEMENT(properties_element))
4581     edit_mode_properties = ED_MODE_PROPERTIES_CONFIG;
4582
4583 #if 0
4584   if (edit_mode_properties == ED_MODE_PROPERTIES_CONFIG &&
4585       !checkPropertiesConfig())
4586     edit_mode_properties = ED_MODE_PROPERTIES_INFO;
4587 #endif
4588
4589   if (IS_CUSTOM_ELEMENT(properties_element))
4590     CopyCustomElementPropertiesToEditor(properties_element);
4591
4592   UnmapLevelEditorWindowGadgets();
4593
4594   SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
4595   ClearWindow();
4596
4597   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS1_YPOS,
4598            "Element Settings", FONT_TITLE_1);
4599
4600   DrawElementBorder(SX + xstart * MINI_TILEX,
4601                     SY + ystart * MINI_TILEY + MINI_TILEY / 2,
4602                     TILEX, TILEY, FALSE);
4603   DrawGraphicAnimationExt(drawto,
4604                           SX + xstart * MINI_TILEX,
4605                           SY + ystart * MINI_TILEY + MINI_TILEY / 2,
4606                           el2img(properties_element), -1, NO_MASKING);
4607
4608   FrameCounter = 0;     /* restart animation frame counter */
4609
4610   DrawElementName((xstart + 3) * MINI_TILEX, (ystart + 1) * MINI_TILEY,
4611                   properties_element);
4612
4613   DrawPropertiesTabulatorGadgets();
4614
4615   if (edit_mode_properties == ED_MODE_PROPERTIES_INFO)
4616     DrawPropertiesInfo();
4617   else if (edit_mode_properties == ED_MODE_PROPERTIES_CONFIG)
4618     DrawPropertiesConfig();
4619   else  /* edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED */
4620     DrawPropertiesAdvanced();
4621 }
4622
4623 static void DrawLineElement(int sx, int sy, int element, boolean change_level)
4624 {
4625   int lx = sx + level_xpos;
4626   int ly = sy + level_ypos;
4627
4628   DrawMiniElement(sx, sy, (element < 0 ? Feld[lx][ly] : element));
4629
4630   if (change_level)
4631     Feld[lx][ly] = element;
4632 }
4633
4634 static void DrawLine(int from_x, int from_y, int to_x, int to_y,
4635                      int element, boolean change_level)
4636 {
4637   if (from_y == to_y)                   /* horizontal line */
4638   {
4639     int x;
4640     int y = from_y;
4641
4642     if (from_x > to_x)
4643       swap_numbers(&from_x, &to_x);
4644
4645     for (x=from_x; x<=to_x; x++)
4646       DrawLineElement(x, y, element, change_level);
4647   }
4648   else if (from_x == to_x)              /* vertical line */
4649   {
4650     int x = from_x;
4651     int y;
4652
4653     if (from_y > to_y)
4654       swap_numbers(&from_y, &to_y);
4655
4656     for (y=from_y; y<=to_y; y++)
4657       DrawLineElement(x, y, element, change_level);
4658   }
4659   else                                  /* diagonal line */
4660   {
4661     int len_x = ABS(to_x - from_x);
4662     int len_y = ABS(to_y - from_y);
4663     int x, y;
4664
4665     if (len_y < len_x)                  /* a < 1 */
4666     {
4667       float a = (float)len_y / (float)len_x;
4668
4669       if (from_x > to_x)
4670         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
4671
4672       for (x=0; x<=len_x; x++)
4673       {
4674         y = (int)(a * x + 0.5) * (to_y < from_y ? -1 : +1);
4675         DrawLineElement(from_x + x, from_y + y, element, change_level);
4676       }
4677     }
4678     else                                /* a >= 1 */
4679     {
4680       float a = (float)len_x / (float)len_y;
4681
4682       if (from_y > to_y)
4683         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
4684
4685       for (y=0; y<=len_y; y++)
4686       {
4687         x = (int)(a * y + 0.5) * (to_x < from_x ? -1 : +1);
4688         DrawLineElement(from_x + x, from_y + y, element, change_level);
4689       }
4690     }
4691   }
4692 }
4693
4694 static void DrawBox(int from_x, int from_y, int to_x, int to_y,
4695                     int element, boolean change_level)
4696 {
4697   DrawLine(from_x, from_y, from_x, to_y, element, change_level);
4698   DrawLine(from_x, to_y, to_x, to_y, element, change_level);
4699   DrawLine(to_x, to_y, to_x, from_y, element, change_level);
4700   DrawLine(to_x, from_y, from_x, from_y, element, change_level);
4701 }
4702
4703 static void DrawFilledBox(int from_x, int from_y, int to_x, int to_y,
4704                           int element, boolean change_level)
4705 {
4706   int y;
4707
4708   if (from_y > to_y)
4709     swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
4710
4711   for (y=from_y; y<=to_y; y++)
4712     DrawLine(from_x, y, to_x, y, element, change_level);
4713 }
4714
4715 static void DrawArcExt(int from_x, int from_y, int to_x2, int to_y2,
4716                        int element, boolean change_level)
4717 {
4718   int to_x = to_x2 - (to_x2 > from_x ? +1 : -1);
4719   int to_y = to_y2 - (to_y2 > from_y ? +1 : -1);
4720   int len_x = ABS(to_x - from_x);
4721   int len_y = ABS(to_y - from_y);
4722   int radius, x, y;
4723
4724   radius = (int)(sqrt((float)(len_x * len_x + len_y * len_y)) + 0.5);
4725
4726   /* not optimal (some points get drawn twice) but simple,
4727      and fast enough for the few points we are drawing */
4728
4729   for (x=0; x<=radius; x++)
4730   {
4731     int sx, sy, lx, ly;
4732
4733     y = (int)(sqrt((float)(radius * radius - x * x)) + 0.5);
4734
4735     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
4736     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
4737     lx = sx + level_xpos;
4738     ly = sy + level_ypos;
4739
4740     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
4741       DrawLineElement(sx, sy, element, change_level);
4742   }
4743
4744   for (y=0; y<=radius; y++)
4745   {
4746     int sx, sy, lx, ly;
4747
4748     x = (int)(sqrt((float)(radius * radius - y * y)) + 0.5);
4749
4750     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
4751     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
4752     lx = sx + level_xpos;
4753     ly = sy + level_ypos;
4754
4755     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
4756       DrawLineElement(sx, sy, element, change_level);
4757   }
4758 }
4759
4760 static void DrawArc(int from_x, int from_y, int to_x, int to_y,
4761                     int element, boolean change_level)
4762 {
4763   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
4764   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
4765
4766   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
4767 }
4768
4769 #define DRAW_CIRCLES_BUTTON_AVAILABLE   0
4770 #if DRAW_CIRCLES_BUTTON_AVAILABLE
4771 static void DrawCircle(int from_x, int from_y, int to_x, int to_y,
4772                        int element, boolean change_level)
4773 {
4774   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
4775   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
4776   int mirror_to_x2 = from_x - (to_x2 - from_x);
4777   int mirror_to_y2 = from_y - (to_y2 - from_y);
4778
4779   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
4780   DrawArcExt(from_x, from_y, mirror_to_x2, to_y2, element, change_level);
4781   DrawArcExt(from_x, from_y, to_x2, mirror_to_y2, element, change_level);
4782   DrawArcExt(from_x, from_y, mirror_to_x2, mirror_to_y2, element,change_level);
4783 }
4784 #endif
4785
4786 static void DrawAreaBorder(int from_x, int from_y, int to_x, int to_y)
4787 {
4788   int from_sx, from_sy;
4789   int to_sx, to_sy;
4790
4791   if (from_x > to_x)
4792     swap_numbers(&from_x, &to_x);
4793
4794   if (from_y > to_y)
4795     swap_numbers(&from_y, &to_y);
4796
4797   from_sx = SX + from_x * MINI_TILEX;
4798   from_sy = SY + from_y * MINI_TILEY;
4799   to_sx = SX + to_x * MINI_TILEX + MINI_TILEX - 1;
4800   to_sy = SY + to_y * MINI_TILEY + MINI_TILEY - 1;
4801
4802   DrawSimpleWhiteLine(drawto, from_sx, from_sy, to_sx, from_sy);
4803   DrawSimpleWhiteLine(drawto, to_sx, from_sy, to_sx, to_sy);
4804   DrawSimpleWhiteLine(drawto, to_sx, to_sy, from_sx, to_sy);
4805   DrawSimpleWhiteLine(drawto, from_sx, to_sy, from_sx, from_sy);
4806
4807   if (from_x == to_x && from_y == to_y)
4808     MarkTileDirty(from_x/2, from_y/2);
4809   else
4810     redraw_mask |= REDRAW_FIELD;
4811 }
4812
4813 static void SelectArea(int from_x, int from_y, int to_x, int to_y,
4814                        int element, boolean change_level)
4815 {
4816   if (element == -1 || change_level)
4817     DrawBox(from_x, from_y, to_x, to_y, -1, FALSE);
4818   else
4819     DrawAreaBorder(from_x, from_y, to_x, to_y);
4820 }
4821
4822 /* values for CopyBrushExt() */
4823 #define CB_AREA_TO_BRUSH        0
4824 #define CB_BRUSH_TO_CURSOR      1
4825 #define CB_BRUSH_TO_LEVEL       2
4826 #define CB_DELETE_OLD_CURSOR    3
4827
4828 static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
4829                          int button, int mode)
4830 {
4831   static short brush_buffer[MAX_ED_FIELDX][MAX_ED_FIELDY];
4832   static int brush_width, brush_height;
4833   static int last_cursor_x = -1, last_cursor_y = -1;
4834   static boolean delete_old_brush;
4835   int new_element = BUTTON_ELEMENT(button);
4836   int x, y;
4837
4838   if (mode == CB_DELETE_OLD_CURSOR && !delete_old_brush)
4839     return;
4840
4841   if (mode == CB_AREA_TO_BRUSH)
4842   {
4843     int from_lx, from_ly;
4844
4845     if (from_x > to_x)
4846       swap_numbers(&from_x, &to_x);
4847
4848     if (from_y > to_y)
4849       swap_numbers(&from_y, &to_y);
4850
4851     brush_width = to_x - from_x + 1;
4852     brush_height = to_y - from_y + 1;
4853
4854     from_lx = from_x + level_xpos;
4855     from_ly = from_y + level_ypos;
4856
4857     for (y=0; y<brush_height; y++)
4858     {
4859       for (x=0; x<brush_width; x++)
4860       {
4861         brush_buffer[x][y] = Feld[from_lx + x][from_ly + y];
4862
4863         if (button != 1)
4864           DrawLineElement(from_x + x, from_y + y, new_element, TRUE);
4865       }
4866     }
4867
4868     if (button != 1)
4869       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4870
4871     delete_old_brush = FALSE;
4872   }
4873   else if (mode == CB_BRUSH_TO_CURSOR || mode == CB_DELETE_OLD_CURSOR ||
4874            mode == CB_BRUSH_TO_LEVEL)
4875   {
4876     int cursor_x = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_x : from_x);
4877     int cursor_y = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_y : from_y);
4878     int cursor_from_x = cursor_x - brush_width / 2;
4879     int cursor_from_y = cursor_y - brush_height / 2;
4880     int border_from_x = cursor_x, border_from_y = cursor_y;
4881     int border_to_x = cursor_x, border_to_y = cursor_y;
4882
4883     if (mode != CB_DELETE_OLD_CURSOR && delete_old_brush)
4884       CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
4885
4886     if (!IN_ED_FIELD(cursor_x, cursor_y) ||
4887         !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos))
4888     {
4889       delete_old_brush = FALSE;
4890       return;
4891     }
4892
4893     for (y=0; y<brush_height; y++)
4894     {
4895       for (x=0; x<brush_width; x++)
4896       {
4897         int sx = cursor_from_x + x;
4898         int sy = cursor_from_y + y;
4899         int lx = sx + level_xpos;
4900         int ly = sy + level_ypos;
4901         boolean change_level = (mode == CB_BRUSH_TO_LEVEL);
4902         int element = (mode == CB_DELETE_OLD_CURSOR ? -1 :
4903                        mode == CB_BRUSH_TO_CURSOR || button == 1 ?
4904                        brush_buffer[x][y] : new_element);
4905
4906         if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
4907         {
4908           if (sx < border_from_x)
4909             border_from_x = sx;
4910           else if (sx > border_to_x)
4911             border_to_x = sx;
4912           if (sy < border_from_y)
4913             border_from_y = sy;
4914           else if (sy > border_to_y)
4915             border_to_y = sy;
4916
4917           DrawLineElement(sx, sy, element, change_level);
4918         }
4919       }
4920     }
4921
4922     if (mode != CB_DELETE_OLD_CURSOR)
4923       DrawAreaBorder(border_from_x, border_from_y, border_to_x, border_to_y);
4924
4925     last_cursor_x = cursor_x;
4926     last_cursor_y = cursor_y;
4927     delete_old_brush = TRUE;
4928   }
4929 }
4930
4931 static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y,
4932                             int button)
4933 {
4934   CopyBrushExt(from_x, from_y, to_x, to_y, button, CB_AREA_TO_BRUSH);
4935 }
4936
4937 static void CopyBrushToLevel(int x, int y, int button)
4938 {
4939   CopyBrushExt(x, y, 0, 0, button, CB_BRUSH_TO_LEVEL);
4940 }
4941
4942 static void CopyBrushToCursor(int x, int y)
4943 {
4944   CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR);
4945 }
4946
4947 static void DeleteBrushFromCursor()
4948 {
4949   CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
4950 }
4951
4952 static void FloodFill(int from_x, int from_y, int fill_element)
4953 {
4954   int i,x,y;
4955   int old_element;
4956   static int check[4][2] = { {-1,0}, {0,-1}, {1,0}, {0,1} };
4957   static int safety = 0;
4958
4959   /* check if starting field still has the desired content */
4960   if (Feld[from_x][from_y] == fill_element)
4961     return;
4962
4963   safety++;
4964
4965   if (safety > lev_fieldx*lev_fieldy)
4966     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
4967
4968   old_element = Feld[from_x][from_y];
4969   Feld[from_x][from_y] = fill_element;
4970
4971   for(i=0;i<4;i++)
4972   {
4973     x = from_x + check[i][0];
4974     y = from_y + check[i][1];
4975
4976     if (IN_LEV_FIELD(x,y) && Feld[x][y] == old_element)
4977       FloodFill(x, y, fill_element);
4978   }
4979
4980   safety--;
4981 }
4982
4983 /* values for DrawLevelText() modes */
4984 #define TEXT_INIT               0
4985 #define TEXT_SETCURSOR          1
4986 #define TEXT_WRITECHAR          2
4987 #define TEXT_BACKSPACE          3
4988 #define TEXT_NEWLINE            4
4989 #define TEXT_END                5
4990 #define TEXT_QUERY_TYPING       6
4991
4992 static int DrawLevelText(int sx, int sy, char letter, int mode)
4993 {
4994   static short delete_buffer[MAX_LEV_FIELDX];
4995   static int start_sx, start_sy;
4996   static int last_sx, last_sy;
4997   static boolean typing = FALSE;
4998   int letter_element = EL_CHAR_ASCII0 + letter;
4999   int lx = 0, ly = 0;
5000
5001   /* map lower case letters to upper case and convert special characters */
5002   if (letter >= 'a' && letter <= 'z')
5003     letter_element = EL_CHAR_ASCII0 + letter + (int)('A' - 'a');
5004   else if (letter == 'ä' || letter == 'Ä')
5005     letter_element = EL_CHAR_AUMLAUT;
5006   else if (letter == 'ö' || letter == 'Ö')
5007     letter_element = EL_CHAR_OUMLAUT;
5008   else if (letter == 'ü' || letter == 'Ãœ')
5009     letter_element = EL_CHAR_UUMLAUT;
5010   else if (letter == '^')
5011     letter_element = EL_CHAR_COPYRIGHT;
5012   else
5013     letter_element = EL_CHAR_ASCII0 + letter;
5014
5015   if (mode != TEXT_INIT)
5016   {
5017     if (!typing)
5018       return FALSE;
5019
5020     if (mode != TEXT_SETCURSOR)
5021     {
5022       sx = last_sx;
5023       sy = last_sy;
5024     }
5025
5026     lx = last_sx + level_xpos;
5027     ly = last_sy + level_ypos;
5028   }
5029
5030   switch (mode)
5031   {
5032     case TEXT_INIT:
5033       if (typing)
5034         DrawLevelText(0, 0, 0, TEXT_END);
5035
5036       typing = TRUE;
5037       start_sx = last_sx = sx;
5038       start_sy = last_sy = sy;
5039       DrawLevelText(sx, sy, 0, TEXT_SETCURSOR);
5040       break;
5041
5042     case TEXT_SETCURSOR:
5043       DrawMiniElement(last_sx, last_sy, Feld[lx][ly]);
5044       DrawAreaBorder(sx, sy, sx, sy);
5045       last_sx = sx;
5046       last_sy = sy;
5047       break;
5048
5049     case TEXT_WRITECHAR:
5050       if (letter_element >= EL_CHAR_START && letter_element <= EL_CHAR_END)
5051       {
5052         delete_buffer[sx - start_sx] = Feld[lx][ly];
5053         Feld[lx][ly] = letter_element;
5054
5055         if (sx + 1 < ed_fieldx && lx + 1 < lev_fieldx)
5056           DrawLevelText(sx + 1, sy, 0, TEXT_SETCURSOR);
5057         else if (sy + 1 < ed_fieldy && ly + 1 < lev_fieldy)
5058           DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
5059         else
5060           DrawLevelText(0, 0, 0, TEXT_END);
5061       }
5062       break;
5063
5064     case TEXT_BACKSPACE:
5065       if (sx > start_sx)
5066       {
5067         Feld[lx - 1][ly] = delete_buffer[sx - start_sx - 1];
5068         DrawMiniElement(sx - 1, sy, Feld[lx - 1][ly]);
5069         DrawLevelText(sx - 1, sy, 0, TEXT_SETCURSOR);
5070       }
5071       break;
5072
5073     case TEXT_NEWLINE:
5074       if (sy + 1 < ed_fieldy - 1 && ly + 1 < lev_fieldy - 1)
5075         DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
5076       else
5077         DrawLevelText(0, 0, 0, TEXT_END);
5078       break;
5079
5080     case TEXT_END:
5081       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5082       DrawMiniElement(sx, sy, Feld[lx][ly]);
5083       typing = FALSE;
5084       break;
5085
5086     case TEXT_QUERY_TYPING:
5087       break;
5088
5089     default:
5090       break;
5091   }
5092
5093   return typing;
5094 }
5095
5096 static void SetTextCursor(int unused_sx, int unused_sy, int sx, int sy,
5097                           int element, boolean change_level)
5098 {
5099   int lx = sx + level_xpos;
5100   int ly = sy + level_ypos;
5101
5102   if (element == -1)
5103     DrawMiniElement(sx, sy, Feld[lx][ly]);
5104   else
5105     DrawAreaBorder(sx, sy, sx, sy);
5106 }
5107
5108 static void CopyLevelToUndoBuffer(int mode)
5109 {
5110   static boolean accumulated_undo = FALSE;
5111   boolean new_undo_buffer_position = TRUE;
5112   int last_border_element;
5113   int x, y;
5114
5115   switch (mode)
5116   {
5117     case UNDO_IMMEDIATE:
5118       accumulated_undo = FALSE;
5119       break;
5120
5121     case UNDO_ACCUMULATE:
5122       if (accumulated_undo)
5123         new_undo_buffer_position = FALSE;
5124       accumulated_undo = TRUE;
5125       break;
5126
5127     default:
5128       break;
5129   }
5130
5131   if (new_undo_buffer_position)
5132   {
5133     /* new position in undo buffer ring */
5134     undo_buffer_position = (undo_buffer_position + 1) % NUM_UNDO_STEPS;
5135
5136     if (undo_buffer_steps < NUM_UNDO_STEPS - 1)
5137       undo_buffer_steps++;
5138   }
5139
5140   for(x=0; x<lev_fieldx; x++)
5141     for(y=0; y<lev_fieldy; y++)
5142       UndoBuffer[undo_buffer_position][x][y] = Feld[x][y];
5143
5144   /* check if drawing operation forces change of border style */
5145   last_border_element = BorderElement;
5146   SetBorderElement();
5147   if (BorderElement != last_border_element)
5148     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5149 }
5150
5151 static void RandomPlacement(int new_element)
5152 {
5153   static boolean free_position[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
5154   int num_free_positions;
5155   int num_percentage;
5156   int num_elements;
5157   int x, y;
5158
5159   /* determine number of free positions for the new elements */
5160   /* (maybe this statement should be formatted a bit more readable...) */
5161   num_free_positions = 0;
5162   for (x=0; x<lev_fieldx; x++)
5163     for (y=0; y<lev_fieldy; y++)
5164       if ((free_position[x][y] =
5165            ((random_placement_background_restricted &&
5166              Feld[x][y] == random_placement_background_element) ||
5167             (!random_placement_background_restricted &&
5168              Feld[x][y] != new_element))) == TRUE)
5169         num_free_positions++;
5170
5171   /* determine number of new elements to place there */
5172   num_percentage = num_free_positions * random_placement_value / 100;
5173   num_elements = (random_placement_method == RANDOM_USE_PERCENTAGE ?
5174                   num_percentage : random_placement_value);
5175
5176   /* if not more free positions than elements to place, fill whole level */
5177   if (num_elements >= num_free_positions)
5178   {
5179     for (x=0; x<lev_fieldx; x++)
5180       for (y=0; y<lev_fieldy; y++)
5181         Feld[x][y] = new_element;
5182
5183     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5184     CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5185     return;
5186   }
5187
5188   while (num_elements > 0)
5189   {
5190     x = RND(lev_fieldx);
5191     y = RND(lev_fieldy);
5192
5193     /* don't place element at the same position twice */
5194     if (free_position[x][y])
5195     {
5196       free_position[x][y] = FALSE;
5197       Feld[x][y] = new_element;
5198       num_elements--;
5199     }
5200   }
5201
5202   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5203   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5204 }
5205
5206 void WrapLevel(int dx, int dy)
5207 {
5208   int wrap_dx = lev_fieldx - dx;
5209   int wrap_dy = lev_fieldy - dy;
5210   int x, y;
5211
5212   for(x=0; x<lev_fieldx; x++)
5213     for(y=0; y<lev_fieldy; y++)
5214       FieldBackup[x][y] = Feld[x][y];
5215
5216   for(x=0; x<lev_fieldx; x++)
5217     for(y=0; y<lev_fieldy; y++)
5218       Feld[x][y] =
5219         FieldBackup[(x + wrap_dx) % lev_fieldx][(y + wrap_dy) % lev_fieldy];
5220
5221   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5222   CopyLevelToUndoBuffer(UNDO_ACCUMULATE);
5223 }
5224
5225 static void HandleDrawingAreas(struct GadgetInfo *gi)
5226 {
5227   static boolean started_inside_drawing_area = FALSE;
5228   int id = gi->custom_id;
5229   boolean button_press_event;
5230   boolean button_release_event;
5231   boolean inside_drawing_area = !gi->event.off_borders;
5232   boolean draw_level = (id == GADGET_ID_DRAWING_LEVEL);
5233   int actual_drawing_function;
5234   int button = gi->event.button;
5235   int new_element = BUTTON_ELEMENT(button);
5236   int sx = gi->event.x, sy = gi->event.y;
5237   int min_sx = 0, min_sy = 0;
5238   int max_sx = gi->drawing.area_xsize - 1, max_sy = gi->drawing.area_ysize - 1;
5239   int item_xsize = gi->drawing.item_xsize, item_ysize = gi->drawing.item_ysize;
5240   int lx = 0, ly = 0;
5241   int min_lx = 0, min_ly = 0;
5242   int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
5243   int x, y;
5244
5245   /* handle info callback for each invocation of action callback */
5246   gi->callback_info(gi);
5247
5248   button_press_event = (gi->event.type == GD_EVENT_PRESSED);
5249   button_release_event = (gi->event.type == GD_EVENT_RELEASED);
5250
5251   /* make sure to stay inside drawing area boundaries */
5252   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
5253   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
5254
5255   if (draw_level)
5256   {
5257     /* get positions inside level field */
5258     lx = sx + level_xpos;
5259     ly = sy + level_ypos;
5260
5261     if (!IN_LEV_FIELD(lx, ly))
5262       inside_drawing_area = FALSE;
5263
5264     /* make sure to stay inside level field boundaries */
5265     lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
5266     ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
5267
5268     /* correct drawing area positions accordingly */
5269     sx = lx - level_xpos;
5270     sy = ly - level_ypos;
5271   }
5272
5273   if (button_press_event)
5274     started_inside_drawing_area = inside_drawing_area;
5275
5276   if (!started_inside_drawing_area)
5277     return;
5278
5279   if (!button && !button_release_event)
5280     return;
5281
5282   /* automatically switch to 'single item' drawing mode, if needed */
5283   actual_drawing_function =
5284     (draw_level || drawing_function == GADGET_ID_PICK_ELEMENT ?
5285      drawing_function : GADGET_ID_SINGLE_ITEMS);
5286
5287   switch (actual_drawing_function)
5288   {
5289     case GADGET_ID_SINGLE_ITEMS:
5290       if (draw_level)
5291       {
5292         if (button_release_event)
5293         {
5294           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5295
5296           if (edit_mode == ED_MODE_DRAWING && draw_with_brush &&
5297               !inside_drawing_area)
5298             DeleteBrushFromCursor();
5299         }
5300
5301         if (!button)
5302           break;
5303
5304         if (draw_with_brush)
5305         {
5306           if (!button_release_event)
5307             CopyBrushToLevel(sx, sy, button);
5308         }
5309         else if (new_element != Feld[lx][ly])
5310         {
5311           if (new_element == EL_PLAYER_1)
5312           {
5313             /* remove player at old position */
5314             for(y=0; y<lev_fieldy; y++)
5315             {
5316               for(x=0; x<lev_fieldx; x++)
5317               {
5318                 if (Feld[x][y] == EL_PLAYER_1)
5319                 {
5320                   Feld[x][y] = EL_EMPTY;
5321                   if (x - level_xpos >= 0 && x - level_xpos < ed_fieldx &&
5322                       y - level_ypos >= 0 && y - level_ypos < ed_fieldy)
5323                     DrawMiniElement(x - level_xpos, y - level_ypos,
5324                                     EL_EMPTY);
5325                 }
5326               }
5327             }
5328           }
5329
5330           Feld[lx][ly] = new_element;
5331           DrawMiniElement(sx, sy, new_element);
5332         }
5333       }
5334       else
5335       {
5336         if (item_xsize == MINI_TILEX && item_ysize == MINI_TILEY)
5337           DrawMiniGraphicExt(drawto,
5338                              gi->x + sx * MINI_TILEX,
5339                              gi->y + sy * MINI_TILEY,
5340                              el2edimg(new_element));
5341         else
5342           DrawGraphicExt(drawto,
5343                          gi->x + sx * TILEX,
5344                          gi->y + sy * TILEY,
5345                          el2img(new_element), 0);
5346
5347         if (id == GADGET_ID_AMOEBA_CONTENT)
5348           level.amoeba_content = new_element;
5349         else if (id == GADGET_ID_CUSTOM_GRAPHIC)
5350         {
5351           new_element = GFX_ELEMENT(new_element);
5352           custom_element.gfx_element = new_element;
5353           element_info[properties_element] = custom_element;
5354
5355           ModifyEditorElementList();
5356           RedrawDrawingElements();
5357
5358           FrameCounter = 0;     /* restart animation frame counter */
5359         }
5360         else if (id == GADGET_ID_CUSTOM_CONTENT)
5361         {
5362           custom_element.content[sx][sy] = new_element;
5363           element_info[properties_element] = custom_element;
5364         }
5365         else if (id == GADGET_ID_CUSTOM_CHANGED)
5366         {
5367           custom_element.change.successor = new_element;
5368           element_info[properties_element] = custom_element;
5369         }
5370         else if (id == GADGET_ID_RANDOM_BACKGROUND)
5371           random_placement_background_element = new_element;
5372         else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
5373                  id <= GADGET_ID_ELEMENT_CONTENT_7)
5374           level.yamyam_content[id - GADGET_ID_ELEMENT_CONTENT_0][sx][sy] =
5375             new_element;
5376       }
5377       break;
5378
5379     case GADGET_ID_CONNECTED_ITEMS:
5380       {
5381         static int last_sx = -1;
5382         static int last_sy = -1;
5383
5384         if (button_release_event)
5385           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5386
5387         if (button)
5388         {
5389           if (!button_press_event)
5390             DrawLine(last_sx, last_sy, sx, sy, new_element, TRUE);
5391
5392           last_sx = sx;
5393           last_sy = sy;
5394         }
5395       }
5396       break;
5397
5398     case GADGET_ID_LINE:
5399     case GADGET_ID_ARC:
5400     case GADGET_ID_RECTANGLE:
5401     case GADGET_ID_FILLED_BOX:
5402     case GADGET_ID_GRAB_BRUSH:
5403     case GADGET_ID_TEXT:
5404       {
5405         static int last_sx = -1;
5406         static int last_sy = -1;
5407         static int start_sx = -1;
5408         static int start_sy = -1;
5409         void (*draw_func)(int, int, int, int, int, boolean);
5410
5411         if (drawing_function == GADGET_ID_LINE)
5412           draw_func = DrawLine;
5413         else if (drawing_function == GADGET_ID_ARC)
5414           draw_func = DrawArc;
5415         else if (drawing_function == GADGET_ID_RECTANGLE)
5416           draw_func = DrawBox;
5417         else if (drawing_function == GADGET_ID_FILLED_BOX)
5418           draw_func = DrawFilledBox;
5419         else if (drawing_function == GADGET_ID_GRAB_BRUSH)
5420           draw_func = SelectArea;
5421         else /* (drawing_function == GADGET_ID_TEXT) */
5422           draw_func = SetTextCursor;
5423
5424         if (button_press_event)
5425         {
5426           draw_func(sx, sy, sx, sy, new_element, FALSE);
5427           start_sx = last_sx = sx;
5428           start_sy = last_sy = sy;
5429
5430           if (drawing_function == GADGET_ID_TEXT)
5431             DrawLevelText(0, 0, 0, TEXT_END);
5432         }
5433         else if (button_release_event)
5434         {
5435           draw_func(start_sx, start_sy, sx, sy, new_element, TRUE);
5436           if (drawing_function == GADGET_ID_GRAB_BRUSH)
5437           {
5438             CopyAreaToBrush(start_sx, start_sy, sx, sy, button);
5439             CopyBrushToCursor(sx, sy);
5440             ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS],
5441                           MB_LEFTBUTTON);
5442             draw_with_brush = TRUE;
5443           }
5444           else if (drawing_function == GADGET_ID_TEXT)
5445             DrawLevelText(sx, sy, 0, TEXT_INIT);
5446           else
5447             CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5448         }
5449         else if (last_sx != sx || last_sy != sy)
5450         {
5451           draw_func(start_sx, start_sy, last_sx, last_sy, -1, FALSE);
5452           draw_func(start_sx, start_sy, sx, sy, new_element, FALSE);
5453           last_sx = sx;
5454           last_sy = sy;
5455         }
5456       }
5457       break;
5458
5459     case GADGET_ID_FLOOD_FILL:
5460       if (button_press_event && Feld[lx][ly] != new_element)
5461       {
5462         FloodFill(lx, ly, new_element);
5463         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5464         CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5465       }
5466       break;
5467
5468     case GADGET_ID_PICK_ELEMENT:
5469       if (button_release_event)
5470         ClickOnGadget(level_editor_gadget[last_drawing_function],
5471                       MB_LEFTBUTTON);
5472       else if (draw_level)
5473         PickDrawingElement(button, Feld[lx][ly]);
5474       else if (id == GADGET_ID_AMOEBA_CONTENT)
5475         PickDrawingElement(button, level.amoeba_content);
5476       else if (id == GADGET_ID_CUSTOM_GRAPHIC)
5477         PickDrawingElement(button, custom_element.gfx_element);
5478       else if (id == GADGET_ID_CUSTOM_CONTENT)
5479         PickDrawingElement(button, custom_element.content[sx][sy]);
5480       else if (id == GADGET_ID_CUSTOM_CHANGED)
5481         PickDrawingElement(button, custom_element.change.successor);
5482       else if (id == GADGET_ID_RANDOM_BACKGROUND)
5483         PickDrawingElement(button, random_placement_background_element);
5484       else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
5485                id <= GADGET_ID_ELEMENT_CONTENT_7)
5486       {
5487         int i = id - GADGET_ID_ELEMENT_CONTENT_0;
5488
5489         PickDrawingElement(button, level.yamyam_content[i][sx][sy]);
5490       }
5491
5492       break;
5493
5494     default:
5495       break;
5496   }
5497 }
5498
5499 static void HandleCounterButtons(struct GadgetInfo *gi)
5500 {
5501   int gadget_id = gi->custom_id;
5502   int counter_id = gi->custom_type_id;
5503   int button = gi->event.button;
5504   int *counter_value = counterbutton_info[counter_id].value;
5505   int step = BUTTON_STEPSIZE(button) *
5506     (gadget_id == counterbutton_info[counter_id].gadget_id_down ? -1 : +1);
5507
5508   if (counter_id == ED_COUNTER_ID_SELECT_LEVEL)
5509   {
5510     boolean pressed = (gi->event.type == GD_EVENT_PRESSED);
5511     boolean released = (gi->event.type == GD_EVENT_RELEASED);
5512     boolean level_changed = LevelChanged();
5513
5514     if ((level_changed && pressed) || (!level_changed && released))
5515       return;
5516
5517     if (level_changed && !Request("Level has changed! Discard changes ?",
5518                                   REQ_ASK))
5519     {
5520       if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
5521         ModifyEditorCounter(counter_id, *counter_value);
5522       return;
5523     }
5524   }
5525
5526   if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
5527     *counter_value = gi->text.number_value;
5528   else
5529     ModifyEditorCounter(counter_id, *counter_value + step);
5530
5531   switch (counter_id)
5532   {
5533     case ED_COUNTER_ID_ELEMENT_CONTENT:
5534       DrawElementContentAreas();
5535       break;
5536
5537     case ED_COUNTER_ID_LEVEL_XSIZE:
5538     case ED_COUNTER_ID_LEVEL_YSIZE:
5539       lev_fieldx = level.fieldx;
5540       lev_fieldy = level.fieldy;
5541       break;
5542
5543     case ED_COUNTER_ID_SELECT_LEVEL:
5544       LoadLevel(level_nr);
5545       TapeErase();
5546       ResetUndoBuffer();
5547       DrawEditModeWindow();
5548       break;
5549
5550     default:
5551       break;
5552   }
5553
5554   if ((counter_id >= ED_COUNTER_ID_CUSTOM_FIRST &&
5555        counter_id <= ED_COUNTER_ID_CUSTOM_LAST) ||
5556       (counter_id >= ED_COUNTER_ID_CHANGE_FIRST &&
5557        counter_id <= ED_COUNTER_ID_CHANGE_LAST))
5558     CopyCustomElementPropertiesToGame(properties_element);
5559 }
5560
5561 static void HandleTextInputGadgets(struct GadgetInfo *gi)
5562 {
5563   strcpy(textinput_info[gi->custom_type_id].value, gi->text.value);
5564 }
5565
5566 static void HandleSelectboxGadgets(struct GadgetInfo *gi)
5567 {
5568   int type_id = gi->custom_type_id;
5569
5570   *selectbox_info[type_id].value =
5571     selectbox_info[type_id].options[gi->selectbox.index].value;
5572
5573   if ((type_id >= ED_SELECTBOX_ID_CUSTOM_FIRST &&
5574        type_id <= ED_SELECTBOX_ID_CUSTOM_LAST) ||
5575       (type_id >= ED_SELECTBOX_ID_CHANGE_FIRST &&
5576        type_id <= ED_SELECTBOX_ID_CHANGE_LAST))
5577     CopyCustomElementPropertiesToGame(properties_element);
5578 }
5579
5580 static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
5581 {
5582   if (gi->custom_type_id >= ED_TEXTBUTTON_ID_PROPERTIES_INFO &&
5583       gi->custom_type_id <= ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED)
5584   {
5585     edit_mode_properties = gi->custom_type_id;
5586
5587     DrawPropertiesWindow();
5588   }
5589 }
5590
5591 static void HandleRadiobuttons(struct GadgetInfo *gi)
5592 {
5593   *radiobutton_info[gi->custom_type_id].value =
5594     radiobutton_info[gi->custom_type_id].checked_value;
5595 }
5596
5597 static void HandleCheckbuttons(struct GadgetInfo *gi)
5598 {
5599   int type_id = gi->custom_type_id;
5600
5601   *checkbutton_info[type_id].value ^= TRUE;
5602
5603   if ((type_id >= ED_CHECKBUTTON_ID_CUSTOM_FIRST &&
5604        type_id <= ED_CHECKBUTTON_ID_CUSTOM_LAST) ||
5605       (type_id >= ED_CHECKBUTTON_ID_CHANGE_FIRST &&
5606        type_id <= ED_CHECKBUTTON_ID_CHANGE_LAST))
5607     CopyCustomElementPropertiesToGame(properties_element);
5608
5609   if (type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC)
5610   {
5611     ModifyEditorElementList();
5612     RedrawDrawingElements();
5613   }
5614 }
5615
5616 static void HandleControlButtons(struct GadgetInfo *gi)
5617 {
5618   int id = gi->custom_id;
5619   int button = gi->event.button;
5620   int step = BUTTON_STEPSIZE(button);
5621   int new_element = BUTTON_ELEMENT(button);
5622   int x, y;
5623
5624   if (edit_mode == ED_MODE_DRAWING && drawing_function == GADGET_ID_TEXT)
5625     DrawLevelText(0, 0, 0, TEXT_END);
5626
5627   if (id < ED_NUM_CTRL1_BUTTONS && id != GADGET_ID_PROPERTIES &&
5628       id != GADGET_ID_PICK_ELEMENT && edit_mode != ED_MODE_DRAWING &&
5629       drawing_function != GADGET_ID_PICK_ELEMENT)
5630   {
5631     DrawDrawingWindow();
5632     edit_mode = ED_MODE_DRAWING;
5633   }
5634
5635   switch (id)
5636   {
5637     case GADGET_ID_SCROLL_LEFT:
5638       if (level_xpos >= 0)
5639       {
5640         if (lev_fieldx < ed_fieldx - 2)
5641           break;
5642
5643         level_xpos -= step;
5644         if (level_xpos < -1)
5645           level_xpos = -1;
5646         if (button == 1)
5647           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_RIGHT);
5648         else
5649           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5650
5651         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
5652                      GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
5653       }
5654       break;
5655
5656     case GADGET_ID_SCROLL_RIGHT:
5657       if (level_xpos <= lev_fieldx - ed_fieldx)
5658       {
5659         if (lev_fieldx < ed_fieldx - 2)
5660           break;
5661
5662         level_xpos += step;
5663         if (level_xpos > lev_fieldx - ed_fieldx + 1)
5664           level_xpos = lev_fieldx - ed_fieldx + 1;
5665         if (button == 1)
5666           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_LEFT);
5667         else
5668           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5669
5670         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
5671                      GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
5672       }
5673       break;
5674
5675     case GADGET_ID_SCROLL_UP:
5676       if (level_ypos >= 0)
5677       {
5678         if (lev_fieldy < ed_fieldy - 2)
5679           break;
5680
5681         level_ypos -= step;
5682         if (level_ypos < -1)
5683           level_ypos = -1;
5684         if (button == 1)
5685           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_DOWN);
5686         else
5687           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5688
5689         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
5690                      GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
5691       }
5692       break;
5693
5694     case GADGET_ID_SCROLL_DOWN:
5695       if (level_ypos <= lev_fieldy - ed_fieldy)
5696       {
5697         if (lev_fieldy < ed_fieldy - 2)
5698           break;
5699
5700         level_ypos += step;
5701         if (level_ypos > lev_fieldy - ed_fieldy + 1)
5702           level_ypos = lev_fieldy - ed_fieldy + 1;
5703         if (button == 1)
5704           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_UP);
5705         else
5706           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5707
5708         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
5709                      GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
5710       }
5711       break;
5712
5713     case GADGET_ID_SCROLL_HORIZONTAL:
5714       level_xpos = gi->event.item_position - 1;
5715       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5716       break;
5717
5718     case GADGET_ID_SCROLL_VERTICAL:
5719       level_ypos = gi->event.item_position - 1;
5720       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5721       break;
5722
5723     case GADGET_ID_SCROLL_LIST_UP:
5724     case GADGET_ID_SCROLL_LIST_DOWN:
5725     case GADGET_ID_SCROLL_LIST_VERTICAL:
5726       if (id == GADGET_ID_SCROLL_LIST_VERTICAL)
5727         element_shift = gi->event.item_position * ED_ELEMENTLIST_BUTTONS_HORIZ;
5728       else
5729       {
5730         step *= (id == GADGET_ID_SCROLL_LIST_UP ? -1 : +1);
5731         element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
5732
5733         if (element_shift < 0)
5734           element_shift = 0;
5735         if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
5736           element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
5737
5738         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
5739                      GDI_SCROLLBAR_ITEM_POSITION,
5740                      element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
5741       }
5742
5743       ModifyEditorElementList();
5744
5745       break;
5746
5747     case GADGET_ID_WRAP_LEFT:
5748       WrapLevel(-step, 0);
5749       break;
5750
5751     case GADGET_ID_WRAP_RIGHT:
5752       WrapLevel(step, 0);
5753       break;
5754
5755     case GADGET_ID_WRAP_UP:
5756       WrapLevel(0, -step);
5757       break;
5758
5759     case GADGET_ID_WRAP_DOWN:
5760       WrapLevel(0, step);
5761       break;
5762
5763     case GADGET_ID_SINGLE_ITEMS:
5764     case GADGET_ID_CONNECTED_ITEMS:
5765     case GADGET_ID_LINE:
5766     case GADGET_ID_ARC:
5767     case GADGET_ID_TEXT:
5768     case GADGET_ID_RECTANGLE:
5769     case GADGET_ID_FILLED_BOX:
5770     case GADGET_ID_FLOOD_FILL:
5771     case GADGET_ID_GRAB_BRUSH:
5772     case GADGET_ID_PICK_ELEMENT:
5773       if (drawing_function != GADGET_ID_PICK_ELEMENT)
5774         last_drawing_function = drawing_function;
5775       drawing_function = id;
5776       draw_with_brush = FALSE;
5777       break;
5778
5779     case GADGET_ID_RANDOM_PLACEMENT:
5780       RandomPlacement(new_element);
5781       break;
5782
5783     case GADGET_ID_PROPERTIES:
5784       if (edit_mode != ED_MODE_PROPERTIES)
5785       {
5786         properties_element = new_element;
5787         DrawPropertiesWindow();
5788         edit_mode = ED_MODE_PROPERTIES;
5789       }
5790       else
5791       {
5792         DrawDrawingWindow();
5793         edit_mode = ED_MODE_DRAWING;
5794       }
5795       break;
5796
5797     case GADGET_ID_UNDO:
5798       if (undo_buffer_steps == 0)
5799       {
5800         Request("Undo buffer empty !", REQ_CONFIRM);
5801         break;
5802       }
5803
5804       if (edit_mode != ED_MODE_DRAWING)
5805       {
5806         DrawDrawingWindow();
5807         edit_mode = ED_MODE_DRAWING;
5808       }
5809
5810       undo_buffer_position =
5811         (undo_buffer_position - 1 + NUM_UNDO_STEPS) % NUM_UNDO_STEPS;
5812       undo_buffer_steps--;
5813
5814       for(x=0; x<lev_fieldx; x++)
5815         for(y=0; y<lev_fieldy; y++)
5816           Feld[x][y] = UndoBuffer[undo_buffer_position][x][y];
5817       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos,level_ypos);
5818       break;
5819
5820     case GADGET_ID_INFO:
5821       if (edit_mode != ED_MODE_INFO)
5822       {
5823         DrawLevelInfoWindow();
5824         edit_mode = ED_MODE_INFO;
5825       }
5826       else
5827       {
5828         DrawDrawingWindow();
5829         edit_mode = ED_MODE_DRAWING;
5830       }
5831       break;
5832
5833     case GADGET_ID_CLEAR:
5834       if (edit_mode != ED_MODE_DRAWING)
5835       {
5836         DrawDrawingWindow();
5837         edit_mode = ED_MODE_DRAWING;
5838       }
5839
5840       for(x=0; x<MAX_LEV_FIELDX; x++) 
5841         for(y=0; y<MAX_LEV_FIELDY; y++) 
5842           Feld[x][y] = (button == 1 ? EL_EMPTY : new_element);
5843       CopyLevelToUndoBuffer(GADGET_ID_CLEAR);
5844
5845       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5846       break;
5847
5848     case GADGET_ID_SAVE:
5849       if (leveldir_current->readonly)
5850       {
5851         Request("This level is read only !", REQ_CONFIRM);
5852         break;
5853       }
5854
5855       if (!LevelContainsPlayer)
5856         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
5857       else
5858       {
5859         if (Request("Save this level and kill the old ?", REQ_ASK))
5860         {
5861           CopyPlayfield(Feld, Ur);
5862
5863           SaveLevel(level_nr);
5864         }
5865       }
5866       break;
5867
5868     case GADGET_ID_TEST:
5869       if (!LevelContainsPlayer)
5870         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
5871       else
5872       {
5873         if (LevelChanged())
5874           level.game_version = GAME_VERSION_ACTUAL;
5875
5876         CopyPlayfield(Ur, FieldBackup);
5877         CopyPlayfield(Feld, Ur);
5878
5879         UnmapLevelEditorGadgets();
5880         UndrawSpecialEditorDoor();
5881
5882         CloseDoor(DOOR_CLOSE_ALL);
5883
5884         DrawCompleteVideoDisplay();
5885
5886         if (setup.autorecord)
5887           TapeStartRecording();
5888
5889         level_editor_test_game = TRUE;
5890         game_status = GAME_MODE_PLAYING;
5891
5892         InitGame();
5893       }
5894       break;
5895
5896     case GADGET_ID_EXIT:
5897       RequestExitLevelEditor(TRUE);     /* if level has changed, ask user */
5898       break;
5899
5900     default:
5901       if (id >= GADGET_ID_ELEMENTLIST_FIRST &&
5902           id <= GADGET_ID_ELEMENTLIST_LAST)
5903       {
5904         int element_position = id - GADGET_ID_ELEMENTLIST_FIRST;
5905         int new_element = editor_elements[element_position + element_shift];
5906
5907         PickDrawingElement(button, new_element);
5908
5909         if (!stick_element_properties_window &&
5910             drawing_function != GADGET_ID_PICK_ELEMENT)
5911         {
5912           properties_element = new_element;
5913           if (edit_mode == ED_MODE_PROPERTIES)
5914             DrawPropertiesWindow();
5915         }
5916
5917         if (drawing_function == GADGET_ID_PICK_ELEMENT)
5918           ClickOnGadget(level_editor_gadget[last_drawing_function],
5919                         MB_LEFTBUTTON);
5920       }
5921 #ifdef DEBUG
5922       else if (gi->event.type == GD_EVENT_PRESSED)
5923         printf("default: HandleControlButtons: GD_EVENT_PRESSED(%d)\n", id);
5924       else if (gi->event.type == GD_EVENT_RELEASED)
5925         printf("default: HandleControlButtons: GD_EVENT_RELEASED(%d)\n", id);
5926       else if (gi->event.type == GD_EVENT_MOVING)
5927         printf("default: HandleControlButtons: GD_EVENT_MOVING(%d)\n", id);
5928       else
5929         printf("default: HandleControlButtons: ? (id == %d)\n", id);
5930 #endif
5931       break;
5932   }
5933 }
5934
5935 void HandleLevelEditorKeyInput(Key key)
5936 {
5937   char letter = getCharFromKey(key);
5938   int button = MB_LEFTBUTTON;
5939
5940   if (drawing_function == GADGET_ID_TEXT &&
5941       DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE)
5942   {
5943     if (letter)
5944       DrawLevelText(0, 0, letter, TEXT_WRITECHAR);
5945     else if (key == KSYM_Delete || key == KSYM_BackSpace)
5946       DrawLevelText(0, 0, 0, TEXT_BACKSPACE);
5947     else if (key == KSYM_Return)
5948       DrawLevelText(0, 0, 0, TEXT_NEWLINE);
5949     else if (key == KSYM_Escape)
5950       DrawLevelText(0, 0, 0, TEXT_END);
5951   }
5952   else if (button_status == MB_RELEASED)
5953   {
5954     int i, id = GADGET_ID_NONE;
5955
5956     switch (key)
5957     {
5958       case KSYM_Left:
5959         id = GADGET_ID_SCROLL_LEFT;
5960         break;
5961       case KSYM_Right:
5962         id = GADGET_ID_SCROLL_RIGHT;
5963         break;
5964       case KSYM_Up:
5965         id = GADGET_ID_SCROLL_UP;
5966         break;
5967       case KSYM_Down:
5968         id = GADGET_ID_SCROLL_DOWN;
5969         break;
5970       case KSYM_Page_Up:
5971         id = GADGET_ID_SCROLL_LIST_UP;
5972         button = MB_RIGHTBUTTON;
5973         break;
5974       case KSYM_Page_Down:
5975         id = GADGET_ID_SCROLL_LIST_DOWN;
5976         button = MB_RIGHTBUTTON;
5977         break;
5978
5979       case KSYM_Escape:
5980         if (edit_mode == ED_MODE_DRAWING)
5981         {
5982           RequestExitLevelEditor(setup.ask_on_escape);
5983         }
5984         else
5985         {
5986           DrawDrawingWindow();
5987           edit_mode = ED_MODE_DRAWING;
5988         }
5989         break;
5990
5991       default:
5992         break;
5993     }
5994
5995     if (id != GADGET_ID_NONE)
5996       ClickOnGadget(level_editor_gadget[id], button);
5997     else if (letter == '.')
5998       ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
5999     else if (key == KSYM_Return || key == setup.shortcut.toggle_pause)
6000       ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button);
6001     else
6002       for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
6003         if (letter && letter == control_info[i].shortcut)
6004           if (!anyTextGadgetActive())
6005             ClickOnGadget(level_editor_gadget[i], button);
6006   }
6007 }
6008
6009 void HandleLevelEditorIdle()
6010 {
6011   static unsigned long action_delay = 0;
6012   unsigned long action_delay_value = GameFrameDelay;
6013   int xpos = 1, ypos = 2;
6014
6015   if (edit_mode != ED_MODE_PROPERTIES)
6016     return;
6017
6018   if (!DelayReached(&action_delay, action_delay_value))
6019     return;
6020
6021   DrawGraphicAnimationExt(drawto,
6022                           SX + xpos * TILEX,
6023                           SY + ypos * TILEY + MINI_TILEY / 2,
6024                           el2img(properties_element), -1, NO_MASKING);
6025
6026   MarkTileDirty(xpos, ypos);
6027   MarkTileDirty(xpos, ypos + 1);
6028
6029   FrameCounter++;       /* increase animation frame counter */
6030 }
6031
6032 void ClearEditorGadgetInfoText()
6033 {
6034   DrawBackground(INFOTEXT_XPOS, INFOTEXT_YPOS, INFOTEXT_XSIZE, INFOTEXT_YSIZE);
6035 }
6036
6037 void HandleEditorGadgetInfoText(void *ptr)
6038 {
6039   struct GadgetInfo *gi = (struct GadgetInfo *)ptr;
6040   char infotext[MAX_OUTPUT_LINESIZE + 1];
6041   char shortcut[MAX_OUTPUT_LINESIZE + 1];
6042   int max_infotext_len = getMaxInfoTextLength();
6043
6044   if (game_status != GAME_MODE_EDITOR)
6045     return;
6046
6047   ClearEditorGadgetInfoText();
6048
6049   if (gi->event.type == GD_EVENT_INFO_LEAVING)
6050     return;
6051
6052   /* misuse this function to delete brush cursor, if needed */
6053   if (edit_mode == ED_MODE_DRAWING && draw_with_brush)
6054     DeleteBrushFromCursor();
6055
6056   if (gi == NULL || gi->info_text == NULL)
6057     return;
6058
6059   strncpy(infotext, gi->info_text, max_infotext_len);
6060   infotext[max_infotext_len] = '\0';
6061
6062   if (gi->custom_id < ED_NUM_CTRL_BUTTONS)
6063   {
6064     int key = control_info[gi->custom_id].shortcut;
6065
6066     if (key)
6067     {
6068       if (gi->custom_id == GADGET_ID_SINGLE_ITEMS)      /* special case 1 */
6069         sprintf(shortcut, " ('.' or '%c')", key);
6070       else if (gi->custom_id == GADGET_ID_TEST)         /* special case 2 */
6071         sprintf(shortcut, " ('Enter' or 'Shift-%c')", key);
6072       else                                              /* normal case */
6073         sprintf(shortcut, " ('%s%c')",
6074                 (key >= 'A' && key <= 'Z' ? "Shift-" : ""), key);
6075
6076       if (strlen(infotext) + strlen(shortcut) <= max_infotext_len)
6077         strcat(infotext, shortcut);
6078     }
6079   }
6080
6081   DrawText(INFOTEXT_XPOS, INFOTEXT_YPOS, infotext, FONT_TEXT_2);
6082 }
6083
6084 static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
6085 {
6086   static int start_lx, start_ly;
6087   char *infotext;
6088   int id = gi->custom_id;
6089   int sx = gi->event.x;
6090   int sy = gi->event.y;
6091   int lx = sx + level_xpos;
6092   int ly = sy + level_ypos;
6093   int min_sx = 0, min_sy = 0;
6094   int max_sx = gi->drawing.area_xsize - 1;
6095   int max_sy = gi->drawing.area_ysize - 1;
6096
6097   ClearEditorGadgetInfoText();
6098
6099   if (gi->event.type == GD_EVENT_INFO_LEAVING)
6100     return;
6101
6102   /* make sure to stay inside drawing area boundaries */
6103   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
6104   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
6105
6106   if (id == GADGET_ID_DRAWING_LEVEL)
6107   {
6108     if (button_status)
6109     {
6110       int min_lx = 0, min_ly = 0;
6111       int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
6112
6113       /* get positions inside level field */
6114       lx = sx + level_xpos;
6115       ly = sy + level_ypos;
6116
6117       /* make sure to stay inside level field boundaries */
6118       lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
6119       ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
6120
6121       /* correct drawing area positions accordingly */
6122       sx = lx - level_xpos;
6123       sy = ly - level_ypos;
6124     }
6125
6126     if (IN_ED_FIELD(sx,sy) && IN_LEV_FIELD(lx, ly))
6127     {
6128       if (button_status)        /* if (gi->state == GD_BUTTON_PRESSED) */
6129       {
6130         if (gi->event.type == GD_EVENT_PRESSED)
6131         {
6132           start_lx = lx;
6133           start_ly = ly;
6134         }
6135
6136         switch (drawing_function)
6137         {
6138           case GADGET_ID_SINGLE_ITEMS:
6139             infotext = "Drawing single items";
6140             break;
6141           case GADGET_ID_CONNECTED_ITEMS:
6142             infotext = "Drawing connected items";
6143             break;
6144           case GADGET_ID_LINE:
6145             infotext = "Drawing line";
6146             break;
6147           case GADGET_ID_ARC:
6148             infotext = "Drawing arc";
6149             break;
6150           case GADGET_ID_TEXT:
6151             infotext = "Setting text cursor";
6152             break;
6153           case GADGET_ID_RECTANGLE:
6154             infotext = "Drawing rectangle";
6155             break;
6156           case GADGET_ID_FILLED_BOX:
6157             infotext = "Drawing filled box";
6158             break;
6159           case GADGET_ID_FLOOD_FILL:
6160             infotext = "Flood fill";
6161             break;
6162           case GADGET_ID_GRAB_BRUSH:
6163             infotext = "Grabbing brush";
6164             break;
6165           case GADGET_ID_PICK_ELEMENT:
6166             infotext = "Picking element";
6167             break;
6168
6169           default:
6170             infotext = "Drawing position";
6171             break;
6172         }
6173
6174         if (drawing_function == GADGET_ID_PICK_ELEMENT)
6175           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6176                     "%s: %d, %d", infotext, lx, ly);
6177         else
6178           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6179                     "%s: %d, %d", infotext,
6180                     ABS(lx - start_lx) + 1, ABS(ly - start_ly) + 1);
6181       }
6182       else if (drawing_function == GADGET_ID_PICK_ELEMENT)
6183         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6184                   "%s", getElementInfoText(Feld[lx][ly]));
6185       else
6186         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6187                   "Level position: %d, %d", lx, ly);
6188     }
6189
6190     /* misuse this function to draw brush cursor, if needed */
6191     if (edit_mode == ED_MODE_DRAWING && draw_with_brush && !button_status)
6192     {
6193       if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
6194         CopyBrushToCursor(sx, sy);
6195       else
6196         DeleteBrushFromCursor();
6197     }
6198   }
6199   else if (drawing_function == GADGET_ID_PICK_ELEMENT)
6200   {
6201     if (id == GADGET_ID_AMOEBA_CONTENT)
6202       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6203                 "%s", getElementInfoText(level.amoeba_content));
6204     else if (id == GADGET_ID_CUSTOM_GRAPHIC)
6205       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6206                 "%s", getElementInfoText(custom_element.use_gfx_element));
6207     else if (id == GADGET_ID_CUSTOM_CONTENT)
6208       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6209                 "%s", getElementInfoText(custom_element.content[sx][sy]));
6210     else if (id == GADGET_ID_CUSTOM_CHANGED)
6211       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6212                 "%s", getElementInfoText(custom_element.change.successor));
6213     else if (id == GADGET_ID_RANDOM_BACKGROUND)
6214       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6215                 "%s", getElementInfoText(random_placement_background_element));
6216     else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
6217              id <= GADGET_ID_ELEMENT_CONTENT_7)
6218     {
6219       int i = id - GADGET_ID_ELEMENT_CONTENT_0;
6220
6221       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6222                 "%s", getElementInfoText(level.yamyam_content[i][sx][sy]));
6223     }
6224   }
6225   else
6226   {
6227     if (id == GADGET_ID_AMOEBA_CONTENT)
6228       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6229                 "Amoeba content");
6230     else if (id == GADGET_ID_CUSTOM_GRAPHIC)
6231       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6232                 "Custom graphic element");
6233     else if (id == GADGET_ID_CUSTOM_CONTENT)
6234       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6235                 "Custom element content position: %d, %d", sx, sy);
6236     else if (id == GADGET_ID_CUSTOM_CHANGED)
6237       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6238                 "New element after change");
6239     else if (id == GADGET_ID_RANDOM_BACKGROUND)
6240       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6241                 "Random placement background");
6242     else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
6243              id <= GADGET_ID_ELEMENT_CONTENT_7)
6244       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6245                 "Content area %d position: %d, %d",
6246                 id - GADGET_ID_ELEMENT_CONTENT_0 + 1, sx, sy);
6247   }
6248 }
6249
6250 void RequestExitLevelEditor(boolean ask_if_level_has_changed)
6251 {
6252   if (!ask_if_level_has_changed ||
6253       !LevelChanged() ||
6254       Request("Level has changed! Exit without saving ?",
6255               REQ_ASK | REQ_STAY_OPEN))
6256   {
6257     CloseDoor(DOOR_CLOSE_1);
6258     /*
6259     CloseDoor(DOOR_CLOSE_ALL);
6260     */
6261     game_status = GAME_MODE_MAIN;
6262     DrawMainMenu();
6263   }
6264   else
6265   {
6266     CloseDoor(DOOR_CLOSE_1);
6267     BlitBitmap(bitmap_db_door, bitmap_db_door,
6268                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
6269                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
6270     OpenDoor(DOOR_OPEN_1);
6271   }
6272 }