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