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