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