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