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