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