884ca0fa5628575863e1c0b7bf489dc38f1af47b
[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 = GAME_MODE_EDITOR;
2611
2612   ReinitializeElementList();
2613
2614   CreateControlButtons();
2615   CreateCounterButtons();
2616   CreateDrawingAreas();
2617   CreateTextInputGadgets();
2618   CreateSelectboxGadgets();
2619   CreateTextbuttonGadgets();
2620   CreateScrollbarGadgets();
2621   CreateCheckbuttonGadgets();
2622
2623   game_status = old_game_status;
2624 }
2625
2626 void FreeLevelEditorGadgets()
2627 {
2628   int i;
2629
2630   for (i=0; i<NUM_EDITOR_GADGETS; i++)
2631     FreeGadget(level_editor_gadget[i]);
2632 }
2633
2634 static void MapCounterButtons(int id)
2635 {
2636   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_down]);
2637   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_text]);
2638   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_up]);
2639 }
2640
2641 static void MapControlButtons()
2642 {
2643   int counter_id;
2644   int i;
2645
2646   /* map toolbox buttons */
2647   for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
2648     MapGadget(level_editor_gadget[i]);
2649
2650   /* map buttons to select elements */
2651   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
2652     MapGadget(level_editor_gadget[GADGET_ID_ELEMENTLIST_FIRST + i]);
2653   MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL]);
2654   MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_UP]);
2655   MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_DOWN]);
2656
2657   /* map buttons to select level */
2658   counter_id = ED_COUNTER_ID_SELECT_LEVEL;
2659   ModifyEditorCounterLimits(counter_id,
2660                             leveldir_current->first_level,
2661                             leveldir_current->last_level);
2662   ModifyEditorCounter(counter_id, *counterbutton_info[counter_id].value);
2663   MapCounterButtons(counter_id);
2664 }
2665
2666 static void MapDrawingArea(int id)
2667 {
2668   MapGadget(level_editor_gadget[id]);
2669 }
2670
2671 static void MapTextInputGadget(int id)
2672 {
2673   MapGadget(level_editor_gadget[textinput_info[id].gadget_id]);
2674 }
2675
2676 static void MapSelectboxGadget(int id)
2677 {
2678   MapGadget(level_editor_gadget[selectbox_info[id].gadget_id]);
2679 }
2680
2681 static void MapTextbuttonGadget(int id)
2682 {
2683   MapGadget(level_editor_gadget[textbutton_info[id].gadget_id]);
2684 }
2685
2686 static void MapRadiobuttonGadget(int id)
2687 {
2688   MapGadget(level_editor_gadget[radiobutton_info[id].gadget_id]);
2689 }
2690
2691 static void MapCheckbuttonGadget(int id)
2692 {
2693   MapGadget(level_editor_gadget[checkbutton_info[id].gadget_id]);
2694 }
2695
2696 static void MapMainDrawingArea()
2697 {
2698   boolean no_horizontal_scrollbar = (lev_fieldx + 2 <= ed_fieldx);
2699   boolean no_vertical_scrollbar = (lev_fieldy + 2 <= ed_fieldy);
2700   int i;
2701
2702   for (i=ED_SCROLLBUTTON_ID_AREA_FIRST; i<=ED_SCROLLBUTTON_ID_AREA_LAST; i++)
2703   {
2704     if (((i == ED_SCROLLBUTTON_ID_AREA_LEFT ||
2705           i == ED_SCROLLBUTTON_ID_AREA_RIGHT) &&
2706          no_horizontal_scrollbar) ||
2707         ((i == ED_SCROLLBUTTON_ID_AREA_UP ||
2708           i == ED_SCROLLBUTTON_ID_AREA_DOWN) &&
2709          no_vertical_scrollbar))
2710       continue;
2711
2712     MapGadget(level_editor_gadget[scrollbutton_info[i].gadget_id]);
2713   }
2714
2715   for (i=ED_SCROLLBAR_ID_AREA_FIRST; i<=ED_SCROLLBAR_ID_AREA_LAST; i++)
2716   {
2717     if ((i == ED_SCROLLBAR_ID_AREA_HORIZONTAL && no_horizontal_scrollbar) ||
2718         (i == ED_SCROLLBAR_ID_AREA_VERTICAL && no_vertical_scrollbar))
2719       continue;
2720
2721     MapGadget(level_editor_gadget[scrollbar_info[i].gadget_id]);
2722   }
2723
2724   MapDrawingArea(GADGET_ID_DRAWING_LEVEL);
2725 }
2726
2727 static void UnmapDrawingArea(int id)
2728 {
2729   UnmapGadget(level_editor_gadget[id]);
2730 }
2731
2732 void UnmapLevelEditorWindowGadgets()
2733 {
2734   int i;
2735
2736   for (i=0; i<NUM_EDITOR_GADGETS; i++)
2737     if (level_editor_gadget[i]->x < SX + SXSIZE)
2738       UnmapGadget(level_editor_gadget[i]);
2739 }
2740
2741 void UnmapLevelEditorGadgets()
2742 {
2743   int i;
2744
2745   for (i=0; i<NUM_EDITOR_GADGETS; i++)
2746     UnmapGadget(level_editor_gadget[i]);
2747 }
2748
2749 static void ResetUndoBuffer()
2750 {
2751   undo_buffer_position = -1;
2752   undo_buffer_steps = -1;
2753   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
2754 }
2755
2756 static void DrawEditModeWindow()
2757 {
2758   if (edit_mode == ED_MODE_INFO)
2759     DrawLevelInfoWindow();
2760   else if (edit_mode == ED_MODE_PROPERTIES)
2761     DrawPropertiesWindow();
2762   else  /* edit_mode == ED_MODE_DRAWING */
2763     DrawDrawingWindow();
2764 }
2765
2766 static boolean LevelChanged()
2767 {
2768   boolean level_changed = FALSE;
2769   int x, y;
2770
2771   for(y=0; y<lev_fieldy; y++) 
2772     for(x=0; x<lev_fieldx; x++)
2773       if (Feld[x][y] != Ur[x][y])
2774         level_changed = TRUE;
2775
2776   return level_changed;
2777 }
2778
2779 static boolean LevelContainsPlayer()
2780 {
2781   boolean player_found = FALSE;
2782   int x, y;
2783
2784   for(y=0; y<lev_fieldy; y++) 
2785     for(x=0; x<lev_fieldx; x++)
2786       if (Feld[x][y] == EL_PLAYER_1 ||
2787           Feld[x][y] == EL_SP_MURPHY) 
2788         player_found = TRUE;
2789
2790   return player_found;
2791 }
2792
2793 static void CopyPlayfield(short src[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
2794                           short dst[MAX_LEV_FIELDX][MAX_LEV_FIELDY])
2795 {
2796   int x, y;
2797
2798   for(x=0; x<lev_fieldx; x++)
2799     for(y=0; y<lev_fieldy; y++) 
2800       dst[x][y] = src[x][y];
2801 }
2802
2803 static void CopyCustomElementPropertiesToEditor(int element)
2804 {
2805   int i;
2806
2807   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
2808     custom_element_properties[i] = HAS_PROPERTY(element, i);
2809 }
2810
2811 static void CopyCustomElementPropertiesToGame(int element)
2812 {
2813   int i;
2814
2815   for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
2816     SET_PROPERTY(element, i, custom_element_properties[i]);
2817 }
2818
2819 void DrawLevelEd()
2820 {
2821   CloseDoor(DOOR_CLOSE_ALL);
2822   OpenDoor(DOOR_OPEN_2 | DOOR_NO_DELAY);
2823
2824   if (level_editor_test_game)
2825   {
2826     CopyPlayfield(Ur, Feld);
2827     CopyPlayfield(FieldBackup, Ur);
2828
2829     level_editor_test_game = FALSE;
2830   }
2831   else
2832   {
2833     edit_mode = ED_MODE_DRAWING;
2834     edit_mode_properties = ED_MODE_PROPERTIES_INFO;
2835
2836     ResetUndoBuffer();
2837
2838     level_xpos = -1;
2839     level_ypos = -1;
2840   }
2841
2842   /* copy default editor door content to main double buffer */
2843   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2844              DOOR_GFX_PAGEX6, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2845
2846   /* draw mouse button brush elements */
2847   DrawMiniGraphicExt(drawto,
2848                      DX + ED_WIN_MB_LEFT_XPOS, DY + ED_WIN_MB_LEFT_YPOS,
2849                      el2edimg(new_element1));
2850   DrawMiniGraphicExt(drawto,
2851                      DX + ED_WIN_MB_MIDDLE_XPOS, DY + ED_WIN_MB_MIDDLE_YPOS,
2852                      el2edimg(new_element2));
2853   DrawMiniGraphicExt(drawto,
2854                      DX + ED_WIN_MB_RIGHT_XPOS, DY + ED_WIN_MB_RIGHT_YPOS,
2855                      el2edimg(new_element3));
2856
2857   /* draw bigger door */
2858   DrawSpecialEditorDoor();
2859
2860   /* draw new control window */
2861   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2862              DOOR_GFX_PAGEX8, 236, EXSIZE, EYSIZE, EX, EY);
2863
2864   redraw_mask |= REDRAW_ALL;
2865
2866   ReinitializeElementListButtons();     /* only needed after setup changes */
2867
2868   UnmapTapeButtons();
2869   MapControlButtons();
2870
2871   /* copy actual editor door content to door double buffer for OpenDoor() */
2872   BlitBitmap(drawto, bitmap_db_door,
2873              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2874
2875   DrawEditModeWindow();
2876
2877   OpenDoor(DOOR_OPEN_1);
2878 }
2879
2880 static void AdjustDrawingAreaGadgets()
2881 {
2882   int ed_xsize = lev_fieldx + 2;
2883   int ed_ysize = lev_fieldy + 2;
2884   int max_ed_fieldx = MAX_ED_FIELDX;
2885   int max_ed_fieldy = MAX_ED_FIELDY;
2886   boolean horizontal_scrollbar_needed;
2887   boolean vertical_scrollbar_needed;
2888   int x, y, width, height;
2889   int xoffset, yoffset;
2890
2891   /* check if we need any scrollbars */
2892   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
2893   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
2894
2895   /* check if we have a smaller editor field because of scrollbars */
2896   if (horizontal_scrollbar_needed)
2897     max_ed_fieldy = MAX_ED_FIELDY - 1;
2898   if (vertical_scrollbar_needed)
2899     max_ed_fieldx = MAX_ED_FIELDX - 1;
2900
2901   /* check again if we now need more scrollbars because of less space */
2902   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
2903   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
2904
2905   /* check if editor field gets even smaller after adding new scrollbars */
2906   if (horizontal_scrollbar_needed)
2907     max_ed_fieldy = MAX_ED_FIELDY - 1;
2908   if (vertical_scrollbar_needed)
2909     max_ed_fieldx = MAX_ED_FIELDX - 1;
2910
2911   ed_fieldx = (ed_xsize < MAX_ED_FIELDX ? ed_xsize : max_ed_fieldx);
2912   ed_fieldy = (ed_ysize < MAX_ED_FIELDY ? ed_ysize : max_ed_fieldy);
2913
2914   ModifyGadget(level_editor_gadget[GADGET_ID_DRAWING_LEVEL],
2915                GDI_WIDTH, ed_fieldx * MINI_TILEX,
2916                GDI_HEIGHT, ed_fieldy * MINI_TILEY,
2917                GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
2918                GDI_END);
2919
2920   xoffset = (ed_fieldx == MAX_ED_FIELDX ? ED_SCROLLBUTTON_XSIZE : 0);
2921   yoffset = (ed_fieldy == MAX_ED_FIELDY ? ED_SCROLLBUTTON_YSIZE : 0);
2922
2923   x = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_RIGHT].x + xoffset;
2924   y = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_DOWN].y + yoffset;
2925
2926   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_RIGHT], GDI_X, x, GDI_END);
2927   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_DOWN], GDI_Y, y, GDI_END);
2928
2929   width = scrollbar_info[ED_SCROLLBAR_ID_AREA_HORIZONTAL].width + xoffset;
2930   height = scrollbar_info[ED_SCROLLBAR_ID_AREA_VERTICAL].height + yoffset;
2931
2932   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
2933                GDI_WIDTH, width,
2934                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldx,
2935                GDI_END);
2936   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
2937                GDI_HEIGHT, height,
2938                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldy,
2939                GDI_END);
2940 }
2941
2942 static void AdjustLevelScrollPosition()
2943 {
2944   if (level_xpos < -1)
2945     level_xpos = -1;
2946   if (level_xpos > lev_fieldx - ed_fieldx + 1)
2947     level_xpos = lev_fieldx - ed_fieldx + 1;
2948   if (lev_fieldx < ed_fieldx - 2)
2949     level_xpos = -1;
2950
2951   if (level_ypos < -1)
2952     level_ypos = -1;
2953   if (level_ypos > lev_fieldy - ed_fieldy + 1)
2954     level_ypos = lev_fieldy - ed_fieldy + 1;
2955   if (lev_fieldy < ed_fieldy - 2)
2956     level_ypos = -1;
2957 }
2958
2959 static void AdjustEditorScrollbar(int id)
2960 {
2961   struct GadgetInfo *gi = level_editor_gadget[id];
2962   int items_max, items_visible, item_position;
2963
2964   if (id == GADGET_ID_SCROLL_HORIZONTAL)
2965   {
2966     items_max = MAX(lev_fieldx + 2, ed_fieldx);
2967     items_visible = ed_fieldx;
2968     item_position = level_xpos + 1;
2969   }
2970   else
2971   {
2972     items_max = MAX(lev_fieldy + 2, ed_fieldy);
2973     items_visible = ed_fieldy;
2974     item_position = level_ypos + 1;
2975   }
2976
2977   if (item_position > items_max - items_visible)
2978     item_position = items_max - items_visible;
2979
2980   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
2981                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
2982 }
2983
2984 static void ModifyEditorTextInput(int textinput_id, char *new_text)
2985 {
2986   int gadget_id = textinput_info[textinput_id].gadget_id;
2987   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
2988
2989   ModifyGadget(gi, GDI_TEXT_VALUE, new_text, GDI_END);
2990 }
2991
2992 static void ModifyEditorCounter(int counter_id, int new_value)
2993 {
2994   int *counter_value = counterbutton_info[counter_id].value;
2995   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
2996   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
2997
2998   ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
2999
3000   if (counter_value != NULL)
3001     *counter_value = gi->text.number_value;
3002 }
3003
3004 static void ModifyEditorCounterLimits(int counter_id, int min, int max)
3005 {
3006   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
3007   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3008
3009   ModifyGadget(gi, GDI_NUMBER_MIN, min, GDI_NUMBER_MAX, max, GDI_END);
3010 }
3011
3012 static void PickDrawingElement(int button, int element)
3013 {
3014   if (button < 1 || button > 3)
3015     return;
3016
3017   if (button == 1)
3018   {
3019     new_element1 = element;
3020     DrawMiniGraphicExt(drawto,
3021                        DX + ED_WIN_MB_LEFT_XPOS, DY + ED_WIN_MB_LEFT_YPOS,
3022                        el2edimg(new_element1));
3023   }
3024   else if (button == 2)
3025   {
3026     new_element2 = element;
3027     DrawMiniGraphicExt(drawto,
3028                        DX + ED_WIN_MB_MIDDLE_XPOS, DY + ED_WIN_MB_MIDDLE_YPOS,
3029                        el2edimg(new_element2));
3030   }
3031   else
3032   {
3033     new_element3 = element;
3034     DrawMiniGraphicExt(drawto,
3035                        DX + ED_WIN_MB_RIGHT_XPOS, DY + ED_WIN_MB_RIGHT_YPOS,
3036                        el2edimg(new_element3));
3037   }
3038
3039   redraw_mask |= REDRAW_DOOR_1;
3040 }
3041
3042 static void DrawDrawingWindow()
3043 {
3044   SetMainBackgroundImage(IMG_UNDEFINED);
3045   ClearWindow();
3046   UnmapLevelEditorWindowGadgets();
3047
3048   AdjustDrawingAreaGadgets();
3049   AdjustLevelScrollPosition();
3050   AdjustEditorScrollbar(GADGET_ID_SCROLL_HORIZONTAL);
3051   AdjustEditorScrollbar(GADGET_ID_SCROLL_VERTICAL);
3052
3053   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3054   MapMainDrawingArea();
3055 }
3056
3057 static void DrawElementBorder(int dest_x, int dest_y, int width, int height)
3058 {
3059   int border_graphic = IMG_EDITOR_ELEMENT_BORDER;
3060   Bitmap *src_bitmap;
3061   int src_x, src_y;
3062   int num_mini_tilex = width / MINI_TILEX + 1;
3063   int num_mini_tiley = width / MINI_TILEY + 1;
3064   int x, y;
3065
3066   getMiniGraphicSource(border_graphic, &src_bitmap, &src_x, &src_y);
3067
3068   for (y=0; y < num_mini_tiley; y++)
3069     for (x=0; x < num_mini_tilex; x++)
3070       BlitBitmap(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3071                  dest_x - MINI_TILEX / 2 + x * MINI_TILEX,
3072                  dest_y - MINI_TILEY / 2 + y * MINI_TILEY);
3073
3074   ClearRectangle(drawto, dest_x - 1, dest_y - 1, width + 2, height + 2);
3075 }
3076
3077 static void DrawRandomPlacementBackgroundArea()
3078 {
3079   int area_x = ED_AREA_RANDOM_BACKGROUND_XPOS / MINI_TILEX;
3080   int area_y = ED_AREA_RANDOM_BACKGROUND_YPOS / MINI_TILEY;
3081   int area_sx = SX + ED_AREA_RANDOM_BACKGROUND_XPOS;
3082   int area_sy = SY + ED_AREA_RANDOM_BACKGROUND_YPOS;
3083
3084   ElementContent[0][0][0] = random_placement_background_element;
3085
3086   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY);
3087   DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
3088
3089   MapDrawingArea(GADGET_ID_RANDOM_BACKGROUND);
3090 }
3091
3092 static void DrawLevelInfoWindow()
3093 {
3094   char infotext[MAX_OUTPUT_LINESIZE + 1];
3095   int max_infotext_len = getMaxInfoTextLength();
3096   int xoffset_above = 0;
3097   int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
3098   int xoffset_right = getCounterGadgetWidth();
3099   int yoffset_right = ED_BORDER_SIZE;
3100   int xoffset_right2 = ED_CHECKBUTTON_XSIZE + 2 * ED_GADGET_DISTANCE;
3101   int yoffset_right2 = ED_BORDER_SIZE;
3102   int i, x, y;
3103
3104   SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
3105   ClearWindow();
3106   UnmapLevelEditorWindowGadgets();
3107
3108   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS_YPOS,
3109            "Level Settings", FONT_TITLE_1);
3110   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS2_YPOS,
3111            "Editor Settings", FONT_TITLE_1);
3112
3113   /* draw counter gadgets */
3114   for (i=ED_COUNTER_ID_LEVEL_FIRST; i<=ED_COUNTER_ID_LEVEL_LAST; i++)
3115   {
3116     if (counterbutton_info[i].infotext_above)
3117     {
3118       x = counterbutton_info[i].x + xoffset_above;
3119       y = counterbutton_info[i].y + yoffset_above;
3120
3121       sprintf(infotext, "%s:", counterbutton_info[i].infotext_above);
3122       infotext[max_infotext_len] = '\0';
3123       DrawTextF(x, y, FONT_TEXT_1, infotext);
3124     }
3125
3126     if (counterbutton_info[i].infotext_right)
3127     {
3128       x = counterbutton_info[i].x + xoffset_right;
3129       y = counterbutton_info[i].y + yoffset_right;
3130
3131       sprintf(infotext, "%s", counterbutton_info[i].infotext_right);
3132       infotext[max_infotext_len] = '\0';
3133       DrawTextF(x, y, FONT_TEXT_1, infotext);
3134     }
3135
3136     ModifyEditorCounter(i, *counterbutton_info[i].value);
3137     MapCounterButtons(i);
3138   }
3139
3140   /* draw text input gadgets */
3141   for (i=ED_TEXTINPUT_ID_LEVEL_FIRST; i<=ED_TEXTINPUT_ID_LEVEL_LAST; i++)
3142   {
3143     x = textinput_info[i].x + xoffset_above;
3144     y = textinput_info[i].y + yoffset_above;
3145
3146     sprintf(infotext, "%s:", textinput_info[i].infotext);
3147     infotext[max_infotext_len] = '\0';
3148
3149     DrawTextF(x, y, FONT_TEXT_1, infotext);
3150     ModifyEditorTextInput(i, textinput_info[i].value);
3151     MapTextInputGadget(i);
3152   }
3153
3154   /* draw radiobutton gadgets */
3155   for (i=ED_RADIOBUTTON_ID_LEVEL_FIRST; i<=ED_RADIOBUTTON_ID_LEVEL_LAST; i++)
3156   {
3157     boolean checked =
3158       (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
3159
3160     x = radiobutton_info[i].x + xoffset_right2;
3161     y = radiobutton_info[i].y + yoffset_right2;
3162
3163     DrawTextF(x, y, FONT_TEXT_1, radiobutton_info[i].text);
3164     ModifyGadget(level_editor_gadget[radiobutton_info[i].gadget_id],
3165                  GDI_CHECKED, checked, GDI_END);
3166     MapRadiobuttonGadget(i);
3167   }
3168
3169   /* draw checkbutton gadgets */
3170   for (i=ED_CHECKBUTTON_ID_LEVEL_FIRST; i<=ED_CHECKBUTTON_ID_LEVEL_LAST; i++)
3171   {
3172     x = checkbutton_info[i].x + xoffset_right2;
3173     y = checkbutton_info[i].y + yoffset_right2;
3174
3175     DrawTextF(x, y, FONT_TEXT_1, checkbutton_info[i].text);
3176     ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
3177                  GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
3178     MapCheckbuttonGadget(i);
3179   }
3180
3181   /* draw drawing area */
3182   DrawRandomPlacementBackgroundArea();
3183 }
3184
3185 static void DrawAmoebaContentArea()
3186 {
3187   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
3188   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
3189   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
3190   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
3191
3192   ElementContent[0][0][0] = level.amoeba_content;
3193
3194   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY);
3195   DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
3196
3197   DrawText(area_sx + TILEX, area_sy + 1, "Content of amoeba", FONT_TEXT_1);
3198
3199   MapDrawingArea(GADGET_ID_AMOEBA_CONTENT);
3200 }
3201
3202 static void DrawCustomChangedArea()
3203 {
3204   int area_x = ED_AREA_ELEM_CONTENT2_XPOS / MINI_TILEX;
3205   int area_y = ED_AREA_ELEM_CONTENT2_YPOS / MINI_TILEY;
3206   int area_sx = SX + ED_AREA_ELEM_CONTENT2_XPOS;
3207   int area_sy = SY + ED_AREA_ELEM_CONTENT2_YPOS;
3208   int i = properties_element - EL_CUSTOM_START;
3209
3210   if (!IS_CUSTOM_ELEMENT(properties_element))
3211   {
3212     /* this should never happen */
3213     Error(ERR_WARN, "element %d is no custom element", properties_element);
3214
3215     return;
3216   }
3217
3218   ElementContent[0][0][0] = level.custom_element_successor[i];
3219
3220   DrawElementBorder(area_sx, area_sy, MINI_TILEX, MINI_TILEY);
3221   DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
3222
3223   DrawText(area_sx + TILEX, area_sy + 1, "Element after change", FONT_TEXT_1);
3224
3225   MapDrawingArea(GADGET_ID_CUSTOM_CHANGED);
3226 }
3227
3228 static void DrawElementContentAreas()
3229 {
3230   int counter_id = ED_COUNTER_ID_ELEM_CONTENT;
3231   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
3232   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
3233   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
3234   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
3235   int xoffset_right = getCounterGadgetWidth();
3236   int yoffset_right = ED_BORDER_SIZE;
3237   int i, x, y;
3238
3239   for (i=0; i<MAX_ELEMENT_CONTENTS; i++)
3240     for (y=0; y<3; y++)
3241       for (x=0; x<3; x++)
3242         ElementContent[i][x][y] = level.yam_content[i][x][y];
3243
3244   for (i=0; i<MAX_ELEMENT_CONTENTS; i++)
3245     UnmapDrawingArea(GADGET_ID_ELEM_CONTENT_0 + i);
3246
3247   /* display counter to choose number of element content areas */
3248   x = counterbutton_info[counter_id].x + xoffset_right;
3249   y = counterbutton_info[counter_id].y + yoffset_right;
3250   DrawTextF(x, y, FONT_TEXT_1, "number of content areas");
3251
3252   ModifyEditorCounter(counter_id, *counterbutton_info[counter_id].value);
3253   MapCounterButtons(counter_id);
3254
3255   /* delete content areas in case of reducing number of them */
3256   DrawBackground(SX, area_sy - MINI_TILEX, SXSIZE, 12 * MINI_TILEY);
3257
3258   for (i=0; i<level.num_yam_contents; i++)
3259     DrawElementBorder(area_sx + 5 * (i % 4) * MINI_TILEX,
3260                       area_sy + 6 * (i / 4) * MINI_TILEY,
3261                       3 * MINI_TILEX, 3 * MINI_TILEY);
3262
3263   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 0 * MINI_TILEY + 1,
3264            "Content", FONT_TEXT_1);
3265   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 1 * MINI_TILEY + 1,
3266            "when",    FONT_TEXT_1);
3267   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 2 * MINI_TILEY + 1,
3268            "smashed", FONT_TEXT_1);
3269
3270   for (i=0; i<level.num_yam_contents; i++)
3271   {
3272     for (y=0; y<3; y++)
3273       for (x=0; x<3; x++)
3274         DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
3275                         ElementContent[i][x][y]);
3276
3277     DrawTextF(area_sx - SX + 5 * (i % 4) * MINI_TILEX + MINI_TILEX + 1,
3278               area_sy - SY + 6 * (i / 4) * MINI_TILEY + 4 * MINI_TILEY - 4,
3279               FONT_TEXT_1, "%d", i + 1);
3280   }
3281
3282   for (i=0; i<level.num_yam_contents; i++)
3283     MapDrawingArea(GADGET_ID_ELEM_CONTENT_0 + i);
3284 }
3285
3286 #define TEXT_COLLECTING         "Score for collecting"
3287 #define TEXT_SMASHING           "Score for smashing"
3288 #define TEXT_CRACKING           "Score for cracking"
3289 #define TEXT_SPEED              "Speed of amoeba growth"
3290 #define TEXT_DURATION           "Duration when activated"
3291
3292 static struct
3293 {
3294   int element;
3295   int *value;
3296   char *text;
3297 } elements_with_counter[] =
3298 {
3299   { EL_EMERALD,         &level.score[SC_EMERALD],       TEXT_COLLECTING },
3300   { EL_BD_DIAMOND,      &level.score[SC_EMERALD],       TEXT_COLLECTING },
3301   { EL_EMERALD_YELLOW,  &level.score[SC_EMERALD],       TEXT_COLLECTING },
3302   { EL_EMERALD_RED,     &level.score[SC_EMERALD],       TEXT_COLLECTING },
3303   { EL_EMERALD_PURPLE,  &level.score[SC_EMERALD],       TEXT_COLLECTING },
3304   { EL_DIAMOND,         &level.score[SC_DIAMOND],       TEXT_COLLECTING },
3305   { EL_BUG_RIGHT,       &level.score[SC_BUG],           TEXT_SMASHING   },
3306   { EL_BUG_UP,          &level.score[SC_BUG],           TEXT_SMASHING   },
3307   { EL_BUG_LEFT,        &level.score[SC_BUG],           TEXT_SMASHING   },
3308   { EL_BUG_DOWN,        &level.score[SC_BUG],           TEXT_SMASHING   },
3309   { EL_BD_BUTTERFLY_RIGHT,&level.score[SC_BUG],         TEXT_SMASHING   },
3310   { EL_BD_BUTTERFLY_UP,   &level.score[SC_BUG],         TEXT_SMASHING   },
3311   { EL_BD_BUTTERFLY_LEFT, &level.score[SC_BUG],         TEXT_SMASHING   },
3312   { EL_BD_BUTTERFLY_DOWN, &level.score[SC_BUG],         TEXT_SMASHING   },
3313   { EL_SPACESHIP_RIGHT, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3314   { EL_SPACESHIP_UP,    &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3315   { EL_SPACESHIP_LEFT,  &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3316   { EL_SPACESHIP_DOWN,  &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3317   { EL_BD_FIREFLY_RIGHT,&level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3318   { EL_BD_FIREFLY_UP,   &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3319   { EL_BD_FIREFLY_LEFT, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3320   { EL_BD_FIREFLY_DOWN, &level.score[SC_SPACESHIP],     TEXT_SMASHING   },
3321   { EL_YAMYAM,          &level.score[SC_YAMYAM],        TEXT_SMASHING   },
3322   { EL_DARK_YAMYAM,     &level.score[SC_YAMYAM],        TEXT_SMASHING   },
3323   { EL_ROBOT,           &level.score[SC_ROBOT],         TEXT_SMASHING   },
3324   { EL_PACMAN_RIGHT,    &level.score[SC_PACMAN],        TEXT_SMASHING   },
3325   { EL_PACMAN_UP,       &level.score[SC_PACMAN],        TEXT_SMASHING   },
3326   { EL_PACMAN_LEFT,     &level.score[SC_PACMAN],        TEXT_SMASHING   },
3327   { EL_PACMAN_DOWN,     &level.score[SC_PACMAN],        TEXT_SMASHING   },
3328   { EL_NUT,             &level.score[SC_NUT],           TEXT_CRACKING   },
3329   { EL_DYNAMITE,        &level.score[SC_DYNAMITE],      TEXT_COLLECTING },
3330   { EL_KEY_1,           &level.score[SC_KEY],           TEXT_COLLECTING },
3331   { EL_KEY_2,           &level.score[SC_KEY],           TEXT_COLLECTING },
3332   { EL_KEY_3,           &level.score[SC_KEY],           TEXT_COLLECTING },
3333   { EL_KEY_4,           &level.score[SC_KEY],           TEXT_COLLECTING },
3334   { EL_EM_KEY_1_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
3335   { EL_EM_KEY_2_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
3336   { EL_EM_KEY_3_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
3337   { EL_EM_KEY_4_FILE,   &level.score[SC_KEY],           TEXT_COLLECTING },
3338   { EL_AMOEBA_WET,      &level.amoeba_speed,            TEXT_SPEED      },
3339   { EL_AMOEBA_DRY,      &level.amoeba_speed,            TEXT_SPEED      },
3340   { EL_AMOEBA_FULL,     &level.amoeba_speed,            TEXT_SPEED      },
3341   { EL_BD_AMOEBA,       &level.amoeba_speed,            TEXT_SPEED      },
3342   { EL_MAGIC_WALL,      &level.time_magic_wall,         TEXT_DURATION   },
3343   { EL_ROBOT_WHEEL,     &level.time_wheel,              TEXT_DURATION   },
3344   { -1,                 NULL,                           NULL            }
3345 };
3346
3347 static boolean checkPropertiesConfig()
3348 {
3349   int i;
3350
3351   if (IS_GEM(properties_element) ||
3352       IS_CUSTOM_ELEMENT(properties_element) ||
3353       HAS_CONTENT(properties_element))
3354     return TRUE;
3355   else
3356     for (i=0; elements_with_counter[i].element != -1; i++)
3357       if (elements_with_counter[i].element == properties_element)
3358         return TRUE;
3359
3360   return FALSE;
3361 }
3362
3363 static void DrawPropertiesConfig()
3364 {
3365   int counter_id = ED_COUNTER_ID_ELEM_SCORE;
3366   int xoffset_right = getCounterGadgetWidth();
3367   int yoffset_right = ED_BORDER_SIZE;
3368   int xoffset_right2 = ED_CHECKBUTTON_XSIZE + 2 * ED_GADGET_DISTANCE;
3369   int yoffset_right2 = ED_BORDER_SIZE;
3370   int i, x, y;
3371
3372   /* check if there are elements where a score can be chosen for */
3373   for (i=0; elements_with_counter[i].element != -1; i++)
3374   {
3375     if (elements_with_counter[i].element == properties_element)
3376     {
3377       int x = counterbutton_info[counter_id].x + xoffset_right;
3378       int y = counterbutton_info[counter_id].y + yoffset_right;
3379
3380       counterbutton_info[counter_id].value = elements_with_counter[i].value;
3381       DrawTextF(x, y, FONT_TEXT_1, elements_with_counter[i].text);
3382
3383       ModifyEditorCounter(counter_id,  *counterbutton_info[counter_id].value);
3384       MapCounterButtons(counter_id);
3385       break;
3386     }
3387   }
3388
3389   if (HAS_CONTENT(properties_element) ||
3390       IS_CUSTOM_ELEMENT(properties_element))
3391   {
3392     /* draw stickybutton gadget */
3393     i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
3394     x = checkbutton_info[i].x + xoffset_right2;
3395     y = checkbutton_info[i].y + yoffset_right2;
3396
3397     DrawTextF(x, y, FONT_TEXT_1, checkbutton_info[i].text);
3398     ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
3399                  GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
3400     MapCheckbuttonGadget(i);
3401
3402     if (HAS_CONTENT(properties_element))
3403     {
3404       if (IS_AMOEBOID(properties_element))
3405         DrawAmoebaContentArea();
3406       else
3407         DrawElementContentAreas();
3408     }
3409 #if 1
3410     else if (IS_CUSTOM_ELEMENT(properties_element))
3411       DrawCustomChangedArea();
3412 #endif
3413   }
3414
3415   if (IS_GEM(properties_element))
3416   {
3417     /* draw checkbutton gadget */
3418     i = ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS;
3419     x = checkbutton_info[i].x + xoffset_right2;
3420     y = checkbutton_info[i].y + yoffset_right2;
3421
3422     DrawTextF(x, y, FONT_TEXT_1, checkbutton_info[i].text);
3423     ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
3424                  GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
3425     MapCheckbuttonGadget(i);
3426   }
3427
3428   if (IS_CUSTOM_ELEMENT(properties_element))
3429   {
3430     CopyCustomElementPropertiesToEditor(properties_element);
3431
3432     for (i =  ED_CHECKBUTTON_ID_CUSTOM_INDESTRUCTIBLE;
3433          i <= ED_CHECKBUTTON_ID_CUSTOM_SLIPPERY; i++)
3434     {
3435       /* draw checkbutton gadget */
3436       x = checkbutton_info[i].x + xoffset_right2;
3437       y = checkbutton_info[i].y + yoffset_right2;
3438
3439       DrawTextF(x, y, FONT_TEXT_1, checkbutton_info[i].text);
3440       ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
3441                    GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
3442       MapCheckbuttonGadget(i);
3443     }
3444
3445 #if 1
3446     /* draw selectbox gadget */
3447     i = ED_SELECTBOX_ID_CUSTOM_CHANGE_CAUSE;
3448     x = selectbox_info[i].x + xoffset_right2;
3449     y = selectbox_info[i].y + yoffset_right2;
3450
3451     selectbox_info[i].index = &index_change_cause;
3452
3453     DrawTextF(x, y, FONT_TEXT_1, selectbox_info[i].text);
3454     ModifyGadget(level_editor_gadget[selectbox_info[i].gadget_id],
3455                  GDI_SELECTBOX_INDEX, *selectbox_info[i].index, GDI_END);
3456     MapSelectboxGadget(i);
3457 #endif
3458   }
3459 }
3460
3461 char *getElementDescriptionFilename(int element)
3462 {
3463   char *docs_dir = options.docs_directory;
3464   char *elements_subdir = "elements";
3465   static char *filename = NULL;
3466   char basename[MAX_FILENAME_LEN];
3467
3468   if (filename != NULL)
3469     free(filename);
3470
3471   /* 1st try: look for element description file for exactly this element */
3472   sprintf(basename, "%s.txt", element_info[element].token_name);
3473   filename = getPath3(docs_dir, elements_subdir, basename);
3474   if (fileExists(filename))
3475     return filename;
3476
3477   free(filename);
3478
3479   /* 2nd try: look for element description file for this element's class */
3480   sprintf(basename, "%s.txt", element_info[element].class_name);
3481   filename = getPath3(docs_dir, elements_subdir, basename);
3482   if (fileExists(filename))
3483     return filename;
3484
3485   return NULL;
3486 }
3487
3488 static boolean PrintInfoText(char *text, int font_nr, int screen_line)
3489 {
3490   int font_height = getFontHeight(font_nr);
3491   int pad_x = ED_SETTINGS_XPOS;
3492   int pad_y = 5 * TILEY;
3493   int sx = SX + pad_x;
3494   int sy = SY + pad_y;
3495   int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
3496
3497   if (screen_line >= max_lines_per_screen)
3498     return FALSE;
3499
3500   DrawText(sx, sy + screen_line * font_height, text, font_nr);
3501
3502   return TRUE;
3503 }
3504
3505 static int PrintElementDescriptionFromFile(char *filename, int screen_line)
3506 {
3507   int font_nr = FONT_TEXT_2;
3508   int font_width = getFontWidth(font_nr);
3509   int pad_x = ED_SETTINGS_XPOS;
3510   int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
3511   char line[MAX_LINE_LEN];
3512   char buffer[max_chars_per_line + 1];
3513   int buffer_len;
3514   int lines_printed = 0;
3515   FILE *file;
3516
3517   if (filename == NULL)
3518     return 0;
3519
3520   if (!(file = fopen(filename, MODE_READ)))
3521     return 0;
3522
3523   buffer[0] = '\0';
3524   buffer_len = 0;
3525
3526   while(!feof(file))
3527   {
3528     char *line_ptr, *word_ptr;
3529     boolean last_line_was_empty = TRUE;
3530
3531     /* read next line of input file */
3532     if (!fgets(line, MAX_LINE_LEN, file))
3533       break;
3534
3535     /* skip comments (lines directly beginning with '#') */
3536     if (line[0] == '#')
3537       continue;
3538
3539     /* cut trailing newline from input line */
3540     for (line_ptr = line; *line_ptr; line_ptr++)
3541     {
3542       if (*line_ptr == '\n' || *line_ptr == '\r')
3543       {
3544         *line_ptr = '\0';
3545         break;
3546       }
3547     }
3548
3549     if (strlen(line) == 0)              /* special case: force empty line */
3550       strcpy(line, "\n");
3551
3552     word_ptr = line;
3553
3554     while (*word_ptr)
3555     {
3556       boolean print_buffer = FALSE;
3557       int word_len;
3558
3559       /* skip leading whitespaces */
3560       while (*word_ptr == ' ' || *word_ptr == '\t')
3561         word_ptr++;
3562
3563       line_ptr = word_ptr;
3564       word_len = 0;
3565
3566       /* look for end of next word */
3567       while (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3568       {
3569         line_ptr++;
3570         word_len++;
3571       }
3572
3573       if (word_len == 0)
3574       {
3575         continue;
3576       }
3577       else if (*word_ptr == '\n')       /* special case: force empty line */
3578       {
3579         if (buffer_len == 0)
3580           word_ptr++;
3581
3582         /* prevent printing of multiple empty lines */
3583         if (buffer_len > 0 || !last_line_was_empty)
3584           print_buffer = TRUE;
3585       }
3586       else if (word_len < max_chars_per_line - buffer_len)
3587       {
3588         /* word fits into text buffer -- add word */
3589
3590         if (buffer_len > 0)
3591           buffer[buffer_len++] = ' ';
3592
3593         strncpy(&buffer[buffer_len], word_ptr, word_len);
3594         buffer_len += word_len;
3595         buffer[buffer_len] = '\0';
3596         word_ptr += word_len;
3597       }
3598       else if (buffer_len > 0)
3599       {
3600         /* not enough space left for word in text buffer -- print buffer */
3601
3602         print_buffer = TRUE;
3603       }
3604       else
3605       {
3606         /* word does not fit at all into empty text buffer -- cut word */
3607
3608         strncpy(buffer, word_ptr, max_chars_per_line);
3609         buffer[max_chars_per_line] = '\0';
3610         word_ptr += max_chars_per_line;
3611         print_buffer = TRUE;
3612       }
3613
3614       if (print_buffer)
3615       {
3616         if (!PrintInfoText(buffer, font_nr, screen_line + lines_printed))
3617           return lines_printed;
3618
3619         last_line_was_empty = (buffer_len == 0);
3620         lines_printed++;
3621
3622         buffer[0] = '\0';
3623         buffer_len = 0;
3624         print_buffer = FALSE;
3625       }
3626     }
3627   }
3628
3629   fclose(file);
3630
3631   if (buffer_len > 0)
3632     if (PrintInfoText(buffer, font_nr, screen_line + lines_printed))
3633       lines_printed++;
3634
3635   return lines_printed;
3636 }
3637
3638 static void DrawPropertiesTabulatorGadgets()
3639 {
3640   struct GadgetInfo *gd_gi = level_editor_gadget[GADGET_ID_PROPERTIES_INFO];
3641   struct GadgetDesign *gd = &gd_gi->alt_design[GD_BUTTON_UNPRESSED];
3642   int gd_x = gd->x + gd_gi->border.width / 2;
3643   int gd_y = gd->y + gd_gi->height - 1;
3644   Pixel tab_color = GetPixel(gd->bitmap, gd_x, gd_y);
3645   int id_first = ED_TEXTBUTTON_ID_PROPERTIES_INFO;
3646   int id_last  = ED_TEXTBUTTON_ID_PROPERTIES_INFO;
3647   int i;
3648
3649   /* draw additional "configure" tabulator for configurable elements */
3650   if (checkPropertiesConfig())
3651     id_last = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG;
3652
3653   /* draw additional "advanced" tabulator for custom elements */
3654   if (IS_CUSTOM_ELEMENT(properties_element))
3655     id_last = ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED;
3656
3657   for (i=id_first; i <= id_last; i++)
3658   {
3659     int gadget_id = textbutton_info[i].gadget_id;
3660     struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3661     boolean active = (i != edit_mode_properties);
3662
3663     /* draw background line below tabulator button */
3664     ClearRectangleOnBackground(drawto, gi->x, gi->y + gi->height, gi->width,1);
3665
3666     /* draw solid line below inactive tabulator buttons */
3667     if (!active && tab_color != BLACK_PIXEL)    /* black => transparent */
3668       FillRectangle(drawto, gi->x, gi->y + gi->height, gi->width,1, tab_color);
3669
3670     ModifyGadget(gi, GDI_ACTIVE, active, GDI_END);
3671     MapTextbuttonGadget(i);
3672   }
3673
3674   /* draw little border line below tabulator buttons */
3675   if (tab_color != BLACK_PIXEL)                 /* black => transparent */
3676     FillRectangle(drawto, gd_gi->x, gd_gi->y + gd_gi->height + 1,
3677                   3 * gd_gi->width + 2 * ED_GADGET_DISTANCE,
3678                   ED_GADGET_DISTANCE, tab_color);
3679 }
3680
3681 static void DrawPropertiesInfo()
3682 {
3683   static struct
3684   {
3685     int value;
3686     char *text;
3687   }
3688   properties[] =
3689   {
3690     { EP_AMOEBALIVE,            "- living amoeba"               },
3691     { EP_AMOEBOID,              "- amoeboid"                    },
3692     { EP_INDESTRUCTIBLE,        "- undestructible"              },
3693     { EP_SLIPPERY,              "- slippery"                    },
3694     { EP_ENEMY,                 "- enemy"                       },
3695     { EP_CAN_FALL,              "- can fall"                    },
3696     { EP_CAN_SMASH,             "- can smash"                   },
3697     { EP_CAN_CHANGE,            "- can change"                  },
3698     { EP_CAN_MOVE,              "- can move"                    },
3699     { EP_DONT_TOUCH,            "- don't touch"                 },
3700     { EP_DONT_GO_TO,            "- don't go to"                 },
3701     { EP_FOOD_DARK_YAMYAM,      "- food for dark yamyam"        },
3702     { EP_BD_ELEMENT,            "- BD style"                    },
3703     { EP_SB_ELEMENT,            "- SB style"                    },
3704     { EP_GEM,                   "- gem"                         },
3705     { EP_INACTIVE,              "- inactive"                    },
3706     { EP_EXPLOSIVE,             "- explosive"                   },
3707     { EP_FOOD_PENGUIN,          "- food for penguin"            },
3708     { EP_PUSHABLE,              "- pushable"                    },
3709     { EP_PLAYER,                "- player"                      },
3710     { EP_HAS_CONTENT,           "- has content"                 },
3711     { EP_DIGGABLE,              "- diggable"                    },
3712     { EP_SP_ELEMENT,            "- SB style"                    },
3713     { EP_WALKABLE_INSIDE,       "- walkable inside"             },
3714     { EP_ACTIVE_BOMB,           "- active bomb"                 },
3715     { EP_BELT,                  "- belt"                        },
3716     { EP_BELT_ACTIVE,           "- active belt"                 },
3717     { EP_BELT_SWITCH,           "- belt switch"                 },
3718     { EP_WALKABLE_UNDER,        "- walkable under"              },
3719     { EP_EM_SLIPPERY_WALL,      "- EM style slippery wall"      },
3720     { EP_CAN_BE_CRUMBLED,       "- can be crumbled"             },
3721     { -1,                       NULL                            }
3722   };
3723   char *filename = getElementDescriptionFilename(properties_element);
3724   char *percentage_text = "In this level:";
3725   char *properties_text = "Standard properties:";
3726   float percentage;
3727   int num_elements_in_level;
3728   int num_standard_properties = 0;
3729   int font1_nr = FONT_TEXT_1;
3730   int font2_nr = FONT_TEXT_2;
3731   int font1_width = getFontWidth(font1_nr);
3732   int font2_height = getFontHeight(font2_nr);
3733   int pad_x = ED_SETTINGS_XPOS;
3734   int pad_y = 5 * TILEY;
3735   int screen_line = 2;
3736   int i, x, y;
3737
3738   num_elements_in_level = 0;
3739   for (y=0; y<lev_fieldy; y++) 
3740     for (x=0; x<lev_fieldx; x++)
3741       if (Feld[x][y] == properties_element)
3742         num_elements_in_level++;
3743   percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
3744
3745   DrawTextF(pad_x, pad_y, font1_nr, percentage_text);
3746   DrawTextF(pad_x + strlen(percentage_text) * font1_width, pad_y,
3747             font2_nr, "%d (%.2f%%)", num_elements_in_level, percentage);
3748
3749   for (i=0; properties[i].value != -1; i++)
3750     if (HAS_PROPERTY(properties_element, properties[i].value))
3751       num_standard_properties++;
3752
3753 #if 1
3754   if (num_standard_properties > 0)
3755   {
3756     DrawTextF(pad_x, pad_y + screen_line * font2_height, font1_nr,
3757               properties_text);
3758     screen_line++;
3759
3760     for (i=0; properties[i].value != -1; i++)
3761     {
3762       if (!HAS_PROPERTY(properties_element, properties[i].value))
3763         continue;
3764
3765       DrawTextF(pad_x, pad_y + screen_line * font2_height, font2_nr,
3766                 properties[i].text);
3767       screen_line++;
3768     }
3769
3770     screen_line++;
3771   }
3772 #endif
3773
3774   PrintInfoText("Description:", FONT_TEXT_1, screen_line);
3775   if (PrintElementDescriptionFromFile(filename, screen_line + 1) == 0)
3776     PrintInfoText("No description available.", FONT_TEXT_1, screen_line);
3777 }
3778
3779 static void DrawPropertiesAdvanced()
3780 {
3781   DrawText(SX + ED_SETTINGS_XPOS, SY + 5 * TILEY,
3782            "Under construction! :-)", FONT_TEXT_1);
3783 }
3784
3785 static void DrawPropertiesWindow()
3786 {
3787   int xstart = 2;
3788   int ystart = 4;
3789
3790   /* make sure that previous properties edit mode exists for this element */
3791   if (edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED &&
3792       !IS_CUSTOM_ELEMENT(properties_element))
3793     edit_mode_properties = ED_MODE_PROPERTIES_CONFIG;
3794
3795   if (edit_mode_properties == ED_MODE_PROPERTIES_CONFIG &&
3796       !checkPropertiesConfig())
3797     edit_mode_properties = ED_MODE_PROPERTIES_INFO;
3798
3799   UnmapLevelEditorWindowGadgets();
3800
3801   SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
3802   ClearWindow();
3803
3804   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS_YPOS,
3805            "Element Settings", FONT_TITLE_1);
3806
3807   DrawElementBorder(SX + xstart * MINI_TILEX,
3808                     SY + ystart * MINI_TILEY + MINI_TILEY / 2,
3809                     TILEX, TILEY);
3810   DrawGraphicAnimationExt(drawto,
3811                           SX + xstart * MINI_TILEX,
3812                           SY + ystart * MINI_TILEY + MINI_TILEY / 2,
3813                           el2img(properties_element), -1, NO_MASKING);
3814
3815   FrameCounter = 0;     /* restart animation frame counter */
3816
3817   DrawTextF((xstart + 3) * MINI_TILEX, (ystart + 1) * MINI_TILEY,
3818             FONT_TEXT_1, getElementInfoText(properties_element));
3819
3820   DrawPropertiesTabulatorGadgets();
3821
3822   if (edit_mode_properties == ED_MODE_PROPERTIES_INFO)
3823     DrawPropertiesInfo();
3824   else if (edit_mode_properties == ED_MODE_PROPERTIES_CONFIG)
3825     DrawPropertiesConfig();
3826   else  /* edit_mode_properties == ED_MODE_PROPERTIES_ADVANCED */
3827     DrawPropertiesAdvanced();
3828 }
3829
3830 static void DrawLineElement(int sx, int sy, int element, boolean change_level)
3831 {
3832   int lx = sx + level_xpos;
3833   int ly = sy + level_ypos;
3834
3835   DrawMiniElement(sx, sy, (element < 0 ? Feld[lx][ly] : element));
3836
3837   if (change_level)
3838     Feld[lx][ly] = element;
3839 }
3840
3841 static void DrawLine(int from_x, int from_y, int to_x, int to_y,
3842                      int element, boolean change_level)
3843 {
3844   if (from_y == to_y)                   /* horizontal line */
3845   {
3846     int x;
3847     int y = from_y;
3848
3849     if (from_x > to_x)
3850       swap_numbers(&from_x, &to_x);
3851
3852     for (x=from_x; x<=to_x; x++)
3853       DrawLineElement(x, y, element, change_level);
3854   }
3855   else if (from_x == to_x)              /* vertical line */
3856   {
3857     int x = from_x;
3858     int y;
3859
3860     if (from_y > to_y)
3861       swap_numbers(&from_y, &to_y);
3862
3863     for (y=from_y; y<=to_y; y++)
3864       DrawLineElement(x, y, element, change_level);
3865   }
3866   else                                  /* diagonal line */
3867   {
3868     int len_x = ABS(to_x - from_x);
3869     int len_y = ABS(to_y - from_y);
3870     int x, y;
3871
3872     if (len_y < len_x)                  /* a < 1 */
3873     {
3874       float a = (float)len_y / (float)len_x;
3875
3876       if (from_x > to_x)
3877         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
3878
3879       for (x=0; x<=len_x; x++)
3880       {
3881         y = (int)(a * x + 0.5) * (to_y < from_y ? -1 : +1);
3882         DrawLineElement(from_x + x, from_y + y, element, change_level);
3883       }
3884     }
3885     else                                /* a >= 1 */
3886     {
3887       float a = (float)len_x / (float)len_y;
3888
3889       if (from_y > to_y)
3890         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
3891
3892       for (y=0; y<=len_y; y++)
3893       {
3894         x = (int)(a * y + 0.5) * (to_x < from_x ? -1 : +1);
3895         DrawLineElement(from_x + x, from_y + y, element, change_level);
3896       }
3897     }
3898   }
3899 }
3900
3901 static void DrawBox(int from_x, int from_y, int to_x, int to_y,
3902                     int element, boolean change_level)
3903 {
3904   DrawLine(from_x, from_y, from_x, to_y, element, change_level);
3905   DrawLine(from_x, to_y, to_x, to_y, element, change_level);
3906   DrawLine(to_x, to_y, to_x, from_y, element, change_level);
3907   DrawLine(to_x, from_y, from_x, from_y, element, change_level);
3908 }
3909
3910 static void DrawFilledBox(int from_x, int from_y, int to_x, int to_y,
3911                           int element, boolean change_level)
3912 {
3913   int y;
3914
3915   if (from_y > to_y)
3916     swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
3917
3918   for (y=from_y; y<=to_y; y++)
3919     DrawLine(from_x, y, to_x, y, element, change_level);
3920 }
3921
3922 static void DrawArcExt(int from_x, int from_y, int to_x2, int to_y2,
3923                        int element, boolean change_level)
3924 {
3925   int to_x = to_x2 - (to_x2 > from_x ? +1 : -1);
3926   int to_y = to_y2 - (to_y2 > from_y ? +1 : -1);
3927   int len_x = ABS(to_x - from_x);
3928   int len_y = ABS(to_y - from_y);
3929   int radius, x, y;
3930
3931   radius = (int)(sqrt((float)(len_x * len_x + len_y * len_y)) + 0.5);
3932
3933   /* not optimal (some points get drawn twice) but simple,
3934      and fast enough for the few points we are drawing */
3935
3936   for (x=0; x<=radius; x++)
3937   {
3938     int sx, sy, lx, ly;
3939
3940     y = (int)(sqrt((float)(radius * radius - x * x)) + 0.5);
3941
3942     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
3943     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
3944     lx = sx + level_xpos;
3945     ly = sy + level_ypos;
3946
3947     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
3948       DrawLineElement(sx, sy, element, change_level);
3949   }
3950
3951   for (y=0; y<=radius; y++)
3952   {
3953     int sx, sy, lx, ly;
3954
3955     x = (int)(sqrt((float)(radius * radius - y * y)) + 0.5);
3956
3957     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
3958     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
3959     lx = sx + level_xpos;
3960     ly = sy + level_ypos;
3961
3962     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
3963       DrawLineElement(sx, sy, element, change_level);
3964   }
3965 }
3966
3967 static void DrawArc(int from_x, int from_y, int to_x, int to_y,
3968                     int element, boolean change_level)
3969 {
3970   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
3971   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
3972
3973   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
3974 }
3975
3976 #define DRAW_CIRCLES_BUTTON_AVAILABLE   0
3977 #if DRAW_CIRCLES_BUTTON_AVAILABLE
3978 static void DrawCircle(int from_x, int from_y, int to_x, int to_y,
3979                        int element, boolean change_level)
3980 {
3981   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
3982   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
3983   int mirror_to_x2 = from_x - (to_x2 - from_x);
3984   int mirror_to_y2 = from_y - (to_y2 - from_y);
3985
3986   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
3987   DrawArcExt(from_x, from_y, mirror_to_x2, to_y2, element, change_level);
3988   DrawArcExt(from_x, from_y, to_x2, mirror_to_y2, element, change_level);
3989   DrawArcExt(from_x, from_y, mirror_to_x2, mirror_to_y2, element,change_level);
3990 }
3991 #endif
3992
3993 static void DrawAreaBorder(int from_x, int from_y, int to_x, int to_y)
3994 {
3995   int from_sx, from_sy;
3996   int to_sx, to_sy;
3997
3998   if (from_x > to_x)
3999     swap_numbers(&from_x, &to_x);
4000
4001   if (from_y > to_y)
4002     swap_numbers(&from_y, &to_y);
4003
4004   from_sx = SX + from_x * MINI_TILEX;
4005   from_sy = SY + from_y * MINI_TILEY;
4006   to_sx = SX + to_x * MINI_TILEX + MINI_TILEX - 1;
4007   to_sy = SY + to_y * MINI_TILEY + MINI_TILEY - 1;
4008
4009   DrawSimpleWhiteLine(drawto, from_sx, from_sy, to_sx, from_sy);
4010   DrawSimpleWhiteLine(drawto, to_sx, from_sy, to_sx, to_sy);
4011   DrawSimpleWhiteLine(drawto, to_sx, to_sy, from_sx, to_sy);
4012   DrawSimpleWhiteLine(drawto, from_sx, to_sy, from_sx, from_sy);
4013
4014   if (from_x == to_x && from_y == to_y)
4015     MarkTileDirty(from_x/2, from_y/2);
4016   else
4017     redraw_mask |= REDRAW_FIELD;
4018 }
4019
4020 static void SelectArea(int from_x, int from_y, int to_x, int to_y,
4021                        int element, boolean change_level)
4022 {
4023   if (element == -1 || change_level)
4024     DrawBox(from_x, from_y, to_x, to_y, -1, FALSE);
4025   else
4026     DrawAreaBorder(from_x, from_y, to_x, to_y);
4027 }
4028
4029 /* values for CopyBrushExt() */
4030 #define CB_AREA_TO_BRUSH        0
4031 #define CB_BRUSH_TO_CURSOR      1
4032 #define CB_BRUSH_TO_LEVEL       2
4033 #define CB_DELETE_OLD_CURSOR    3
4034
4035 static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
4036                          int button, int mode)
4037 {
4038   static short brush_buffer[MAX_ED_FIELDX][MAX_ED_FIELDY];
4039   static int brush_width, brush_height;
4040   static int last_cursor_x = -1, last_cursor_y = -1;
4041   static boolean delete_old_brush;
4042   int new_element = BUTTON_ELEMENT(button);
4043   int x, y;
4044
4045   if (mode == CB_DELETE_OLD_CURSOR && !delete_old_brush)
4046     return;
4047
4048   if (mode == CB_AREA_TO_BRUSH)
4049   {
4050     int from_lx, from_ly;
4051
4052     if (from_x > to_x)
4053       swap_numbers(&from_x, &to_x);
4054
4055     if (from_y > to_y)
4056       swap_numbers(&from_y, &to_y);
4057
4058     brush_width = to_x - from_x + 1;
4059     brush_height = to_y - from_y + 1;
4060
4061     from_lx = from_x + level_xpos;
4062     from_ly = from_y + level_ypos;
4063
4064     for (y=0; y<brush_height; y++)
4065     {
4066       for (x=0; x<brush_width; x++)
4067       {
4068         brush_buffer[x][y] = Feld[from_lx + x][from_ly + y];
4069
4070         if (button != 1)
4071           DrawLineElement(from_x + x, from_y + y, new_element, TRUE);
4072       }
4073     }
4074
4075     if (button != 1)
4076       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4077
4078     delete_old_brush = FALSE;
4079   }
4080   else if (mode == CB_BRUSH_TO_CURSOR || mode == CB_DELETE_OLD_CURSOR ||
4081            mode == CB_BRUSH_TO_LEVEL)
4082   {
4083     int cursor_x = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_x : from_x);
4084     int cursor_y = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_y : from_y);
4085     int cursor_from_x = cursor_x - brush_width / 2;
4086     int cursor_from_y = cursor_y - brush_height / 2;
4087     int border_from_x = cursor_x, border_from_y = cursor_y;
4088     int border_to_x = cursor_x, border_to_y = cursor_y;
4089
4090     if (mode != CB_DELETE_OLD_CURSOR && delete_old_brush)
4091       CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
4092
4093     if (!IN_ED_FIELD(cursor_x, cursor_y) ||
4094         !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos))
4095     {
4096       delete_old_brush = FALSE;
4097       return;
4098     }
4099
4100     for (y=0; y<brush_height; y++)
4101     {
4102       for (x=0; x<brush_width; x++)
4103       {
4104         int sx = cursor_from_x + x;
4105         int sy = cursor_from_y + y;
4106         int lx = sx + level_xpos;
4107         int ly = sy + level_ypos;
4108         boolean change_level = (mode == CB_BRUSH_TO_LEVEL);
4109         int element = (mode == CB_DELETE_OLD_CURSOR ? -1 :
4110                        mode == CB_BRUSH_TO_CURSOR || button == 1 ?
4111                        brush_buffer[x][y] : new_element);
4112
4113         if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
4114         {
4115           if (sx < border_from_x)
4116             border_from_x = sx;
4117           else if (sx > border_to_x)
4118             border_to_x = sx;
4119           if (sy < border_from_y)
4120             border_from_y = sy;
4121           else if (sy > border_to_y)
4122             border_to_y = sy;
4123
4124           DrawLineElement(sx, sy, element, change_level);
4125         }
4126       }
4127     }
4128
4129     if (mode != CB_DELETE_OLD_CURSOR)
4130       DrawAreaBorder(border_from_x, border_from_y, border_to_x, border_to_y);
4131
4132     last_cursor_x = cursor_x;
4133     last_cursor_y = cursor_y;
4134     delete_old_brush = TRUE;
4135   }
4136 }
4137
4138 static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y,
4139                             int button)
4140 {
4141   CopyBrushExt(from_x, from_y, to_x, to_y, button, CB_AREA_TO_BRUSH);
4142 }
4143
4144 static void CopyBrushToLevel(int x, int y, int button)
4145 {
4146   CopyBrushExt(x, y, 0, 0, button, CB_BRUSH_TO_LEVEL);
4147 }
4148
4149 static void CopyBrushToCursor(int x, int y)
4150 {
4151   CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR);
4152 }
4153
4154 static void DeleteBrushFromCursor()
4155 {
4156   CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
4157 }
4158
4159 static void FloodFill(int from_x, int from_y, int fill_element)
4160 {
4161   int i,x,y;
4162   int old_element;
4163   static int check[4][2] = { {-1,0}, {0,-1}, {1,0}, {0,1} };
4164   static int safety = 0;
4165
4166   /* check if starting field still has the desired content */
4167   if (Feld[from_x][from_y] == fill_element)
4168     return;
4169
4170   safety++;
4171
4172   if (safety > lev_fieldx*lev_fieldy)
4173     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
4174
4175   old_element = Feld[from_x][from_y];
4176   Feld[from_x][from_y] = fill_element;
4177
4178   for(i=0;i<4;i++)
4179   {
4180     x = from_x + check[i][0];
4181     y = from_y + check[i][1];
4182
4183     if (IN_LEV_FIELD(x,y) && Feld[x][y] == old_element)
4184       FloodFill(x, y, fill_element);
4185   }
4186
4187   safety--;
4188 }
4189
4190 /* values for DrawLevelText() modes */
4191 #define TEXT_INIT               0
4192 #define TEXT_SETCURSOR          1
4193 #define TEXT_WRITECHAR          2
4194 #define TEXT_BACKSPACE          3
4195 #define TEXT_NEWLINE            4
4196 #define TEXT_END                5
4197 #define TEXT_QUERY_TYPING       6
4198
4199 static int DrawLevelText(int sx, int sy, char letter, int mode)
4200 {
4201   static short delete_buffer[MAX_LEV_FIELDX];
4202   static int start_sx, start_sy;
4203   static int last_sx, last_sy;
4204   static boolean typing = FALSE;
4205   int letter_element = EL_CHAR_ASCII0 + letter;
4206   int lx = 0, ly = 0;
4207
4208   /* map lower case letters to upper case and convert special characters */
4209   if (letter >= 'a' && letter <= 'z')
4210     letter_element = EL_CHAR_ASCII0 + letter + (int)('A' - 'a');
4211   else if (letter == 'ä' || letter == 'Ä')
4212     letter_element = EL_CHAR_AUMLAUT;
4213   else if (letter == 'ö' || letter == 'Ö')
4214     letter_element = EL_CHAR_OUMLAUT;
4215   else if (letter == 'ü' || letter == 'Ãœ')
4216     letter_element = EL_CHAR_UUMLAUT;
4217   else if (letter == '^')
4218     letter_element = EL_CHAR_COPYRIGHT;
4219   else
4220     letter_element = EL_CHAR_ASCII0 + letter;
4221
4222   if (mode != TEXT_INIT)
4223   {
4224     if (!typing)
4225       return FALSE;
4226
4227     if (mode != TEXT_SETCURSOR)
4228     {
4229       sx = last_sx;
4230       sy = last_sy;
4231     }
4232
4233     lx = last_sx + level_xpos;
4234     ly = last_sy + level_ypos;
4235   }
4236
4237   switch (mode)
4238   {
4239     case TEXT_INIT:
4240       if (typing)
4241         DrawLevelText(0, 0, 0, TEXT_END);
4242
4243       typing = TRUE;
4244       start_sx = last_sx = sx;
4245       start_sy = last_sy = sy;
4246       DrawLevelText(sx, sy, 0, TEXT_SETCURSOR);
4247       break;
4248
4249     case TEXT_SETCURSOR:
4250       DrawMiniElement(last_sx, last_sy, Feld[lx][ly]);
4251       DrawAreaBorder(sx, sy, sx, sy);
4252       last_sx = sx;
4253       last_sy = sy;
4254       break;
4255
4256     case TEXT_WRITECHAR:
4257       if (letter_element >= EL_CHAR_START && letter_element <= EL_CHAR_END)
4258       {
4259         delete_buffer[sx - start_sx] = Feld[lx][ly];
4260         Feld[lx][ly] = letter_element;
4261
4262         if (sx + 1 < ed_fieldx && lx + 1 < lev_fieldx)
4263           DrawLevelText(sx + 1, sy, 0, TEXT_SETCURSOR);
4264         else if (sy + 1 < ed_fieldy && ly + 1 < lev_fieldy)
4265           DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
4266         else
4267           DrawLevelText(0, 0, 0, TEXT_END);
4268       }
4269       break;
4270
4271     case TEXT_BACKSPACE:
4272       if (sx > start_sx)
4273       {
4274         Feld[lx - 1][ly] = delete_buffer[sx - start_sx - 1];
4275         DrawMiniElement(sx - 1, sy, Feld[lx - 1][ly]);
4276         DrawLevelText(sx - 1, sy, 0, TEXT_SETCURSOR);
4277       }
4278       break;
4279
4280     case TEXT_NEWLINE:
4281       if (sy + 1 < ed_fieldy - 1 && ly + 1 < lev_fieldy - 1)
4282         DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
4283       else
4284         DrawLevelText(0, 0, 0, TEXT_END);
4285       break;
4286
4287     case TEXT_END:
4288       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4289       DrawMiniElement(sx, sy, Feld[lx][ly]);
4290       typing = FALSE;
4291       break;
4292
4293     case TEXT_QUERY_TYPING:
4294       break;
4295
4296     default:
4297       break;
4298   }
4299
4300   return typing;
4301 }
4302
4303 static void SetTextCursor(int unused_sx, int unused_sy, int sx, int sy,
4304                           int element, boolean change_level)
4305 {
4306   int lx = sx + level_xpos;
4307   int ly = sy + level_ypos;
4308
4309   if (element == -1)
4310     DrawMiniElement(sx, sy, Feld[lx][ly]);
4311   else
4312     DrawAreaBorder(sx, sy, sx, sy);
4313 }
4314
4315 static void CopyLevelToUndoBuffer(int mode)
4316 {
4317   static boolean accumulated_undo = FALSE;
4318   boolean new_undo_buffer_position = TRUE;
4319   int last_border_element;
4320   int x, y;
4321
4322   switch (mode)
4323   {
4324     case UNDO_IMMEDIATE:
4325       accumulated_undo = FALSE;
4326       break;
4327
4328     case UNDO_ACCUMULATE:
4329       if (accumulated_undo)
4330         new_undo_buffer_position = FALSE;
4331       accumulated_undo = TRUE;
4332       break;
4333
4334     default:
4335       break;
4336   }
4337
4338   if (new_undo_buffer_position)
4339   {
4340     /* new position in undo buffer ring */
4341     undo_buffer_position = (undo_buffer_position + 1) % NUM_UNDO_STEPS;
4342
4343     if (undo_buffer_steps < NUM_UNDO_STEPS - 1)
4344       undo_buffer_steps++;
4345   }
4346
4347   for(x=0; x<lev_fieldx; x++)
4348     for(y=0; y<lev_fieldy; y++)
4349       UndoBuffer[undo_buffer_position][x][y] = Feld[x][y];
4350
4351   /* check if drawing operation forces change of border style */
4352   last_border_element = BorderElement;
4353   SetBorderElement();
4354   if (BorderElement != last_border_element)
4355     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4356
4357 #if 0
4358 #ifdef DEBUG
4359   printf("level saved to undo buffer\n");
4360 #endif
4361 #endif
4362 }
4363
4364 static void RandomPlacement(int new_element)
4365 {
4366   static boolean free_position[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
4367   int num_free_positions;
4368   int num_percentage;
4369   int num_elements;
4370   int x, y;
4371
4372   /* determine number of free positions for the new elements */
4373   /* (maybe this statement should be formatted a bit more readable...) */
4374   num_free_positions = 0;
4375   for (x=0; x<lev_fieldx; x++)
4376     for (y=0; y<lev_fieldy; y++)
4377       if ((free_position[x][y] =
4378            ((random_placement_background_restricted &&
4379              Feld[x][y] == random_placement_background_element) ||
4380             (!random_placement_background_restricted &&
4381              Feld[x][y] != new_element))) == TRUE)
4382         num_free_positions++;
4383
4384   /* determine number of new elements to place there */
4385   num_percentage = num_free_positions * random_placement_value / 100;
4386   num_elements = (random_placement_method == RANDOM_USE_PERCENTAGE ?
4387                   num_percentage : random_placement_value);
4388
4389   /* if not more free positions than elements to place, fill whole level */
4390   if (num_elements >= num_free_positions)
4391   {
4392     for (x=0; x<lev_fieldx; x++)
4393       for (y=0; y<lev_fieldy; y++)
4394         Feld[x][y] = new_element;
4395
4396     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4397     CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4398     return;
4399   }
4400
4401   while (num_elements > 0)
4402   {
4403     x = RND(lev_fieldx);
4404     y = RND(lev_fieldy);
4405
4406     /* don't place element at the same position twice */
4407     if (free_position[x][y])
4408     {
4409       free_position[x][y] = FALSE;
4410       Feld[x][y] = new_element;
4411       num_elements--;
4412     }
4413   }
4414
4415   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4416   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4417 }
4418
4419 void WrapLevel(int dx, int dy)
4420 {
4421   int wrap_dx = lev_fieldx - dx;
4422   int wrap_dy = lev_fieldy - dy;
4423   int x, y;
4424
4425   for(x=0; x<lev_fieldx; x++)
4426     for(y=0; y<lev_fieldy; y++)
4427       FieldBackup[x][y] = Feld[x][y];
4428
4429   for(x=0; x<lev_fieldx; x++)
4430     for(y=0; y<lev_fieldy; y++)
4431       Feld[x][y] =
4432         FieldBackup[(x + wrap_dx) % lev_fieldx][(y + wrap_dy) % lev_fieldy];
4433
4434   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4435   CopyLevelToUndoBuffer(UNDO_ACCUMULATE);
4436 }
4437
4438 static void HandleDrawingAreas(struct GadgetInfo *gi)
4439 {
4440   static boolean started_inside_drawing_area = FALSE;
4441   int id = gi->custom_id;
4442   boolean button_press_event;
4443   boolean button_release_event;
4444   boolean inside_drawing_area = !gi->event.off_borders;
4445   boolean draw_level = (id == GADGET_ID_DRAWING_LEVEL);
4446   int actual_drawing_function;
4447   int button = gi->event.button;
4448   int new_element = BUTTON_ELEMENT(button);
4449   int sx = gi->event.x, sy = gi->event.y;
4450   int min_sx = 0, min_sy = 0;
4451   int max_sx = gi->drawing.area_xsize - 1, max_sy = gi->drawing.area_ysize - 1;
4452   int lx = 0, ly = 0;
4453   int min_lx = 0, min_ly = 0;
4454   int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
4455   int x, y;
4456
4457   /* handle info callback for each invocation of action callback */
4458   gi->callback_info(gi);
4459
4460   button_press_event = (gi->event.type == GD_EVENT_PRESSED);
4461   button_release_event = (gi->event.type == GD_EVENT_RELEASED);
4462
4463   /* make sure to stay inside drawing area boundaries */
4464   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
4465   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
4466
4467   if (draw_level)
4468   {
4469     /* get positions inside level field */
4470     lx = sx + level_xpos;
4471     ly = sy + level_ypos;
4472
4473     if (!IN_LEV_FIELD(lx, ly))
4474       inside_drawing_area = FALSE;
4475
4476     /* make sure to stay inside level field boundaries */
4477     lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
4478     ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
4479
4480     /* correct drawing area positions accordingly */
4481     sx = lx - level_xpos;
4482     sy = ly - level_ypos;
4483   }
4484
4485   if (button_press_event)
4486     started_inside_drawing_area = inside_drawing_area;
4487
4488   if (!started_inside_drawing_area)
4489     return;
4490
4491   if (!button && !button_release_event)
4492     return;
4493
4494   /* automatically switch to 'single item' drawing mode, if needed */
4495   actual_drawing_function =
4496     (draw_level ? drawing_function : GADGET_ID_SINGLE_ITEMS);
4497
4498   switch (actual_drawing_function)
4499   {
4500     case GADGET_ID_SINGLE_ITEMS:
4501       if (draw_level)
4502       {
4503         if (button_release_event)
4504         {
4505           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4506
4507           if (edit_mode == ED_MODE_DRAWING && draw_with_brush &&
4508               !inside_drawing_area)
4509             DeleteBrushFromCursor();
4510         }
4511
4512         if (!button)
4513           break;
4514
4515         if (draw_with_brush)
4516         {
4517           if (!button_release_event)
4518             CopyBrushToLevel(sx, sy, button);
4519         }
4520         else if (new_element != Feld[lx][ly])
4521         {
4522           if (new_element == EL_PLAYER_1)
4523           {
4524             /* remove player at old position */
4525             for(y=0; y<lev_fieldy; y++)
4526             {
4527               for(x=0; x<lev_fieldx; x++)
4528               {
4529                 if (Feld[x][y] == EL_PLAYER_1)
4530                 {
4531                   Feld[x][y] = EL_EMPTY;
4532                   if (x - level_xpos >= 0 && x - level_xpos < ed_fieldx &&
4533                       y - level_ypos >= 0 && y - level_ypos < ed_fieldy)
4534                     DrawMiniElement(x - level_xpos, y - level_ypos,
4535                                     EL_EMPTY);
4536                 }
4537               }
4538             }
4539           }
4540
4541           Feld[lx][ly] = new_element;
4542           DrawMiniElement(sx, sy, new_element);
4543         }
4544       }
4545       else
4546       {
4547         DrawMiniGraphicExt(drawto,
4548                            gi->x + sx * MINI_TILEX,
4549                            gi->y + sy * MINI_TILEY,
4550                            el2edimg(new_element));
4551         DrawMiniGraphicExt(window,
4552                            gi->x + sx * MINI_TILEX,
4553                            gi->y + sy * MINI_TILEY,
4554                            el2edimg(new_element));
4555
4556         if (id == GADGET_ID_AMOEBA_CONTENT)
4557           level.amoeba_content = new_element;
4558         else if (id == GADGET_ID_CUSTOM_CHANGED &&
4559                  IS_CUSTOM_ELEMENT(properties_element))
4560         {
4561           int i = properties_element - EL_CUSTOM_START;
4562
4563           level.custom_element_successor[i] = new_element;
4564         }
4565         else if (id == GADGET_ID_RANDOM_BACKGROUND)
4566           random_placement_background_element = new_element;
4567         else if (id >= GADGET_ID_ELEM_CONTENT_0 &&
4568                  id <= GADGET_ID_ELEM_CONTENT_7)
4569           level.yam_content[id - GADGET_ID_ELEM_CONTENT_0][sx][sy] =
4570             new_element;
4571       }
4572       break;
4573
4574     case GADGET_ID_CONNECTED_ITEMS:
4575       {
4576         static int last_sx = -1;
4577         static int last_sy = -1;
4578
4579         if (button_release_event)
4580           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4581
4582         if (button)
4583         {
4584           if (!button_press_event)
4585             DrawLine(last_sx, last_sy, sx, sy, new_element, TRUE);
4586
4587           last_sx = sx;
4588           last_sy = sy;
4589         }
4590       }
4591       break;
4592
4593     case GADGET_ID_LINE:
4594     case GADGET_ID_ARC:
4595     case GADGET_ID_RECTANGLE:
4596     case GADGET_ID_FILLED_BOX:
4597     case GADGET_ID_GRAB_BRUSH:
4598     case GADGET_ID_TEXT:
4599       {
4600         static int last_sx = -1;
4601         static int last_sy = -1;
4602         static int start_sx = -1;
4603         static int start_sy = -1;
4604         void (*draw_func)(int, int, int, int, int, boolean);
4605
4606         if (drawing_function == GADGET_ID_LINE)
4607           draw_func = DrawLine;
4608         else if (drawing_function == GADGET_ID_ARC)
4609           draw_func = DrawArc;
4610         else if (drawing_function == GADGET_ID_RECTANGLE)
4611           draw_func = DrawBox;
4612         else if (drawing_function == GADGET_ID_FILLED_BOX)
4613           draw_func = DrawFilledBox;
4614         else if (drawing_function == GADGET_ID_GRAB_BRUSH)
4615           draw_func = SelectArea;
4616         else /* (drawing_function == GADGET_ID_TEXT) */
4617           draw_func = SetTextCursor;
4618
4619         if (button_press_event)
4620         {
4621           draw_func(sx, sy, sx, sy, new_element, FALSE);
4622           start_sx = last_sx = sx;
4623           start_sy = last_sy = sy;
4624
4625           if (drawing_function == GADGET_ID_TEXT)
4626             DrawLevelText(0, 0, 0, TEXT_END);
4627         }
4628         else if (button_release_event)
4629         {
4630           draw_func(start_sx, start_sy, sx, sy, new_element, TRUE);
4631           if (drawing_function == GADGET_ID_GRAB_BRUSH)
4632           {
4633             CopyAreaToBrush(start_sx, start_sy, sx, sy, button);
4634             CopyBrushToCursor(sx, sy);
4635             ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS],
4636                           MB_LEFTBUTTON);
4637             draw_with_brush = TRUE;
4638           }
4639           else if (drawing_function == GADGET_ID_TEXT)
4640             DrawLevelText(sx, sy, 0, TEXT_INIT);
4641           else
4642             CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4643         }
4644         else if (last_sx != sx || last_sy != sy)
4645         {
4646           draw_func(start_sx, start_sy, last_sx, last_sy, -1, FALSE);
4647           draw_func(start_sx, start_sy, sx, sy, new_element, FALSE);
4648           last_sx = sx;
4649           last_sy = sy;
4650         }
4651       }
4652       break;
4653
4654     case GADGET_ID_FLOOD_FILL:
4655       if (button_press_event && Feld[lx][ly] != new_element)
4656       {
4657         FloodFill(lx, ly, new_element);
4658         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4659         CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
4660       }
4661       break;
4662
4663     case GADGET_ID_PICK_ELEMENT:
4664       if (button_release_event)
4665         ClickOnGadget(level_editor_gadget[last_drawing_function],
4666                       MB_LEFTBUTTON);
4667       else
4668         PickDrawingElement(button, Feld[lx][ly]);
4669
4670       break;
4671
4672     default:
4673       break;
4674   }
4675 }
4676
4677 static void HandleCounterButtons(struct GadgetInfo *gi)
4678 {
4679   int gadget_id = gi->custom_id;
4680   int counter_id = gi->custom_type_id;
4681   int button = gi->event.button;
4682   int *counter_value = counterbutton_info[counter_id].value;
4683   int step = BUTTON_STEPSIZE(button) *
4684     (gadget_id == counterbutton_info[counter_id].gadget_id_down ? -1 : +1);
4685
4686   if (counter_id == ED_COUNTER_ID_SELECT_LEVEL)
4687   {
4688     boolean pressed = (gi->event.type == GD_EVENT_PRESSED);
4689     boolean released = (gi->event.type == GD_EVENT_RELEASED);
4690     boolean level_changed = LevelChanged();
4691
4692     if ((level_changed && pressed) || (!level_changed && released))
4693       return;
4694
4695     if (level_changed && !Request("Level has changed! Discard changes ?",
4696                                   REQ_ASK))
4697     {
4698       if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
4699         ModifyEditorCounter(counter_id, *counter_value);
4700       return;
4701     }
4702   }
4703
4704   if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
4705     *counter_value = gi->text.number_value;
4706   else
4707     ModifyEditorCounter(counter_id, *counter_value + step);
4708
4709   switch (counter_id)
4710   {
4711     case ED_COUNTER_ID_ELEM_CONTENT:
4712       DrawElementContentAreas();
4713       break;
4714
4715     case ED_COUNTER_ID_LEVEL_XSIZE:
4716     case ED_COUNTER_ID_LEVEL_YSIZE:
4717       lev_fieldx = level.fieldx;
4718       lev_fieldy = level.fieldy;
4719       break;
4720
4721     case ED_COUNTER_ID_SELECT_LEVEL:
4722       LoadLevel(level_nr);
4723       TapeErase();
4724       ResetUndoBuffer();
4725       DrawEditModeWindow();
4726       break;
4727
4728     default:
4729       break;
4730   }
4731 }
4732
4733 static void HandleTextInputGadgets(struct GadgetInfo *gi)
4734 {
4735   strcpy(textinput_info[gi->custom_type_id].value, gi->text.value);
4736 }
4737
4738 static void HandleSelectboxGadgets(struct GadgetInfo *gi)
4739 {
4740   int type_id = gi->custom_type_id;
4741
4742   *selectbox_info[type_id].index = gi->selectbox.index;
4743
4744 #if 0
4745   printf("Selected text value: '%s' [%d]\n",
4746          selectbox_info[type_id].options[gi->selectbox.index].text,
4747          selectbox_info[type_id].options[gi->selectbox.index].value);
4748 #endif
4749 }
4750
4751 static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
4752 {
4753   if (gi->custom_type_id >= ED_TEXTBUTTON_ID_PROPERTIES_INFO &&
4754       gi->custom_type_id <= ED_TEXTBUTTON_ID_PROPERTIES_ADVANCED)
4755   {
4756     edit_mode_properties = gi->custom_type_id;
4757
4758     DrawPropertiesWindow();
4759   }
4760 }
4761
4762 static void HandleRadiobuttons(struct GadgetInfo *gi)
4763 {
4764   *radiobutton_info[gi->custom_type_id].value =
4765     radiobutton_info[gi->custom_type_id].checked_value;
4766 }
4767
4768 static void HandleCheckbuttons(struct GadgetInfo *gi)
4769 {
4770   int type_id = gi->custom_type_id;
4771
4772   *checkbutton_info[type_id].value ^= TRUE;
4773
4774   if (type_id >= ED_CHECKBUTTON_ID_CUSTOM_FIRST &&
4775       type_id <= ED_CHECKBUTTON_ID_CUSTOM_LAST)
4776     CopyCustomElementPropertiesToGame(properties_element);
4777 }
4778
4779 static void HandleControlButtons(struct GadgetInfo *gi)
4780 {
4781   int id = gi->custom_id;
4782   int button = gi->event.button;
4783   int step = BUTTON_STEPSIZE(button);
4784   int new_element = BUTTON_ELEMENT(button);
4785   int i, x, y;
4786
4787   if (edit_mode == ED_MODE_DRAWING && drawing_function == GADGET_ID_TEXT)
4788     DrawLevelText(0, 0, 0, TEXT_END);
4789
4790   if (id < ED_NUM_CTRL1_BUTTONS && id != GADGET_ID_PROPERTIES &&
4791       edit_mode != ED_MODE_DRAWING)
4792   {
4793     DrawDrawingWindow();
4794     edit_mode = ED_MODE_DRAWING;
4795   }
4796
4797   switch (id)
4798   {
4799     case GADGET_ID_SCROLL_LEFT:
4800       if (level_xpos >= 0)
4801       {
4802         if (lev_fieldx < ed_fieldx - 2)
4803           break;
4804
4805         level_xpos -= step;
4806         if (level_xpos < -1)
4807           level_xpos = -1;
4808         if (button == 1)
4809           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_RIGHT);
4810         else
4811           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4812
4813         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
4814                      GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
4815       }
4816       break;
4817
4818     case GADGET_ID_SCROLL_RIGHT:
4819       if (level_xpos <= lev_fieldx - ed_fieldx)
4820       {
4821         if (lev_fieldx < ed_fieldx - 2)
4822           break;
4823
4824         level_xpos += step;
4825         if (level_xpos > lev_fieldx - ed_fieldx + 1)
4826           level_xpos = lev_fieldx - ed_fieldx + 1;
4827         if (button == 1)
4828           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_LEFT);
4829         else
4830           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4831
4832         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
4833                      GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
4834       }
4835       break;
4836
4837     case GADGET_ID_SCROLL_UP:
4838       if (level_ypos >= 0)
4839       {
4840         if (lev_fieldy < ed_fieldy - 2)
4841           break;
4842
4843         level_ypos -= step;
4844         if (level_ypos < -1)
4845           level_ypos = -1;
4846         if (button == 1)
4847           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_DOWN);
4848         else
4849           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4850
4851         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
4852                      GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
4853       }
4854       break;
4855
4856     case GADGET_ID_SCROLL_DOWN:
4857       if (level_ypos <= lev_fieldy - ed_fieldy)
4858       {
4859         if (lev_fieldy < ed_fieldy - 2)
4860           break;
4861
4862         level_ypos += step;
4863         if (level_ypos > lev_fieldy - ed_fieldy + 1)
4864           level_ypos = lev_fieldy - ed_fieldy + 1;
4865         if (button == 1)
4866           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_UP);
4867         else
4868           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4869
4870         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
4871                      GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
4872       }
4873       break;
4874
4875     case GADGET_ID_SCROLL_HORIZONTAL:
4876       level_xpos = gi->event.item_position - 1;
4877       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4878       break;
4879
4880     case GADGET_ID_SCROLL_VERTICAL:
4881       level_ypos = gi->event.item_position - 1;
4882       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
4883       break;
4884
4885     case GADGET_ID_SCROLL_LIST_UP:
4886     case GADGET_ID_SCROLL_LIST_DOWN:
4887     case GADGET_ID_SCROLL_LIST_VERTICAL:
4888       if (id == GADGET_ID_SCROLL_LIST_VERTICAL)
4889         element_shift = gi->event.item_position * ED_ELEMENTLIST_BUTTONS_HORIZ;
4890       else
4891       {
4892         step *= (id == GADGET_ID_SCROLL_LIST_UP ? -1 : +1);
4893         element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
4894
4895         if (element_shift < 0)
4896           element_shift = 0;
4897         if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
4898           element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
4899
4900         ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
4901                      GDI_SCROLLBAR_ITEM_POSITION,
4902                      element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
4903       }
4904
4905       for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
4906       {
4907         int gadget_id = GADGET_ID_ELEMENTLIST_FIRST + i;
4908         struct GadgetInfo *gi = level_editor_gadget[gadget_id];
4909         struct GadgetDesign *gd = &gi->deco.design;
4910         int element = editor_elements[element_shift + i];
4911
4912         UnmapGadget(gi);
4913         getMiniGraphicSource(el2edimg(element), &gd->bitmap, &gd->x, &gd->y);
4914         ModifyGadget(gi, GDI_INFO_TEXT, getElementInfoText(element), GDI_END);
4915         MapGadget(gi);
4916       }
4917       break;
4918
4919     case GADGET_ID_WRAP_LEFT:
4920       WrapLevel(-step, 0);
4921       break;
4922
4923     case GADGET_ID_WRAP_RIGHT:
4924       WrapLevel(step, 0);
4925       break;
4926
4927     case GADGET_ID_WRAP_UP:
4928       WrapLevel(0, -step);
4929       break;
4930
4931     case GADGET_ID_WRAP_DOWN:
4932       WrapLevel(0, step);
4933       break;
4934
4935     case GADGET_ID_SINGLE_ITEMS:
4936     case GADGET_ID_CONNECTED_ITEMS:
4937     case GADGET_ID_LINE:
4938     case GADGET_ID_ARC:
4939     case GADGET_ID_TEXT:
4940     case GADGET_ID_RECTANGLE:
4941     case GADGET_ID_FILLED_BOX:
4942     case GADGET_ID_FLOOD_FILL:
4943     case GADGET_ID_GRAB_BRUSH:
4944     case GADGET_ID_PICK_ELEMENT:
4945       last_drawing_function = drawing_function;
4946       drawing_function = id;
4947       draw_with_brush = FALSE;
4948       break;
4949
4950     case GADGET_ID_RANDOM_PLACEMENT:
4951       RandomPlacement(new_element);
4952       break;
4953
4954     case GADGET_ID_PROPERTIES:
4955       if (edit_mode != ED_MODE_PROPERTIES)
4956       {
4957         properties_element = new_element;
4958         DrawPropertiesWindow();
4959         edit_mode = ED_MODE_PROPERTIES;
4960       }
4961       else
4962       {
4963         DrawDrawingWindow();
4964         edit_mode = ED_MODE_DRAWING;
4965       }
4966       break;
4967
4968     case GADGET_ID_UNDO:
4969       if (undo_buffer_steps == 0)
4970       {
4971         Request("Undo buffer empty !", REQ_CONFIRM);
4972         break;
4973       }
4974
4975       undo_buffer_position =
4976         (undo_buffer_position - 1 + NUM_UNDO_STEPS) % NUM_UNDO_STEPS;
4977       undo_buffer_steps--;
4978
4979       for(x=0; x<lev_fieldx; x++)
4980         for(y=0; y<lev_fieldy; y++)
4981           Feld[x][y] = UndoBuffer[undo_buffer_position][x][y];
4982       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos,level_ypos);
4983       break;
4984
4985     case GADGET_ID_INFO:
4986       if (edit_mode != ED_MODE_INFO)
4987       {
4988         DrawLevelInfoWindow();
4989         edit_mode = ED_MODE_INFO;
4990       }
4991       else
4992       {
4993         DrawDrawingWindow();
4994         edit_mode = ED_MODE_DRAWING;
4995       }
4996       break;
4997
4998     case GADGET_ID_CLEAR:
4999       for(x=0; x<MAX_LEV_FIELDX; x++) 
5000         for(y=0; y<MAX_LEV_FIELDY; y++) 
5001           Feld[x][y] = (button == 1 ? EL_EMPTY : new_element);
5002       CopyLevelToUndoBuffer(GADGET_ID_CLEAR);
5003
5004       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
5005       break;
5006
5007     case GADGET_ID_SAVE:
5008       if (leveldir_current->readonly)
5009       {
5010         Request("This level is read only !", REQ_CONFIRM);
5011         break;
5012       }
5013
5014       if (!LevelContainsPlayer)
5015         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
5016       else
5017       {
5018         if (Request("Save this level and kill the old ?", REQ_ASK))
5019         {
5020           CopyPlayfield(Feld, Ur);
5021
5022           SaveLevel(level_nr);
5023         }
5024       }
5025       break;
5026
5027     case GADGET_ID_TEST:
5028       if (!LevelContainsPlayer)
5029         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
5030       else
5031       {
5032         if (LevelChanged())
5033           level.game_version = GAME_VERSION_ACTUAL;
5034
5035         CopyPlayfield(Ur, FieldBackup);
5036         CopyPlayfield(Feld, Ur);
5037
5038         UnmapLevelEditorGadgets();
5039         UndrawSpecialEditorDoor();
5040
5041         CloseDoor(DOOR_CLOSE_ALL);
5042
5043         DrawCompleteVideoDisplay();
5044
5045         if (setup.autorecord)
5046           TapeStartRecording();
5047
5048         level_editor_test_game = TRUE;
5049         game_status = GAME_MODE_PLAYING;
5050
5051         InitGame();
5052       }
5053       break;
5054
5055     case GADGET_ID_EXIT:
5056       RequestExitLevelEditor(TRUE);     /* if level has changed, ask user */
5057       break;
5058
5059     default:
5060       if (id >= GADGET_ID_ELEMENTLIST_FIRST &&
5061           id <= GADGET_ID_ELEMENTLIST_LAST)
5062       {
5063         int element_position = id - GADGET_ID_ELEMENTLIST_FIRST;
5064         int new_element = editor_elements[element_position + element_shift];
5065
5066         PickDrawingElement(button, new_element);
5067
5068 #if 1
5069         if (!stick_element_properties_window)
5070 #else
5071         if (!HAS_CONTENT(properties_element) ||
5072             !stick_element_properties_window)
5073 #endif
5074         {
5075           properties_element = new_element;
5076           if (edit_mode == ED_MODE_PROPERTIES)
5077             DrawPropertiesWindow();
5078         }
5079
5080         if (drawing_function == GADGET_ID_PICK_ELEMENT)
5081           ClickOnGadget(level_editor_gadget[last_drawing_function],
5082                         MB_LEFTBUTTON);
5083       }
5084 #ifdef DEBUG
5085       else if (gi->event.type == GD_EVENT_PRESSED)
5086         printf("default: HandleControlButtons: GD_EVENT_PRESSED(%d)\n", id);
5087       else if (gi->event.type == GD_EVENT_RELEASED)
5088         printf("default: HandleControlButtons: GD_EVENT_RELEASED(%d)\n", id);
5089       else if (gi->event.type == GD_EVENT_MOVING)
5090         printf("default: HandleControlButtons: GD_EVENT_MOVING(%d)\n", id);
5091       else
5092         printf("default: HandleControlButtons: ? (id == %d)\n", id);
5093 #endif
5094       break;
5095   }
5096 }
5097
5098 void HandleLevelEditorKeyInput(Key key)
5099 {
5100   char letter = getCharFromKey(key);
5101   int button = MB_LEFTBUTTON;
5102
5103   if (drawing_function == GADGET_ID_TEXT &&
5104       DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE)
5105   {
5106     if (letter)
5107       DrawLevelText(0, 0, letter, TEXT_WRITECHAR);
5108     else if (key == KSYM_Delete || key == KSYM_BackSpace)
5109       DrawLevelText(0, 0, 0, TEXT_BACKSPACE);
5110     else if (key == KSYM_Return)
5111       DrawLevelText(0, 0, 0, TEXT_NEWLINE);
5112     else if (key == KSYM_Escape)
5113       DrawLevelText(0, 0, 0, TEXT_END);
5114   }
5115   else if (button_status == MB_RELEASED)
5116   {
5117     int i, id = GADGET_ID_NONE;
5118
5119     switch (key)
5120     {
5121       case KSYM_Left:
5122         id = GADGET_ID_SCROLL_LEFT;
5123         break;
5124       case KSYM_Right:
5125         id = GADGET_ID_SCROLL_RIGHT;
5126         break;
5127       case KSYM_Up:
5128         id = GADGET_ID_SCROLL_UP;
5129         break;
5130       case KSYM_Down:
5131         id = GADGET_ID_SCROLL_DOWN;
5132         break;
5133       case KSYM_Page_Up:
5134         id = GADGET_ID_SCROLL_LIST_UP;
5135         button = MB_RIGHTBUTTON;
5136         break;
5137       case KSYM_Page_Down:
5138         id = GADGET_ID_SCROLL_LIST_DOWN;
5139         button = MB_RIGHTBUTTON;
5140         break;
5141
5142       case KSYM_Escape:
5143         if (edit_mode == ED_MODE_DRAWING)
5144         {
5145           RequestExitLevelEditor(setup.ask_on_escape);
5146         }
5147         else
5148         {
5149           DrawDrawingWindow();
5150           edit_mode = ED_MODE_DRAWING;
5151         }
5152         break;
5153
5154       default:
5155         break;
5156     }
5157
5158     if (id != GADGET_ID_NONE)
5159       ClickOnGadget(level_editor_gadget[id], button);
5160     else if (letter == '.')
5161       ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
5162     else if (key == KSYM_Return || key == setup.shortcut.toggle_pause)
5163       ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button);
5164     else
5165       for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
5166         if (letter && letter == control_info[i].shortcut)
5167           if (!anyTextGadgetActive())
5168             ClickOnGadget(level_editor_gadget[i], button);
5169   }
5170 }
5171
5172 void HandleLevelEditorIdle()
5173 {
5174   static unsigned long action_delay = 0;
5175   unsigned long action_delay_value = GameFrameDelay;
5176   int xpos = 1, ypos = 2;
5177
5178   if (edit_mode != ED_MODE_PROPERTIES)
5179     return;
5180
5181   if (!DelayReached(&action_delay, action_delay_value))
5182     return;
5183
5184   DrawGraphicAnimationExt(drawto,
5185                           SX + xpos * TILEX,
5186                           SY + ypos * TILEY + MINI_TILEY / 2,
5187                           el2img(properties_element), -1, NO_MASKING);
5188
5189   MarkTileDirty(xpos, ypos);
5190   MarkTileDirty(xpos, ypos + 1);
5191
5192   FrameCounter++;       /* increase animation frame counter */
5193 }
5194
5195 void ClearEditorGadgetInfoText()
5196 {
5197   DrawBackground(INFOTEXT_XPOS, INFOTEXT_YPOS, INFOTEXT_XSIZE, INFOTEXT_YSIZE);
5198 }
5199
5200 void HandleEditorGadgetInfoText(void *ptr)
5201 {
5202   struct GadgetInfo *gi = (struct GadgetInfo *)ptr;
5203   char infotext[MAX_OUTPUT_LINESIZE + 1];
5204   char shortcut[MAX_OUTPUT_LINESIZE + 1];
5205   int max_infotext_len = getMaxInfoTextLength();
5206
5207   if (game_status != GAME_MODE_EDITOR)
5208     return;
5209
5210   ClearEditorGadgetInfoText();
5211
5212   if (gi->event.type == GD_EVENT_INFO_LEAVING)
5213     return;
5214
5215   /* misuse this function to delete brush cursor, if needed */
5216   if (edit_mode == ED_MODE_DRAWING && draw_with_brush)
5217     DeleteBrushFromCursor();
5218
5219   if (gi == NULL || gi->info_text == NULL)
5220     return;
5221
5222   strncpy(infotext, gi->info_text, max_infotext_len);
5223   infotext[max_infotext_len] = '\0';
5224
5225   if (gi->custom_id < ED_NUM_CTRL_BUTTONS)
5226   {
5227     int key = control_info[gi->custom_id].shortcut;
5228
5229     if (key)
5230     {
5231       if (gi->custom_id == GADGET_ID_SINGLE_ITEMS)      /* special case 1 */
5232         sprintf(shortcut, " ('.' or '%c')", key);
5233       else if (gi->custom_id == GADGET_ID_TEST)         /* special case 2 */
5234         sprintf(shortcut, " ('Enter' or 'Shift-%c')", key);
5235       else                                              /* normal case */
5236         sprintf(shortcut, " ('%s%c')",
5237                 (key >= 'A' && key <= 'Z' ? "Shift-" : ""), key);
5238
5239       if (strlen(infotext) + strlen(shortcut) <= max_infotext_len)
5240         strcat(infotext, shortcut);
5241     }
5242   }
5243
5244   DrawText(INFOTEXT_XPOS, INFOTEXT_YPOS, infotext, FONT_TEXT_2);
5245 }
5246
5247 static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
5248 {
5249   static int start_lx, start_ly;
5250   char *infotext;
5251   int id = gi->custom_id;
5252   int sx = gi->event.x;
5253   int sy = gi->event.y;
5254   int lx = sx + level_xpos;
5255   int ly = sy + level_ypos;
5256   int min_sx = 0, min_sy = 0;
5257   int max_sx = gi->drawing.area_xsize - 1;
5258   int max_sy = gi->drawing.area_ysize - 1;
5259
5260   ClearEditorGadgetInfoText();
5261
5262   if (gi->event.type == GD_EVENT_INFO_LEAVING)
5263     return;
5264
5265   /* make sure to stay inside drawing area boundaries */
5266   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
5267   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
5268
5269   if (id == GADGET_ID_DRAWING_LEVEL)
5270   {
5271     if (button_status)
5272     {
5273       int min_lx = 0, min_ly = 0;
5274       int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
5275
5276       /* get positions inside level field */
5277       lx = sx + level_xpos;
5278       ly = sy + level_ypos;
5279
5280       /* make sure to stay inside level field boundaries */
5281       lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
5282       ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
5283
5284       /* correct drawing area positions accordingly */
5285       sx = lx - level_xpos;
5286       sy = ly - level_ypos;
5287     }
5288
5289     if (IN_ED_FIELD(sx,sy) && IN_LEV_FIELD(lx, ly))
5290     {
5291       if (button_status)        /* if (gi->state == GD_BUTTON_PRESSED) */
5292       {
5293         if (gi->event.type == GD_EVENT_PRESSED)
5294         {
5295           start_lx = lx;
5296           start_ly = ly;
5297         }
5298
5299         switch (drawing_function)
5300         {
5301           case GADGET_ID_SINGLE_ITEMS:
5302             infotext = "Drawing single items";
5303             break;
5304           case GADGET_ID_CONNECTED_ITEMS:
5305             infotext = "Drawing connected items";
5306             break;
5307           case GADGET_ID_LINE:
5308             infotext = "Drawing line";
5309             break;
5310           case GADGET_ID_ARC:
5311             infotext = "Drawing arc";
5312             break;
5313           case GADGET_ID_TEXT:
5314             infotext = "Setting text cursor";
5315             break;
5316           case GADGET_ID_RECTANGLE:
5317             infotext = "Drawing rectangle";
5318             break;
5319           case GADGET_ID_FILLED_BOX:
5320             infotext = "Drawing filled box";
5321             break;
5322           case GADGET_ID_FLOOD_FILL:
5323             infotext = "Flood fill";
5324             break;
5325           case GADGET_ID_GRAB_BRUSH:
5326             infotext = "Grabbing brush";
5327             break;
5328           case GADGET_ID_PICK_ELEMENT:
5329             infotext = "Picking element";
5330             break;
5331
5332           default:
5333             infotext = "Drawing position";
5334             break;
5335         }
5336
5337         if (drawing_function == GADGET_ID_PICK_ELEMENT)
5338           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5339                     "%s: %d, %d", infotext, lx, ly);
5340         else
5341           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5342                     "%s: %d, %d", infotext,
5343                     ABS(lx - start_lx) + 1, ABS(ly - start_ly) + 1);
5344       }
5345       else if (drawing_function == GADGET_ID_PICK_ELEMENT)
5346         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5347                   "%s", getElementInfoText(Feld[lx][ly]));
5348       else
5349         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5350                   "Level position: %d, %d", lx, ly);
5351     }
5352
5353     /* misuse this function to draw brush cursor, if needed */
5354     if (edit_mode == ED_MODE_DRAWING && draw_with_brush && !button_status)
5355     {
5356       if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
5357         CopyBrushToCursor(sx, sy);
5358       else
5359         DeleteBrushFromCursor();
5360     }
5361   }
5362   else if (id == GADGET_ID_AMOEBA_CONTENT)
5363     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5364               "Amoeba content");
5365   else if (id == GADGET_ID_CUSTOM_CHANGED)
5366     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5367               "Next element after change");
5368   else if (id == GADGET_ID_RANDOM_BACKGROUND)
5369     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5370               "Random placement background");
5371   else
5372     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FONT_TEXT_2,
5373               "Content area %d position: %d, %d",
5374               id - GADGET_ID_ELEM_CONTENT_0 + 1, sx, sy);
5375 }
5376
5377 void RequestExitLevelEditor(boolean ask_if_level_has_changed)
5378 {
5379   if (!ask_if_level_has_changed ||
5380       !LevelChanged() ||
5381       Request("Level has changed! Exit without saving ?",
5382               REQ_ASK | REQ_STAY_OPEN))
5383   {
5384     CloseDoor(DOOR_CLOSE_1);
5385     /*
5386     CloseDoor(DOOR_CLOSE_ALL);
5387     */
5388     game_status = GAME_MODE_MAIN;
5389     DrawMainMenu();
5390   }
5391   else
5392   {
5393     CloseDoor(DOOR_CLOSE_1);
5394     BlitBitmap(bitmap_db_door, bitmap_db_door,
5395                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
5396                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
5397     OpenDoor(DOOR_OPEN_1);
5398   }
5399 }