rnd-20030717-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   if (selectbox_info[id].text_left)
3482     DrawTextF(x, y, FONT_TEXT_1, selectbox_info[id].text_left);
3483
3484   if (selectbox_info[id].text_right)
3485   {
3486     struct GadgetInfo *gi = level_editor_gadget[selectbox_info[id].gadget_id];
3487
3488     x = gi->x + gi->width + xoffset_right;
3489     y = SY + selectbox_info[id].y + yoffset_right;
3490
3491     DrawText(x, y, selectbox_info[id].text_right, FONT_TEXT_1);
3492   }
3493
3494   ModifyEditorSelectbox(id, *selectbox_info[id].value);
3495
3496   MapGadget(level_editor_gadget[selectbox_info[id].gadget_id]);
3497 }
3498
3499 static void MapTextbuttonGadget(int id)
3500 {
3501   MapGadget(level_editor_gadget[textbutton_info[id].gadget_id]);
3502 }
3503
3504 static void MapRadiobuttonGadget(int id)
3505 {
3506   int xoffset_right = ED_XOFFSET_CHECKBOX;
3507   int yoffset_right = ED_BORDER_SIZE;
3508   int x = radiobutton_info[id].x + xoffset_right;
3509   int y = radiobutton_info[id].y + yoffset_right;
3510   boolean checked =
3511     (*radiobutton_info[id].value == radiobutton_info[id].checked_value);
3512
3513   if (radiobutton_info[id].text_right)
3514     DrawTextF(x, y, FONT_TEXT_1, radiobutton_info[id].text_right);
3515
3516   ModifyGadget(level_editor_gadget[radiobutton_info[id].gadget_id],
3517                GDI_CHECKED, checked, GDI_END);
3518
3519   MapGadget(level_editor_gadget[radiobutton_info[id].gadget_id]);
3520 }
3521
3522 static void MapCheckbuttonGadget(int id)
3523 {
3524   int xoffset_right = ED_XOFFSET_CHECKBOX;
3525   int yoffset_right = ED_BORDER_SIZE;
3526   int x = checkbutton_info[id].x + xoffset_right;
3527   int y = checkbutton_info[id].y + yoffset_right;
3528
3529   if (checkbutton_info[id].text_right)
3530     DrawTextF(x, y, FONT_TEXT_1, checkbutton_info[id].text_right);
3531
3532   ModifyGadget(level_editor_gadget[checkbutton_info[id].gadget_id],
3533                GDI_CHECKED, *checkbutton_info[id].value,
3534                GDI_Y, SY + checkbutton_info[id].y, GDI_END);
3535
3536   MapGadget(level_editor_gadget[checkbutton_info[id].gadget_id]);
3537 }
3538
3539 static void MapMainDrawingArea()
3540 {
3541   boolean no_horizontal_scrollbar = (lev_fieldx + 2 <= ed_fieldx);
3542   boolean no_vertical_scrollbar = (lev_fieldy + 2 <= ed_fieldy);
3543   int i;
3544
3545   for (i=ED_SCROLLBUTTON_ID_AREA_FIRST; i<=ED_SCROLLBUTTON_ID_AREA_LAST; i++)
3546   {
3547     if (((i == ED_SCROLLBUTTON_ID_AREA_LEFT ||
3548           i == ED_SCROLLBUTTON_ID_AREA_RIGHT) &&
3549          no_horizontal_scrollbar) ||
3550         ((i == ED_SCROLLBUTTON_ID_AREA_UP ||
3551           i == ED_SCROLLBUTTON_ID_AREA_DOWN) &&
3552          no_vertical_scrollbar))
3553       continue;
3554
3555     MapGadget(level_editor_gadget[scrollbutton_info[i].gadget_id]);
3556   }
3557
3558   for (i=ED_SCROLLBAR_ID_AREA_FIRST; i<=ED_SCROLLBAR_ID_AREA_LAST; i++)
3559   {
3560     if ((i == ED_SCROLLBAR_ID_AREA_HORIZONTAL && no_horizontal_scrollbar) ||
3561         (i == ED_SCROLLBAR_ID_AREA_VERTICAL && no_vertical_scrollbar))
3562       continue;
3563
3564     MapGadget(level_editor_gadget[scrollbar_info[i].gadget_id]);
3565   }
3566
3567   MapDrawingArea(GADGET_ID_DRAWING_LEVEL);
3568 }
3569
3570 static void UnmapDrawingArea(int id)
3571 {
3572   UnmapGadget(level_editor_gadget[id]);
3573 }
3574
3575 void UnmapLevelEditorWindowGadgets()
3576 {
3577   int i;
3578
3579   for (i=0; i<NUM_EDITOR_GADGETS; i++)
3580     if (level_editor_gadget[i]->x < SX + SXSIZE)
3581       UnmapGadget(level_editor_gadget[i]);
3582 }
3583
3584 void UnmapLevelEditorGadgets()
3585 {
3586   int i;
3587
3588   for (i=0; i<NUM_EDITOR_GADGETS; i++)
3589     UnmapGadget(level_editor_gadget[i]);
3590 }
3591
3592 static void ResetUndoBuffer()
3593 {
3594   undo_buffer_position = -1;
3595   undo_buffer_steps = -1;
3596   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3597 }
3598
3599 static void DrawEditModeWindow()
3600 {
3601   ModifyEditorElementList();
3602   RedrawDrawingElements();
3603
3604   if (edit_mode == ED_MODE_INFO)
3605     DrawLevelInfoWindow();
3606   else if (edit_mode == ED_MODE_PROPERTIES)
3607     DrawPropertiesWindow();
3608   else  /* edit_mode == ED_MODE_DRAWING */
3609     DrawDrawingWindow();
3610 }
3611
3612 static boolean LevelChanged()
3613 {
3614   boolean level_changed = FALSE;
3615   int x, y;
3616
3617   for(y=0; y<lev_fieldy; y++) 
3618     for(x=0; x<lev_fieldx; x++)
3619       if (Feld[x][y] != level.field[x][y])
3620         level_changed = TRUE;
3621
3622   return level_changed;
3623 }
3624
3625 static boolean LevelContainsPlayer()
3626 {
3627   boolean player_found = FALSE;
3628   int x, y;
3629
3630   for(y=0; y<lev_fieldy; y++) 
3631     for(x=0; x<lev_fieldx; x++)
3632       if (Feld[x][y] == EL_PLAYER_1 ||
3633           Feld[x][y] == EL_SP_MURPHY) 
3634         player_found = TRUE;
3635
3636   return player_found;
3637 }
3638
3639 static void CopyPlayfield(short src[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
3640                           short dst[MAX_LEV_FIELDX][MAX_LEV_FIELDY])
3641 {
3642   int x, y;
3643
3644   for(x=0; x<lev_fieldx; x++)
3645     for(y=0; y<lev_fieldy; y++) 
3646       dst[x][y] = src[x][y];
3647 }
3648
3649 static void CopyCustomElementPropertiesToEditor(int element)
3650 {
3651   int i;
3652
3653   /* needed here to initialize combined element properties */
3654   InitElementPropertiesEngine(level.game_version);
3655
3656   custom_element = element_info[element];
3657
3658   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
3659     custom_element_properties[i] = HAS_PROPERTY(element, i);
3660
3661   for (i=0; i < NUM_CHANGE_EVENTS; i++)
3662     custom_element_change_events[i] = HAS_CHANGE_EVENT(element, i);
3663
3664   /* ---------- element settings: configure (custom elements) ------------- */
3665
3666   /* set accessible layer selectbox help value */
3667   custom_element.access_type =
3668     (IS_WALKABLE(element) ? EP_WALKABLE :
3669      IS_PASSABLE(element) ? EP_PASSABLE :
3670      custom_element.access_type);
3671   custom_element.access_layer =
3672     (IS_ACCESSIBLE_OVER(element) ? EP_ACCESSIBLE_OVER :
3673      IS_ACCESSIBLE_INSIDE(element) ? EP_ACCESSIBLE_INSIDE :
3674      IS_ACCESSIBLE_UNDER(element) ? EP_ACCESSIBLE_UNDER :
3675      custom_element.access_layer);
3676   custom_element_properties[EP_ACCESSIBLE] =
3677     (IS_ACCESSIBLE_OVER(element) ||
3678      IS_ACCESSIBLE_INSIDE(element) ||
3679      IS_ACCESSIBLE_UNDER(element));
3680
3681   /* set walk-to-object action selectbox help value */
3682   custom_element.walk_to_action =
3683     (IS_DIGGABLE(element) ? EP_DIGGABLE :
3684      IS_COLLECTIBLE(element) ? EP_COLLECTIBLE :
3685      IS_PUSHABLE(element) ? EP_PUSHABLE :
3686      custom_element.walk_to_action);
3687   custom_element_properties[EP_WALK_TO_OBJECT] =
3688     (IS_DIGGABLE(element) ||
3689      IS_COLLECTIBLE(element) ||
3690      IS_PUSHABLE(element));
3691
3692   /* set smash targets selectbox help value */
3693   custom_element.smash_targets =
3694     (CAN_SMASH_EVERYTHING(element) ? EP_CAN_SMASH_EVERYTHING :
3695      CAN_SMASH_ENEMIES(element) ? EP_CAN_SMASH_ENEMIES :
3696      CAN_SMASH_PLAYER(element) ? EP_CAN_SMASH_PLAYER :
3697      custom_element.smash_targets);
3698   custom_element_properties[EP_CAN_SMASH] =
3699     (CAN_SMASH_EVERYTHING(element) ||
3700      CAN_SMASH_ENEMIES(element) ||
3701      CAN_SMASH_PLAYER(element));
3702
3703   /* set deadliness selectbox help value */
3704   custom_element.deadliness =
3705     (DONT_TOUCH(element) ? EP_DONT_TOUCH :
3706      DONT_COLLIDE_WITH(element) ? EP_DONT_COLLIDE_WITH :
3707      DONT_RUN_INTO(element) ? EP_DONT_RUN_INTO :
3708      custom_element.deadliness);
3709   custom_element_properties[EP_DEADLY] =
3710     (DONT_TOUCH(element) ||
3711      DONT_COLLIDE_WITH(element) ||
3712      DONT_RUN_INTO(element));
3713
3714   /* set consistency selectbox help value */
3715   custom_element.consistency =
3716     (IS_INDESTRUCTIBLE(element) ? EP_INDESTRUCTIBLE :
3717      CAN_EXPLODE(element) ? EP_CAN_EXPLODE :
3718      custom_element.consistency);
3719   custom_element_properties[EP_EXPLODE_RESULT] =
3720     (IS_INDESTRUCTIBLE(element) ||
3721      CAN_EXPLODE(element));
3722
3723   /* special case: sub-settings dependent from main setting */
3724   if (CAN_EXPLODE_BY_FIRE(element))
3725     custom_element.can_explode_by_fire = TRUE;
3726   if (CAN_EXPLODE_SMASHED(element))
3727     custom_element.can_explode_smashed = TRUE;
3728   if (CAN_EXPLODE_IMPACT(element))
3729     custom_element.can_explode_impact  = TRUE;
3730
3731   /* ---------- element settings: advanced (custom elements) --------------- */
3732
3733   /* set change by player selectbox help value */
3734   custom_element.change_player_action =
3735     (HAS_CHANGE_EVENT(element, CE_PUSHED_BY_PLAYER) ? CE_PUSHED_BY_PLAYER :
3736      HAS_CHANGE_EVENT(element, CE_PRESSED_BY_PLAYER) ? CE_PRESSED_BY_PLAYER :
3737      HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
3738      custom_element.change_player_action);
3739
3740   /* set change by collision selectbox help value */
3741   custom_element.change_collide_action =
3742     (HAS_CHANGE_EVENT(element, CE_SMASHED) ? CE_SMASHED :
3743      HAS_CHANGE_EVENT(element, CE_IMPACT) ? CE_IMPACT :
3744      HAS_CHANGE_EVENT(element, CE_COLLISION) ? CE_COLLISION :
3745      custom_element.change_collide_action);
3746
3747   /* set change by other element action selectbox help value */
3748   custom_element.change_other_action =
3749     (HAS_CHANGE_EVENT(element, CE_OTHER_GETS_COLLECTED) ? CE_OTHER_GETS_COLLECTED :
3750      HAS_CHANGE_EVENT(element, CE_OTHER_GETS_PUSHED) ? CE_OTHER_GETS_PUSHED :
3751      HAS_CHANGE_EVENT(element, CE_OTHER_GETS_PRESSED) ? CE_OTHER_GETS_PRESSED :
3752      HAS_CHANGE_EVENT(element, CE_OTHER_GETS_TOUCHED) ? CE_OTHER_GETS_TOUCHED :
3753      HAS_CHANGE_EVENT(element, CE_OTHER_IS_EXPLODING) ? CE_OTHER_IS_EXPLODING :
3754      HAS_CHANGE_EVENT(element, CE_OTHER_IS_CHANGING) ? CE_OTHER_IS_CHANGING :
3755      HAS_CHANGE_EVENT(element, CE_OTHER_IS_TOUCHING) ? CE_OTHER_IS_TOUCHING :
3756      custom_element.change_other_action);
3757 }
3758
3759 static void CopyCustomElementPropertiesToGame(int element)
3760 {
3761   int i;
3762   int access_type_and_layer;
3763
3764   if (level.use_custom_template)
3765   {
3766     if (Request("Copy and modify level tem- plate ?", REQ_ASK))
3767     {
3768       level.use_custom_template = FALSE;
3769       ModifyGadget(level_editor_gadget[GADGET_ID_CUSTOM_USE_TEMPLATE],
3770                    GDI_CHECKED, FALSE, GDI_END);
3771     }
3772     else
3773     {
3774       LoadLevelTemplate(-1);
3775
3776       DrawEditModeWindow();
3777     }
3778   }
3779
3780   element_info[element] = custom_element;
3781
3782   /* ---------- element settings: configure (custom elements) ------------- */
3783
3784   /* set accessible property from checkbox and selectbox */
3785   custom_element_properties[EP_WALKABLE_OVER] = FALSE;
3786   custom_element_properties[EP_WALKABLE_INSIDE] = FALSE;
3787   custom_element_properties[EP_WALKABLE_UNDER] = FALSE;
3788   custom_element_properties[EP_PASSABLE_OVER] = FALSE;
3789   custom_element_properties[EP_PASSABLE_INSIDE] = FALSE;
3790   custom_element_properties[EP_PASSABLE_UNDER] = FALSE;
3791   access_type_and_layer = ((custom_element.access_type == EP_WALKABLE ?
3792                             EP_WALKABLE_OVER : EP_PASSABLE_OVER) +
3793                            (custom_element.access_layer - EP_ACCESSIBLE_OVER));
3794   custom_element_properties[access_type_and_layer] =
3795     custom_element_properties[EP_ACCESSIBLE];
3796
3797   /* set walk-to-object property from checkbox and selectbox */
3798   custom_element_properties[EP_DIGGABLE] = FALSE;
3799   custom_element_properties[EP_COLLECTIBLE] = FALSE;
3800   custom_element_properties[EP_PUSHABLE] = FALSE;
3801   custom_element_properties[custom_element.walk_to_action] =
3802     custom_element_properties[EP_WALK_TO_OBJECT];
3803
3804   /* set smash property from checkbox and selectbox */
3805   custom_element_properties[EP_CAN_SMASH_PLAYER] = FALSE;
3806   custom_element_properties[EP_CAN_SMASH_ENEMIES] = FALSE;
3807   custom_element_properties[EP_CAN_SMASH_EVERYTHING] = FALSE;
3808   custom_element_properties[custom_element.smash_targets] =
3809     custom_element_properties[EP_CAN_SMASH];
3810
3811   /* set deadliness property from checkbox and selectbox */
3812   custom_element_properties[EP_DONT_RUN_INTO] = FALSE;
3813   custom_element_properties[EP_DONT_COLLIDE_WITH] = FALSE;
3814   custom_element_properties[EP_DONT_TOUCH] = FALSE;
3815   custom_element_properties[custom_element.deadliness] =
3816     custom_element_properties[EP_DEADLY];
3817
3818   /* set consistency property from checkbox and selectbox */
3819   custom_element_properties[EP_INDESTRUCTIBLE] = FALSE;
3820   custom_element_properties[EP_CAN_EXPLODE] = FALSE;
3821   custom_element_properties[EP_CAN_EXPLODE_BY_FIRE] = FALSE;
3822   custom_element_properties[EP_CAN_EXPLODE_SMASHED] = FALSE;
3823   custom_element_properties[EP_CAN_EXPLODE_IMPACT] = FALSE;
3824   custom_element_properties[custom_element.consistency] =
3825     custom_element_properties[EP_EXPLODE_RESULT];
3826
3827   /* special case: sub-settings dependent from main setting */
3828   if (custom_element_properties[EP_CAN_EXPLODE])
3829   {
3830     custom_element_properties[EP_CAN_EXPLODE_BY_FIRE] =
3831       custom_element.can_explode_by_fire;
3832     custom_element_properties[EP_CAN_EXPLODE_SMASHED] =
3833       custom_element.can_explode_smashed;
3834     custom_element_properties[EP_CAN_EXPLODE_IMPACT] =
3835       custom_element.can_explode_impact;
3836   }
3837
3838   /* ---------- element settings: advanced (custom elements) --------------- */
3839
3840   /* set player change event from checkbox and selectbox */
3841   custom_element_change_events[CE_TOUCHED_BY_PLAYER] = FALSE;
3842   custom_element_change_events[CE_PRESSED_BY_PLAYER] = FALSE;
3843   custom_element_change_events[CE_PUSHED_BY_PLAYER] = FALSE;
3844   custom_element_change_events[custom_element.change_player_action] =
3845     custom_element_change_events[CE_BY_PLAYER];
3846
3847   /* set collision change event from checkbox and selectbox */
3848   custom_element_change_events[CE_COLLISION] = FALSE;
3849   custom_element_change_events[CE_IMPACT] = FALSE;
3850   custom_element_change_events[CE_SMASHED] = FALSE;
3851   custom_element_change_events[custom_element.change_collide_action] =
3852     custom_element_change_events[CE_BY_COLLISION];
3853
3854   /* set other element action change event from checkbox and selectbox */
3855   custom_element_change_events[CE_OTHER_IS_TOUCHING] = FALSE;
3856   custom_element_change_events[CE_OTHER_IS_CHANGING] = FALSE;
3857   custom_element_change_events[CE_OTHER_IS_EXPLODING] = FALSE;
3858   custom_element_change_events[CE_OTHER_GETS_TOUCHED] = FALSE;
3859   custom_element_change_events[CE_OTHER_GETS_PRESSED] = FALSE;
3860   custom_element_change_events[CE_OTHER_GETS_PUSHED] = FALSE;
3861   custom_element_change_events[CE_OTHER_GETS_COLLECTED] = FALSE;
3862   custom_element_change_events[custom_element.change_other_action] =
3863     custom_element_change_events[CE_BY_OTHER];
3864
3865   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
3866     SET_PROPERTY(element, i, custom_element_properties[i]);
3867
3868   for (i=0; i < NUM_CHANGE_EVENTS; i++)
3869     SET_CHANGE_EVENT(element, i, custom_element_change_events[i]);
3870
3871   /* copy change events also to special level editor variable */
3872   custom_element = element_info[element];
3873 }
3874
3875 void DrawLevelEd()
3876 {
3877   CloseDoor(DOOR_CLOSE_ALL);
3878   OpenDoor(DOOR_OPEN_2 | DOOR_NO_DELAY);
3879
3880   if (level_editor_test_game)
3881   {
3882     CopyPlayfield(level.field, Feld);
3883     CopyPlayfield(FieldBackup, level.field);
3884
3885     level_editor_test_game = FALSE;
3886   }
3887   else
3888   {
3889     edit_mode = ED_MODE_DRAWING;
3890     edit_mode_properties = ED_MODE_PROPERTIES_INFO;
3891
3892     ResetUndoBuffer();
3893
3894     level_xpos = -1;
3895     level_ypos = -1;
3896   }
3897
3898   /* copy default editor door content to main double buffer */
3899   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3900              DOOR_GFX_PAGEX6, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3901
3902 #if 0
3903   /* draw mouse button brush elements */
3904   RedrawDrawingElements();
3905 #endif
3906
3907   /* draw bigger door */
3908   DrawSpecialEditorDoor();
3909
3910   /* draw new control window */
3911   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3912              DOOR_GFX_PAGEX8, 236, EXSIZE, EYSIZE, EX, EY);
3913
3914   redraw_mask |= REDRAW_ALL;
3915
3916   ReinitializeElementListButtons();     /* only needed after setup changes */
3917 #if 0
3918   ModifyEditorElementList();            /* may be needed for custom elements */
3919 #endif
3920
3921   UnmapTapeButtons();
3922   MapControlButtons();
3923
3924   DrawEditModeWindow();
3925
3926   /* copy actual editor door content to door double buffer for OpenDoor() */
3927   BlitBitmap(drawto, bitmap_db_door,
3928              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3929
3930   OpenDoor(DOOR_OPEN_1);
3931 }
3932
3933 static void AdjustDrawingAreaGadgets()
3934 {
3935   int ed_xsize = lev_fieldx + 2;
3936   int ed_ysize = lev_fieldy + 2;
3937   int max_ed_fieldx = MAX_ED_FIELDX;
3938   int max_ed_fieldy = MAX_ED_FIELDY;
3939   boolean horizontal_scrollbar_needed;
3940   boolean vertical_scrollbar_needed;
3941   int x, y, width, height;
3942   int xoffset, yoffset;
3943
3944   /* check if we need any scrollbars */
3945   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
3946   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
3947
3948   /* check if we have a smaller editor field because of scrollbars */
3949   if (horizontal_scrollbar_needed)
3950     max_ed_fieldy = MAX_ED_FIELDY - 1;
3951   if (vertical_scrollbar_needed)
3952     max_ed_fieldx = MAX_ED_FIELDX - 1;
3953
3954   /* check again if we now need more scrollbars because of less space */
3955   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
3956   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
3957
3958   /* check if editor field gets even smaller after adding new scrollbars */
3959   if (horizontal_scrollbar_needed)
3960     max_ed_fieldy = MAX_ED_FIELDY - 1;
3961   if (vertical_scrollbar_needed)
3962     max_ed_fieldx = MAX_ED_FIELDX - 1;
3963
3964   ed_fieldx = (ed_xsize < MAX_ED_FIELDX ? ed_xsize : max_ed_fieldx);
3965   ed_fieldy = (ed_ysize < MAX_ED_FIELDY ? ed_ysize : max_ed_fieldy);
3966
3967   ModifyGadget(level_editor_gadget[GADGET_ID_DRAWING_LEVEL],
3968                GDI_WIDTH, ed_fieldx * MINI_TILEX,
3969                GDI_HEIGHT, ed_fieldy * MINI_TILEY,
3970                GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
3971                GDI_END);
3972
3973   xoffset = (ed_fieldx == MAX_ED_FIELDX ? ED_SCROLLBUTTON_XSIZE : 0);
3974   yoffset = (ed_fieldy == MAX_ED_FIELDY ? ED_SCROLLBUTTON_YSIZE : 0);
3975
3976   x = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_RIGHT].x + xoffset;
3977   y = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_DOWN].y + yoffset;
3978
3979   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_RIGHT], GDI_X, x, GDI_END);
3980   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_DOWN], GDI_Y, y, GDI_END);
3981
3982   width = scrollbar_info[ED_SCROLLBAR_ID_AREA_HORIZONTAL].width + xoffset;
3983   height = scrollbar_info[ED_SCROLLBAR_ID_AREA_VERTICAL].height + yoffset;
3984
3985   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
3986                GDI_WIDTH, width,
3987                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldx,
3988                GDI_END);
3989   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
3990                GDI_HEIGHT, height,
3991                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldy,
3992                GDI_END);
3993 }
3994
3995 static void AdjustLevelScrollPosition()
3996 {
3997   if (level_xpos < -1)
3998     level_xpos = -1;
3999   if (level_xpos > lev_fieldx - ed_fieldx + 1)
4000     level_xpos = lev_fieldx - ed_fieldx + 1;
4001   if (lev_fieldx < ed_fieldx - 2)
4002     level_xpos = -1;
4003
4004   if (level_ypos < -1)
4005     level_ypos = -1;
4006   if (level_ypos > lev_fieldy - ed_fieldy + 1)
4007     level_ypos = lev_fieldy - ed_fieldy + 1;
4008   if (lev_fieldy < ed_fieldy - 2)
4009     level_ypos = -1;
4010 }
4011
4012 static void AdjustEditorScrollbar(int id)
4013 {
4014   struct GadgetInfo *gi = level_editor_gadget[id];
4015   int items_max, items_visible, item_position;
4016
4017   if (id == GADGET_ID_SCROLL_HORIZONTAL)
4018   {
4019     items_max = MAX(lev_fieldx + 2, ed_fieldx);
4020     items_visible = ed_fieldx;
4021     item_position = level_xpos + 1;
4022   }
4023   else
4024   {
4025     items_max = MAX(lev_fieldy + 2, ed_fieldy);
4026     items_visible = ed_fieldy;
4027     item_position = level_ypos + 1;
4028   }
4029
4030   if (item_position > items_max - items_visible)
4031     item_position = items_max - items_visible;
4032
4033   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
4034                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
4035 }
4036
4037 static void ModifyEditorCounter(int counter_id, int new_value)
4038 {
4039   int *counter_value = counterbutton_info[counter_id].value;
4040   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
4041   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4042
4043   ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
4044
4045   if (counter_value != NULL)
4046     *counter_value = gi->text.number_value;
4047 }
4048
4049 static void ModifyEditorCounterLimits(int counter_id, int min, int max)
4050 {
4051   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
4052   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4053
4054   ModifyGadget(gi, GDI_NUMBER_MIN, min, GDI_NUMBER_MAX, max, GDI_END);
4055 }
4056
4057 static void ModifyEditorSelectbox(int selectbox_id, int new_value)
4058 {
4059   int gadget_id = selectbox_info[selectbox_id].gadget_id;
4060   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4061   int new_index_value = 0;
4062   int i;
4063
4064   for(i=0; selectbox_info[selectbox_id].options[i].text != NULL; i++)
4065     if (selectbox_info[selectbox_id].options[i].value == new_value)
4066       new_index_value = i;
4067
4068   *selectbox_info[selectbox_id].value =
4069     selectbox_info[selectbox_id].options[new_index_value].value;
4070
4071   ModifyGadget(gi, GDI_SELECTBOX_INDEX, new_index_value, GDI_END);
4072 }
4073
4074 static void ModifyEditorElementList()
4075 {
4076   int i;
4077
4078   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
4079   {
4080     int gadget_id = GADGET_ID_ELEMENTLIST_FIRST + i;
4081     struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4082     struct GadgetDesign *gd = &gi->deco.design;
4083     int element = editor_elements[element_shift + i];
4084
4085     UnmapGadget(gi);
4086     getMiniGraphicSource(el2edimg(element), &gd->bitmap, &gd->x, &gd->y);
4087     ModifyGadget(gi, GDI_INFO_TEXT, getElementInfoText(element), GDI_END);
4088     MapGadget(gi);
4089   }
4090 }
4091
4092 static void PickDrawingElement(int button, int element)
4093 {
4094   if (button < 1 || button > 3)
4095     return;
4096
4097   if (button == 1)
4098   {
4099     new_element1 = element;
4100     DrawMiniGraphicExt(drawto,
4101                        DX + ED_WIN_MB_LEFT_XPOS, DY + ED_WIN_MB_LEFT_YPOS,
4102                        el2edimg(new_element1));
4103   }
4104   else if (button == 2)
4105   {
4106     new_element2 = element;
4107     DrawMiniGraphicExt(drawto,
4108                        DX + ED_WIN_MB_MIDDLE_XPOS, DY + ED_WIN_MB_MIDDLE_YPOS,
4109                        el2edimg(new_element2));
4110   }
4111   else
4112   {
4113     new_element3 = element;
4114     DrawMiniGraphicExt(drawto,
4115                        DX + ED_WIN_MB_RIGHT_XPOS, DY + ED_WIN_MB_RIGHT_YPOS,
4116                        el2edimg(new_element3));
4117   }
4118
4119   redraw_mask |= REDRAW_DOOR_1;
4120 }
4121
4122 static void RedrawDrawingElements()
4123 {
4124   PickDrawingElement(1, new_element1);
4125   PickDrawingElement(2, new_element2);
4126   PickDrawingElement(3, new_element3);
4127 }
4128
4129 static void DrawDrawingWindow()
4130 {
4131   stick_element_properties_window = FALSE;
4132
4133   SetMainBackgroundImage(IMG_UNDEFINED);
4134   ClearWindow();
4135   UnmapLevelEditorWindowGadgets();
4136
4137   AdjustDrawingAreaGadgets();
4138   AdjustLevelScrollPosition();
4139   AdjustEditorScrollbar(GADGET_ID_SCROLL_HORIZONTAL);
4140   AdjustEditorScrollbar(GADGET_ID_SCROLL_VERTICAL);
4141
4142   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4143   MapMainDrawingArea();
4144 }
4145
4146 static void DrawElementBorder(int dest_x, int dest_y, int width, int height,
4147                               boolean input)
4148 {
4149   int border_graphic =
4150     (input ? IMG_EDITOR_ELEMENT_BORDER_INPUT : IMG_EDITOR_ELEMENT_BORDER);
4151   Bitmap *src_bitmap;
4152   int src_x, src_y;
4153   int num_mini_tilex = width / MINI_TILEX + 1;
4154   int num_mini_tiley = width / MINI_TILEY + 1;
4155   int x, y;
4156
4157   getMiniGraphicSource(border_graphic, &src_bitmap, &src_x, &src_y);
4158
4159   for (y=0; y < num_mini_tiley; y++)
4160     for (x=0; x < num_mini_tilex; x++)
4161       BlitBitmap(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
4162                  dest_x - MINI_TILEX / 2 + x * MINI_TILEX,
4163                  dest_y - MINI_TILEY / 2 + y * MINI_TILEY);
4164
4165   ClearRectangle(drawto, dest_x - 1, dest_y - 1, width + 2, height + 2);
4166 }
4167
4168 static void DrawRandomPlacementBackgroundArea()
4169 {
4170   int area_x = ED_AREA_RANDOM_BACKGROUND_XPOS / MINI_TILEX;
4171   int area_y = ED_AREA_RANDOM_BACKGROUND_YPOS / MINI_TILEY;
4172   int area_sx = SX + ED_AREA_RANDOM_BACKGROUND_XPOS;
4173   int area_sy = SY + ED_AREA_RANDOM_BACKGROUND_YPOS;
4174
4175   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
4176   DrawMiniElement(area_x, area_y, random_placement_background_element);
4177
4178   MapDrawingArea(GADGET_ID_RANDOM_BACKGROUND);
4179 }
4180
4181 static void DrawLevelInfoWindow()
4182 {
4183   int i;
4184
4185   stick_element_properties_window = FALSE;
4186
4187   SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
4188   ClearWindow();
4189   UnmapLevelEditorWindowGadgets();
4190
4191   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS1_YPOS,
4192            "Level Settings", FONT_TITLE_1);
4193   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS2_YPOS,
4194            "Editor Settings", FONT_TITLE_1);
4195
4196   /* draw counter gadgets */
4197   for (i=ED_COUNTER_ID_LEVEL_FIRST; i<=ED_COUNTER_ID_LEVEL_LAST; i++)
4198     MapCounterButtons(i);
4199
4200   /* draw checkbutton gadgets */
4201   for (i=ED_CHECKBUTTON_ID_LEVEL_FIRST; i<=ED_CHECKBUTTON_ID_LEVEL_LAST; i++)
4202     MapCheckbuttonGadget(i);
4203
4204   /* draw radiobutton gadgets */
4205   for (i=ED_RADIOBUTTON_ID_LEVEL_FIRST; i<=ED_RADIOBUTTON_ID_LEVEL_LAST; i++)
4206     MapRadiobuttonGadget(i);
4207
4208   /* draw text input gadgets */
4209   for (i=ED_TEXTINPUT_ID_LEVEL_FIRST; i<=ED_TEXTINPUT_ID_LEVEL_LAST; i++)
4210     MapTextInputGadget(i);
4211
4212   /* draw drawing area */
4213   DrawRandomPlacementBackgroundArea();
4214 }
4215
4216 static void DrawAmoebaContentArea()
4217 {
4218   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
4219   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
4220   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
4221   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
4222
4223   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
4224   DrawMiniElement(area_x, area_y, level.amoeba_content);
4225
4226   DrawText(area_sx + TILEX, area_sy + 1, "Content of amoeba", FONT_TEXT_1);
4227
4228   MapDrawingArea(GADGET_ID_AMOEBA_CONTENT);
4229 }
4230
4231 static void DrawCustomGraphicElementArea()
4232 {
4233   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_GRAPHIC];
4234   int xpos = ED_AREA_ELEM_CONTENT3_XPOS;
4235   int ypos = ED_AREA_ELEM_CONTENT3_YPOS;
4236   int area_sx = SX + xpos;
4237   int area_sy = SY + ypos;
4238
4239   if (!IS_CUSTOM_ELEMENT(properties_element))
4240   {
4241     /* this should never happen */
4242     Error(ERR_WARN, "element %d is no custom element", properties_element);
4243
4244     return;
4245   }
4246
4247   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
4248   DrawMiniGraphicExt(drawto, gi->x, gi->y,
4249                      el2edimg(custom_element.gfx_element));
4250
4251   MapDrawingArea(GADGET_ID_CUSTOM_GRAPHIC);
4252 }
4253
4254 static void DrawCustomContentArea()
4255 {
4256   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_CONTENT];
4257   int area_sx = SX + ED_AREA_ELEM_CONTENT4_XPOS;
4258   int area_sy = SY + ED_AREA_ELEM_CONTENT4_YPOS;
4259   int x, y;
4260
4261   if (!IS_CUSTOM_ELEMENT(properties_element))
4262   {
4263     /* this should never happen */
4264     Error(ERR_WARN, "element %d is no custom element", properties_element);
4265
4266     return;
4267   }
4268
4269   DrawElementBorder(area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY, TRUE);
4270
4271   for (y=0; y<3; y++)
4272     for (x=0; x<3; x++)
4273       DrawMiniGraphicExt(drawto, gi->x + x * MINI_TILEX,gi->y + y * MINI_TILEY,
4274                          el2edimg(custom_element.content[x][y]));
4275
4276   MapDrawingArea(GADGET_ID_CUSTOM_CONTENT);
4277 }
4278
4279 static void DrawCustomChangeTargetArea()
4280 {
4281   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_CHANGE_TARGET];
4282   int xpos = ED_AREA_ELEM_CONTENT2_XPOS;
4283   int ypos = ED_AREA_ELEM_CONTENT2_YPOS;
4284   int area_sx = SX + xpos;
4285   int area_sy = SY + ypos;
4286
4287   if (!IS_CUSTOM_ELEMENT(properties_element))
4288   {
4289     /* this should never happen */
4290     Error(ERR_WARN, "element %d is no custom element", properties_element);
4291
4292     return;
4293   }
4294
4295   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
4296   DrawMiniGraphicExt(drawto, gi->x, gi->y,
4297                      el2edimg(custom_element.change.target_element));
4298
4299   MapDrawingArea(GADGET_ID_CUSTOM_CHANGE_TARGET);
4300 }
4301
4302 static void DrawCustomChangeContentArea()
4303 {
4304   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_CHANGE_CONTENT];
4305   int area_sx = SX + ED_AREA_ELEM_CONTENT6_XPOS;
4306   int area_sy = SY + ED_AREA_ELEM_CONTENT6_YPOS;
4307   int x, y;
4308
4309   if (!IS_CUSTOM_ELEMENT(properties_element))
4310   {
4311     /* this should never happen */
4312     Error(ERR_WARN, "element %d is no custom element", properties_element);
4313
4314     return;
4315   }
4316
4317   DrawElementBorder(area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY, TRUE);
4318
4319   for (y=0; y<3; y++)
4320     for (x=0; x<3; x++)
4321       DrawMiniGraphicExt(drawto, gi->x + x * MINI_TILEX,gi->y + y * MINI_TILEY,
4322                          el2edimg(custom_element.change.content[x][y]));
4323
4324   MapDrawingArea(GADGET_ID_CUSTOM_CHANGE_CONTENT);
4325 }
4326
4327 static void DrawCustomChangeTriggerArea()
4328 {
4329   struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_CUSTOM_CHANGE_TRIGGER];
4330   int xpos = ED_AREA_ELEM_CONTENT5_XPOS;
4331   int ypos = ED_AREA_ELEM_CONTENT5_YPOS;
4332   int area_sx = SX + xpos;
4333   int area_sy = SY + ypos;
4334
4335   if (!IS_CUSTOM_ELEMENT(properties_element))
4336   {
4337     /* this should never happen */
4338     Error(ERR_WARN, "element %d is no custom element", properties_element);
4339
4340     return;
4341   }
4342
4343   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY, TRUE);
4344   DrawMiniGraphicExt(drawto, gi->x, gi->y,
4345                      el2edimg(custom_element.change.trigger_element));
4346
4347   MapDrawingArea(GADGET_ID_CUSTOM_CHANGE_TRIGGER);
4348 }
4349
4350 static void DrawElementContentAreas()
4351 {
4352   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
4353   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
4354   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
4355   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
4356   int i, x, y;
4357
4358   for (i=0; i<MAX_ELEMENT_CONTENTS; i++)
4359     UnmapDrawingArea(GADGET_ID_ELEMENT_CONTENT_0 + i);
4360
4361   /* display counter to choose number of element content areas */
4362   MapCounterButtons(ED_COUNTER_ID_ELEMENT_CONTENT);
4363
4364   /* delete content areas in case of reducing number of them */
4365   DrawBackground(SX, area_sy - MINI_TILEX, SXSIZE, 12 * MINI_TILEY);
4366
4367   for (i=0; i<level.num_yamyam_contents; i++)
4368     DrawElementBorder(area_sx + 5 * (i % 4) * MINI_TILEX,
4369                       area_sy + 6 * (i / 4) * MINI_TILEY,
4370                       3 * MINI_TILEX, 3 * MINI_TILEY, TRUE);
4371
4372   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 0 * MINI_TILEY + 1,
4373            "Content", FONT_TEXT_1);
4374   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 1 * MINI_TILEY + 1,
4375            "when",    FONT_TEXT_1);
4376   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 2 * MINI_TILEY + 1,
4377            "smashed", FONT_TEXT_1);
4378
4379   for (i=0; i<level.num_yamyam_contents; i++)
4380   {
4381     for (y=0; y<3; y++)
4382       for (x=0; x<3; x++)
4383         DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
4384                         level.yamyam_content[i][x][y]);
4385
4386     DrawTextF(area_sx - SX + 5 * (i % 4) * MINI_TILEX + MINI_TILEX + 1,
4387               area_sy - SY + 6 * (i / 4) * MINI_TILEY + 4 * MINI_TILEY - 4,
4388               FONT_TEXT_1, "%d", i + 1);
4389   }
4390
4391   for (i=0; i<level.num_yamyam_contents; i++)
4392     MapDrawingArea(GADGET_ID_ELEMENT_CONTENT_0 + i);
4393 }
4394
4395 char *getElementDescriptionFilename(int element)
4396 {
4397   char *docs_dir = options.docs_directory;
4398   char *elements_subdir = "elements";
4399   static char *filename = NULL;
4400   char basename[MAX_FILENAME_LEN];
4401
4402   if (filename != NULL)
4403     free(filename);
4404
4405   /* 1st try: look for element description file for exactly this element */
4406   sprintf(basename, "%s.txt", element_info[element].token_name);
4407   filename = getPath3(docs_dir, elements_subdir, basename);
4408   if (fileExists(filename))
4409     return filename;
4410
4411   free(filename);
4412
4413   /* 2nd try: look for element description file for this element's class */
4414   sprintf(basename, "%s.txt", element_info[element].class_name);
4415   filename = getPath3(docs_dir, elements_subdir, basename);
4416   if (fileExists(filename))
4417     return filename;
4418
4419   return NULL;
4420 }
4421
4422 static boolean PrintInfoText(char *text, int font_nr, int screen_line)
4423 {
4424   int font_height = getFontHeight(font_nr);
4425   int pad_x = ED_SETTINGS_XPOS(0);
4426   int pad_y = ED_SETTINGS_YPOS(0) + ED_BORDER_SIZE;
4427   int sx = SX + pad_x;
4428   int sy = SY + pad_y;
4429   int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
4430
4431   if (screen_line >= max_lines_per_screen)
4432     return FALSE;
4433
4434   DrawText(sx, sy + screen_line * font_height, text, font_nr);
4435
4436   return TRUE;
4437 }
4438
4439 static int PrintElementDescriptionFromFile(char *filename, int screen_line)
4440 {
4441   int font_nr = FONT_TEXT_2;
4442   int font_width = getFontWidth(font_nr);
4443   int pad_x = ED_SETTINGS_XPOS(0);
4444   int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
4445   char line[MAX_LINE_LEN];
4446   char buffer[max_chars_per_line + 1];
4447   int buffer_len;
4448   int lines_printed = 0;
4449   FILE *file;
4450
4451   if (filename == NULL)
4452     return 0;
4453
4454   if (!(file = fopen(filename, MODE_READ)))
4455     return 0;
4456
4457   buffer[0] = '\0';
4458   buffer_len = 0;
4459
4460   while(!feof(file))
4461   {
4462     char *line_ptr, *word_ptr;
4463     boolean last_line_was_empty = TRUE;
4464
4465     /* read next line of input file */
4466     if (!fgets(line, MAX_LINE_LEN, file))
4467       break;
4468
4469     /* skip comments (lines directly beginning with '#') */
4470     if (line[0] == '#')
4471       continue;
4472
4473     /* cut trailing newline from input line */
4474     for (line_ptr = line; *line_ptr; line_ptr++)
4475     {
4476       if (*line_ptr == '\n' || *line_ptr == '\r')
4477       {
4478         *line_ptr = '\0';
4479         break;
4480       }
4481     }
4482
4483     if (strlen(line) == 0)              /* special case: force empty line */
4484       strcpy(line, "\n");
4485
4486     word_ptr = line;
4487
4488     while (*word_ptr)
4489     {
4490       boolean print_buffer = FALSE;
4491       int word_len;
4492
4493       /* skip leading whitespaces */
4494       while (*word_ptr == ' ' || *word_ptr == '\t')
4495         word_ptr++;
4496
4497       line_ptr = word_ptr;
4498       word_len = 0;
4499
4500       /* look for end of next word */
4501       while (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
4502       {
4503         line_ptr++;
4504         word_len++;
4505       }
4506
4507       if (word_len == 0)
4508       {
4509         continue;
4510       }
4511       else if (*word_ptr == '\n')       /* special case: force empty line */
4512       {
4513         if (buffer_len == 0)
4514           word_ptr++;
4515
4516         /* prevent printing of multiple empty lines */
4517         if (buffer_len > 0 || !last_line_was_empty)
4518           print_buffer = TRUE;
4519       }
4520       else if (word_len < max_chars_per_line - buffer_len)
4521       {
4522         /* word fits into text buffer -- add word */
4523
4524         if (buffer_len > 0)
4525           buffer[buffer_len++] = ' ';
4526
4527         strncpy(&buffer[buffer_len], word_ptr, word_len);
4528         buffer_len += word_len;
4529         buffer[buffer_len] = '\0';
4530         word_ptr += word_len;
4531       }
4532       else if (buffer_len > 0)
4533       {
4534         /* not enough space left for word in text buffer -- print buffer */
4535
4536         print_buffer = TRUE;
4537       }
4538       else
4539       {
4540         /* word does not fit at all into empty text buffer -- cut word */
4541
4542         strncpy(buffer, word_ptr, max_chars_per_line);
4543         buffer[max_chars_per_line] = '\0';
4544         word_ptr += max_chars_per_line;
4545         print_buffer = TRUE;
4546       }
4547
4548       if (print_buffer)
4549       {
4550         if (!PrintInfoText(buffer, font_nr, screen_line + lines_printed))
4551           return lines_printed;
4552
4553         last_line_was_empty = (buffer_len == 0);
4554         lines_printed++;
4555
4556         buffer[0] = '\0';
4557         buffer_len = 0;
4558         print_buffer = FALSE;
4559       }
4560     }
4561   }
4562
4563   fclose(file);
4564
4565   if (buffer_len > 0)
4566     if (PrintInfoText(buffer, font_nr, screen_line + lines_printed))
4567       lines_printed++;
4568
4569   return lines_printed;
4570 }
4571
4572 static void DrawPropertiesTabulatorGadgets()
4573 {
4574   struct GadgetInfo *gd_gi = level_editor_gadget[GADGET_ID_PROPERTIES_INFO];
4575   struct GadgetDesign *gd = &gd_gi->alt_design[GD_BUTTON_UNPRESSED];
4576   int gd_x = gd->x + gd_gi->border.width / 2;
4577   int gd_y = gd->y + gd_gi->height - 1;
4578   Pixel tab_color = GetPixel(gd->bitmap, gd_x, gd_y);
4579   int id_first = ED_TEXTBUTTON_ID_PROPERTIES_INFO;
4580   int id_last  = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG;
4581   int i;
4582
4583   /* draw additional "advanced" tabulator for custom elements */
4584   if (IS_CUSTOM_ELEMENT(properties_element))
4585     id_last = ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED;
4586
4587   for (i=id_first; i <= id_last; i++)
4588   {
4589     int gadget_id = textbutton_info[i].gadget_id;
4590     struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4591     boolean active = (i != edit_mode_properties);
4592
4593     /* draw background line below tabulator button */
4594     ClearRectangleOnBackground(drawto, gi->x, gi->y + gi->height, gi->width,1);
4595
4596     /* draw solid line below inactive tabulator buttons */
4597     if (!active && tab_color != BLACK_PIXEL)    /* black => transparent */
4598       FillRectangle(drawto, gi->x, gi->y + gi->height, gi->width,1, tab_color);
4599
4600     ModifyGadget(gi, GDI_ACTIVE, active, GDI_END);
4601     MapTextbuttonGadget(i);
4602   }
4603
4604   /* draw little border line below tabulator buttons */
4605   if (tab_color != BLACK_PIXEL)                 /* black => transparent */
4606     FillRectangle(drawto, gd_gi->x, gd_gi->y + gd_gi->height + 1,
4607                   3 * gd_gi->width + 2 * ED_GADGET_DISTANCE,
4608                   ED_GADGET_DISTANCE, tab_color);
4609 }
4610
4611 static void DrawPropertiesInfo()
4612 {
4613   static struct
4614   {
4615     int value;
4616     char *text;
4617   }
4618   properties[] =
4619   {
4620     /* configurable properties */
4621     { EP_INDESTRUCTIBLE,        "- undestructible"                      },
4622     { EP_SLIPPERY,              "- slippery for falling objects"        },
4623     { EP_EM_SLIPPERY_WALL,      "- slippery for some gems (EM style)"   },
4624
4625     { EP_DIGGABLE,              "- diggable"                            },
4626     { EP_COLLECTIBLE,           "- collectible"                         },
4627     { EP_PUSHABLE,              "- pushable"                            },
4628
4629     { EP_CAN_MOVE,              "- can move"                            },
4630     { EP_CAN_FALL,              "- can fall"                            },
4631 #if 0
4632     { EP_CAN_SMASH,             "- can smash"                           },
4633 #endif
4634     { EP_CAN_SMASH_PLAYER,      "- can smash player"                    },
4635     { EP_CAN_SMASH_ENEMIES,     "- can smash good and bad guys"         },
4636     { EP_CAN_SMASH_EVERYTHING,  "- can smash everything smashable"      },
4637     { EP_CAN_EXPLODE,           "- can explode"                         },
4638     { EP_CAN_EXPLODE_BY_FIRE,   "  - by fire or explosions"             },
4639     { EP_CAN_EXPLODE_SMASHED,   "  - when smashed"                      },
4640     { EP_CAN_EXPLODE_IMPACT,    "  - on impact"                         },
4641
4642     { EP_DONT_RUN_INTO,         "- deadly when running into"            },
4643     { EP_DONT_COLLIDE_WITH,     "- deadly when colliding with"          },
4644     { EP_DONT_TOUCH,            "- deadly when touching"                },
4645
4646     { EP_WALKABLE_OVER,         "- player can walk over it"             },
4647     { EP_WALKABLE_INSIDE,       "- player can walk inside it"           },
4648     { EP_WALKABLE_UNDER,        "- player can walk under it"            },
4649     { EP_PASSABLE_OVER,         "- player can pass over it"             },
4650     { EP_PASSABLE_INSIDE,       "- player can pass through it"          },
4651     { EP_PASSABLE_UNDER,        "- player can pass under it"            },
4652
4653     /* pre-defined properties */
4654     { EP_CAN_PASS_MAGIC_WALL,   "- can pass magic walls"                },
4655     { EP_HAS_CONTENT,           "- can contain other elements"          },
4656
4657     { -1,                       NULL                                    }
4658   };
4659   char *filename = getElementDescriptionFilename(properties_element);
4660   char *percentage_text = "In this level:";
4661   char *properties_text = "Standard properties:";
4662   float percentage;
4663   int num_elements_in_level;
4664   int num_standard_properties = 0;
4665   int font1_nr = FONT_TEXT_1;
4666   int font2_nr = FONT_TEXT_2;
4667   int font1_width = getFontWidth(font1_nr);
4668   int font2_height = getFontHeight(font2_nr);
4669   int pad_x = ED_SETTINGS_XPOS(0);
4670   int pad_y = ED_SETTINGS_YPOS(0) + ED_BORDER_SIZE;
4671   int screen_line = 0;
4672   int i, x, y;
4673
4674   /* ----- print number of elements / percentage of this element in level */
4675
4676   num_elements_in_level = 0;
4677   for (y=0; y<lev_fieldy; y++) 
4678     for (x=0; x<lev_fieldx; x++)
4679       if (Feld[x][y] == properties_element)
4680         num_elements_in_level++;
4681   percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
4682
4683   DrawTextF(pad_x, pad_y + screen_line * font2_height, font1_nr,
4684             percentage_text);
4685   DrawTextF(pad_x + strlen(percentage_text) * font1_width,
4686             pad_y + screen_line++ * font2_height, font2_nr,
4687             "%d (%.2f%%)", num_elements_in_level, percentage);
4688
4689   screen_line++;
4690
4691   /* ----- print standard properties of this element */
4692
4693   DrawTextF(pad_x, pad_y + screen_line++ * font2_height, font1_nr,
4694             properties_text);
4695
4696   for (i=0; properties[i].value != -1; i++)
4697   {
4698     if (!HAS_PROPERTY(properties_element, properties[i].value))
4699       continue;
4700
4701     DrawTextF(pad_x, pad_y + screen_line++ * font2_height, font2_nr,
4702               properties[i].text);
4703     num_standard_properties++;
4704   }
4705
4706   if (num_standard_properties == 0)
4707     DrawTextF(pad_x + strlen(properties_text) * font1_width,
4708               pad_y + (screen_line - 1) * font2_height, font2_nr, "none");
4709
4710   screen_line++;
4711
4712   /* ----- print special description of this element */
4713
4714   PrintInfoText("Description:", FONT_TEXT_1, screen_line);
4715   if (PrintElementDescriptionFromFile(filename, screen_line + 1) == 0)
4716     PrintInfoText("No description available.", FONT_TEXT_1, screen_line);
4717 }
4718
4719 #define TEXT_COLLECTING         "Score for collecting"
4720 #define TEXT_SMASHING           "Score for smashing"
4721 #define TEXT_CRACKING           "Score for cracking"
4722 #define TEXT_SPEED              "Speed of amoeba growth"
4723 #define TEXT_DURATION           "Duration when activated"
4724
4725 static struct
4726 {
4727   int element;
4728   int *value;
4729   char *text;
4730 } elements_with_counter[] =
4731 {
4732   { EL_EMERALD,         &level.score[SC_EMERALD],       TEXT_COLLECTING },
4733   { EL_BD_DIAMOND,      &level.score[SC_EMERALD],       TEXT_COLLECTING },
4734   { EL_EMERALD_YELLOW,  &level.score[SC_EMERALD],       TEXT_COLLECTING },
4735   { EL_EMERALD_RED,     &level.score[SC_EMERALD],       TEXT_COLLECTING },
4736   { EL_EMERALD_PURPLE,  &level.score[SC_EMERALD],       TEXT_COLLECTING },
4737   { EL_SP_INFOTRON,     &level.score[SC_EMERALD],       TEXT_COLLECTING },
4738   { EL_DIAMOND,         &level.score[SC_DIAMOND],       TEXT_COLLECTING },
4739   { EL_CRYSTAL,         &level.score[SC_CRYSTAL],       TEXT_COLLECTING },
4740   { EL_PEARL,           &level.score[SC_PEARL],         TEXT_COLLECTING },
4741   { EL_BUG_RIGHT,       &level.score[SC_BUG],           TEXT_SMASHING   },
4742   { EL_BUG_UP,          &level.score[SC_BUG],           TEXT_SMASHING   },
4743   { EL_BUG_LEFT,        &level.score[SC_BUG],           TEXT_SMASHING   },
4744   { EL_BUG_DOWN,        &level.score[SC_BUG],           TEXT_SMASHING   },
4745   { EL_BD_BUTTERFLY_RIGHT,&level.score[SC_BUG],         TEXT_SMASHING   },
4746   { EL_BD_BUTTERFLY_UP,   &level.score[SC_BUG],         TEXT_SMASHING   },
4747   { EL_BD_BUTTERFLY_LEFT, &level.score[SC_BUG],         TEXT_SMASHING   },
4748   { EL_BD_BUTTERFLY_DOWN, &level.score[SC_BUG],         TEXT_SMASHING   },
4749   { EL_SP_ELECTRON,     &level.score[SC_BUG],           TEXT_SMASHING   },
4750   { EL_SPACESHIP_RIGHT, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4751   { EL_SPACESHIP_UP,    &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4752   { EL_SPACESHIP_LEFT,  &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4753   { EL_SPACESHIP_DOWN,  &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4754   { EL_BD_FIREFLY_RIGHT,&level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4755   { EL_BD_FIREFLY_UP,   &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4756   { EL_BD_FIREFLY_LEFT, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4757   { EL_BD_FIREFLY_DOWN, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4758   { EL_SP_SNIKSNAK,     &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
4759   { EL_YAMYAM,          &level.score[SC_YAMYAM],        TEXT_SMASHING   },
4760   { EL_DARK_YAMYAM,     &level.score[SC_YAMYAM],        TEXT_SMASHING   },
4761   { EL_ROBOT,           &level.score[SC_ROBOT],         TEXT_SMASHING   },
4762   { EL_PACMAN_RIGHT,    &level.score[SC_PACMAN],        TEXT_SMASHING   },
4763   { EL_PACMAN_UP,       &level.score[SC_PACMAN],        TEXT_SMASHING   },
4764   { EL_PACMAN_LEFT,     &level.score[SC_PACMAN],        TEXT_SMASHING   },
4765   { EL_PACMAN_DOWN,     &level.score[SC_PACMAN],        TEXT_SMASHING   },
4766   { EL_NUT,             &level.score[SC_NUT],           TEXT_CRACKING   },
4767   { EL_DYNAMITE,        &level.score[SC_DYNAMITE],      TEXT_COLLECTING },
4768   { EL_DYNABOMB_INCREASE_NUMBER,&level.score[SC_DYNAMITE],TEXT_COLLECTING },
4769   { EL_DYNABOMB_INCREASE_SIZE,  &level.score[SC_DYNAMITE],TEXT_COLLECTING },
4770   { EL_DYNABOMB_INCREASE_POWER, &level.score[SC_DYNAMITE],TEXT_COLLECTING },
4771   { EL_SHIELD_NORMAL,   &level.score[SC_SHIELD],        TEXT_COLLECTING },
4772   { EL_SHIELD_DEADLY,   &level.score[SC_SHIELD],        TEXT_COLLECTING },
4773   { EL_EXTRA_TIME,      &level.score[SC_TIME_BONUS],    TEXT_COLLECTING },
4774   { EL_KEY_1,           &level.score[SC_KEY],           TEXT_COLLECTING },
4775   { EL_KEY_2,           &level.score[SC_KEY],           TEXT_COLLECTING },
4776   { EL_KEY_3,           &level.score[SC_KEY],           TEXT_COLLECTING },
4777   { EL_KEY_4,           &level.score[SC_KEY],           TEXT_COLLECTING },
4778   { EL_EM_KEY_1_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4779   { EL_EM_KEY_2_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4780   { EL_EM_KEY_3_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4781   { EL_EM_KEY_4_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
4782   { EL_AMOEBA_WET,      &level.amoeba_speed,            TEXT_SPEED      },
4783   { EL_AMOEBA_DRY,      &level.amoeba_speed,            TEXT_SPEED      },
4784   { EL_AMOEBA_FULL,     &level.amoeba_speed,            TEXT_SPEED      },
4785   { EL_BD_AMOEBA,       &level.amoeba_speed,            TEXT_SPEED      },
4786   { EL_MAGIC_WALL,      &level.time_magic_wall,         TEXT_DURATION   },
4787   { EL_ROBOT_WHEEL,     &level.time_wheel,              TEXT_DURATION   },
4788   { -1,                 NULL,                           NULL            }
4789 };
4790
4791 static boolean checkPropertiesConfig()
4792 {
4793   int i;
4794
4795   if (IS_GEM(properties_element) ||
4796       IS_CUSTOM_ELEMENT(properties_element) ||
4797       HAS_CONTENT(properties_element))
4798     return TRUE;
4799   else
4800     for (i=0; elements_with_counter[i].element != -1; i++)
4801       if (elements_with_counter[i].element == properties_element)
4802         return TRUE;
4803
4804   return FALSE;
4805 }
4806
4807 static void DrawPropertiesConfig()
4808 {
4809   int i;
4810
4811   if (!checkPropertiesConfig())
4812   {
4813     PrintInfoText("No configuration options available.", FONT_TEXT_1, 0);
4814
4815     return;
4816   }
4817
4818   /* check if there are elements where a score can be chosen for */
4819   for (i=0; elements_with_counter[i].element != -1; i++)
4820   {
4821     if (elements_with_counter[i].element == properties_element)
4822     {
4823       int counter_id = ED_COUNTER_ID_ELEMENT_SCORE;
4824
4825       counterbutton_info[counter_id].value = elements_with_counter[i].value;
4826       counterbutton_info[counter_id].text_right= elements_with_counter[i].text;
4827       MapCounterButtons(counter_id);
4828
4829       break;
4830     }
4831   }
4832
4833   if (HAS_CONTENT(properties_element))
4834   {
4835     /* draw stickybutton gadget */
4836     i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
4837     checkbutton_info[i].y = ED_COUNTER_YPOS(4);
4838     MapCheckbuttonGadget(i);
4839
4840     if (IS_AMOEBOID(properties_element))
4841       DrawAmoebaContentArea();
4842     else
4843       DrawElementContentAreas();
4844   }
4845
4846   if (IS_GEM(properties_element))
4847     MapCheckbuttonGadget(ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS);
4848
4849   if (IS_CUSTOM_ELEMENT(properties_element))
4850   {
4851     /* draw stickybutton gadget */
4852     i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
4853     checkbutton_info[i].y = ED_SETTINGS_YPOS(0);
4854     MapCheckbuttonGadget(i);
4855
4856     /* draw checkbutton gadgets */
4857     for (i =  ED_CHECKBUTTON_ID_CUSTOM_FIRST;
4858          i <= ED_CHECKBUTTON_ID_CUSTOM_LAST; i++)
4859       MapCheckbuttonGadget(i);
4860
4861     /* draw counter gadgets */
4862     for (i=ED_COUNTER_ID_CUSTOM_FIRST; i<=ED_COUNTER_ID_CUSTOM_LAST; i++)
4863       MapCounterButtons(i);
4864
4865     /* draw selectbox gadgets */
4866     for (i=ED_SELECTBOX_ID_CUSTOM_FIRST; i <= ED_SELECTBOX_ID_CUSTOM_LAST; i++)
4867       MapSelectboxGadget(i);
4868
4869     /* draw drawing area gadgets */
4870     DrawCustomContentArea();
4871
4872     /* draw text input gadgets */
4873     MapTextInputGadget(ED_TEXTINPUT_ID_ELEMENT_NAME);
4874   }
4875 }
4876
4877 static void DrawPropertiesAdvancedDrawingAreas()
4878 {
4879   DrawCustomGraphicElementArea();
4880   DrawCustomChangeTargetArea();
4881   DrawCustomChangeTriggerArea();
4882   DrawCustomChangeContentArea();
4883
4884   redraw_mask |= REDRAW_FIELD;
4885 }
4886
4887 static void DrawPropertiesAdvanced()
4888 {
4889   int i;
4890
4891   /* draw stickybutton gadget */
4892   i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
4893   checkbutton_info[i].y = ED_SETTINGS_YPOS(0);
4894   MapCheckbuttonGadget(i);
4895
4896   /* draw checkbutton gadgets */
4897   for (i =  ED_CHECKBUTTON_ID_CHANGE_FIRST;
4898        i <= ED_CHECKBUTTON_ID_CHANGE_LAST; i++)
4899     MapCheckbuttonGadget(i);
4900
4901   /* draw counter gadgets */
4902   for (i=ED_COUNTER_ID_CHANGE_FIRST; i<=ED_COUNTER_ID_CHANGE_LAST; i++)
4903     MapCounterButtons(i);
4904
4905   /* draw selectbox gadgets */
4906   for (i=ED_SELECTBOX_ID_CHANGE_FIRST; i<=ED_SELECTBOX_ID_CHANGE_LAST; i++)
4907     MapSelectboxGadget(i);
4908
4909   /* draw textbutton gadgets */
4910   MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE);
4911
4912   /* draw drawing area gadgets */
4913   DrawPropertiesAdvancedDrawingAreas();
4914 }
4915
4916 static void DrawElementName(int x, int y, int element)
4917 {
4918   char *element_name = getElementInfoText(element);
4919   int font_nr = FONT_TEXT_1;
4920   int font_width = getFontWidth(font_nr);
4921   int font_height = getFontHeight(font_nr);
4922   int max_text_width = SXSIZE - x - ED_SETTINGS_XPOS(0);
4923   int max_chars_per_line = max_text_width / font_width;
4924   char buffer[max_chars_per_line + 1];
4925
4926   if (strlen(element_name) <= max_chars_per_line)
4927     DrawTextF(x, y, font_nr, element_name);
4928   else
4929   {
4930     int next_pos = max_chars_per_line;
4931
4932     strncpy(buffer, element_name, max_chars_per_line);
4933     buffer[max_chars_per_line] = '\0';
4934
4935     if (element_name[max_chars_per_line] == ' ')
4936       next_pos++;
4937     else
4938     {
4939       int i;
4940
4941       for (i = max_chars_per_line - 1; i >= 0; i--)
4942         if (buffer[i] == ' ')
4943           break;
4944
4945       if (strlen(&element_name[i + 1]) <= max_chars_per_line)
4946       {
4947         buffer[i] = '\0';
4948         next_pos = i + 1;
4949       }
4950     }
4951
4952     DrawTextF(x, y - font_height / 2, font_nr, buffer);
4953
4954     strncpy(buffer, &element_name[next_pos], max_chars_per_line);
4955     buffer[max_chars_per_line] = '\0';
4956
4957     DrawTextF(x, y + font_height / 2, font_nr, buffer);
4958   }
4959 }
4960
4961 static void DrawPropertiesWindow()
4962 {
4963   int xstart = 2;
4964   int ystart = 4;
4965
4966   stick_element_properties_window = FALSE;
4967
4968   /* make sure that previous properties edit mode exists for this element */
4969   if (edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED &&
4970       !IS_CUSTOM_ELEMENT(properties_element))
4971     edit_mode_properties = ED_MODE_PROPERTIES_CONFIG;
4972
4973   if (IS_CUSTOM_ELEMENT(properties_element))
4974     CopyCustomElementPropertiesToEditor(properties_element);
4975
4976   UnmapLevelEditorWindowGadgets();
4977
4978   SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
4979   ClearWindow();
4980
4981   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS1_YPOS,
4982            "Element Settings", FONT_TITLE_1);
4983
4984   DrawElementBorder(SX + xstart * MINI_TILEX,
4985                     SY + ystart * MINI_TILEY + MINI_TILEY / 2,
4986                     TILEX, TILEY, FALSE);
4987   DrawGraphicAnimationExt(drawto,
4988                           SX + xstart * MINI_TILEX,
4989                           SY + ystart * MINI_TILEY + MINI_TILEY / 2,
4990                           el2img(properties_element), -1, NO_MASKING);
4991
4992   FrameCounter = 0;     /* restart animation frame counter */
4993
4994   DrawElementName((xstart + 3) * MINI_TILEX, (ystart + 1) * MINI_TILEY,
4995                   properties_element);
4996
4997   DrawPropertiesTabulatorGadgets();
4998
4999   if (edit_mode_properties == ED_MODE_PROPERTIES_INFO)
5000     DrawPropertiesInfo();
5001   else if (edit_mode_properties == ED_MODE_PROPERTIES_CONFIG)
5002     DrawPropertiesConfig();
5003   else  /* edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED */
5004     DrawPropertiesAdvanced();
5005 }
5006
5007 static void UpdateCustomElementGraphicGadgets()
5008 {
5009   ModifyEditorElementList();
5010   RedrawDrawingElements();
5011
5012   if (edit_mode == ED_MODE_PROPERTIES &&
5013       edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED)
5014     DrawPropertiesAdvancedDrawingAreas();
5015 }
5016
5017 static void DrawLineElement(int sx, int sy, int element, boolean change_level)
5018 {
5019   int lx = sx + level_xpos;
5020   int ly = sy + level_ypos;
5021
5022   DrawMiniElement(sx, sy, (element < 0 ? Feld[lx][ly] : element));
5023
5024   if (change_level)
5025     Feld[lx][ly] = element;
5026 }
5027
5028 static void DrawLine(int from_x, int from_y, int to_x, int to_y,
5029                      int element, boolean change_level)
5030 {
5031   if (from_y == to_y)                   /* horizontal line */
5032   {
5033     int x;
5034     int y = from_y;
5035
5036     if (from_x > to_x)
5037       swap_numbers(&from_x, &to_x);
5038
5039     for (x=from_x; x<=to_x; x++)
5040       DrawLineElement(x, y, element, change_level);
5041   }
5042   else if (from_x == to_x)              /* vertical line */
5043   {
5044     int x = from_x;
5045     int y;
5046
5047     if (from_y > to_y)
5048       swap_numbers(&from_y, &to_y);
5049
5050     for (y=from_y; y<=to_y; y++)
5051       DrawLineElement(x, y, element, change_level);
5052   }
5053   else                                  /* diagonal line */
5054   {
5055     int len_x = ABS(to_x - from_x);
5056     int len_y = ABS(to_y - from_y);
5057     int x, y;
5058
5059     if (len_y < len_x)                  /* a < 1 */
5060     {
5061       float a = (float)len_y / (float)len_x;
5062
5063       if (from_x > to_x)
5064         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
5065
5066       for (x=0; x<=len_x; x++)
5067       {
5068         y = (int)(a * x + 0.5) * (to_y < from_y ? -1 : +1);
5069         DrawLineElement(from_x + x, from_y + y, element, change_level);
5070       }
5071     }
5072     else                                /* a >= 1 */
5073     {
5074       float a = (float)len_x / (float)len_y;
5075
5076       if (from_y > to_y)
5077         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
5078
5079       for (y=0; y<=len_y; y++)
5080       {
5081         x = (int)(a * y + 0.5) * (to_x < from_x ? -1 : +1);
5082         DrawLineElement(from_x + x, from_y + y, element, change_level);
5083       }
5084     }
5085   }
5086 }
5087
5088 static void DrawBox(int from_x, int from_y, int to_x, int to_y,
5089                     int element, boolean change_level)
5090 {
5091   DrawLine(from_x, from_y, from_x, to_y, element, change_level);
5092   DrawLine(from_x, to_y, to_x, to_y, element, change_level);
5093   DrawLine(to_x, to_y, to_x, from_y, element, change_level);
5094   DrawLine(to_x, from_y, from_x, from_y, element, change_level);
5095 }
5096
5097 static void DrawFilledBox(int from_x, int from_y, int to_x, int to_y,
5098                           int element, boolean change_level)
5099 {
5100   int y;
5101
5102   if (from_y > to_y)
5103     swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
5104
5105   for (y=from_y; y<=to_y; y++)
5106     DrawLine(from_x, y, to_x, y, element, change_level);
5107 }
5108
5109 static void DrawArcExt(int from_x, int from_y, int to_x2, int to_y2,
5110                        int element, boolean change_level)
5111 {
5112   int to_x = to_x2 - (to_x2 > from_x ? +1 : -1);
5113   int to_y = to_y2 - (to_y2 > from_y ? +1 : -1);
5114   int len_x = ABS(to_x - from_x);
5115   int len_y = ABS(to_y - from_y);
5116   int radius, x, y;
5117
5118   radius = (int)(sqrt((float)(len_x * len_x + len_y * len_y)) + 0.5);
5119
5120   /* not optimal (some points get drawn twice) but simple,
5121      and fast enough for the few points we are drawing */
5122
5123   for (x=0; x<=radius; x++)
5124   {
5125     int sx, sy, lx, ly;
5126
5127     y = (int)(sqrt((float)(radius * radius - x * x)) + 0.5);
5128
5129     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
5130     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
5131     lx = sx + level_xpos;
5132     ly = sy + level_ypos;
5133
5134     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
5135       DrawLineElement(sx, sy, element, change_level);
5136   }
5137
5138   for (y=0; y<=radius; y++)
5139   {
5140     int sx, sy, lx, ly;
5141
5142     x = (int)(sqrt((float)(radius * radius - y * y)) + 0.5);
5143
5144     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
5145     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
5146     lx = sx + level_xpos;
5147     ly = sy + level_ypos;
5148
5149     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
5150       DrawLineElement(sx, sy, element, change_level);
5151   }
5152 }
5153
5154 static void DrawArc(int from_x, int from_y, int to_x, int to_y,
5155                     int element, boolean change_level)
5156 {
5157   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
5158   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
5159
5160   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
5161 }
5162
5163 #define DRAW_CIRCLES_BUTTON_AVAILABLE   0
5164 #if DRAW_CIRCLES_BUTTON_AVAILABLE
5165 static void DrawCircle(int from_x, int from_y, int to_x, int to_y,
5166                        int element, boolean change_level)
5167 {
5168   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
5169   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
5170   int mirror_to_x2 = from_x - (to_x2 - from_x);
5171   int mirror_to_y2 = from_y - (to_y2 - from_y);
5172
5173   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
5174   DrawArcExt(from_x, from_y, mirror_to_x2, to_y2, element, change_level);
5175   DrawArcExt(from_x, from_y, to_x2, mirror_to_y2, element, change_level);
5176   DrawArcExt(from_x, from_y, mirror_to_x2, mirror_to_y2, element,change_level);
5177 }
5178 #endif
5179
5180 static void DrawAreaBorder(int from_x, int from_y, int to_x, int to_y)
5181 {
5182   int from_sx, from_sy;
5183   int to_sx, to_sy;
5184
5185   if (from_x > to_x)
5186     swap_numbers(&from_x, &to_x);
5187
5188   if (from_y > to_y)
5189     swap_numbers(&from_y, &to_y);
5190
5191   from_sx = SX + from_x * MINI_TILEX;
5192   from_sy = SY + from_y * MINI_TILEY;
5193   to_sx = SX + to_x * MINI_TILEX + MINI_TILEX - 1;
5194   to_sy = SY + to_y * MINI_TILEY + MINI_TILEY - 1;
5195
5196   DrawSimpleWhiteLine(drawto, from_sx, from_sy, to_sx, from_sy);
5197   DrawSimpleWhiteLine(drawto, to_sx, from_sy, to_sx, to_sy);
5198   DrawSimpleWhiteLine(drawto, to_sx, to_sy, from_sx, to_sy);
5199   DrawSimpleWhiteLine(drawto, from_sx, to_sy, from_sx, from_sy);
5200
5201   if (from_x == to_x && from_y == to_y)
5202     MarkTileDirty(from_x/2, from_y/2);
5203   else
5204     redraw_mask |= REDRAW_FIELD;
5205 }
5206
5207 static void SelectArea(int from_x, int from_y, int to_x, int to_y,
5208                        int element, boolean change_level)
5209 {
5210   if (element == -1 || change_level)
5211     DrawBox(from_x, from_y, to_x, to_y, -1, FALSE);
5212   else
5213     DrawAreaBorder(from_x, from_y, to_x, to_y);
5214 }
5215
5216 /* values for CopyBrushExt() */
5217 #define CB_AREA_TO_BRUSH        0
5218 #define CB_BRUSH_TO_CURSOR      1
5219 #define CB_BRUSH_TO_LEVEL       2
5220 #define CB_DELETE_OLD_CURSOR    3
5221
5222 static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
5223                          int button, int mode)
5224 {
5225   static short brush_buffer[MAX_ED_FIELDX][MAX_ED_FIELDY];
5226   static int brush_width, brush_height;
5227   static int last_cursor_x = -1, last_cursor_y = -1;
5228   static boolean delete_old_brush;
5229   int new_element = BUTTON_ELEMENT(button);
5230   int x, y;
5231
5232   if (mode == CB_DELETE_OLD_CURSOR && !delete_old_brush)
5233     return;
5234
5235   if (mode == CB_AREA_TO_BRUSH)
5236   {
5237     int from_lx, from_ly;
5238
5239     if (from_x > to_x)
5240       swap_numbers(&from_x, &to_x);
5241
5242     if (from_y > to_y)
5243       swap_numbers(&from_y, &to_y);
5244
5245     brush_width = to_x - from_x + 1;
5246     brush_height = to_y - from_y + 1;
5247
5248     from_lx = from_x + level_xpos;
5249     from_ly = from_y + level_ypos;
5250
5251     for (y=0; y<brush_height; y++)
5252     {
5253       for (x=0; x<brush_width; x++)
5254       {
5255         brush_buffer[x][y] = Feld[from_lx + x][from_ly + y];
5256
5257         if (button != 1)
5258           DrawLineElement(from_x + x, from_y + y, new_element, TRUE);
5259       }
5260     }
5261
5262     if (button != 1)
5263       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5264
5265     delete_old_brush = FALSE;
5266   }
5267   else if (mode == CB_BRUSH_TO_CURSOR || mode == CB_DELETE_OLD_CURSOR ||
5268            mode == CB_BRUSH_TO_LEVEL)
5269   {
5270     int cursor_x = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_x : from_x);
5271     int cursor_y = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_y : from_y);
5272     int cursor_from_x = cursor_x - brush_width / 2;
5273     int cursor_from_y = cursor_y - brush_height / 2;
5274     int border_from_x = cursor_x, border_from_y = cursor_y;
5275     int border_to_x = cursor_x, border_to_y = cursor_y;
5276
5277     if (mode != CB_DELETE_OLD_CURSOR && delete_old_brush)
5278       CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
5279
5280     if (!IN_ED_FIELD(cursor_x, cursor_y) ||
5281         !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos))
5282     {
5283       delete_old_brush = FALSE;
5284       return;
5285     }
5286
5287     for (y=0; y<brush_height; y++)
5288     {
5289       for (x=0; x<brush_width; x++)
5290       {
5291         int sx = cursor_from_x + x;
5292         int sy = cursor_from_y + y;
5293         int lx = sx + level_xpos;
5294         int ly = sy + level_ypos;
5295         boolean change_level = (mode == CB_BRUSH_TO_LEVEL);
5296         int element = (mode == CB_DELETE_OLD_CURSOR ? -1 :
5297                        mode == CB_BRUSH_TO_CURSOR || button == 1 ?
5298                        brush_buffer[x][y] : new_element);
5299
5300         if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
5301         {
5302           if (sx < border_from_x)
5303             border_from_x = sx;
5304           else if (sx > border_to_x)
5305             border_to_x = sx;
5306           if (sy < border_from_y)
5307             border_from_y = sy;
5308           else if (sy > border_to_y)
5309             border_to_y = sy;
5310
5311           DrawLineElement(sx, sy, element, change_level);
5312         }
5313       }
5314     }
5315
5316     if (mode != CB_DELETE_OLD_CURSOR)
5317       DrawAreaBorder(border_from_x, border_from_y, border_to_x, border_to_y);
5318
5319     last_cursor_x = cursor_x;
5320     last_cursor_y = cursor_y;
5321     delete_old_brush = TRUE;
5322   }
5323 }
5324
5325 static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y,
5326                             int button)
5327 {
5328   CopyBrushExt(from_x, from_y, to_x, to_y, button, CB_AREA_TO_BRUSH);
5329 }
5330
5331 static void CopyBrushToLevel(int x, int y, int button)
5332 {
5333   CopyBrushExt(x, y, 0, 0, button, CB_BRUSH_TO_LEVEL);
5334 }
5335
5336 static void CopyBrushToCursor(int x, int y)
5337 {
5338   CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR);
5339 }
5340
5341 static void DeleteBrushFromCursor()
5342 {
5343   CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
5344 }
5345
5346 static void FloodFill(int from_x, int from_y, int fill_element)
5347 {
5348   int i,x,y;
5349   int old_element;
5350   static int check[4][2] = { {-1,0}, {0,-1}, {1,0}, {0,1} };
5351   static int safety = 0;
5352
5353   /* check if starting field still has the desired content */
5354   if (Feld[from_x][from_y] == fill_element)
5355     return;
5356
5357   safety++;
5358
5359   if (safety > lev_fieldx*lev_fieldy)
5360     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
5361
5362   old_element = Feld[from_x][from_y];
5363   Feld[from_x][from_y] = fill_element;
5364
5365   for(i=0;i<4;i++)
5366   {
5367     x = from_x + check[i][0];
5368     y = from_y + check[i][1];
5369
5370     if (IN_LEV_FIELD(x,y) && Feld[x][y] == old_element)
5371       FloodFill(x, y, fill_element);
5372   }
5373
5374   safety--;
5375 }
5376
5377 /* values for DrawLevelText() modes */
5378 #define TEXT_INIT               0
5379 #define TEXT_SETCURSOR          1
5380 #define TEXT_WRITECHAR          2
5381 #define TEXT_BACKSPACE          3
5382 #define TEXT_NEWLINE            4
5383 #define TEXT_END                5
5384 #define TEXT_QUERY_TYPING       6
5385
5386 static int DrawLevelText(int sx, int sy, char letter, int mode)
5387 {
5388   static short delete_buffer[MAX_LEV_FIELDX];
5389   static int start_sx, start_sy;
5390   static int last_sx, last_sy;
5391   static boolean typing = FALSE;
5392   int letter_element = EL_CHAR_ASCII0 + letter;
5393   int lx = 0, ly = 0;
5394
5395   /* map lower case letters to upper case and convert special characters */
5396   if (letter >= 'a' && letter <= 'z')
5397     letter_element = EL_CHAR_ASCII0 + letter + (int)('A' - 'a');
5398   else if (letter == 'ä' || letter == 'Ä')
5399     letter_element = EL_CHAR_AUMLAUT;
5400   else if (letter == 'ö' || letter == 'Ö')
5401     letter_element = EL_CHAR_OUMLAUT;
5402   else if (letter == 'ü' || letter == 'Ãœ')
5403     letter_element = EL_CHAR_UUMLAUT;
5404   else if (letter == '^')
5405     letter_element = EL_CHAR_COPYRIGHT;
5406   else
5407     letter_element = EL_CHAR_ASCII0 + letter;
5408
5409   if (mode != TEXT_INIT)
5410   {
5411     if (!typing)
5412       return FALSE;
5413
5414     if (mode != TEXT_SETCURSOR)
5415     {
5416       sx = last_sx;
5417       sy = last_sy;
5418     }
5419
5420     lx = last_sx + level_xpos;
5421     ly = last_sy + level_ypos;
5422   }
5423
5424   switch (mode)
5425   {
5426     case TEXT_INIT:
5427       if (typing)
5428         DrawLevelText(0, 0, 0, TEXT_END);
5429
5430       typing = TRUE;
5431       start_sx = last_sx = sx;
5432       start_sy = last_sy = sy;
5433       DrawLevelText(sx, sy, 0, TEXT_SETCURSOR);
5434       break;
5435
5436     case TEXT_SETCURSOR:
5437       DrawMiniElement(last_sx, last_sy, Feld[lx][ly]);
5438       DrawAreaBorder(sx, sy, sx, sy);
5439       last_sx = sx;
5440       last_sy = sy;
5441       break;
5442
5443     case TEXT_WRITECHAR:
5444       if (letter_element >= EL_CHAR_START && letter_element <= EL_CHAR_END)
5445       {
5446         delete_buffer[sx - start_sx] = Feld[lx][ly];
5447         Feld[lx][ly] = letter_element;
5448
5449         if (sx + 1 < ed_fieldx && lx + 1 < lev_fieldx)
5450           DrawLevelText(sx + 1, sy, 0, TEXT_SETCURSOR);
5451         else if (sy + 1 < ed_fieldy && ly + 1 < lev_fieldy)
5452           DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
5453         else
5454           DrawLevelText(0, 0, 0, TEXT_END);
5455       }
5456       break;
5457
5458     case TEXT_BACKSPACE:
5459       if (sx > start_sx)
5460       {
5461         Feld[lx - 1][ly] = delete_buffer[sx - start_sx - 1];
5462         DrawMiniElement(sx - 1, sy, Feld[lx - 1][ly]);
5463         DrawLevelText(sx - 1, sy, 0, TEXT_SETCURSOR);
5464       }
5465       break;
5466
5467     case TEXT_NEWLINE:
5468       if (sy + 1 < ed_fieldy - 1 && ly + 1 < lev_fieldy - 1)
5469         DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
5470       else
5471         DrawLevelText(0, 0, 0, TEXT_END);
5472       break;
5473
5474     case TEXT_END:
5475       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5476       DrawMiniElement(sx, sy, Feld[lx][ly]);
5477       typing = FALSE;
5478       break;
5479
5480     case TEXT_QUERY_TYPING:
5481       break;
5482
5483     default:
5484       break;
5485   }
5486
5487   return typing;
5488 }
5489
5490 static void SetTextCursor(int unused_sx, int unused_sy, int sx, int sy,
5491                           int element, boolean change_level)
5492 {
5493   int lx = sx + level_xpos;
5494   int ly = sy + level_ypos;
5495
5496   if (element == -1)
5497     DrawMiniElement(sx, sy, Feld[lx][ly]);
5498   else
5499     DrawAreaBorder(sx, sy, sx, sy);
5500 }
5501
5502 static void CopyLevelToUndoBuffer(int mode)
5503 {
5504   static boolean accumulated_undo = FALSE;
5505   boolean new_undo_buffer_position = TRUE;
5506   int last_border_element;
5507   int x, y;
5508
5509   switch (mode)
5510   {
5511     case UNDO_IMMEDIATE:
5512       accumulated_undo = FALSE;
5513       break;
5514
5515     case UNDO_ACCUMULATE:
5516       if (accumulated_undo)
5517         new_undo_buffer_position = FALSE;
5518       accumulated_undo = TRUE;
5519       break;
5520
5521     default:
5522       break;
5523   }
5524
5525   if (new_undo_buffer_position)
5526   {
5527     /* new position in undo buffer ring */
5528     undo_buffer_position = (undo_buffer_position + 1) % NUM_UNDO_STEPS;
5529
5530     if (undo_buffer_steps < NUM_UNDO_STEPS - 1)
5531       undo_buffer_steps++;
5532   }
5533
5534   for(x=0; x<lev_fieldx; x++)
5535     for(y=0; y<lev_fieldy; y++)
5536       UndoBuffer[undo_buffer_position][x][y] = Feld[x][y];
5537
5538   /* check if drawing operation forces change of border style */
5539   last_border_element = BorderElement;
5540   SetBorderElement();
5541   if (BorderElement != last_border_element)
5542     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5543 }
5544
5545 static void RandomPlacement(int new_element)
5546 {
5547   static boolean free_position[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
5548   int num_free_positions;
5549   int num_percentage;
5550   int num_elements;
5551   int x, y;
5552
5553   /* determine number of free positions for the new elements */
5554   /* (maybe this statement should be formatted a bit more readable...) */
5555   num_free_positions = 0;
5556   for (x=0; x<lev_fieldx; x++)
5557     for (y=0; y<lev_fieldy; y++)
5558       if ((free_position[x][y] =
5559            ((random_placement_background_restricted &&
5560              Feld[x][y] == random_placement_background_element) ||
5561             (!random_placement_background_restricted &&
5562              Feld[x][y] != new_element))) == TRUE)
5563         num_free_positions++;
5564
5565   /* determine number of new elements to place there */
5566   num_percentage = num_free_positions * random_placement_value / 100;
5567   num_elements = (random_placement_method == RANDOM_USE_PERCENTAGE ?
5568                   num_percentage : random_placement_value);
5569
5570   /* if not more free positions than elements to place, fill whole level */
5571   if (num_elements >= num_free_positions)
5572   {
5573     for (x=0; x<lev_fieldx; x++)
5574       for (y=0; y<lev_fieldy; y++)
5575         Feld[x][y] = new_element;
5576
5577     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5578     CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5579     return;
5580   }
5581
5582   while (num_elements > 0)
5583   {
5584     x = RND(lev_fieldx);
5585     y = RND(lev_fieldy);
5586
5587     /* don't place element at the same position twice */
5588     if (free_position[x][y])
5589     {
5590       free_position[x][y] = FALSE;
5591       Feld[x][y] = new_element;
5592       num_elements--;
5593     }
5594   }
5595
5596   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5597   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5598 }
5599
5600 void WrapLevel(int dx, int dy)
5601 {
5602   int wrap_dx = lev_fieldx - dx;
5603   int wrap_dy = lev_fieldy - dy;
5604   int x, y;
5605
5606   for(x=0; x<lev_fieldx; x++)
5607     for(y=0; y<lev_fieldy; y++)
5608       FieldBackup[x][y] = Feld[x][y];
5609
5610   for(x=0; x<lev_fieldx; x++)
5611     for(y=0; y<lev_fieldy; y++)
5612       Feld[x][y] =
5613         FieldBackup[(x + wrap_dx) % lev_fieldx][(y + wrap_dy) % lev_fieldy];
5614
5615   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5616   CopyLevelToUndoBuffer(UNDO_ACCUMULATE);
5617 }
5618
5619 static void HandleDrawingAreas(struct GadgetInfo *gi)
5620 {
5621   static boolean started_inside_drawing_area = FALSE;
5622   int id = gi->custom_id;
5623   boolean button_press_event;
5624   boolean button_release_event;
5625   boolean inside_drawing_area = !gi->event.off_borders;
5626   boolean draw_level = (id == GADGET_ID_DRAWING_LEVEL);
5627   int actual_drawing_function;
5628   int button = gi->event.button;
5629   int new_element = BUTTON_ELEMENT(button);
5630   int sx = gi->event.x, sy = gi->event.y;
5631   int min_sx = 0, min_sy = 0;
5632   int max_sx = gi->drawing.area_xsize - 1, max_sy = gi->drawing.area_ysize - 1;
5633   int item_xsize = gi->drawing.item_xsize, item_ysize = gi->drawing.item_ysize;
5634   int lx = 0, ly = 0;
5635   int min_lx = 0, min_ly = 0;
5636   int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
5637   int x, y;
5638
5639   /* handle info callback for each invocation of action callback */
5640   gi->callback_info(gi);
5641
5642   button_press_event = (gi->event.type == GD_EVENT_PRESSED);
5643   button_release_event = (gi->event.type == GD_EVENT_RELEASED);
5644
5645   /* make sure to stay inside drawing area boundaries */
5646   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
5647   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
5648
5649   if (draw_level)
5650   {
5651     /* get positions inside level field */
5652     lx = sx + level_xpos;
5653     ly = sy + level_ypos;
5654
5655     if (!IN_LEV_FIELD(lx, ly))
5656       inside_drawing_area = FALSE;
5657
5658     /* make sure to stay inside level field boundaries */
5659     lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
5660     ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
5661
5662     /* correct drawing area positions accordingly */
5663     sx = lx - level_xpos;
5664     sy = ly - level_ypos;
5665   }
5666
5667   if (button_press_event)
5668     started_inside_drawing_area = inside_drawing_area;
5669
5670   if (!started_inside_drawing_area)
5671     return;
5672
5673   if (!button && !button_release_event)
5674     return;
5675
5676   /* automatically switch to 'single item' drawing mode, if needed */
5677   actual_drawing_function =
5678     (draw_level || drawing_function == GADGET_ID_PICK_ELEMENT ?
5679      drawing_function : GADGET_ID_SINGLE_ITEMS);
5680
5681   /* clicking into drawing area with pressed Control key picks element */
5682   if (GetKeyModState() & KMOD_Control)
5683     actual_drawing_function = GADGET_ID_PICK_ELEMENT;
5684
5685   switch (actual_drawing_function)
5686   {
5687     case GADGET_ID_SINGLE_ITEMS:
5688       if (draw_level)
5689       {
5690         if (button_release_event)
5691         {
5692           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5693
5694           if (edit_mode == ED_MODE_DRAWING && draw_with_brush &&
5695               !inside_drawing_area)
5696             DeleteBrushFromCursor();
5697         }
5698
5699         if (!button)
5700           break;
5701
5702         if (draw_with_brush)
5703         {
5704           if (!button_release_event)
5705             CopyBrushToLevel(sx, sy, button);
5706         }
5707         else if (new_element != Feld[lx][ly])
5708         {
5709           if (new_element == EL_PLAYER_1)
5710           {
5711             /* remove player at old position */
5712             for(y=0; y<lev_fieldy; y++)
5713             {
5714               for(x=0; x<lev_fieldx; x++)
5715               {
5716                 if (Feld[x][y] == EL_PLAYER_1)
5717                 {
5718                   Feld[x][y] = EL_EMPTY;
5719                   if (x - level_xpos >= 0 && x - level_xpos < ed_fieldx &&
5720                       y - level_ypos >= 0 && y - level_ypos < ed_fieldy)
5721                     DrawMiniElement(x - level_xpos, y - level_ypos,
5722                                     EL_EMPTY);
5723                 }
5724               }
5725             }
5726           }
5727
5728           Feld[lx][ly] = new_element;
5729           DrawMiniElement(sx, sy, new_element);
5730         }
5731       }
5732       else
5733       {
5734         if (item_xsize == MINI_TILEX && item_ysize == MINI_TILEY)
5735           DrawMiniGraphicExt(drawto,
5736                              gi->x + sx * MINI_TILEX,
5737                              gi->y + sy * MINI_TILEY,
5738                              el2edimg(new_element));
5739         else
5740           DrawGraphicExt(drawto,
5741                          gi->x + sx * TILEX,
5742                          gi->y + sy * TILEY,
5743                          el2img(new_element), 0);
5744
5745         if (id == GADGET_ID_AMOEBA_CONTENT)
5746           level.amoeba_content = new_element;
5747         else if (id == GADGET_ID_CUSTOM_GRAPHIC)
5748         {
5749           new_element = GFX_ELEMENT(new_element);
5750           custom_element.gfx_element = new_element;
5751
5752           CopyCustomElementPropertiesToGame(properties_element);
5753
5754           UpdateCustomElementGraphicGadgets();
5755
5756           FrameCounter = 0;     /* restart animation frame counter */
5757         }
5758         else if (id == GADGET_ID_CUSTOM_CONTENT)
5759         {
5760           custom_element.content[sx][sy] = new_element;
5761
5762           CopyCustomElementPropertiesToGame(properties_element);
5763         }
5764         else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
5765         {
5766           custom_element.change.target_element = new_element;
5767
5768           CopyCustomElementPropertiesToGame(properties_element);
5769         }
5770         else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
5771         {
5772           custom_element.change.content[sx][sy] = new_element;
5773
5774           CopyCustomElementPropertiesToGame(properties_element);
5775         }
5776         else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
5777         {
5778           custom_element.change.trigger_element = new_element;
5779
5780           CopyCustomElementPropertiesToGame(properties_element);
5781         }
5782         else if (id == GADGET_ID_RANDOM_BACKGROUND)
5783           random_placement_background_element = new_element;
5784         else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
5785                  id <= GADGET_ID_ELEMENT_CONTENT_7)
5786           level.yamyam_content[id - GADGET_ID_ELEMENT_CONTENT_0][sx][sy] =
5787             new_element;
5788       }
5789       break;
5790
5791     case GADGET_ID_CONNECTED_ITEMS:
5792       {
5793         static int last_sx = -1;
5794         static int last_sy = -1;
5795
5796         if (button_release_event)
5797           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5798
5799         if (button)
5800         {
5801           if (!button_press_event)
5802             DrawLine(last_sx, last_sy, sx, sy, new_element, TRUE);
5803
5804           last_sx = sx;
5805           last_sy = sy;
5806         }
5807       }
5808       break;
5809
5810     case GADGET_ID_LINE:
5811     case GADGET_ID_ARC:
5812     case GADGET_ID_RECTANGLE:
5813     case GADGET_ID_FILLED_BOX:
5814     case GADGET_ID_GRAB_BRUSH:
5815     case GADGET_ID_TEXT:
5816       {
5817         static int last_sx = -1;
5818         static int last_sy = -1;
5819         static int start_sx = -1;
5820         static int start_sy = -1;
5821         void (*draw_func)(int, int, int, int, int, boolean);
5822
5823         if (drawing_function == GADGET_ID_LINE)
5824           draw_func = DrawLine;
5825         else if (drawing_function == GADGET_ID_ARC)
5826           draw_func = DrawArc;
5827         else if (drawing_function == GADGET_ID_RECTANGLE)
5828           draw_func = DrawBox;
5829         else if (drawing_function == GADGET_ID_FILLED_BOX)
5830           draw_func = DrawFilledBox;
5831         else if (drawing_function == GADGET_ID_GRAB_BRUSH)
5832           draw_func = SelectArea;
5833         else /* (drawing_function == GADGET_ID_TEXT) */
5834           draw_func = SetTextCursor;
5835
5836         if (button_press_event)
5837         {
5838           draw_func(sx, sy, sx, sy, new_element, FALSE);
5839           start_sx = last_sx = sx;
5840           start_sy = last_sy = sy;
5841
5842           if (drawing_function == GADGET_ID_TEXT)
5843             DrawLevelText(0, 0, 0, TEXT_END);
5844         }
5845         else if (button_release_event)
5846         {
5847           draw_func(start_sx, start_sy, sx, sy, new_element, TRUE);
5848           if (drawing_function == GADGET_ID_GRAB_BRUSH)
5849           {
5850             CopyAreaToBrush(start_sx, start_sy, sx, sy, button);
5851             CopyBrushToCursor(sx, sy);
5852             ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS],
5853                           MB_LEFTBUTTON);
5854             draw_with_brush = TRUE;
5855           }
5856           else if (drawing_function == GADGET_ID_TEXT)
5857             DrawLevelText(sx, sy, 0, TEXT_INIT);
5858           else
5859             CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5860         }
5861         else if (last_sx != sx || last_sy != sy)
5862         {
5863           draw_func(start_sx, start_sy, last_sx, last_sy, -1, FALSE);
5864           draw_func(start_sx, start_sy, sx, sy, new_element, FALSE);
5865           last_sx = sx;
5866           last_sy = sy;
5867         }
5868       }
5869       break;
5870
5871     case GADGET_ID_FLOOD_FILL:
5872       if (button_press_event && Feld[lx][ly] != new_element)
5873       {
5874         FloodFill(lx, ly, new_element);
5875         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5876         CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
5877       }
5878       break;
5879
5880     case GADGET_ID_PICK_ELEMENT:
5881       if (button_release_event)
5882         ClickOnGadget(level_editor_gadget[last_drawing_function],
5883                       MB_LEFTBUTTON);
5884       else if (draw_level)
5885         PickDrawingElement(button, Feld[lx][ly]);
5886       else if (id == GADGET_ID_AMOEBA_CONTENT)
5887         PickDrawingElement(button, level.amoeba_content);
5888       else if (id == GADGET_ID_CUSTOM_GRAPHIC)
5889         PickDrawingElement(button, custom_element.gfx_element);
5890       else if (id == GADGET_ID_CUSTOM_CONTENT)
5891         PickDrawingElement(button, custom_element.content[sx][sy]);
5892       else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
5893         PickDrawingElement(button, custom_element.change.target_element);
5894       else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
5895         PickDrawingElement(button, custom_element.change.content[sx][sy]);
5896       else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
5897         PickDrawingElement(button, custom_element.change.trigger_element);
5898       else if (id == GADGET_ID_RANDOM_BACKGROUND)
5899         PickDrawingElement(button, random_placement_background_element);
5900       else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
5901                id <= GADGET_ID_ELEMENT_CONTENT_7)
5902       {
5903         int i = id - GADGET_ID_ELEMENT_CONTENT_0;
5904
5905         PickDrawingElement(button, level.yamyam_content[i][sx][sy]);
5906       }
5907
5908       break;
5909
5910     default:
5911       break;
5912   }
5913 }
5914
5915 static void HandleCounterButtons(struct GadgetInfo *gi)
5916 {
5917   int gadget_id = gi->custom_id;
5918   int counter_id = gi->custom_type_id;
5919   int button = gi->event.button;
5920   int *counter_value = counterbutton_info[counter_id].value;
5921   int step = BUTTON_STEPSIZE(button) *
5922     (gadget_id == counterbutton_info[counter_id].gadget_id_down ? -1 : +1);
5923
5924   if (counter_id == ED_COUNTER_ID_SELECT_LEVEL)
5925   {
5926     boolean pressed = (gi->event.type == GD_EVENT_PRESSED);
5927     boolean released = (gi->event.type == GD_EVENT_RELEASED);
5928     boolean level_changed = LevelChanged();
5929
5930     if ((level_changed && pressed) || (!level_changed && released))
5931       return;
5932
5933     if (level_changed && !Request("Level has changed! Discard changes ?",
5934                                   REQ_ASK))
5935     {
5936       if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
5937         ModifyEditorCounter(counter_id, *counter_value);
5938       return;
5939     }
5940   }
5941
5942   if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
5943     *counter_value = gi->text.number_value;
5944   else
5945     ModifyEditorCounter(counter_id, *counter_value + step);
5946
5947   switch (counter_id)
5948   {
5949     case ED_COUNTER_ID_ELEMENT_CONTENT:
5950       DrawElementContentAreas();
5951       break;
5952
5953     case ED_COUNTER_ID_LEVEL_XSIZE:
5954     case ED_COUNTER_ID_LEVEL_YSIZE:
5955       lev_fieldx = level.fieldx;
5956       lev_fieldy = level.fieldy;
5957       break;
5958
5959     case ED_COUNTER_ID_SELECT_LEVEL:
5960       LoadLevel(level_nr);
5961       TapeErase();
5962       ResetUndoBuffer();
5963       DrawEditModeWindow();
5964       break;
5965
5966     default:
5967       break;
5968   }
5969
5970   if ((counter_id >= ED_COUNTER_ID_CUSTOM_FIRST &&
5971        counter_id <= ED_COUNTER_ID_CUSTOM_LAST) ||
5972       (counter_id >= ED_COUNTER_ID_CHANGE_FIRST &&
5973        counter_id <= ED_COUNTER_ID_CHANGE_LAST))
5974     CopyCustomElementPropertiesToGame(properties_element);
5975 }
5976
5977 static void HandleTextInputGadgets(struct GadgetInfo *gi)
5978 {
5979   int type_id = gi->custom_type_id;
5980
5981   strcpy(textinput_info[type_id].value, gi->text.value);
5982
5983   if (type_id == ED_TEXTINPUT_ID_ELEMENT_NAME)
5984   {
5985     CopyCustomElementPropertiesToGame(properties_element);
5986
5987     ModifyEditorElementList();  /* update changed button info text */
5988   }
5989 }
5990
5991 static void HandleSelectboxGadgets(struct GadgetInfo *gi)
5992 {
5993   int type_id = gi->custom_type_id;
5994
5995   *selectbox_info[type_id].value =
5996     selectbox_info[type_id].options[gi->selectbox.index].value;
5997
5998   if ((type_id >= ED_SELECTBOX_ID_CUSTOM_FIRST &&
5999        type_id <= ED_SELECTBOX_ID_CUSTOM_LAST) ||
6000       (type_id >= ED_SELECTBOX_ID_CHANGE_FIRST &&
6001        type_id <= ED_SELECTBOX_ID_CHANGE_LAST))
6002     CopyCustomElementPropertiesToGame(properties_element);
6003 }
6004
6005 static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
6006 {
6007   int type_id = gi->custom_type_id;
6008
6009   if (type_id >= ED_TEXTBUTTON_ID_PROPERTIES_INFO &&
6010       type_id <= ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED)
6011   {
6012     edit_mode_properties = gi->custom_type_id;
6013
6014     DrawPropertiesWindow();
6015   }
6016   else if (type_id == ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE)
6017   {
6018     boolean new_template = (!LevelFileExists(-1));
6019
6020     if (new_template ||
6021         Request("Save this tem- plate and kill the old ?", REQ_ASK))
6022       SaveLevelTemplate();
6023
6024     if (new_template)
6025       Request("Tem- plate saved !", REQ_CONFIRM);
6026   }
6027 }
6028
6029 static void HandleRadiobuttons(struct GadgetInfo *gi)
6030 {
6031   *radiobutton_info[gi->custom_type_id].value =
6032     radiobutton_info[gi->custom_type_id].checked_value;
6033 }
6034
6035 static void HandleCheckbuttons(struct GadgetInfo *gi)
6036 {
6037   int type_id = gi->custom_type_id;
6038
6039   *checkbutton_info[type_id].value ^= TRUE;
6040
6041   if ((type_id >= ED_CHECKBUTTON_ID_CUSTOM_FIRST &&
6042        type_id <= ED_CHECKBUTTON_ID_CUSTOM_LAST) ||
6043       (type_id >= ED_CHECKBUTTON_ID_CHANGE_FIRST &&
6044        type_id <= ED_CHECKBUTTON_ID_CHANGE_LAST &&
6045        type_id != ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE))
6046   {
6047     CopyCustomElementPropertiesToGame(properties_element);
6048   }
6049
6050   if (type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC)
6051   {
6052     UpdateCustomElementGraphicGadgets();
6053   }
6054   else if (type_id == ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE)
6055   {
6056     if (level.use_custom_template && !LevelFileExists(-1))
6057     {
6058       Request("No level tem- plate found !", REQ_CONFIRM);
6059
6060       level.use_custom_template = FALSE;
6061       ModifyGadget(gi, GDI_CHECKED, FALSE, GDI_END);
6062
6063       return;
6064     }
6065
6066     LoadLevelTemplate(level.use_custom_template ? -1 : level_nr);
6067
6068     DrawEditModeWindow();
6069   }
6070 }
6071
6072 static void HandleControlButtons(struct GadgetInfo *gi)
6073 {
6074   int id = gi->custom_id;
6075   int button = gi->event.button;
6076   int step = BUTTON_STEPSIZE(button);
6077   int new_element = BUTTON_ELEMENT(button);
6078   int x, y;
6079
6080   if (edit_mode == ED_MODE_DRAWING && drawing_function == GADGET_ID_TEXT)
6081     DrawLevelText(0, 0, 0, TEXT_END);
6082
6083   if (id < ED_NUM_CTRL1_BUTTONS && id != GADGET_ID_PROPERTIES &&
6084       id != GADGET_ID_PICK_ELEMENT && edit_mode != ED_MODE_DRAWING &&
6085       drawing_function != GADGET_ID_PICK_ELEMENT &&
6086       !(GetKeyModState() & KMOD_Control))
6087   {
6088     DrawDrawingWindow();
6089     edit_mode = ED_MODE_DRAWING;
6090   }
6091
6092   switch (id)
6093   {
6094     case GADGET_ID_SCROLL_LEFT:
6095       if (level_xpos >= 0)
6096       {
6097         if (lev_fieldx < ed_fieldx - 2)
6098           break;
6099
6100         level_xpos -= step;
6101         if (level_xpos < -1)
6102           level_xpos = -1;
6103         if (button == 1)
6104           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_RIGHT);
6105         else
6106           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6107
6108         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
6109                      GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
6110       }
6111       break;
6112
6113     case GADGET_ID_SCROLL_RIGHT:
6114       if (level_xpos <= lev_fieldx - ed_fieldx)
6115       {
6116         if (lev_fieldx < ed_fieldx - 2)
6117           break;
6118
6119         level_xpos += step;
6120         if (level_xpos > lev_fieldx - ed_fieldx + 1)
6121           level_xpos = lev_fieldx - ed_fieldx + 1;
6122         if (button == 1)
6123           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_LEFT);
6124         else
6125           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6126
6127         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
6128                      GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
6129       }
6130       break;
6131
6132     case GADGET_ID_SCROLL_UP:
6133       if (level_ypos >= 0)
6134       {
6135         if (lev_fieldy < ed_fieldy - 2)
6136           break;
6137
6138         level_ypos -= step;
6139         if (level_ypos < -1)
6140           level_ypos = -1;
6141         if (button == 1)
6142           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_DOWN);
6143         else
6144           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6145
6146         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
6147                      GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
6148       }
6149       break;
6150
6151     case GADGET_ID_SCROLL_DOWN:
6152       if (level_ypos <= lev_fieldy - ed_fieldy)
6153       {
6154         if (lev_fieldy < ed_fieldy - 2)
6155           break;
6156
6157         level_ypos += step;
6158         if (level_ypos > lev_fieldy - ed_fieldy + 1)
6159           level_ypos = lev_fieldy - ed_fieldy + 1;
6160         if (button == 1)
6161           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_UP);
6162         else
6163           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6164
6165         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
6166                      GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
6167       }
6168       break;
6169
6170     case GADGET_ID_SCROLL_HORIZONTAL:
6171       level_xpos = gi->event.item_position - 1;
6172       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6173       break;
6174
6175     case GADGET_ID_SCROLL_VERTICAL:
6176       level_ypos = gi->event.item_position - 1;
6177       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6178       break;
6179
6180     case GADGET_ID_SCROLL_LIST_UP:
6181     case GADGET_ID_SCROLL_LIST_DOWN:
6182     case GADGET_ID_SCROLL_LIST_VERTICAL:
6183       if (id == GADGET_ID_SCROLL_LIST_VERTICAL)
6184         element_shift = gi->event.item_position * ED_ELEMENTLIST_BUTTONS_HORIZ;
6185       else
6186       {
6187         step *= (id == GADGET_ID_SCROLL_LIST_UP ? -1 : +1);
6188         element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
6189
6190         if (element_shift < 0)
6191           element_shift = 0;
6192         if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
6193           element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
6194
6195         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
6196                      GDI_SCROLLBAR_ITEM_POSITION,
6197                      element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
6198       }
6199
6200       ModifyEditorElementList();
6201
6202       break;
6203
6204     case GADGET_ID_WRAP_LEFT:
6205       WrapLevel(-step, 0);
6206       break;
6207
6208     case GADGET_ID_WRAP_RIGHT:
6209       WrapLevel(step, 0);
6210       break;
6211
6212     case GADGET_ID_WRAP_UP:
6213       WrapLevel(0, -step);
6214       break;
6215
6216     case GADGET_ID_WRAP_DOWN:
6217       WrapLevel(0, step);
6218       break;
6219
6220     case GADGET_ID_SINGLE_ITEMS:
6221     case GADGET_ID_CONNECTED_ITEMS:
6222     case GADGET_ID_LINE:
6223     case GADGET_ID_ARC:
6224     case GADGET_ID_TEXT:
6225     case GADGET_ID_RECTANGLE:
6226     case GADGET_ID_FILLED_BOX:
6227     case GADGET_ID_FLOOD_FILL:
6228     case GADGET_ID_GRAB_BRUSH:
6229     case GADGET_ID_PICK_ELEMENT:
6230       if (drawing_function != GADGET_ID_PICK_ELEMENT)
6231         last_drawing_function = drawing_function;
6232       drawing_function = id;
6233       draw_with_brush = FALSE;
6234       break;
6235
6236     case GADGET_ID_RANDOM_PLACEMENT:
6237       RandomPlacement(new_element);
6238       break;
6239
6240     case GADGET_ID_PROPERTIES:
6241       if (edit_mode != ED_MODE_PROPERTIES)
6242       {
6243         properties_element = new_element;
6244         DrawPropertiesWindow();
6245         edit_mode = ED_MODE_PROPERTIES;
6246       }
6247       else
6248       {
6249         DrawDrawingWindow();
6250         edit_mode = ED_MODE_DRAWING;
6251       }
6252       break;
6253
6254     case GADGET_ID_UNDO:
6255       if (undo_buffer_steps == 0)
6256       {
6257         Request("Undo buffer empty !", REQ_CONFIRM);
6258         break;
6259       }
6260
6261       if (edit_mode != ED_MODE_DRAWING)
6262       {
6263         DrawDrawingWindow();
6264         edit_mode = ED_MODE_DRAWING;
6265       }
6266
6267       undo_buffer_position =
6268         (undo_buffer_position - 1 + NUM_UNDO_STEPS) % NUM_UNDO_STEPS;
6269       undo_buffer_steps--;
6270
6271       for(x=0; x<lev_fieldx; x++)
6272         for(y=0; y<lev_fieldy; y++)
6273           Feld[x][y] = UndoBuffer[undo_buffer_position][x][y];
6274       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos,level_ypos);
6275       break;
6276
6277     case GADGET_ID_INFO:
6278       if (edit_mode != ED_MODE_INFO)
6279       {
6280         DrawLevelInfoWindow();
6281         edit_mode = ED_MODE_INFO;
6282       }
6283       else
6284       {
6285         DrawDrawingWindow();
6286         edit_mode = ED_MODE_DRAWING;
6287       }
6288       break;
6289
6290     case GADGET_ID_CLEAR:
6291       if (edit_mode != ED_MODE_DRAWING)
6292       {
6293         DrawDrawingWindow();
6294         edit_mode = ED_MODE_DRAWING;
6295       }
6296
6297       for(x=0; x<MAX_LEV_FIELDX; x++) 
6298         for(y=0; y<MAX_LEV_FIELDY; y++) 
6299           Feld[x][y] = (button == 1 ? EL_EMPTY : new_element);
6300       CopyLevelToUndoBuffer(GADGET_ID_CLEAR);
6301
6302       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
6303       break;
6304
6305     case GADGET_ID_SAVE:
6306       if (leveldir_current->readonly)
6307       {
6308         Request("This level is read only !", REQ_CONFIRM);
6309         break;
6310       }
6311
6312       if (!LevelContainsPlayer)
6313         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
6314       else
6315       {
6316         boolean new_level = (!LevelFileExists(level_nr));
6317
6318         if (new_level ||
6319             Request("Save this level and kill the old ?", REQ_ASK))
6320         {
6321           CopyPlayfield(Feld, level.field);
6322
6323           SaveLevel(level_nr);
6324         }
6325
6326         if (new_level)
6327           Request("Level saved !", REQ_CONFIRM);
6328       }
6329       break;
6330
6331     case GADGET_ID_TEST:
6332       if (!LevelContainsPlayer)
6333         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
6334       else
6335       {
6336         if (LevelChanged())
6337           level.game_version = GAME_VERSION_ACTUAL;
6338
6339         CopyPlayfield(level.field, FieldBackup);
6340         CopyPlayfield(Feld, level.field);
6341
6342         UnmapLevelEditorGadgets();
6343         UndrawSpecialEditorDoor();
6344
6345         CloseDoor(DOOR_CLOSE_ALL);
6346
6347         DrawCompleteVideoDisplay();
6348
6349         if (setup.autorecord)
6350           TapeStartRecording();
6351
6352         level_editor_test_game = TRUE;
6353         game_status = GAME_MODE_PLAYING;
6354
6355         InitGame();
6356       }
6357       break;
6358
6359     case GADGET_ID_EXIT:
6360       RequestExitLevelEditor(TRUE);     /* if level has changed, ask user */
6361       break;
6362
6363     default:
6364       if (id >= GADGET_ID_ELEMENTLIST_FIRST &&
6365           id <= GADGET_ID_ELEMENTLIST_LAST)
6366       {
6367         int element_position = id - GADGET_ID_ELEMENTLIST_FIRST;
6368         int new_element = editor_elements[element_position + element_shift];
6369
6370         PickDrawingElement(button, new_element);
6371
6372         if (!stick_element_properties_window &&
6373             drawing_function != GADGET_ID_PICK_ELEMENT &&
6374             !(GetKeyModState() & KMOD_Control))
6375         {
6376           properties_element = new_element;
6377           if (edit_mode == ED_MODE_PROPERTIES)
6378             DrawPropertiesWindow();
6379         }
6380
6381         if (drawing_function == GADGET_ID_PICK_ELEMENT)
6382           ClickOnGadget(level_editor_gadget[last_drawing_function],
6383                         MB_LEFTBUTTON);
6384       }
6385 #ifdef DEBUG
6386       else if (gi->event.type == GD_EVENT_PRESSED)
6387         printf("default: HandleControlButtons: GD_EVENT_PRESSED(%d)\n", id);
6388       else if (gi->event.type == GD_EVENT_RELEASED)
6389         printf("default: HandleControlButtons: GD_EVENT_RELEASED(%d)\n", id);
6390       else if (gi->event.type == GD_EVENT_MOVING)
6391         printf("default: HandleControlButtons: GD_EVENT_MOVING(%d)\n", id);
6392       else
6393         printf("default: HandleControlButtons: ? (id == %d)\n", id);
6394 #endif
6395       break;
6396   }
6397 }
6398
6399 void HandleLevelEditorKeyInput(Key key)
6400 {
6401   char letter = getCharFromKey(key);
6402   int button = MB_LEFTBUTTON;
6403
6404   if (drawing_function == GADGET_ID_TEXT &&
6405       DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE)
6406   {
6407     if (letter)
6408       DrawLevelText(0, 0, letter, TEXT_WRITECHAR);
6409     else if (key == KSYM_Delete || key == KSYM_BackSpace)
6410       DrawLevelText(0, 0, 0, TEXT_BACKSPACE);
6411     else if (key == KSYM_Return)
6412       DrawLevelText(0, 0, 0, TEXT_NEWLINE);
6413     else if (key == KSYM_Escape)
6414       DrawLevelText(0, 0, 0, TEXT_END);
6415   }
6416   else if (button_status == MB_RELEASED)
6417   {
6418     int i, id = GADGET_ID_NONE;
6419
6420     switch (key)
6421     {
6422       case KSYM_Left:
6423         id = GADGET_ID_SCROLL_LEFT;
6424         break;
6425       case KSYM_Right:
6426         id = GADGET_ID_SCROLL_RIGHT;
6427         break;
6428       case KSYM_Up:
6429         id = GADGET_ID_SCROLL_UP;
6430         break;
6431       case KSYM_Down:
6432         id = GADGET_ID_SCROLL_DOWN;
6433         break;
6434       case KSYM_Page_Up:
6435         id = GADGET_ID_SCROLL_LIST_UP;
6436         button = MB_RIGHTBUTTON;
6437         break;
6438       case KSYM_Page_Down:
6439         id = GADGET_ID_SCROLL_LIST_DOWN;
6440         button = MB_RIGHTBUTTON;
6441         break;
6442
6443       case KSYM_Escape:
6444         if (edit_mode == ED_MODE_DRAWING)
6445         {
6446           RequestExitLevelEditor(setup.ask_on_escape);
6447         }
6448         else
6449         {
6450           DrawDrawingWindow();
6451           edit_mode = ED_MODE_DRAWING;
6452         }
6453         break;
6454
6455       default:
6456         break;
6457     }
6458
6459     if (id != GADGET_ID_NONE)
6460       ClickOnGadget(level_editor_gadget[id], button);
6461     else if (letter == '.')
6462       ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
6463     else if (key == KSYM_Return || key == setup.shortcut.toggle_pause)
6464       ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button);
6465     else
6466       for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
6467         if (letter && letter == control_info[i].shortcut)
6468           if (!anyTextGadgetActive())
6469             ClickOnGadget(level_editor_gadget[i], button);
6470   }
6471 }
6472
6473 void HandleLevelEditorIdle()
6474 {
6475   static unsigned long action_delay = 0;
6476   unsigned long action_delay_value = GameFrameDelay;
6477   int xpos = 1, ypos = 2;
6478
6479   if (edit_mode != ED_MODE_PROPERTIES)
6480     return;
6481
6482   if (!DelayReached(&action_delay, action_delay_value))
6483     return;
6484
6485   DrawGraphicAnimationExt(drawto,
6486                           SX + xpos * TILEX,
6487                           SY + ypos * TILEY + MINI_TILEY / 2,
6488                           el2img(properties_element), -1, NO_MASKING);
6489
6490   MarkTileDirty(xpos, ypos);
6491   MarkTileDirty(xpos, ypos + 1);
6492
6493   FrameCounter++;       /* increase animation frame counter */
6494 }
6495
6496 void ClearEditorGadgetInfoText()
6497 {
6498   DrawBackground(INFOTEXT_XPOS, INFOTEXT_YPOS, INFOTEXT_XSIZE, INFOTEXT_YSIZE);
6499 }
6500
6501 void HandleEditorGadgetInfoText(void *ptr)
6502 {
6503   struct GadgetInfo *gi = (struct GadgetInfo *)ptr;
6504   char infotext[MAX_OUTPUT_LINESIZE + 1];
6505   char shortcut[MAX_OUTPUT_LINESIZE + 1];
6506   int max_infotext_len = getMaxInfoTextLength();
6507
6508   if (game_status != GAME_MODE_EDITOR)
6509     return;
6510
6511   ClearEditorGadgetInfoText();
6512
6513   if (gi->event.type == GD_EVENT_INFO_LEAVING)
6514     return;
6515
6516   /* misuse this function to delete brush cursor, if needed */
6517   if (edit_mode == ED_MODE_DRAWING && draw_with_brush)
6518     DeleteBrushFromCursor();
6519
6520   if (gi == NULL || gi->info_text == NULL)
6521     return;
6522
6523   strncpy(infotext, gi->info_text, max_infotext_len);
6524   infotext[max_infotext_len] = '\0';
6525
6526   if (gi->custom_id < ED_NUM_CTRL_BUTTONS)
6527   {
6528     int key = control_info[gi->custom_id].shortcut;
6529
6530     if (key)
6531     {
6532       if (gi->custom_id == GADGET_ID_SINGLE_ITEMS)      /* special case 1 */
6533         sprintf(shortcut, " ('.' or '%c')", key);
6534       else if (gi->custom_id == GADGET_ID_PICK_ELEMENT) /* special case 2 */
6535         sprintf(shortcut, " ('%c' or 'Ctrl')", key);
6536       else if (gi->custom_id == GADGET_ID_TEST)         /* special case 3 */
6537         sprintf(shortcut, " ('Enter' or 'Shift-%c')", key);
6538       else                                              /* normal case */
6539         sprintf(shortcut, " ('%s%c')",
6540                 (key >= 'A' && key <= 'Z' ? "Shift-" : ""), key);
6541
6542       if (strlen(infotext) + strlen(shortcut) <= max_infotext_len)
6543         strcat(infotext, shortcut);
6544     }
6545   }
6546
6547   DrawText(INFOTEXT_XPOS, INFOTEXT_YPOS, infotext, FONT_TEXT_2);
6548 }
6549
6550 static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
6551 {
6552   static int start_lx, start_ly;
6553   char *infotext;
6554   int id = gi->custom_id;
6555   int sx = gi->event.x;
6556   int sy = gi->event.y;
6557   int lx = sx + level_xpos;
6558   int ly = sy + level_ypos;
6559   int min_sx = 0, min_sy = 0;
6560   int max_sx = gi->drawing.area_xsize - 1;
6561   int max_sy = gi->drawing.area_ysize - 1;
6562   int actual_drawing_function = drawing_function;
6563
6564   /* pressed Control key: simulate picking element */
6565   if (GetKeyModState() & KMOD_Control)
6566     actual_drawing_function = GADGET_ID_PICK_ELEMENT;
6567
6568   ClearEditorGadgetInfoText();
6569
6570   if (gi->event.type == GD_EVENT_INFO_LEAVING)
6571     return;
6572
6573   /* make sure to stay inside drawing area boundaries */
6574   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
6575   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
6576
6577   if (id == GADGET_ID_DRAWING_LEVEL)
6578   {
6579     if (button_status)
6580     {
6581       int min_lx = 0, min_ly = 0;
6582       int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
6583
6584       /* get positions inside level field */
6585       lx = sx + level_xpos;
6586       ly = sy + level_ypos;
6587
6588       /* make sure to stay inside level field boundaries */
6589       lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
6590       ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
6591
6592       /* correct drawing area positions accordingly */
6593       sx = lx - level_xpos;
6594       sy = ly - level_ypos;
6595     }
6596
6597     if (IN_ED_FIELD(sx,sy) && IN_LEV_FIELD(lx, ly))
6598     {
6599       if (button_status)        /* if (gi->state == GD_BUTTON_PRESSED) */
6600       {
6601         if (gi->event.type == GD_EVENT_PRESSED)
6602         {
6603           start_lx = lx;
6604           start_ly = ly;
6605         }
6606
6607         switch (actual_drawing_function)
6608         {
6609           case GADGET_ID_SINGLE_ITEMS:
6610             infotext = "Drawing single items";
6611             break;
6612           case GADGET_ID_CONNECTED_ITEMS:
6613             infotext = "Drawing connected items";
6614             break;
6615           case GADGET_ID_LINE:
6616             infotext = "Drawing line";
6617             break;
6618           case GADGET_ID_ARC:
6619             infotext = "Drawing arc";
6620             break;
6621           case GADGET_ID_TEXT:
6622             infotext = "Setting text cursor";
6623             break;
6624           case GADGET_ID_RECTANGLE:
6625             infotext = "Drawing rectangle";
6626             break;
6627           case GADGET_ID_FILLED_BOX:
6628             infotext = "Drawing filled box";
6629             break;
6630           case GADGET_ID_FLOOD_FILL:
6631             infotext = "Flood fill";
6632             break;
6633           case GADGET_ID_GRAB_BRUSH:
6634             infotext = "Grabbing brush";
6635             break;
6636           case GADGET_ID_PICK_ELEMENT:
6637             infotext = "Picking element";
6638             break;
6639
6640           default:
6641             infotext = "Drawing position";
6642             break;
6643         }
6644
6645         if (actual_drawing_function == GADGET_ID_PICK_ELEMENT)
6646           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6647                     "%s: %d, %d", infotext, lx, ly);
6648         else
6649           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6650                     "%s: %d, %d", infotext,
6651                     ABS(lx - start_lx) + 1, ABS(ly - start_ly) + 1);
6652       }
6653       else if (actual_drawing_function == GADGET_ID_PICK_ELEMENT)
6654         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6655                   "%s", getElementInfoText(Feld[lx][ly]));
6656       else
6657         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6658                   "Level position: %d, %d", lx, ly);
6659     }
6660
6661     /* misuse this function to draw brush cursor, if needed */
6662     if (edit_mode == ED_MODE_DRAWING && draw_with_brush && !button_status)
6663     {
6664       if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
6665         CopyBrushToCursor(sx, sy);
6666       else
6667         DeleteBrushFromCursor();
6668     }
6669   }
6670   else if (actual_drawing_function == GADGET_ID_PICK_ELEMENT)
6671   {
6672     if (id == GADGET_ID_AMOEBA_CONTENT)
6673       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6674                 getElementInfoText(level.amoeba_content));
6675     else if (id == GADGET_ID_CUSTOM_GRAPHIC)
6676       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6677                 getElementInfoText(custom_element.gfx_element));
6678     else if (id == GADGET_ID_CUSTOM_CONTENT)
6679       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6680                 getElementInfoText(custom_element.content[sx][sy]));
6681     else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
6682       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6683                 getElementInfoText(custom_element.change.target_element));
6684     else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
6685       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6686                 getElementInfoText(custom_element.change.content[sx][sy]));
6687     else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
6688       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6689                 getElementInfoText(custom_element.change.trigger_element));
6690     else if (id == GADGET_ID_RANDOM_BACKGROUND)
6691       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6692                 getElementInfoText(random_placement_background_element));
6693     else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
6694              id <= GADGET_ID_ELEMENT_CONTENT_7)
6695     {
6696       int i = id - GADGET_ID_ELEMENT_CONTENT_0;
6697
6698       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2, "%s",
6699                 getElementInfoText(level.yamyam_content[i][sx][sy]));
6700     }
6701   }
6702   else
6703   {
6704     if (id == GADGET_ID_AMOEBA_CONTENT)
6705       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6706                 "Amoeba content");
6707     else if (id == GADGET_ID_CUSTOM_GRAPHIC)
6708       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6709                 "Custom graphic element");
6710     else if (id == GADGET_ID_CUSTOM_CONTENT)
6711       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6712                 "Custom element content position: %d, %d", sx, sy);
6713     else if (id == GADGET_ID_CUSTOM_CHANGE_TARGET)
6714       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6715                 "New element after change");
6716     else if (id == GADGET_ID_CUSTOM_CHANGE_CONTENT)
6717       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6718                 "New extended elements after change");
6719     else if (id == GADGET_ID_CUSTOM_CHANGE_TRIGGER)
6720       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6721                 "Other element triggering change");
6722     else if (id == GADGET_ID_RANDOM_BACKGROUND)
6723       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6724                 "Random placement background");
6725     else if (id >= GADGET_ID_ELEMENT_CONTENT_0 &&
6726              id <= GADGET_ID_ELEMENT_CONTENT_7)
6727       DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
6728                 "Content area %d position: %d, %d",
6729                 id - GADGET_ID_ELEMENT_CONTENT_0 + 1, sx, sy);
6730   }
6731 }
6732
6733 void RequestExitLevelEditor(boolean ask_if_level_has_changed)
6734 {
6735   if (!ask_if_level_has_changed ||
6736       !LevelChanged() ||
6737       Request("Level has changed! Exit without saving ?",
6738               REQ_ASK | REQ_STAY_OPEN))
6739   {
6740     CloseDoor(DOOR_CLOSE_1);
6741     /*
6742     CloseDoor(DOOR_CLOSE_ALL);
6743     */
6744     game_status = GAME_MODE_MAIN;
6745     DrawMainMenu();
6746   }
6747   else
6748   {
6749     CloseDoor(DOOR_CLOSE_1);
6750     BlitBitmap(bitmap_db_door, bitmap_db_door,
6751                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
6752                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
6753     OpenDoor(DOOR_OPEN_1);
6754   }
6755 }