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