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