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