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