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