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