rnd-19990214-3
[rocksndiamonds.git] / src / editor.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  editor.c                                                *
12 ***********************************************************/
13
14 #include <math.h>
15
16 #include "editor.h"
17 #include "screens.h"
18 #include "tools.h"
19 #include "misc.h"
20 #include "buttons.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             7
27 #define ED_WIN_MB_LEFT_YPOS             6
28 #define ED_WIN_LEVELNR_XPOS             77
29 #define ED_WIN_LEVELNR_YPOS             7
30 #define ED_WIN_MB_MIDDLE_XPOS           7
31 #define ED_WIN_MB_MIDDLE_YPOS           258
32 #define ED_WIN_MB_RIGHT_XPOS            77
33 #define ED_WIN_MB_RIGHT_YPOS            258
34
35 /* other constants for the editor */
36 #define ED_SCROLL_NO                    0
37 #define ED_SCROLL_LEFT                  1
38 #define ED_SCROLL_RIGHT                 2
39 #define ED_SCROLL_UP                    4
40 #define ED_SCROLL_DOWN                  8
41
42 /* screens in the level editor */
43 #define ED_MODE_DRAWING                 0
44 #define ED_MODE_INFO                    1
45 #define ED_MODE_PROPERTIES              2
46
47 /* how many steps can be cancelled */
48 #define NUM_UNDO_STEPS                  (10 + 1)
49
50 /* values for elements with score */
51 #define MIN_SCORE                       0
52 #define MAX_SCORE                       255
53
54 /* values for elements with content */
55 #define MIN_ELEM_CONTENT                1
56 #define MAX_ELEM_CONTENT                8
57
58 /* values for the control window */
59 #define ED_CTRL_BUTTONS_GFX_YPOS        236
60 #define ED_CTRL_BUTTONS_ALT_GFX_YPOS    142
61
62 #define ED_CTRL1_BUTTONS_HORIZ          4
63 #define ED_CTRL1_BUTTONS_VERT           4
64 #define ED_CTRL1_BUTTON_XSIZE           22
65 #define ED_CTRL1_BUTTON_YSIZE           22
66 #define ED_CTRL1_BUTTONS_XPOS           6
67 #define ED_CTRL1_BUTTONS_YPOS           6
68 #define ED_CTRL2_BUTTONS_HORIZ          3
69 #define ED_CTRL2_BUTTONS_VERT           2
70 #define ED_CTRL2_BUTTON_XSIZE           30
71 #define ED_CTRL2_BUTTON_YSIZE           20
72 #define ED_CTRL2_BUTTONS_XPOS           5
73 #define ED_CTRL2_BUTTONS_YPOS           99
74 #define ED_NUM_CTRL1_BUTTONS   (ED_CTRL1_BUTTONS_HORIZ * ED_CTRL1_BUTTONS_VERT)
75 #define ED_NUM_CTRL2_BUTTONS   (ED_CTRL2_BUTTONS_HORIZ * ED_CTRL2_BUTTONS_VERT)
76 #define ED_NUM_CTRL_BUTTONS    (ED_NUM_CTRL1_BUTTONS + ED_NUM_CTRL2_BUTTONS)
77
78 /* values for the element list */
79 #define ED_ELEMENTLIST_UP_XPOS          35
80 #define ED_ELEMENTLIST_UP_YPOS          5
81 #define ED_ELEMENTLIST_UP_ALT_YPOS      140
82 #define ED_ELEMENTLIST_DOWN_XPOS        35
83 #define ED_ELEMENTLIST_DOWN_YPOS        250
84 #define ED_ELEMENTLIST_DOWN_ALT_YPOS    165
85 #define ED_ELEMENTLIST_UPDOWN_XSIZE     30
86 #define ED_ELEMENTLIST_UPDOWN_YSIZE     25
87 #define ED_ELEMENTLIST_XPOS             6
88 #define ED_ELEMENTLIST_YPOS             30
89 #define ED_ELEMENTLIST_ALT_YPOS         190
90 #define ED_ELEMENTLIST_XSIZE            22
91 #define ED_ELEMENTLIST_YSIZE            22
92 #define ED_ELEMENTLIST_BUTTONS_HORIZ    4
93 #define ED_ELEMENTLIST_BUTTONS_VERT     10
94 #define ED_NUM_ELEMENTLIST_BUTTONS      (ED_ELEMENTLIST_BUTTONS_HORIZ * \
95                                          ED_ELEMENTLIST_BUTTONS_VERT)
96
97 /* values for the setting windows */
98 #define ED_SETTINGS_XPOS                (MINI_TILEX + 8)
99 #define ED_SETTINGS2_XPOS               MINI_TILEX
100 #define ED_SETTINGS_YPOS                MINI_TILEY
101 #define ED_SETTINGS2_YPOS               (ED_SETTINGS_YPOS + 12 * TILEY - 2)
102
103 /* values for counter gadgets */
104 #define ED_COUNT_ELEM_SCORE_XPOS        ED_SETTINGS_XPOS
105 #define ED_COUNT_ELEM_SCORE_YPOS        (14 * MINI_TILEY)
106 #define ED_COUNT_ELEM_CONTENT_XPOS      ED_SETTINGS_XPOS
107 #define ED_COUNT_ELEM_CONTENT_YPOS      (19 * MINI_TILEY)
108
109 #define ED_COUNTER_YSTART               (ED_SETTINGS_YPOS + 2 * TILEY)
110 #define ED_COUNTER_YDISTANCE            (3 * MINI_TILEY)
111 #define ED_COUNTER_YPOS(n)              (ED_COUNTER_YSTART + \
112                                          n * ED_COUNTER_YDISTANCE)
113 #define ED_COUNTER2_YPOS(n)             (ED_COUNTER_YSTART + \
114                                          n * ED_COUNTER_YDISTANCE - 2)
115 /* standard distances */
116 #define ED_BORDER_SIZE                  3
117 #define ED_GADGET_DISTANCE              2
118
119 /* values for element content drawing areas */
120 #define ED_AREA_ELEM_CONTENT_XPOS       ( 2 * MINI_TILEX)
121 #define ED_AREA_ELEM_CONTENT_YPOS       (22 * MINI_TILEY)
122
123 /* values for random placement background drawing area */
124 #define ED_AREA_RANDOM_BACKGROUND_XPOS  (29 * MINI_TILEX)
125 #define ED_AREA_RANDOM_BACKGROUND_YPOS  (31 * MINI_TILEY)
126
127 /* values for scrolling gadgets */
128 #define ED_SCROLLBUTTON_XPOS            24
129 #define ED_SCROLLBUTTON_YPOS            0
130 #define ED_SCROLLBAR_XPOS               24
131 #define ED_SCROLLBAR_YPOS               64
132
133 #define ED_SCROLLBUTTON_XSIZE           16
134 #define ED_SCROLLBUTTON_YSIZE           16
135
136 #define ED_SCROLL_UP_XPOS               (SXSIZE - ED_SCROLLBUTTON_XSIZE)
137 #define ED_SCROLL_UP_YPOS               (0)
138 #define ED_SCROLL_DOWN_XPOS             ED_SCROLL_UP_XPOS
139 #define ED_SCROLL_DOWN_YPOS             (SYSIZE - 3 * ED_SCROLLBUTTON_YSIZE)
140 #define ED_SCROLL_LEFT_XPOS             (0)
141 #define ED_SCROLL_LEFT_YPOS             (SYSIZE - 2 * ED_SCROLLBUTTON_YSIZE)
142 #define ED_SCROLL_RIGHT_XPOS            (SXSIZE - 2 * ED_SCROLLBUTTON_XSIZE)
143 #define ED_SCROLL_RIGHT_YPOS            ED_SCROLL_LEFT_YPOS
144 #define ED_SCROLL_HORIZONTAL_XPOS (ED_SCROLL_LEFT_XPOS + ED_SCROLLBUTTON_XSIZE)
145 #define ED_SCROLL_HORIZONTAL_YPOS       ED_SCROLL_LEFT_YPOS
146 #define ED_SCROLL_HORIZONTAL_XSIZE      (SXSIZE - 3 * ED_SCROLLBUTTON_XSIZE)
147 #define ED_SCROLL_HORIZONTAL_YSIZE      ED_SCROLLBUTTON_YSIZE
148 #define ED_SCROLL_VERTICAL_XPOS         ED_SCROLL_UP_XPOS
149 #define ED_SCROLL_VERTICAL_YPOS   (ED_SCROLL_UP_YPOS + ED_SCROLLBUTTON_YSIZE)
150 #define ED_SCROLL_VERTICAL_XSIZE        ED_SCROLLBUTTON_XSIZE
151 #define ED_SCROLL_VERTICAL_YSIZE        (SYSIZE - 4 * ED_SCROLLBUTTON_YSIZE)
152
153 /* values for checkbutton gadgets */
154 #define ED_CHECKBUTTON_XSIZE            ED_BUTTON_COUNT_XSIZE
155 #define ED_CHECKBUTTON_YSIZE            ED_BUTTON_COUNT_YSIZE
156 #define ED_CHECKBUTTON_UNCHECKED_XPOS   ED_BUTTON_MINUS_XPOS
157 #define ED_CHECKBUTTON_CHECKED_XPOS     ED_BUTTON_PLUS_XPOS
158 #define ED_CHECKBUTTON_YPOS             (ED_BUTTON_MINUS_YPOS + 22)
159 #define ED_STICKYBUTTON_YPOS            (ED_BUTTON_MINUS_YPOS + 88)
160
161 #define GADGET_ID_NONE                  -1
162
163 /* drawing toolbox buttons */
164 #define GADGET_ID_SINGLE_ITEMS          0
165 #define GADGET_ID_CONNECTED_ITEMS       1
166 #define GADGET_ID_LINE                  2
167 #define GADGET_ID_ARC                   3
168 #define GADGET_ID_RECTANGLE             4
169 #define GADGET_ID_FILLED_BOX            5
170 #define GADGET_ID_WRAP_UP               6
171 #define GADGET_ID_TEXT                  7
172 #define GADGET_ID_FLOOD_FILL            8
173 #define GADGET_ID_WRAP_LEFT             9
174 #define GADGET_ID_PROPERTIES            10
175 #define GADGET_ID_WRAP_RIGHT            11
176 #define GADGET_ID_RANDOM_PLACEMENT      12
177 #define GADGET_ID_GRAB_BRUSH            13
178 #define GADGET_ID_WRAP_DOWN             14
179 #define GADGET_ID_PICK_ELEMENT          15
180 #define GADGET_ID_UNDO                  16
181 #define GADGET_ID_INFO                  17
182 #define GADGET_ID_SAVE                  18
183 #define GADGET_ID_CLEAR                 19
184 #define GADGET_ID_TEST                  20
185 #define GADGET_ID_EXIT                  21
186
187 /* counter button identifiers */
188 #define GADGET_ID_ELEM_SCORE_DOWN       22
189 #define GADGET_ID_ELEM_SCORE_TEXT       23
190 #define GADGET_ID_ELEM_SCORE_UP         24
191 #define GADGET_ID_ELEM_CONTENT_DOWN     25
192 #define GADGET_ID_ELEM_CONTENT_TEXT     26
193 #define GADGET_ID_ELEM_CONTENT_UP       27
194 #define GADGET_ID_LEVEL_XSIZE_DOWN      28
195 #define GADGET_ID_LEVEL_XSIZE_TEXT      29
196 #define GADGET_ID_LEVEL_XSIZE_UP        30
197 #define GADGET_ID_LEVEL_YSIZE_DOWN      31
198 #define GADGET_ID_LEVEL_YSIZE_TEXT      32
199 #define GADGET_ID_LEVEL_YSIZE_UP        33
200 #define GADGET_ID_LEVEL_RANDOM_DOWN     34
201 #define GADGET_ID_LEVEL_RANDOM_TEXT     35
202 #define GADGET_ID_LEVEL_RANDOM_UP       36
203 #define GADGET_ID_LEVEL_COLLECT_DOWN    37
204 #define GADGET_ID_LEVEL_COLLECT_TEXT    38
205 #define GADGET_ID_LEVEL_COLLECT_UP      39
206 #define GADGET_ID_LEVEL_TIMELIMIT_DOWN  40
207 #define GADGET_ID_LEVEL_TIMELIMIT_TEXT  41
208 #define GADGET_ID_LEVEL_TIMELIMIT_UP    42
209 #define GADGET_ID_LEVEL_TIMESCORE_DOWN  43
210 #define GADGET_ID_LEVEL_TIMESCORE_TEXT  44
211 #define GADGET_ID_LEVEL_TIMESCORE_UP    45
212
213 /* drawing area identifiers */
214 #define GADGET_ID_DRAWING_LEVEL 46
215 #define GADGET_ID_ELEM_CONTENT_0        47
216 #define GADGET_ID_ELEM_CONTENT_1        48
217 #define GADGET_ID_ELEM_CONTENT_2        49
218 #define GADGET_ID_ELEM_CONTENT_3        50
219 #define GADGET_ID_ELEM_CONTENT_4        51
220 #define GADGET_ID_ELEM_CONTENT_5        52
221 #define GADGET_ID_ELEM_CONTENT_6        53
222 #define GADGET_ID_ELEM_CONTENT_7        54
223 #define GADGET_ID_AMOEBA_CONTENT        55
224
225 /* text input identifiers */
226 #define GADGET_ID_LEVEL_NAME            56
227 #define GADGET_ID_LEVEL_AUTHOR          57
228
229 /* gadgets for scrolling of drawing area */
230 #define GADGET_ID_SCROLL_UP             58
231 #define GADGET_ID_SCROLL_DOWN           59
232 #define GADGET_ID_SCROLL_LEFT           60
233 #define GADGET_ID_SCROLL_RIGHT          61
234 #define GADGET_ID_SCROLL_HORIZONTAL     62
235 #define GADGET_ID_SCROLL_VERTICAL       63
236
237 /* gadgets for scrolling element list */
238 #define GADGET_ID_ELEMENTLIST_UP        64
239 #define GADGET_ID_ELEMENTLIST_DOWN      65
240
241 /* gadgets for buttons in element list */
242 #define GADGET_ID_ELEMENTLIST_FIRST     66
243 #define GADGET_ID_ELEMENTLIST_LAST      105
244
245 /* buttons for level settings */
246 #define GADGET_ID_RANDOM_PERCENTAGE     106
247 #define GADGET_ID_RANDOM_QUANTITY       107
248 #define GADGET_ID_RANDOM_RESTRICTED     108
249 #define GADGET_ID_DOUBLE_SPEED          109
250 #define GADGET_ID_GRAVITY               110
251 #define GADGET_ID_STICK_ELEMENT         111
252
253 /* another drawing area for random placement */
254 #define GADGET_ID_RANDOM_BACKGROUND     112
255
256 #define NUM_EDITOR_GADGETS              113
257
258 /* radio button numbers */
259 #define RADIO_NR_NONE                   0
260 #define RADIO_NR_DRAWING_TOOLBOX        1
261 #define RADIO_NR_RANDOM_ELEMENTS        2
262
263 /* values for counter gadgets */
264 #define ED_COUNTER_ID_ELEM_SCORE        0
265 #define ED_COUNTER_ID_ELEM_CONTENT      1
266 #define ED_COUNTER_ID_LEVEL_XSIZE       2
267 #define ED_COUNTER_ID_LEVEL_YSIZE       3
268 #define ED_COUNTER_ID_LEVEL_COLLECT     4
269 #define ED_COUNTER_ID_LEVEL_TIMELIMIT   5
270 #define ED_COUNTER_ID_LEVEL_TIMESCORE   6
271 #define ED_COUNTER_ID_LEVEL_RANDOM      7
272
273 #define ED_NUM_COUNTERBUTTONS           8
274
275 #define ED_COUNTER_ID_LEVEL_FIRST       ED_COUNTER_ID_LEVEL_XSIZE
276 #define ED_COUNTER_ID_LEVEL_LAST        ED_COUNTER_ID_LEVEL_RANDOM
277
278 /* values for scrollbutton gadgets */
279 #define ED_SCROLLBUTTON_ID_AREA_UP      0
280 #define ED_SCROLLBUTTON_ID_AREA_DOWN    1
281 #define ED_SCROLLBUTTON_ID_AREA_LEFT    2
282 #define ED_SCROLLBUTTON_ID_AREA_RIGHT   3
283 #define ED_SCROLLBUTTON_ID_LIST_UP      4
284 #define ED_SCROLLBUTTON_ID_LIST_DOWN    5
285
286 #define ED_NUM_SCROLLBUTTONS            6
287
288 /* values for scrollbar gadgets */
289 #define ED_SCROLLBAR_ID_HORIZONTAL      0
290 #define ED_SCROLLBAR_ID_VERTICAL        1
291
292 #define ED_NUM_SCROLLBARS               2
293
294 /* values for text input gadgets */
295 #define ED_TEXTINPUT_ID_LEVEL_NAME      0
296 #define ED_TEXTINPUT_ID_LEVEL_AUTHOR    1
297
298 #define ED_NUM_TEXTINPUT                2
299
300 #define ED_TEXTINPUT_ID_LEVEL_FIRST     ED_TEXTINPUT_ID_LEVEL_NAME
301 #define ED_TEXTINPUT_ID_LEVEL_LAST      ED_TEXTINPUT_ID_LEVEL_AUTHOR
302
303 /* values for checkbutton gadgets */
304 #define ED_CHECKBUTTON_ID_DOUBLE_SPEED          0
305 #define ED_CHECKBUTTON_ID_GRAVITY               1
306 #define ED_CHECKBUTTON_ID_RANDOM_RESTRICTED     2
307 #define ED_CHECKBUTTON_ID_STICK_ELEMENT         3
308
309 #define ED_NUM_CHECKBUTTONS                     4
310
311 #define ED_CHECKBUTTON_ID_LEVEL_FIRST   ED_CHECKBUTTON_ID_DOUBLE_SPEED
312 #define ED_CHECKBUTTON_ID_LEVEL_LAST    ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
313
314 /* values for radiobutton gadgets */
315 #define ED_RADIOBUTTON_ID_PERCENTAGE    0
316 #define ED_RADIOBUTTON_ID_QUANTITY              1
317
318 #define ED_NUM_RADIOBUTTONS             2
319
320 #define ED_RADIOBUTTON_ID_LEVEL_FIRST   ED_RADIOBUTTON_ID_PERCENTAGE
321 #define ED_RADIOBUTTON_ID_LEVEL_LAST    ED_RADIOBUTTON_ID_QUANTITY
322
323 /* values for CopyLevelToUndoBuffer() */
324 #define UNDO_IMMEDIATE                  0
325 #define UNDO_ACCUMULATE                 1
326
327 /* values for ClearEditorGadgetInfoText() and HandleGadgetInfoText() */
328 #define INFOTEXT_XPOS                   SX
329 #define INFOTEXT_YPOS                   (SY + SYSIZE - MINI_TILEX + 2)
330 #define INFOTEXT_XSIZE                  SXSIZE
331 #define INFOTEXT_YSIZE                  MINI_TILEX
332 #define MAX_INFOTEXT_LEN                (SXSIZE / FONT2_XSIZE)
333
334 static struct
335 {
336   char shortcut;
337   char *text;
338 } control_info[ED_NUM_CTRL_BUTTONS] =
339 {
340   { 's', "draw single items" },
341   { 'd', "draw connected items" },
342   { 'l', "draw lines" },
343   { 'a', "draw arcs" },
344   { 'r', "draw outline rectangles" },
345   { 'R', "draw filled rectangles" },
346   { '\0', "wrap (rotate) level up" },
347   { 't', "enter text elements" },
348   { 'f', "flood fill" },
349   { '\0', "wrap (rotate) level left" },
350   { '?', "properties of drawing element" },
351   { '\0', "wrap (rotate) level right" },
352   { '\0', "random element placement" },
353   { 'b', "grab brush" },
354   { '\0', "wrap (rotate) level down" },
355   { ',', "pick drawing element" },
356   { 'U', "undo last operation" },
357   { 'I', "level properties" },
358   { 'S', "save level" },
359   { 'C', "clear level" },
360   { 'T', "test level" },
361   { 'E', "exit level editor" }
362 };
363
364 /* pointers to counter values */
365 static int *gadget_elem_score_value = NULL;
366 static int *gadget_elem_content_value = NULL;
367 static int *gadget_level_xsize_value = NULL;
368 static int *gadget_level_ysize_value = NULL;
369 static int *gadget_level_random_value = NULL;
370 static int *gadget_level_collect_value = NULL;
371 static int *gadget_level_timelimit_value = NULL;
372 static int *gadget_level_timescore_value = NULL;
373
374 static struct
375 {
376   int x, y;
377   int min_value, max_value;
378   int gadget_id_down, gadget_id_up;
379   int gadget_id_text;
380   int **counter_value;
381   char *infotext_above, *infotext_right;
382 } counterbutton_info[ED_NUM_COUNTERBUTTONS] =
383 {
384   {
385     ED_COUNT_ELEM_SCORE_XPOS,           ED_COUNT_ELEM_SCORE_YPOS,
386     MIN_SCORE,                          MAX_SCORE,
387     GADGET_ID_ELEM_SCORE_DOWN,          GADGET_ID_ELEM_SCORE_UP,
388     GADGET_ID_ELEM_SCORE_TEXT,
389     &gadget_elem_score_value,
390     "element score",                    NULL
391   },
392   {
393     ED_COUNT_ELEM_CONTENT_XPOS,         ED_COUNT_ELEM_CONTENT_YPOS,
394     MIN_ELEM_CONTENT,                   MAX_ELEM_CONTENT,
395     GADGET_ID_ELEM_CONTENT_DOWN,        GADGET_ID_ELEM_CONTENT_UP,
396     GADGET_ID_ELEM_CONTENT_TEXT,
397     &gadget_elem_content_value,
398     "element content",                  NULL
399   },
400   {
401     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(2),
402     MIN_LEV_FIELDX,                     MAX_LEV_FIELDX,
403     GADGET_ID_LEVEL_XSIZE_DOWN,         GADGET_ID_LEVEL_XSIZE_UP,
404     GADGET_ID_LEVEL_XSIZE_TEXT,
405     &gadget_level_xsize_value,
406     "playfield size",                   "width",
407   },
408   {
409     ED_SETTINGS_XPOS + 2 * DXSIZE,      ED_COUNTER_YPOS(2),
410     MIN_LEV_FIELDY,                     MAX_LEV_FIELDY,
411     GADGET_ID_LEVEL_YSIZE_DOWN,         GADGET_ID_LEVEL_YSIZE_UP,
412     GADGET_ID_LEVEL_YSIZE_TEXT,
413     &gadget_level_ysize_value,
414     NULL,                               "height",
415   },
416   {
417     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(3),
418     0,                                  999,
419     GADGET_ID_LEVEL_COLLECT_DOWN,       GADGET_ID_LEVEL_COLLECT_UP,
420     GADGET_ID_LEVEL_COLLECT_TEXT,
421     &gadget_level_collect_value,
422     "number of emeralds to collect",    NULL
423   },
424   {
425     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(4),
426     0,                                  999,
427     GADGET_ID_LEVEL_TIMELIMIT_DOWN,     GADGET_ID_LEVEL_TIMELIMIT_UP,
428     GADGET_ID_LEVEL_TIMELIMIT_TEXT,
429     &gadget_level_timelimit_value,
430     "time available to solve level",    "(0 => no time limit)"
431   },
432   {
433     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(5),
434     0,                                  255,
435     GADGET_ID_LEVEL_TIMESCORE_DOWN,     GADGET_ID_LEVEL_TIMESCORE_UP,
436     GADGET_ID_LEVEL_TIMESCORE_TEXT,
437     &gadget_level_timescore_value,
438     "score for each 10 seconds left",   NULL
439   },
440   {
441     ED_SETTINGS_XPOS,                   ED_COUNTER2_YPOS(8),
442     1,                                  100,
443     GADGET_ID_LEVEL_RANDOM_DOWN,        GADGET_ID_LEVEL_RANDOM_UP,
444     GADGET_ID_LEVEL_RANDOM_TEXT,
445     &gadget_level_random_value,
446     "random element placement",         "in"
447   }
448 };
449
450 static struct
451 {
452   int x, y;
453   int gadget_id;
454   int size;
455   char *value;
456   char *infotext;
457 } textinput_info[ED_NUM_TEXTINPUT] =
458 {
459   {
460     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(0),
461     GADGET_ID_LEVEL_NAME,
462     MAX_LEVEL_NAME_LEN,
463     level.name,
464     "Title"
465   },
466   {
467     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(1),
468     GADGET_ID_LEVEL_AUTHOR,
469     MAX_LEVEL_AUTHOR_LEN,
470     level.author,
471     "Author"
472   }
473 };
474
475 static struct
476 {
477   int xpos, ypos;
478   int x, y;
479   int gadget_id;
480   char *infotext;
481 } scrollbutton_info[ED_NUM_SCROLLBUTTONS] =
482 {
483   {
484     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 0 * ED_SCROLLBUTTON_YSIZE,
485     ED_SCROLL_UP_XPOS,      ED_SCROLL_UP_YPOS,
486     GADGET_ID_SCROLL_UP,
487     "scroll level editing area up"
488   },
489   {
490     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 1 * ED_SCROLLBUTTON_YSIZE,
491     ED_SCROLL_DOWN_XPOS,    ED_SCROLL_DOWN_YPOS,
492     GADGET_ID_SCROLL_DOWN,
493     "scroll level editing area down"
494   },
495   {
496     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 2 * ED_SCROLLBUTTON_YSIZE,
497     ED_SCROLL_LEFT_XPOS,    ED_SCROLL_LEFT_YPOS,
498     GADGET_ID_SCROLL_LEFT,
499     "scroll level editing area left"
500   },
501   {
502     ED_SCROLLBUTTON_XPOS,   ED_SCROLLBUTTON_YPOS + 3 * ED_SCROLLBUTTON_YSIZE,
503     ED_SCROLL_RIGHT_XPOS,   ED_SCROLL_RIGHT_YPOS,
504     GADGET_ID_SCROLL_RIGHT,
505     "scroll level editing area right"
506   },
507   {
508     ED_ELEMENTLIST_UP_XPOS, ED_ELEMENTLIST_UP_ALT_YPOS,
509     ED_ELEMENTLIST_UP_XPOS, ED_ELEMENTLIST_UP_YPOS,
510     GADGET_ID_ELEMENTLIST_UP,
511     "scroll element list up ('Page Up')"
512   },
513   {
514     ED_ELEMENTLIST_DOWN_XPOS, ED_ELEMENTLIST_DOWN_ALT_YPOS,
515     ED_ELEMENTLIST_DOWN_XPOS, ED_ELEMENTLIST_DOWN_YPOS,
516     GADGET_ID_ELEMENTLIST_DOWN,
517     "scroll element list down ('Page Down')"
518   }
519 };
520
521 static struct
522 {
523   int xpos, ypos;
524   int x, y;
525   int width, height;
526   int type;
527   int gadget_id;
528   char *infotext;
529 } scrollbar_info[ED_NUM_SCROLLBARS] =
530 {
531   {
532     ED_SCROLLBAR_XPOS,          ED_SCROLLBAR_YPOS,
533     ED_SCROLL_HORIZONTAL_XPOS,  ED_SCROLL_HORIZONTAL_YPOS,
534     ED_SCROLL_HORIZONTAL_XSIZE, ED_SCROLL_HORIZONTAL_YSIZE,
535     GD_TYPE_SCROLLBAR_HORIZONTAL,
536     GADGET_ID_SCROLL_HORIZONTAL,
537     "scroll level editing area horizontally"
538   },
539   {
540     ED_SCROLLBAR_XPOS,          ED_SCROLLBAR_YPOS,
541     ED_SCROLL_VERTICAL_XPOS,    ED_SCROLL_VERTICAL_YPOS,
542     ED_SCROLL_VERTICAL_XSIZE,   ED_SCROLL_VERTICAL_YSIZE,
543     GD_TYPE_SCROLLBAR_VERTICAL,
544     GADGET_ID_SCROLL_VERTICAL,
545     "scroll level editing area vertically"
546   }
547 };
548
549 /* values for random placement */
550 #define RANDOM_USE_PERCENTAGE           0
551 #define RANDOM_USE_QUANTITY             1
552
553 static int random_placement_value = 10;
554 static int random_placement_method = RANDOM_USE_QUANTITY;
555 static int random_placement_background_element = EL_ERDREICH;
556 static boolean random_placement_background_restricted = FALSE;
557 static boolean stick_element_properties_window = FALSE;
558
559 static struct
560 {
561   int x, y;
562   int gadget_id;
563   int radio_button_nr;
564   int *value;
565   int checked_value;
566   char *text, *infotext;
567 } radiobutton_info[ED_NUM_RADIOBUTTONS] =
568 {
569   {
570     ED_SETTINGS_XPOS + 160,             ED_COUNTER2_YPOS(8),
571     GADGET_ID_RANDOM_PERCENTAGE,
572     RADIO_NR_RANDOM_ELEMENTS,
573     &random_placement_method,           RANDOM_USE_PERCENTAGE,
574     "percentage",                       "use percentage for random elements"
575   },
576   {
577     ED_SETTINGS_XPOS + 340,             ED_COUNTER2_YPOS(8),
578     GADGET_ID_RANDOM_QUANTITY,
579     RADIO_NR_RANDOM_ELEMENTS,
580     &random_placement_method,           RANDOM_USE_QUANTITY,
581     "quantity",                         "use quantity for random elements"
582   }
583 };
584
585 static struct
586 {
587   int x, y;
588   int gadget_id;
589   boolean *value;
590   char *text, *infotext;
591 } checkbutton_info[ED_NUM_CHECKBUTTONS] =
592 {
593   {
594     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(6) - MINI_TILEY,
595     GADGET_ID_DOUBLE_SPEED,
596     &level.double_speed,
597     "double speed movement",            "set movement speed of player"
598   },
599   {
600     ED_SETTINGS_XPOS + 340,             ED_COUNTER_YPOS(6) - MINI_TILEY,
601     GADGET_ID_GRAVITY,
602     &level.gravity,
603     "gravity",                          "set level gravity"
604   },
605   {
606     ED_SETTINGS_XPOS,                   ED_COUNTER2_YPOS(9) - MINI_TILEY,
607     GADGET_ID_RANDOM_RESTRICTED,
608     &random_placement_background_restricted,
609     "restrict random placement to",     "set random placement restriction"
610   },
611   {
612     ED_SETTINGS_XPOS,                   ED_COUNTER_YPOS(4),
613     GADGET_ID_STICK_ELEMENT,
614     &stick_element_properties_window,
615     "stick window to edit content",     "stick window to edit content"
616   }
617 };
618
619 /* maximal size of level editor drawing area */
620 #define MAX_ED_FIELDX           (2 * SCR_FIELDX)
621 #define MAX_ED_FIELDY           (2 * SCR_FIELDY - 1)
622
623 /* actual size of level editor drawing area */
624 static int ed_fieldx = MAX_ED_FIELDX - 1, ed_fieldy = MAX_ED_FIELDY - 1;
625
626 /* actual position of level editor drawing area in level playfield */
627 static int level_xpos = -1, level_ypos = -1;
628
629 #define IN_ED_FIELD(x,y)  ((x)>=0 && (x)<ed_fieldx && (y)>=0 &&(y)<ed_fieldx)
630
631 /* drawing elements on the three mouse buttons */
632 static int new_element1 = EL_MAUERWERK;
633 static int new_element2 = EL_LEERRAUM;
634 static int new_element3 = EL_ERDREICH;
635
636 #define BUTTON_ELEMENT(button) (button == 1 ? new_element1 : \
637                                 button == 2 ? new_element2 : \
638                                 button == 3 ? new_element3 : EL_LEERRAUM)
639
640 /* forward declaration for internal use */
641 static void DrawDrawingWindow();
642 static void DrawLevelInfoWindow();
643 static void DrawPropertiesWindow();
644 static void CopyLevelToUndoBuffer(int);
645 static void HandleControlButtons(struct GadgetInfo *);
646 static void HandleCounterButtons(struct GadgetInfo *);
647 static void HandleDrawingAreas(struct GadgetInfo *);
648 static void HandleDrawingAreaInfo(struct GadgetInfo *);
649 static void HandleTextInputGadgets(struct GadgetInfo *);
650
651 static struct GadgetInfo *level_editor_gadget[NUM_EDITOR_GADGETS];
652
653 static int drawing_function = GADGET_ID_SINGLE_ITEMS;
654 static int last_drawing_function = GADGET_ID_SINGLE_ITEMS;
655 static boolean draw_with_brush = FALSE;
656 static int properties_element = 0;
657
658 static short ElementContent[MAX_ELEM_CONTENT][3][3];
659 static short FieldBackup[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
660 static short UndoBuffer[NUM_UNDO_STEPS][MAX_LEV_FIELDX][MAX_LEV_FIELDY];
661 static int undo_buffer_position = 0;
662 static int undo_buffer_steps = 0;
663
664 static int edit_mode;
665
666 static int counter_xsize = DXSIZE + FONT2_XSIZE - 2 * ED_GADGET_DISTANCE;
667
668 int element_shift = 0;
669
670 int editor_element[] =
671 {
672   EL_CHAR('B'),
673   EL_CHAR('O'),
674   EL_CHAR('U'),
675   EL_CHAR('L'),
676
677   EL_CHAR_MINUS,
678   EL_CHAR('D'),
679   EL_CHAR('E'),
680   EL_CHAR('R'),
681
682   EL_CHAR('D'),
683   EL_CHAR('A'),
684   EL_CHAR('S'),
685   EL_CHAR('H'),
686
687   EL_SPIELFIGUR,
688   EL_LEERRAUM,
689   EL_ERDREICH,
690   EL_BETON,
691
692   EL_FELSBODEN,
693   EL_SIEB2_INAKTIV,
694   EL_AUSGANG_ZU,
695   EL_AUSGANG_AUF,
696
697   EL_EDELSTEIN_BD,
698   EL_BUTTERFLY_O,
699   EL_FIREFLY_O,
700   EL_FELSBROCKEN,
701
702   EL_BUTTERFLY_L,
703   EL_FIREFLY_L,
704   EL_BUTTERFLY_R,
705   EL_FIREFLY_R,
706
707   EL_AMOEBE_BD,
708   EL_BUTTERFLY_U,
709   EL_FIREFLY_U,
710   EL_LEERRAUM,
711
712   EL_CHAR('E'),
713   EL_CHAR('M'),
714   EL_CHAR('E'),
715   EL_CHAR_MINUS,
716
717   EL_CHAR('R'),
718   EL_CHAR('A'),
719   EL_CHAR('L'),
720   EL_CHAR('D'),
721
722   EL_CHAR('M'),
723   EL_CHAR('I'),
724   EL_CHAR('N'),
725   EL_CHAR('E'),
726
727   EL_SPIELER1,
728   EL_SPIELER2,
729   EL_SPIELER3,
730   EL_SPIELER4,
731
732   EL_SPIELFIGUR,
733   EL_LEERRAUM,
734   EL_ERDREICH,
735   EL_FELSBROCKEN,
736
737   EL_BETON,
738   EL_MAUERWERK,
739   EL_FELSBODEN,
740   EL_SIEB_INAKTIV,
741
742   EL_EDELSTEIN,
743   EL_DIAMANT,
744   EL_KOKOSNUSS,
745   EL_BOMBE,
746
747   EL_ERZ_EDEL,
748   EL_ERZ_DIAM,
749   EL_MORAST_LEER,
750   EL_MORAST_VOLL,
751
752   EL_DYNAMIT_AUS,
753   EL_DYNAMIT,
754   EL_AUSGANG_ZU,
755   EL_AUSGANG_AUF,
756
757   EL_MAMPFER,
758   EL_KAEFER_O,
759   EL_FLIEGER_O,
760   EL_ROBOT,
761
762   EL_KAEFER_L,
763   EL_FLIEGER_L,
764   EL_KAEFER_R,
765   EL_FLIEGER_R,
766
767   EL_ABLENK_AUS,
768   EL_KAEFER_U,
769   EL_FLIEGER_U,
770   EL_UNSICHTBAR,
771
772   EL_BADEWANNE1,
773   EL_SALZSAEURE,
774   EL_BADEWANNE2,
775   EL_LEERRAUM,
776
777   EL_BADEWANNE3,
778   EL_BADEWANNE4,
779   EL_BADEWANNE5,
780   EL_LEERRAUM,
781
782   EL_TROPFEN,
783   EL_AMOEBE_TOT,
784   EL_AMOEBE_NASS,
785   EL_AMOEBE_NORM,
786
787   EL_EM_KEY_1_FILE,
788   EL_EM_KEY_2_FILE,
789   EL_EM_KEY_3_FILE,
790   EL_EM_KEY_4_FILE,
791
792   EL_EM_GATE_1,
793   EL_EM_GATE_2,
794   EL_EM_GATE_3,
795   EL_EM_GATE_4,
796
797   EL_EM_GATE_1X,
798   EL_EM_GATE_2X,
799   EL_EM_GATE_3X,
800   EL_EM_GATE_4X,
801
802   EL_CHAR('M'),
803   EL_CHAR('O'),
804   EL_CHAR('R'),
805   EL_CHAR('E'),
806
807   EL_SCHLUESSEL1,
808   EL_SCHLUESSEL2,
809   EL_SCHLUESSEL3,
810   EL_SCHLUESSEL4,
811
812   EL_PFORTE1,
813   EL_PFORTE2,
814   EL_PFORTE3,
815   EL_PFORTE4,
816
817   EL_PFORTE1X,
818   EL_PFORTE2X,
819   EL_PFORTE3X,
820   EL_PFORTE4X,
821
822   EL_PFEIL_L,
823   EL_PFEIL_R,
824   EL_PFEIL_O,
825   EL_PFEIL_U,
826
827   EL_AMOEBE_VOLL,
828   EL_EDELSTEIN_GELB,
829   EL_EDELSTEIN_ROT,
830   EL_EDELSTEIN_LILA,
831
832   EL_ERZ_EDEL_BD,
833   EL_ERZ_EDEL_GELB,
834   EL_ERZ_EDEL_ROT,
835   EL_ERZ_EDEL_LILA,
836
837   EL_LIFE,
838   EL_PACMAN_O,
839   EL_ZEIT_VOLL,
840   EL_ZEIT_LEER,
841
842   EL_PACMAN_L,
843   EL_MAMPFER2,
844   EL_PACMAN_R,
845   EL_MAUER_LEBT,
846
847   EL_LIFE_ASYNC,
848   EL_PACMAN_U,
849   EL_BIRNE_AUS,
850   EL_BIRNE_EIN,
851
852   EL_DYNABOMB_NR,
853   EL_DYNABOMB_SZ,
854   EL_DYNABOMB_XL,
855   EL_BADEWANNE,
856
857   EL_MAULWURF,
858   EL_PINGUIN,
859   EL_SCHWEIN,
860   EL_DRACHE,
861
862   EL_SONDE,
863   EL_MAUER_X,
864   EL_MAUER_Y,
865   EL_MAUER_XY,
866
867   EL_INVISIBLE_STEEL,
868   EL_UNSICHTBAR,
869   EL_SPEED_PILL,
870   EL_BLACK_ORB,
871
872   EL_CHAR('S'),
873   EL_CHAR('O'),
874   EL_CHAR('K'),
875   EL_CHAR('O'),
876
877   EL_CHAR_MINUS,
878   EL_CHAR('B'),
879   EL_CHAR('A'),
880   EL_CHAR('N'),
881
882   EL_SOKOBAN_OBJEKT,
883   EL_SOKOBAN_FELD_LEER,
884   EL_SOKOBAN_FELD_VOLL,
885   EL_BETON,
886
887   EL_LEERRAUM,
888   EL_LEERRAUM,
889   EL_LEERRAUM,
890   EL_LEERRAUM,
891
892   EL_CHAR('S'),
893   EL_CHAR('U'),
894   EL_CHAR('P'),
895   EL_CHAR('A'),
896
897   EL_CHAR('P'),
898   EL_CHAR('L'),
899   EL_CHAR('E'),
900   EL_CHAR('X'),
901
902   EL_SP_EMPTY,
903   EL_SP_ZONK,
904   EL_SP_BASE,
905   EL_SP_MURPHY,
906
907   EL_SP_INFOTRON,
908   EL_SP_CHIP_SINGLE,
909   EL_SP_HARD_GRAY,
910   EL_SP_EXIT,
911
912   EL_SP_DISK_ORANGE,
913   EL_SP_PORT1_RIGHT,
914   EL_SP_PORT1_DOWN,
915   EL_SP_PORT1_LEFT,
916
917   EL_SP_PORT1_UP,
918   EL_SP_PORT2_RIGHT,
919   EL_SP_PORT2_DOWN,
920   EL_SP_PORT2_LEFT,
921
922   EL_SP_PORT2_UP,
923   EL_SP_SNIKSNAK,
924   EL_SP_DISK_YELLOW,
925   EL_SP_TERMINAL,
926
927   EL_SP_DISK_RED,
928   EL_SP_PORT_Y,
929   EL_SP_PORT_X,
930   EL_SP_PORT_XY,
931
932   EL_SP_ELECTRON,
933   EL_SP_BUG,
934   EL_SP_CHIP_LEFT,
935   EL_SP_CHIP_RIGHT,
936
937   EL_SP_HARD_BASE1,
938   EL_SP_HARD_GREEN,
939   EL_SP_HARD_BLUE,
940   EL_SP_HARD_RED,
941
942   EL_SP_HARD_YELLOW,
943   EL_SP_HARD_BASE2,
944   EL_SP_HARD_BASE3,
945   EL_SP_HARD_BASE4,
946
947   EL_SP_HARD_BASE5,
948   EL_SP_HARD_BASE6,
949   EL_SP_CHIP_UPPER,
950   EL_SP_CHIP_LOWER,
951
952   /*
953   EL_CHAR('D'),
954   EL_CHAR('Y'),
955   EL_CHAR('N'),
956   EL_CHAR('A'),
957
958   EL_CHAR('B'),
959   EL_CHAR('L'),
960   EL_CHAR('A'),
961   EL_CHAR('S'),
962
963   EL_CHAR_MINUS,
964   EL_CHAR('T'),
965   EL_CHAR('E'),
966   EL_CHAR('R'),
967   */
968
969   EL_LEERRAUM,
970   EL_LEERRAUM,
971   EL_LEERRAUM,
972   EL_LEERRAUM,
973
974   EL_CHAR(' '),
975   EL_CHAR('!'),
976   EL_CHAR('"'),
977   EL_CHAR('#'),
978
979   EL_CHAR('$'),
980   EL_CHAR('%'),
981   EL_CHAR('&'),
982   EL_CHAR('\''),
983
984   EL_CHAR('('),
985   EL_CHAR(')'),
986   EL_CHAR('*'),
987   EL_CHAR('+'),
988
989   EL_CHAR(','),
990   EL_CHAR('-'),
991   EL_CHAR('.'),
992   EL_CHAR('/'),
993
994   EL_CHAR('0'),
995   EL_CHAR('1'),
996   EL_CHAR('2'),
997   EL_CHAR('3'),
998
999   EL_CHAR('4'),
1000   EL_CHAR('5'),
1001   EL_CHAR('6'),
1002   EL_CHAR('7'),
1003
1004   EL_CHAR('8'),
1005   EL_CHAR('9'),
1006   EL_CHAR(':'),
1007   EL_CHAR(';'),
1008
1009   EL_CHAR('<'),
1010   EL_CHAR('='),
1011   EL_CHAR('>'),
1012   EL_CHAR('?'),
1013
1014   EL_CHAR('@'),
1015   EL_CHAR('A'),
1016   EL_CHAR('B'),
1017   EL_CHAR('C'),
1018
1019   EL_CHAR('D'),
1020   EL_CHAR('E'),
1021   EL_CHAR('F'),
1022   EL_CHAR('G'),
1023
1024   EL_CHAR('H'),
1025   EL_CHAR('I'),
1026   EL_CHAR('J'),
1027   EL_CHAR('K'),
1028
1029   EL_CHAR('L'),
1030   EL_CHAR('M'),
1031   EL_CHAR('N'),
1032   EL_CHAR('O'),
1033
1034   EL_CHAR('P'),
1035   EL_CHAR('Q'),
1036   EL_CHAR('R'),
1037   EL_CHAR('S'),
1038
1039   EL_CHAR('T'),
1040   EL_CHAR('U'),
1041   EL_CHAR('V'),
1042   EL_CHAR('W'),
1043
1044   EL_CHAR('X'),
1045   EL_CHAR('Y'),
1046   EL_CHAR('Z'),
1047   EL_CHAR('Ä'),
1048
1049   EL_CHAR('Ö'),
1050   EL_CHAR('Ãœ'),
1051   EL_CHAR('^'),
1052   EL_CHAR(' ')
1053 };
1054 int elements_in_list = sizeof(editor_element)/sizeof(int);
1055
1056 static void ScrollMiniLevel(int from_x, int from_y, int scroll)
1057 {
1058   int x,y;
1059   int dx = (scroll == ED_SCROLL_LEFT ? -1 : scroll == ED_SCROLL_RIGHT ? 1 : 0);
1060   int dy = (scroll == ED_SCROLL_UP   ? -1 : scroll == ED_SCROLL_DOWN  ? 1 : 0);
1061
1062   XCopyArea(display, drawto, drawto, gc,
1063             SX + (dx == -1 ? MINI_TILEX : 0),
1064             SY + (dy == -1 ? MINI_TILEY : 0),
1065             (ed_fieldx * MINI_TILEX) - (dx != 0 ? MINI_TILEX : 0),
1066             (ed_fieldy * MINI_TILEY) - (dy != 0 ? MINI_TILEY : 0),
1067             SX + (dx == +1 ? MINI_TILEX : 0),
1068             SY + (dy == +1 ? MINI_TILEY : 0));
1069   if (dx)
1070   {
1071     x = (dx == 1 ? 0 : ed_fieldx - 1);
1072     for(y=0; y<ed_fieldy; y++)
1073       DrawMiniElementOrWall(x, y, from_x, from_y);
1074   }
1075   else if (dy)
1076   {
1077     y = (dy == 1 ? 0 : ed_fieldy - 1);
1078     for(x=0; x<ed_fieldx; x++)
1079       DrawMiniElementOrWall(x, y, from_x, from_y);
1080   }
1081
1082   redraw_mask |= REDRAW_FIELD;
1083   BackToFront();
1084 }
1085
1086 static void CreateControlButtons()
1087 {
1088   Pixmap gd_pixmap = pix[PIX_DOOR];
1089   struct GadgetInfo *gi;
1090   unsigned long event_mask;
1091   int i;
1092
1093   /* create toolbox buttons */
1094   for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
1095   {
1096     int id = i;
1097     int width, height;
1098     int gd_xoffset, gd_yoffset;
1099     int gd_x1, gd_x2, gd_y1, gd_y2;
1100     int button_type;
1101     int radio_button_nr;
1102     boolean checked;
1103
1104     if (id == GADGET_ID_SINGLE_ITEMS ||
1105         id == GADGET_ID_CONNECTED_ITEMS ||
1106         id == GADGET_ID_LINE ||
1107         id == GADGET_ID_ARC ||
1108         id == GADGET_ID_TEXT ||
1109         id == GADGET_ID_RECTANGLE ||
1110         id == GADGET_ID_FILLED_BOX ||
1111         id == GADGET_ID_FLOOD_FILL ||
1112         id == GADGET_ID_GRAB_BRUSH ||
1113         id == GADGET_ID_PICK_ELEMENT)
1114     {
1115       button_type = GD_TYPE_RADIO_BUTTON;
1116       radio_button_nr = RADIO_NR_DRAWING_TOOLBOX;
1117       checked = (id == drawing_function ? TRUE : FALSE);
1118       event_mask = GD_EVENT_PRESSED;
1119     }
1120     else
1121     {
1122       button_type = GD_TYPE_NORMAL_BUTTON;
1123       radio_button_nr = RADIO_NR_NONE;
1124       checked = FALSE;
1125
1126       if (id == GADGET_ID_WRAP_LEFT ||
1127           id == GADGET_ID_WRAP_RIGHT ||
1128           id == GADGET_ID_WRAP_UP ||
1129           id == GADGET_ID_WRAP_DOWN)
1130         event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
1131       else
1132         event_mask = GD_EVENT_RELEASED;
1133     }
1134
1135     if (id < ED_NUM_CTRL1_BUTTONS)
1136     {
1137       int x = i % ED_CTRL1_BUTTONS_HORIZ;
1138       int y = i / ED_CTRL1_BUTTONS_HORIZ;
1139
1140       gd_xoffset = ED_CTRL1_BUTTONS_XPOS + x * ED_CTRL1_BUTTON_XSIZE;
1141       gd_yoffset = ED_CTRL1_BUTTONS_YPOS + y * ED_CTRL1_BUTTON_YSIZE;
1142       width = ED_CTRL1_BUTTON_XSIZE;
1143       height = ED_CTRL1_BUTTON_YSIZE;
1144     }
1145     else
1146     {
1147       int x = (i - ED_NUM_CTRL1_BUTTONS) % ED_CTRL2_BUTTONS_HORIZ;
1148       int y = (i - ED_NUM_CTRL1_BUTTONS) / ED_CTRL2_BUTTONS_HORIZ;
1149
1150       gd_xoffset = ED_CTRL2_BUTTONS_XPOS + x * ED_CTRL2_BUTTON_XSIZE;
1151       gd_yoffset = ED_CTRL2_BUTTONS_YPOS + y * ED_CTRL2_BUTTON_YSIZE;
1152       width = ED_CTRL2_BUTTON_XSIZE;
1153       height = ED_CTRL2_BUTTON_YSIZE;
1154     }
1155
1156     gd_x1 = DOOR_GFX_PAGEX8 + gd_xoffset;
1157     gd_x2 = DOOR_GFX_PAGEX7 + gd_xoffset;
1158     gd_y1  = DOOR_GFX_PAGEY1 + ED_CTRL_BUTTONS_GFX_YPOS + gd_yoffset;
1159     gd_y2  = DOOR_GFX_PAGEY1 + ED_CTRL_BUTTONS_ALT_GFX_YPOS + gd_yoffset;
1160
1161     gi = CreateGadget(GDI_CUSTOM_ID, id,
1162                       GDI_INFO_TEXT, control_info[i].text,
1163                       GDI_X, EX + gd_xoffset,
1164                       GDI_Y, EY + gd_yoffset,
1165                       GDI_WIDTH, width,
1166                       GDI_HEIGHT, height,
1167                       GDI_TYPE, button_type,
1168                       GDI_STATE, GD_BUTTON_UNPRESSED,
1169                       GDI_RADIO_NR, radio_button_nr,
1170                       GDI_CHECKED, checked,
1171                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
1172                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
1173                       GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
1174                       GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
1175                       GDI_EVENT_MASK, event_mask,
1176                       GDI_CALLBACK_ACTION, HandleControlButtons,
1177                       GDI_END);
1178
1179     if (gi == NULL)
1180       Error(ERR_EXIT, "cannot create gadget");
1181
1182     level_editor_gadget[id] = gi;
1183   }
1184
1185   /* create buttons for scrolling of drawing area and element list */
1186   for (i=0; i<ED_NUM_SCROLLBUTTONS; i++)
1187   {
1188     int id = scrollbutton_info[i].gadget_id;
1189     int x, y, width, height;
1190     int gd_x1, gd_x2, gd_y1, gd_y2;
1191
1192     x = scrollbutton_info[i].x;
1193     y = scrollbutton_info[i].y;
1194
1195     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
1196
1197     if (id == GADGET_ID_ELEMENTLIST_UP ||
1198         id == GADGET_ID_ELEMENTLIST_DOWN)
1199     {
1200       x += DX;
1201       y += DY;
1202       width = ED_ELEMENTLIST_UPDOWN_XSIZE;
1203       height = ED_ELEMENTLIST_UPDOWN_YSIZE;
1204       gd_x1 = DOOR_GFX_PAGEX6 + scrollbutton_info[i].xpos;
1205       gd_y1 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].y;
1206       gd_x2 = gd_x1;
1207       gd_y2 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].ypos;
1208     }
1209     else
1210     {
1211       x += SX;
1212       y += SY;
1213       width = ED_SCROLLBUTTON_XSIZE;
1214       height = ED_SCROLLBUTTON_YSIZE;
1215       gd_x1 = DOOR_GFX_PAGEX8 + scrollbutton_info[i].xpos;
1216       gd_y1 = DOOR_GFX_PAGEY1 + scrollbutton_info[i].ypos;
1217       gd_x2 = gd_x1 - ED_SCROLLBUTTON_XSIZE;
1218       gd_y2 = gd_y1;
1219    }
1220
1221     gi = CreateGadget(GDI_CUSTOM_ID, id,
1222                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
1223                       GDI_X, x,
1224                       GDI_Y, y,
1225                       GDI_WIDTH, width,
1226                       GDI_HEIGHT, height,
1227                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
1228                       GDI_STATE, GD_BUTTON_UNPRESSED,
1229                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
1230                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
1231                       GDI_EVENT_MASK, event_mask,
1232                       GDI_CALLBACK_ACTION, HandleControlButtons,
1233                       GDI_END);
1234
1235     if (gi == NULL)
1236       Error(ERR_EXIT, "cannot create gadget");
1237
1238     level_editor_gadget[id] = gi;
1239   }
1240
1241   /* create buttons for element list */
1242   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
1243   {
1244     Pixmap deco_pixmap;
1245     int deco_x, deco_y, deco_xpos, deco_ypos;
1246     int gd_xoffset, gd_yoffset;
1247     int gd_x, gd_y1, gd_y2;
1248     int x = i % ED_ELEMENTLIST_BUTTONS_HORIZ;
1249     int y = i / ED_ELEMENTLIST_BUTTONS_HORIZ;
1250     int id = GADGET_ID_ELEMENTLIST_FIRST + i;
1251
1252     event_mask = GD_EVENT_RELEASED;
1253
1254     gd_xoffset = ED_ELEMENTLIST_XPOS + x * ED_ELEMENTLIST_XSIZE;
1255     gd_yoffset = ED_ELEMENTLIST_YPOS + y * ED_ELEMENTLIST_YSIZE;
1256
1257     gd_x = DOOR_GFX_PAGEX6 + ED_ELEMENTLIST_XPOS;
1258     gd_y1 = DOOR_GFX_PAGEY1 + ED_ELEMENTLIST_YPOS;
1259     gd_y2 = DOOR_GFX_PAGEY1 + ED_ELEMENTLIST_ALT_YPOS;
1260
1261     getMiniGraphicSource(el2gfx(editor_element[i]),
1262                          &deco_pixmap, &deco_x, &deco_y);
1263     deco_xpos = (ED_ELEMENTLIST_XSIZE - MINI_TILEX) / 2;
1264     deco_ypos = (ED_ELEMENTLIST_YSIZE - MINI_TILEY) / 2;
1265
1266     gi = CreateGadget(GDI_CUSTOM_ID, id,
1267                       GDI_INFO_TEXT, element_info[editor_element[i]],
1268                       GDI_X, DX + gd_xoffset,
1269                       GDI_Y, DY + gd_yoffset,
1270                       GDI_WIDTH, ED_ELEMENTLIST_XSIZE,
1271                       GDI_HEIGHT, ED_ELEMENTLIST_YSIZE,
1272                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
1273                       GDI_STATE, GD_BUTTON_UNPRESSED,
1274                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x, gd_y1,
1275                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x, gd_y2,
1276                       GDI_DECORATION_DESIGN, deco_pixmap, deco_x, deco_y,
1277                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
1278                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
1279                       GDI_DECORATION_SHIFTING, 1, 1,
1280                       GDI_EVENT_MASK, event_mask,
1281                       GDI_CALLBACK_ACTION, HandleControlButtons,
1282                       GDI_END);
1283
1284     if (gi == NULL)
1285       Error(ERR_EXIT, "cannot create gadget");
1286
1287     level_editor_gadget[id] = gi;
1288   }
1289 }
1290
1291 static void CreateCounterButtons()
1292 {
1293   int i;
1294
1295   for (i=0; i<ED_NUM_COUNTERBUTTONS; i++)
1296   {
1297     int j;
1298     int xpos = SX + counterbutton_info[i].x;    /* xpos of down count button */
1299     int ypos = SY + counterbutton_info[i].y;
1300
1301     for (j=0; j<2; j++)
1302     {
1303       Pixmap gd_pixmap = pix[PIX_DOOR];
1304       struct GadgetInfo *gi;
1305       int id = (j == 0 ?
1306                 counterbutton_info[i].gadget_id_down :
1307                 counterbutton_info[i].gadget_id_up);
1308       int gd_xoffset;
1309       int gd_x, gd_x1, gd_x2, gd_y;
1310       unsigned long event_mask;
1311       char infotext[MAX_INFOTEXT_LEN + 1];
1312
1313       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
1314
1315       gd_xoffset = (j == 0 ? ED_BUTTON_MINUS_XPOS : ED_BUTTON_PLUS_XPOS);
1316       gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
1317       gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
1318       gd_y  = DOOR_GFX_PAGEY1 + ED_BUTTON_COUNT_YPOS;
1319
1320       sprintf(infotext, "%s counter value by 1, 5 or 10",
1321               (j == 0 ? "decrease" : "increase"));
1322
1323       gi = CreateGadget(GDI_CUSTOM_ID, id,
1324                         GDI_INFO_TEXT, infotext,
1325                         GDI_X, xpos,
1326                         GDI_Y, ypos,
1327                         GDI_WIDTH, ED_BUTTON_COUNT_XSIZE,
1328                         GDI_HEIGHT, ED_BUTTON_COUNT_YSIZE,
1329                         GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
1330                         GDI_STATE, GD_BUTTON_UNPRESSED,
1331                         GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
1332                         GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
1333                         GDI_EVENT_MASK, event_mask,
1334                         GDI_CALLBACK_ACTION, HandleCounterButtons,
1335                         GDI_END);
1336
1337       if (gi == NULL)
1338         Error(ERR_EXIT, "cannot create gadget");
1339
1340       level_editor_gadget[id] = gi;
1341       xpos += gi->width + ED_GADGET_DISTANCE;   /* xpos of text count button */
1342
1343       if (j == 0)
1344       {
1345         id = counterbutton_info[i].gadget_id_text;
1346         event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
1347
1348         gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
1349         gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
1350
1351         gi = CreateGadget(GDI_CUSTOM_ID, id,
1352                           GDI_INFO_TEXT, "enter counter value",
1353                           GDI_X, xpos,
1354                           GDI_Y, ypos,
1355                           GDI_TYPE, GD_TYPE_TEXTINPUT_NUMERIC,
1356                           GDI_NUMBER_VALUE, 0,
1357                           GDI_NUMBER_MIN, counterbutton_info[i].min_value,
1358                           GDI_NUMBER_MAX, counterbutton_info[i].max_value,
1359                           GDI_TEXT_SIZE, 3,
1360                           GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x, gd_y,
1361                           GDI_DESIGN_PRESSED, gd_pixmap, gd_x, gd_y,
1362                           GDI_DESIGN_BORDER, ED_BORDER_SIZE,
1363                           GDI_EVENT_MASK, event_mask,
1364                           GDI_CALLBACK_ACTION, HandleCounterButtons,
1365                           GDI_END);
1366
1367         if (gi == NULL)
1368           Error(ERR_EXIT, "cannot create gadget");
1369
1370         level_editor_gadget[id] = gi;
1371         xpos += gi->width + ED_GADGET_DISTANCE; /* xpos of up count button */
1372       }
1373     }
1374   }
1375 }
1376
1377 static void CreateDrawingAreas()
1378 {
1379   struct GadgetInfo *gi;
1380   unsigned long event_mask;
1381   int id;
1382   int i;
1383
1384   event_mask =
1385     GD_EVENT_PRESSED | GD_EVENT_RELEASED | GD_EVENT_MOVING |
1386     GD_EVENT_OFF_BORDERS;
1387
1388   /* one for the level drawing area ... */
1389   id = GADGET_ID_DRAWING_LEVEL;
1390   gi = CreateGadget(GDI_CUSTOM_ID, id,
1391                     GDI_X, SX,
1392                     GDI_Y, SY,
1393                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
1394                     GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
1395                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
1396                     GDI_EVENT_MASK, event_mask,
1397                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
1398                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
1399                     GDI_END);
1400
1401   if (gi == NULL)
1402     Error(ERR_EXIT, "cannot create gadget");
1403
1404   level_editor_gadget[id] = gi;
1405
1406   /* ... up to eight areas for element content ... */
1407   for (i=0; i<MAX_ELEM_CONTENT; i++)
1408   {
1409     int gx = SX + ED_AREA_ELEM_CONTENT_XPOS + 5 * (i % 4) * MINI_TILEX;
1410     int gy = SX + ED_AREA_ELEM_CONTENT_YPOS + 6 * (i / 4) * MINI_TILEY;
1411
1412     id = GADGET_ID_ELEM_CONTENT_0 + i;
1413     gi = CreateGadget(GDI_CUSTOM_ID, id,
1414                       GDI_X, gx,
1415                       GDI_Y, gy,
1416                       GDI_WIDTH, 3 * MINI_TILEX,
1417                       GDI_HEIGHT, 3 * MINI_TILEY,
1418                       GDI_TYPE, GD_TYPE_DRAWING_AREA,
1419                       GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
1420                       GDI_EVENT_MASK, event_mask,
1421                       GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
1422                       GDI_CALLBACK_ACTION, HandleDrawingAreas,
1423                       GDI_END);
1424
1425     if (gi == NULL)
1426       Error(ERR_EXIT, "cannot create gadget");
1427
1428     level_editor_gadget[id] = gi;
1429   }
1430
1431   /* ... one for the amoeba content */
1432   id = GADGET_ID_AMOEBA_CONTENT;
1433   gi = CreateGadget(GDI_CUSTOM_ID, id,
1434                     GDI_X, SX + ED_AREA_ELEM_CONTENT_XPOS,
1435                     GDI_Y, SY + ED_AREA_ELEM_CONTENT_YPOS,
1436                     GDI_WIDTH, MINI_TILEX,
1437                     GDI_HEIGHT, MINI_TILEY,
1438                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
1439                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
1440                     GDI_EVENT_MASK, event_mask,
1441                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
1442                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
1443                     GDI_END);
1444
1445   if (gi == NULL)
1446     Error(ERR_EXIT, "cannot create gadget");
1447
1448   level_editor_gadget[id] = gi;
1449
1450   /* ... and one for random placement background restrictions */
1451
1452   id = GADGET_ID_RANDOM_BACKGROUND;
1453   gi = CreateGadget(GDI_CUSTOM_ID, id,
1454                     GDI_X, SX + ED_AREA_RANDOM_BACKGROUND_XPOS,
1455                     GDI_Y, SY + ED_AREA_RANDOM_BACKGROUND_YPOS,
1456                     GDI_WIDTH, MINI_TILEX,
1457                     GDI_HEIGHT, MINI_TILEY,
1458                     GDI_TYPE, GD_TYPE_DRAWING_AREA,
1459                     GDI_ITEM_SIZE, MINI_TILEX, MINI_TILEY,
1460                     GDI_EVENT_MASK, event_mask,
1461                     GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
1462                     GDI_CALLBACK_ACTION, HandleDrawingAreas,
1463                     GDI_END);
1464
1465   if (gi == NULL)
1466     Error(ERR_EXIT, "cannot create gadget");
1467
1468   level_editor_gadget[id] = gi;
1469 }
1470
1471 static void CreateTextInputGadgets()
1472 {
1473   int i;
1474
1475   for (i=0; i<ED_NUM_TEXTINPUT; i++)
1476   {
1477     Pixmap gd_pixmap = pix[PIX_DOOR];
1478     int gd_x, gd_y;
1479     struct GadgetInfo *gi;
1480     unsigned long event_mask;
1481     char infotext[1024];
1482     int id = textinput_info[i].gadget_id;
1483
1484     event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
1485
1486     gd_x = DOOR_GFX_PAGEX4 + ED_WIN_COUNT_XPOS;
1487     gd_y = DOOR_GFX_PAGEY1 + ED_WIN_COUNT_YPOS;
1488
1489     sprintf(infotext, "Enter %s", textinput_info[i].infotext);
1490     infotext[MAX_INFOTEXT_LEN] = '\0';
1491
1492     gi = CreateGadget(GDI_CUSTOM_ID, id,
1493                       GDI_INFO_TEXT, infotext,
1494                       GDI_X, SX + textinput_info[i].x,
1495                       GDI_Y, SY + textinput_info[i].y,
1496                       GDI_TYPE, GD_TYPE_TEXTINPUT_ALPHANUMERIC,
1497                       GDI_TEXT_VALUE, textinput_info[i].value,
1498                       GDI_TEXT_SIZE, textinput_info[i].size,
1499                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x, gd_y,
1500                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x, gd_y,
1501                       GDI_DESIGN_BORDER, ED_BORDER_SIZE,
1502                       GDI_EVENT_MASK, event_mask,
1503                       GDI_CALLBACK_ACTION, HandleTextInputGadgets,
1504                       GDI_END);
1505
1506     if (gi == NULL)
1507       Error(ERR_EXIT, "cannot create gadget");
1508
1509     level_editor_gadget[id] = gi;
1510   }
1511 }
1512
1513 static void CreateScrollbarGadgets()
1514 {
1515   int i;
1516
1517   for (i=0; i<ED_NUM_SCROLLBARS; i++)
1518   {
1519     int id = scrollbar_info[i].gadget_id;
1520     Pixmap gd_pixmap = pix[PIX_DOOR];
1521     int gd_x1, gd_x2, gd_y1, gd_y2;
1522     struct GadgetInfo *gi;
1523     int items_max, items_visible, item_position;
1524     unsigned long event_mask;
1525
1526     if (scrollbar_info[i].type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1527     {
1528       items_max = MAX(lev_fieldx + 2, ed_fieldx);
1529       items_visible = ed_fieldx;
1530       item_position = 0;
1531     }
1532     else
1533     {
1534       items_max = MAX(lev_fieldy + 2, ed_fieldy);
1535       items_visible = ed_fieldy;
1536       item_position = 0;
1537     }
1538
1539     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
1540
1541     gd_x1 = DOOR_GFX_PAGEX8 + scrollbar_info[i].xpos;
1542     gd_x2 = gd_x1 - ED_SCROLLBUTTON_XSIZE;
1543     gd_y1 = DOOR_GFX_PAGEY1 + scrollbar_info[i].ypos;
1544     gd_y2 = DOOR_GFX_PAGEY1 + scrollbar_info[i].ypos;
1545
1546     gi = CreateGadget(GDI_CUSTOM_ID, id,
1547                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
1548                       GDI_X, SX + scrollbar_info[i].x,
1549                       GDI_Y, SY + scrollbar_info[i].y,
1550                       GDI_WIDTH, scrollbar_info[i].width,
1551                       GDI_HEIGHT, scrollbar_info[i].height,
1552                       GDI_TYPE, scrollbar_info[i].type,
1553                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
1554                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
1555                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
1556                       GDI_STATE, GD_BUTTON_UNPRESSED,
1557                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
1558                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
1559                       GDI_DESIGN_BORDER, ED_BORDER_SIZE,
1560                       GDI_EVENT_MASK, event_mask,
1561                       GDI_CALLBACK_ACTION, HandleControlButtons,
1562                       GDI_END);
1563
1564     if (gi == NULL)
1565       Error(ERR_EXIT, "cannot create gadget");
1566
1567     level_editor_gadget[id] = gi;
1568   }
1569 }
1570
1571 static void CreateCheckbuttonGadgets()
1572 {
1573   Pixmap gd_pixmap = pix[PIX_DOOR];
1574   struct GadgetInfo *gi;
1575   unsigned long event_mask;
1576   int gd_x1, gd_x2, gd_x3, gd_x4, gd_y;
1577   boolean checked;
1578   int i;
1579
1580   event_mask = GD_EVENT_PRESSED;
1581
1582   gd_x1 = DOOR_GFX_PAGEX4 + ED_CHECKBUTTON_UNCHECKED_XPOS;
1583   gd_x2 = DOOR_GFX_PAGEX3 + ED_CHECKBUTTON_UNCHECKED_XPOS;
1584   gd_x3 = DOOR_GFX_PAGEX4 + ED_CHECKBUTTON_CHECKED_XPOS;
1585   gd_x4 = DOOR_GFX_PAGEX3 + ED_CHECKBUTTON_CHECKED_XPOS;
1586   gd_y  = DOOR_GFX_PAGEY1 + ED_CHECKBUTTON_YPOS;
1587
1588   for (i=0; i<ED_NUM_RADIOBUTTONS; i++)
1589   {
1590     int id = radiobutton_info[i].gadget_id;
1591
1592     checked =
1593       (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
1594
1595     gi = CreateGadget(GDI_CUSTOM_ID, id,
1596                       GDI_INFO_TEXT, radiobutton_info[i].infotext,
1597                       GDI_X, SX + radiobutton_info[i].x,
1598                       GDI_Y, SY + radiobutton_info[i].y,
1599                       GDI_WIDTH, ED_CHECKBUTTON_XSIZE,
1600                       GDI_HEIGHT, ED_CHECKBUTTON_YSIZE,
1601                       GDI_TYPE, GD_TYPE_RADIO_BUTTON,
1602                       GDI_RADIO_NR, radiobutton_info[i].radio_button_nr,
1603                       GDI_CHECKED, checked,
1604                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
1605                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
1606                       GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x3, gd_y,
1607                       GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x4, gd_y,
1608                       GDI_EVENT_MASK, event_mask,
1609                       GDI_CALLBACK_ACTION, HandleControlButtons,
1610                       GDI_END);
1611
1612     if (gi == NULL)
1613       Error(ERR_EXIT, "cannot create gadget");
1614
1615     level_editor_gadget[id] = gi;
1616   }
1617
1618   for (i=0; i<ED_NUM_CHECKBUTTONS; i++)
1619   {
1620     int id = checkbutton_info[i].gadget_id;
1621
1622     if (id == GADGET_ID_STICK_ELEMENT)
1623       gd_y  = DOOR_GFX_PAGEY1 + ED_STICKYBUTTON_YPOS;
1624     else
1625       gd_y  = DOOR_GFX_PAGEY1 + ED_CHECKBUTTON_YPOS;
1626
1627     gi = CreateGadget(GDI_CUSTOM_ID, id,
1628                       GDI_INFO_TEXT, checkbutton_info[i].infotext,
1629                       GDI_X, SX + checkbutton_info[i].x,
1630                       GDI_Y, SY + checkbutton_info[i].y,
1631                       GDI_WIDTH, ED_CHECKBUTTON_XSIZE,
1632                       GDI_HEIGHT, ED_CHECKBUTTON_YSIZE,
1633                       GDI_TYPE, GD_TYPE_CHECK_BUTTON,
1634                       GDI_CHECKED, *checkbutton_info[i].value,
1635                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y,
1636                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y,
1637                       GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x3, gd_y,
1638                       GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x4, gd_y,
1639                       GDI_EVENT_MASK, event_mask,
1640                       GDI_CALLBACK_ACTION, HandleControlButtons,
1641                       GDI_END);
1642
1643     if (gi == NULL)
1644       Error(ERR_EXIT, "cannot create gadget");
1645
1646     level_editor_gadget[id] = gi;
1647   }
1648 }
1649
1650 void CreateLevelEditorGadgets()
1651 {
1652   CreateControlButtons();
1653   CreateCounterButtons();
1654   CreateDrawingAreas();
1655   CreateTextInputGadgets();
1656   CreateScrollbarGadgets();
1657   CreateCheckbuttonGadgets();
1658 }
1659
1660 static void MapControlButtons()
1661 {
1662   int i;
1663
1664   for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
1665     MapGadget(level_editor_gadget[i]);
1666   for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
1667     MapGadget(level_editor_gadget[GADGET_ID_ELEMENTLIST_FIRST + i]);
1668 }
1669
1670 static void MapCounterButtons(int id)
1671 {
1672   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_down]);
1673   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_text]);
1674   MapGadget(level_editor_gadget[counterbutton_info[id].gadget_id_up]);
1675 }
1676
1677 static void MapDrawingArea(int id)
1678 {
1679   MapGadget(level_editor_gadget[id]);
1680 }
1681
1682 static void MapTextInputGadget(int id)
1683 {
1684   MapGadget(level_editor_gadget[textinput_info[id].gadget_id]);
1685 }
1686
1687 static void MapRadiobuttonGadget(int id)
1688 {
1689   MapGadget(level_editor_gadget[radiobutton_info[id].gadget_id]);
1690 }
1691
1692 static void MapCheckbuttonGadget(int id)
1693 {
1694   MapGadget(level_editor_gadget[checkbutton_info[id].gadget_id]);
1695 }
1696
1697 static void MapMainDrawingArea()
1698 {
1699   boolean no_horizontal_scrollbar = (lev_fieldx + 2 <= ed_fieldx);
1700   boolean no_vertical_scrollbar = (lev_fieldy + 2 <= ed_fieldy);
1701   int i;
1702
1703   for (i=0; i<ED_NUM_SCROLLBUTTONS; i++)
1704   {
1705     if (((i == ED_SCROLLBUTTON_ID_AREA_LEFT ||
1706           i == ED_SCROLLBUTTON_ID_AREA_RIGHT) &&
1707          no_horizontal_scrollbar) ||
1708         ((i == ED_SCROLLBUTTON_ID_AREA_UP ||
1709           i == ED_SCROLLBUTTON_ID_AREA_DOWN) &&
1710          no_vertical_scrollbar))
1711       continue;
1712
1713     MapGadget(level_editor_gadget[scrollbutton_info[i].gadget_id]);
1714   }
1715
1716   for (i=0; i<ED_NUM_SCROLLBARS; i++)
1717   {
1718     if ((i == ED_SCROLLBAR_ID_HORIZONTAL && no_horizontal_scrollbar) ||
1719         (i == ED_SCROLLBAR_ID_VERTICAL && no_vertical_scrollbar))
1720       continue;
1721
1722     MapGadget(level_editor_gadget[scrollbar_info[i].gadget_id]);
1723   }
1724
1725   MapDrawingArea(GADGET_ID_DRAWING_LEVEL);
1726 }
1727
1728 static void UnmapDrawingArea(int id)
1729 {
1730   UnmapGadget(level_editor_gadget[id]);
1731 }
1732
1733 void UnmapLevelEditorWindowGadgets()
1734 {
1735   int i;
1736
1737   for (i=0; i<NUM_EDITOR_GADGETS; i++)
1738     if (level_editor_gadget[i]->x < DX)
1739       UnmapGadget(level_editor_gadget[i]);
1740 }
1741
1742 void UnmapLevelEditorGadgets()
1743 {
1744   int i;
1745
1746   for (i=0; i<NUM_EDITOR_GADGETS; i++)
1747     UnmapGadget(level_editor_gadget[i]);
1748 }
1749
1750 void DrawLevelEd()
1751 {
1752   int i, x, y, graphic;
1753
1754   edit_mode = ED_MODE_DRAWING;
1755
1756   CloseDoor(DOOR_CLOSE_ALL);
1757
1758   OpenDoor(DOOR_OPEN_2 | DOOR_NO_DELAY);
1759
1760   if (level_editor_test_game)
1761   {
1762     for(x=0; x<lev_fieldx; x++)
1763       for(y=0; y<lev_fieldy; y++)
1764         Feld[x][y] = Ur[x][y];
1765
1766     for(x=0; x<lev_fieldx; x++)
1767       for(y=0; y<lev_fieldy; y++)
1768         Ur[x][y] = FieldBackup[x][y];
1769
1770     level_editor_test_game = FALSE;
1771   }
1772   else
1773   {
1774     level_xpos = -1;
1775     level_ypos = -1;
1776     undo_buffer_position = -1;
1777     undo_buffer_steps = -1;
1778     CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
1779   }
1780
1781   /*
1782   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
1783   FadeToFront();
1784   */
1785
1786   XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
1787             DOOR_GFX_PAGEX6,DOOR_GFX_PAGEY1,
1788             DXSIZE,DYSIZE,
1789             DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
1790   XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
1791             DOOR_GFX_PAGEX6+ED_BUTTON_ELEM_XPOS,
1792             DOOR_GFX_PAGEY1+ED_BUTTON_ELEM_YPOS,
1793             4*ED_BUTTON_ELEM_XSIZE,5*ED_BUTTON_ELEM_YSIZE,
1794             DOOR_GFX_PAGEX1+ED_BUTTON_ELEM_XPOS,
1795             DOOR_GFX_PAGEY1+ED_BUTTON_EUP_Y2POS);
1796
1797   for(i=0;i<MAX_ELEM_X*MAX_ELEM_Y;i++)
1798   {
1799     if (i < elements_in_list)
1800       graphic = el2gfx(editor_element[i + element_shift]);
1801     else
1802       graphic = GFX_LEERRAUM;
1803
1804     DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
1805                        DOOR_GFX_PAGEX1+ED_BUTTON_ELEM_XPOS+3 + 
1806                        (i%MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
1807                        DOOR_GFX_PAGEY1+ED_BUTTON_ELEM_YPOS+3 +
1808                        (i/MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
1809                        graphic);
1810   }
1811
1812   DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
1813                      DOOR_GFX_PAGEX1+ED_WIN_MB_LEFT_XPOS,
1814                      DOOR_GFX_PAGEY1+ED_WIN_MB_LEFT_YPOS,
1815                      el2gfx(new_element1));
1816   DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
1817                      DOOR_GFX_PAGEX1+ED_WIN_MB_MIDDLE_XPOS,
1818                      DOOR_GFX_PAGEY1+ED_WIN_MB_MIDDLE_YPOS,
1819                      el2gfx(new_element2));
1820   DrawMiniGraphicExt(pix[PIX_DB_DOOR],gc,
1821                      DOOR_GFX_PAGEX1+ED_WIN_MB_RIGHT_XPOS,
1822                      DOOR_GFX_PAGEY1+ED_WIN_MB_RIGHT_YPOS,
1823                      el2gfx(new_element3));
1824   DrawTextExt(pix[PIX_DB_DOOR],gc,
1825               DOOR_GFX_PAGEX2+ED_WIN_LEVELNR_XPOS,
1826               DOOR_GFX_PAGEY1+ED_WIN_LEVELNR_YPOS,
1827               int2str(level_nr,2),FS_SMALL,FC_SPECIAL1);
1828   XCopyArea(display,pix[PIX_DB_DOOR],pix[PIX_DB_DOOR],gc,
1829             DOOR_GFX_PAGEX2+ED_WIN_LEVELNR_XPOS+3,
1830             DOOR_GFX_PAGEY1+ED_WIN_LEVELNR_YPOS,
1831             7,FONT3_YSIZE,
1832             DOOR_GFX_PAGEX1+ED_WIN_LEVELNR_XPOS,
1833             DOOR_GFX_PAGEY1+ED_WIN_LEVELNR_YPOS);
1834   XCopyArea(display,pix[PIX_DB_DOOR],pix[PIX_DB_DOOR],gc,
1835             DOOR_GFX_PAGEX2+ED_WIN_LEVELNR_XPOS+14,
1836             DOOR_GFX_PAGEY1+ED_WIN_LEVELNR_YPOS,
1837             7,FONT3_YSIZE,
1838             DOOR_GFX_PAGEX1+ED_WIN_LEVELNR_XPOS+9,
1839             DOOR_GFX_PAGEY1+ED_WIN_LEVELNR_YPOS);
1840
1841   XCopyArea(display,pix[PIX_DOOR],pix[PIX_DB_DOOR],gc,
1842             DOOR_GFX_PAGEX6,DOOR_GFX_PAGEY2,
1843             VXSIZE,VYSIZE,
1844             DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
1845
1846   /* draw bigger door */
1847   XCopyArea(display, pix[PIX_DOOR], drawto, gc,
1848             DOOR_GFX_PAGEX7, 0,
1849             108, 64,
1850             EX - 4, EY - 12);
1851
1852   /* draw new control window */
1853   XCopyArea(display, pix[PIX_DOOR], drawto, gc,
1854             DOOR_GFX_PAGEX8, 236,
1855             EXSIZE, EYSIZE,
1856             EX, EY);
1857
1858   redraw_mask |= REDRAW_ALL;
1859
1860   OpenDoor(DOOR_OPEN_1);
1861
1862   strcpy(level_editor_gadget[GADGET_ID_LEVEL_NAME]->text.value, level.name);
1863
1864   MapControlButtons();
1865
1866   /*
1867   MapMainDrawingArea();
1868   */
1869
1870   DrawDrawingWindow();
1871   FadeToFront();
1872
1873   /*
1874   OpenDoor(DOOR_OPEN_1 | DOOR_OPEN_2);
1875   */
1876 }
1877
1878 static void AdjustDrawingAreaGadgets()
1879 {
1880   int ed_xsize = lev_fieldx + 2;
1881   int ed_ysize = lev_fieldy + 2;
1882   int max_ed_fieldx = MAX_ED_FIELDX;
1883   int max_ed_fieldy = MAX_ED_FIELDY;
1884   boolean horizontal_scrollbar_needed;
1885   boolean vertical_scrollbar_needed;
1886   int x, y, width, height;
1887   int xoffset, yoffset;
1888
1889   /* check if we need any scrollbars */
1890   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
1891   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
1892
1893   /* check if we have a smaller editor field because of scrollbars */
1894   if (horizontal_scrollbar_needed)
1895     max_ed_fieldy = MAX_ED_FIELDY - 1;
1896   if (vertical_scrollbar_needed)
1897     max_ed_fieldx = MAX_ED_FIELDX - 1;
1898
1899   /* check again if we now need more scrollbars because of less space */
1900   horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
1901   vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
1902
1903   /* check if editor field gets even smaller after adding new scrollbars */
1904   if (horizontal_scrollbar_needed)
1905     max_ed_fieldy = MAX_ED_FIELDY - 1;
1906   if (vertical_scrollbar_needed)
1907     max_ed_fieldx = MAX_ED_FIELDX - 1;
1908
1909   ed_fieldx = (ed_xsize < MAX_ED_FIELDX ? ed_xsize : max_ed_fieldx);
1910   ed_fieldy = (ed_ysize < MAX_ED_FIELDY ? ed_ysize : max_ed_fieldy);
1911
1912   ModifyGadget(level_editor_gadget[GADGET_ID_DRAWING_LEVEL],
1913                GDI_WIDTH, ed_fieldx * MINI_TILEX,
1914                GDI_HEIGHT, ed_fieldy * MINI_TILEY,
1915                GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
1916                GDI_END);
1917
1918   xoffset = (ed_fieldx == MAX_ED_FIELDX ? ED_SCROLLBUTTON_XSIZE : 0);
1919   yoffset = (ed_fieldy == MAX_ED_FIELDY ? ED_SCROLLBUTTON_YSIZE : 0);
1920
1921   x = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_RIGHT].x + xoffset;
1922   y = SX + scrollbutton_info[ED_SCROLLBUTTON_ID_AREA_DOWN].y + yoffset;
1923
1924   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_RIGHT], GDI_X, x, GDI_END);
1925   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_DOWN], GDI_Y, y, GDI_END);
1926
1927   width = scrollbar_info[ED_SCROLLBAR_ID_HORIZONTAL].width + xoffset;
1928   height = scrollbar_info[ED_SCROLLBAR_ID_VERTICAL].height + yoffset;
1929
1930   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
1931                GDI_WIDTH, width,
1932                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldx,
1933                GDI_END);
1934   ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
1935                GDI_HEIGHT, height,
1936                GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldy,
1937                GDI_END);
1938 }
1939
1940 static void AdjustLevelScrollPosition()
1941 {
1942   if (level_xpos < -1)
1943     level_xpos = -1;
1944   if (level_xpos > lev_fieldx - ed_fieldx + 1)
1945     level_xpos = lev_fieldx - ed_fieldx + 1;
1946   if (lev_fieldx < ed_fieldx - 2)
1947     level_xpos = -1;
1948
1949   if (level_ypos < -1)
1950     level_ypos = -1;
1951   if (level_ypos > lev_fieldy - ed_fieldy + 1)
1952     level_ypos = lev_fieldy - ed_fieldy + 1;
1953   if (lev_fieldy < ed_fieldy - 2)
1954     level_ypos = -1;
1955 }
1956
1957 static void AdjustEditorScrollbar(int id)
1958 {
1959   struct GadgetInfo *gi = level_editor_gadget[id];
1960   int items_max, items_visible, item_position;
1961
1962   if (id == GADGET_ID_SCROLL_HORIZONTAL)
1963   {
1964     items_max = MAX(lev_fieldx + 2, ed_fieldx);
1965     items_visible = ed_fieldx;
1966     item_position = level_xpos + 1;
1967   }
1968   else
1969   {
1970     items_max = MAX(lev_fieldy + 2, ed_fieldy);
1971     items_visible = ed_fieldy;
1972     item_position = level_ypos + 1;
1973   }
1974
1975   if (item_position > items_max - items_visible)
1976     item_position = items_max - items_visible;
1977
1978   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
1979                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
1980 }
1981
1982 static void ModifyEditorTextInput(int textinput_id, char *new_text)
1983 {
1984   int gadget_id = textinput_info[textinput_id].gadget_id;
1985   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
1986
1987   ModifyGadget(gi, GDI_TEXT_VALUE, new_text, GDI_END);
1988 }
1989
1990 static void ModifyEditorCounter(int counter_id, int new_value)
1991 {
1992   int *counter_value = *counterbutton_info[counter_id].counter_value;
1993   int gadget_id = counterbutton_info[counter_id].gadget_id_text;
1994   struct GadgetInfo *gi = level_editor_gadget[gadget_id];
1995
1996   ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
1997
1998   if (counter_value != NULL)
1999     *counter_value = gi->text.number_value;
2000 }
2001
2002 static void PickDrawingElement(int button, int element)
2003 {
2004   if (button < 1 || button > 3)
2005     return;
2006
2007   if (button == 1)
2008   {
2009     new_element1 = element;
2010     DrawMiniGraphicExt(drawto, gc,
2011                        DX + ED_WIN_MB_LEFT_XPOS,
2012                        DY + ED_WIN_MB_LEFT_YPOS,
2013                        el2gfx(new_element1));
2014   }
2015   else if (button == 2)
2016   {
2017     new_element2 = element;
2018     DrawMiniGraphicExt(drawto, gc,
2019                        DX + ED_WIN_MB_MIDDLE_XPOS,
2020                        DY + ED_WIN_MB_MIDDLE_YPOS,
2021                        el2gfx(new_element2));
2022   }
2023   else
2024   {
2025     new_element3 = element;
2026     DrawMiniGraphicExt(drawto, gc,
2027                        DX + ED_WIN_MB_RIGHT_XPOS,
2028                        DY + ED_WIN_MB_RIGHT_YPOS,
2029                        el2gfx(new_element3));
2030   }
2031
2032   redraw_mask |= REDRAW_DOOR_1;
2033 }
2034
2035 static void DrawDrawingWindow()
2036 {
2037   ClearWindow();
2038   UnmapLevelEditorWindowGadgets();
2039   AdjustDrawingAreaGadgets();
2040   AdjustLevelScrollPosition();
2041   AdjustEditorScrollbar(GADGET_ID_SCROLL_HORIZONTAL);
2042   AdjustEditorScrollbar(GADGET_ID_SCROLL_VERTICAL);
2043   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
2044   MapMainDrawingArea();
2045 }
2046
2047 static void DrawRandomPlacementBackgroundArea()
2048 {
2049   int area_x = ED_AREA_RANDOM_BACKGROUND_XPOS / MINI_TILEX;
2050   int area_y = ED_AREA_RANDOM_BACKGROUND_YPOS / MINI_TILEY;
2051   int area_sx = SX + ED_AREA_RANDOM_BACKGROUND_XPOS;
2052   int area_sy = SY + ED_AREA_RANDOM_BACKGROUND_YPOS;
2053   int x, y;
2054
2055   ElementContent[0][0][0] = random_placement_background_element;
2056
2057   /* draw decorative border for the object */
2058   for (y=0; y<2; y++)
2059     for (x=0; x<2; x++)
2060       DrawMiniElement(area_x + x, area_y + y, EL_ERDREICH);
2061
2062   XFillRectangle(display, drawto, gc,
2063                  area_sx + MINI_TILEX/2 - 1, area_sy + MINI_TILEY/2 - 1,
2064                  MINI_TILEX + 2, MINI_TILEY + 2);
2065
2066   /* copy border to the right location */
2067   XCopyArea(display, drawto, drawto, gc,
2068             area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY,
2069             area_sx - MINI_TILEX/2, area_sy - MINI_TILEY/2);
2070
2071   DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
2072
2073   MapDrawingArea(GADGET_ID_RANDOM_BACKGROUND);
2074 }
2075
2076 static void DrawLevelInfoWindow()
2077 {
2078   char infotext[1024];
2079   int xoffset_above = 0;
2080   int yoffset_above = -(MINI_TILEX + ED_GADGET_DISTANCE);
2081   int xoffset_right = counter_xsize;
2082   int yoffset_right = ED_BORDER_SIZE;
2083   int xoffset_right2 = ED_CHECKBUTTON_XSIZE + 2 * ED_GADGET_DISTANCE;
2084   int yoffset_right2 = ED_BORDER_SIZE;
2085   int font_color = FC_GREEN;
2086   int i, x, y;
2087
2088   ClearWindow();
2089   UnmapLevelEditorWindowGadgets();
2090
2091   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS_YPOS,
2092            "Level Settings", FS_BIG, FC_YELLOW);
2093   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS2_YPOS,
2094            "Editor Settings", FS_BIG, FC_YELLOW);
2095
2096   gadget_level_xsize_value = &lev_fieldx;
2097   gadget_level_ysize_value = &lev_fieldy;
2098   gadget_level_random_value = &random_placement_value;
2099   gadget_level_collect_value = &level.edelsteine;
2100   gadget_level_timelimit_value = &level.time;
2101   gadget_level_timescore_value = &level.score[10];
2102
2103   /* draw counter gadgets */
2104   for (i=ED_COUNTER_ID_LEVEL_FIRST; i<=ED_COUNTER_ID_LEVEL_LAST; i++)
2105   {
2106     if (counterbutton_info[i].infotext_above)
2107     {
2108       x = counterbutton_info[i].x + xoffset_above;
2109       y = counterbutton_info[i].y + yoffset_above;
2110
2111       sprintf(infotext, "%s:", counterbutton_info[i].infotext_above);
2112       infotext[MAX_INFOTEXT_LEN] = '\0';
2113       DrawTextF(x, y, font_color, infotext);
2114     }
2115
2116     if (counterbutton_info[i].infotext_right)
2117     {
2118       x = counterbutton_info[i].x + xoffset_right;
2119       y = counterbutton_info[i].y + yoffset_right;
2120
2121       sprintf(infotext, "%s", counterbutton_info[i].infotext_right);
2122       infotext[MAX_INFOTEXT_LEN] = '\0';
2123       DrawTextF(x, y, font_color, infotext);
2124     }
2125
2126     ModifyEditorCounter(i, **counterbutton_info[i].counter_value);
2127     MapCounterButtons(i);
2128   }
2129
2130   /* draw text input gadgets */
2131   for (i=ED_TEXTINPUT_ID_LEVEL_FIRST; i<=ED_TEXTINPUT_ID_LEVEL_LAST; i++)
2132   {
2133     x = textinput_info[i].x + xoffset_above;
2134     y = textinput_info[i].y + yoffset_above;
2135
2136     sprintf(infotext, "%s:", textinput_info[i].infotext);
2137     infotext[MAX_INFOTEXT_LEN] = '\0';
2138
2139     DrawTextF(x, y, font_color, infotext);
2140     ModifyEditorTextInput(i, textinput_info[i].value);
2141     MapTextInputGadget(i);
2142   }
2143
2144   /* draw radiobutton gadgets */
2145   for (i=ED_RADIOBUTTON_ID_LEVEL_FIRST; i<=ED_RADIOBUTTON_ID_LEVEL_LAST; i++)
2146   {
2147     boolean checked =
2148       (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
2149
2150     x = radiobutton_info[i].x + xoffset_right2;
2151     y = radiobutton_info[i].y + yoffset_right2;
2152
2153     DrawTextF(x, y, font_color, radiobutton_info[i].text);
2154     ModifyGadget(level_editor_gadget[radiobutton_info[i].gadget_id],
2155                  GDI_CHECKED, checked, GDI_END);
2156     MapRadiobuttonGadget(i);
2157   }
2158
2159   /* draw checkbutton gadgets */
2160   for (i=ED_CHECKBUTTON_ID_LEVEL_FIRST; i<=ED_CHECKBUTTON_ID_LEVEL_LAST; i++)
2161   {
2162     x = checkbutton_info[i].x + xoffset_right2;
2163     y = checkbutton_info[i].y + yoffset_right2;
2164
2165     DrawTextF(x, y, font_color, checkbutton_info[i].text);
2166     ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
2167                  GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
2168     MapCheckbuttonGadget(i);
2169   }
2170
2171   /* draw drawing area */
2172   DrawRandomPlacementBackgroundArea();
2173 }
2174
2175 static void DrawAmoebaContentArea()
2176 {
2177   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
2178   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
2179   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
2180   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
2181   int font_color = FC_GREEN;
2182   int x, y;
2183
2184   ElementContent[0][0][0] = level.amoebe_inhalt;
2185
2186   /* draw decorative border for the object */
2187   for (y=0; y<2; y++)
2188     for (x=0; x<2; x++)
2189       DrawMiniElement(area_x + x, area_y + y, EL_ERDREICH);
2190
2191   XFillRectangle(display, drawto, gc,
2192                  area_sx + MINI_TILEX/2 - 1, area_sy + MINI_TILEY/2 - 1,
2193                  MINI_TILEX + 2, MINI_TILEY + 2);
2194
2195   /* copy border to the right location */
2196   XCopyArea(display, drawto, drawto, gc,
2197             area_sx, area_sy, 3 * MINI_TILEX, 3 * MINI_TILEY,
2198             area_sx - MINI_TILEX/2, area_sy - MINI_TILEY/2);
2199
2200   DrawText(area_sx + TILEX, area_sy + 1, "Content of amoeba",
2201            FS_SMALL, font_color);
2202
2203   DrawMiniElement(area_x, area_y, ElementContent[0][0][0]);
2204
2205   MapDrawingArea(GADGET_ID_AMOEBA_CONTENT);
2206 }
2207
2208 static void DrawElementContentAreas()
2209 {
2210   int *num_areas = &MampferMax;
2211   int area_x = ED_AREA_ELEM_CONTENT_XPOS / MINI_TILEX;
2212   int area_y = ED_AREA_ELEM_CONTENT_YPOS / MINI_TILEY;
2213   int area_sx = SX + ED_AREA_ELEM_CONTENT_XPOS;
2214   int area_sy = SY + ED_AREA_ELEM_CONTENT_YPOS;
2215   int xoffset_right = counter_xsize;
2216   int yoffset_right = ED_BORDER_SIZE;
2217   int font_color = FC_GREEN;
2218   int i, x, y;
2219
2220   for (i=0; i<MAX_ELEM_CONTENT; i++)
2221     for (y=0; y<3; y++)
2222       for (x=0; x<3; x++)
2223         ElementContent[i][x][y] = level.mampfer_inhalt[i][x][y];
2224
2225   for (i=0; i<MAX_ELEM_CONTENT; i++)
2226     UnmapDrawingArea(GADGET_ID_ELEM_CONTENT_0 + i);
2227
2228   /* display counter to choose number of element content areas */
2229   gadget_elem_content_value = num_areas;
2230
2231   x = counterbutton_info[ED_COUNTER_ID_ELEM_CONTENT].x + xoffset_right;
2232   y = counterbutton_info[ED_COUNTER_ID_ELEM_CONTENT].y + yoffset_right;
2233   DrawTextF(x, y, font_color, "number of content areas");
2234   ModifyEditorCounter(ED_COUNTER_ID_ELEM_CONTENT, *gadget_elem_content_value);
2235   MapCounterButtons(ED_COUNTER_ID_ELEM_CONTENT);
2236
2237   /* delete content areas in case of reducing number of them */
2238   XFillRectangle(display, backbuffer, gc,
2239                  SX, area_sy - MINI_TILEX,
2240                  SXSIZE, 12 * MINI_TILEY);
2241
2242   /* draw some decorative border for the objects */
2243   for (i=0; i<*num_areas; i++)
2244   {
2245     for (y=0; y<4; y++)
2246       for (x=0; x<4; x++)
2247         DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
2248                         EL_ERDREICH);
2249
2250     XFillRectangle(display, drawto, gc,
2251                    area_sx + 5 * (i % 4) * MINI_TILEX + MINI_TILEX/2 - 1,
2252                    area_sy + 6 * (i / 4) * MINI_TILEY + MINI_TILEY/2 - 1,
2253                    3 * MINI_TILEX + 2, 3 * MINI_TILEY + 2);
2254   }
2255
2256   /* copy border to the right location */
2257   XCopyArea(display, drawto, drawto, gc,
2258             area_sx, area_sy, (5 * 4 + 1) * MINI_TILEX, 11 * MINI_TILEY,
2259             area_sx - MINI_TILEX/2, area_sy - MINI_TILEY/2);
2260
2261   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 0 * MINI_TILEY + 1,
2262            "Content", FS_SMALL, font_color);
2263   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 1 * MINI_TILEY + 1,
2264            "when", FS_SMALL, font_color);
2265   DrawText(area_sx + (5 * 4 - 1) * MINI_TILEX, area_sy + 2 * MINI_TILEY + 1,
2266            "smashed", FS_SMALL, font_color);
2267
2268   for (i=0; i<*num_areas; i++)
2269   {
2270     for (y=0; y<3; y++)
2271       for (x=0; x<3; x++)
2272         DrawMiniElement(area_x + 5 * (i % 4) + x, area_y + 6 * (i / 4) + y,
2273                         ElementContent[i][x][y]);
2274
2275     DrawTextF(area_sx - SX + 5 * (i % 4) * MINI_TILEX + MINI_TILEX + 1,
2276               area_sy - SY + 6 * (i / 4) * MINI_TILEY + 4 * MINI_TILEY - 4,
2277               font_color, "%d", i + 1);
2278   }
2279
2280   for (i=0; i<*num_areas; i++)
2281     MapDrawingArea(GADGET_ID_ELEM_CONTENT_0 + i);
2282 }
2283
2284 #define TEXT_COLLECTING         "Score for collecting"
2285 #define TEXT_SMASHING           "Score for smashing"
2286 #define TEXT_CRACKING           "Score for cracking"
2287 #define TEXT_SPEED              "Speed of amoeba growth"
2288 #define TEXT_DURATION           "Duration when activated"
2289
2290 static void DrawPropertiesWindow()
2291 {
2292   int num_elements_in_level;
2293   float percentage;
2294   int xoffset_right = counter_xsize;
2295   int yoffset_right = ED_BORDER_SIZE;
2296   int xoffset_right2 = ED_CHECKBUTTON_XSIZE + 2 * ED_GADGET_DISTANCE;
2297   int yoffset_right2 = ED_BORDER_SIZE;
2298   int xstart = 2;
2299   int ystart = 4;
2300   int font_color = FC_GREEN;
2301   int i, x, y;
2302   static struct
2303   {
2304     int element;
2305     int *counter_value;
2306     char *text;
2307   } elements_with_counter[] =
2308   {
2309     { EL_EDELSTEIN,     &level.score[0],        TEXT_COLLECTING },
2310     { EL_EDELSTEIN_BD,  &level.score[0],        TEXT_COLLECTING },
2311     { EL_EDELSTEIN_GELB,&level.score[0],        TEXT_COLLECTING },
2312     { EL_EDELSTEIN_ROT, &level.score[0],        TEXT_COLLECTING },
2313     { EL_EDELSTEIN_LILA,&level.score[0],        TEXT_COLLECTING },
2314     { EL_DIAMANT,       &level.score[1],        TEXT_COLLECTING },
2315     { EL_KAEFER_R,      &level.score[2],        TEXT_SMASHING },
2316     { EL_KAEFER_O,      &level.score[2],        TEXT_SMASHING },
2317     { EL_KAEFER_L,      &level.score[2],        TEXT_SMASHING },
2318     { EL_KAEFER_U,      &level.score[2],        TEXT_SMASHING },
2319     { EL_BUTTERFLY_R,   &level.score[2],        TEXT_SMASHING },
2320     { EL_BUTTERFLY_O,   &level.score[2],        TEXT_SMASHING },
2321     { EL_BUTTERFLY_L,   &level.score[2],        TEXT_SMASHING },
2322     { EL_BUTTERFLY_U,   &level.score[2],        TEXT_SMASHING },
2323     { EL_FLIEGER_R,     &level.score[3],        TEXT_SMASHING },
2324     { EL_FLIEGER_O,     &level.score[3],        TEXT_SMASHING },
2325     { EL_FLIEGER_L,     &level.score[3],        TEXT_SMASHING },
2326     { EL_FLIEGER_U,     &level.score[3],        TEXT_SMASHING },
2327     { EL_FIREFLY_R,     &level.score[3],        TEXT_SMASHING },
2328     { EL_FIREFLY_O,     &level.score[3],        TEXT_SMASHING },
2329     { EL_FIREFLY_L,     &level.score[3],        TEXT_SMASHING },
2330     { EL_FIREFLY_U,     &level.score[3],        TEXT_SMASHING },
2331     { EL_MAMPFER,       &level.score[4],        TEXT_SMASHING },
2332     { EL_MAMPFER2,      &level.score[4],        TEXT_SMASHING },
2333     { EL_ROBOT,         &level.score[5],        TEXT_SMASHING },
2334     { EL_PACMAN_R,      &level.score[6],        TEXT_SMASHING },
2335     { EL_PACMAN_O,      &level.score[6],        TEXT_SMASHING },
2336     { EL_PACMAN_L,      &level.score[6],        TEXT_SMASHING },
2337     { EL_PACMAN_U,      &level.score[6],        TEXT_SMASHING },
2338     { EL_KOKOSNUSS,     &level.score[7],        TEXT_CRACKING },
2339     { EL_DYNAMIT_AUS,   &level.score[8],        TEXT_COLLECTING },
2340     { EL_SCHLUESSEL1,   &level.score[9],        TEXT_COLLECTING },
2341     { EL_SCHLUESSEL2,   &level.score[9],        TEXT_COLLECTING },
2342     { EL_SCHLUESSEL3,   &level.score[9],        TEXT_COLLECTING },
2343     { EL_SCHLUESSEL4,   &level.score[9],        TEXT_COLLECTING },
2344     { EL_EM_KEY_1_FILE, &level.score[9],        TEXT_COLLECTING },
2345     { EL_EM_KEY_2_FILE, &level.score[9],        TEXT_COLLECTING },
2346     { EL_EM_KEY_3_FILE, &level.score[9],        TEXT_COLLECTING },
2347     { EL_EM_KEY_4_FILE, &level.score[9],        TEXT_COLLECTING },
2348     { EL_AMOEBE_NASS,   &level.tempo_amoebe,    TEXT_SPEED },
2349     { EL_AMOEBE_NORM,   &level.tempo_amoebe,    TEXT_SPEED },
2350     { EL_AMOEBE_VOLL,   &level.tempo_amoebe,    TEXT_SPEED },
2351     { EL_AMOEBE_BD,     &level.tempo_amoebe,    TEXT_SPEED },
2352     { EL_SIEB_INAKTIV,  &level.dauer_sieb,      TEXT_DURATION },
2353     { EL_ABLENK_AUS,    &level.dauer_ablenk,    TEXT_DURATION },
2354     { -1, NULL, NULL }
2355   };
2356
2357   ClearWindow();
2358   UnmapLevelEditorWindowGadgets();
2359
2360   DrawText(SX + ED_SETTINGS2_XPOS, SY + ED_SETTINGS_YPOS,
2361            "Element Settings", FS_BIG, FC_YELLOW);
2362
2363   /* draw some decorative border for the object */
2364   for (y=0; y<3; y++)
2365     for (x=0; x<3; x++)
2366       DrawMiniElement(xstart + x , ystart + y, EL_ERDREICH);
2367
2368   XFillRectangle(display, drawto, gc,
2369                  SX + xstart * MINI_TILEX + MINI_TILEX/2 - 1,
2370                  SY + ystart * MINI_TILEY + MINI_TILEY/2 - 1,
2371                  TILEX + 2, TILEY + 2);
2372
2373   /* copy border to the right location */
2374   XCopyArea(display, drawto, drawto, gc,
2375             SX + xstart * MINI_TILEX,
2376             SY + ystart * MINI_TILEY,
2377             2 * TILEX, 2 * TILEY,
2378             SX + xstart * MINI_TILEX - MINI_TILEX/2,
2379             SY + ystart * MINI_TILEY - MINI_TILEY/2);
2380
2381   DrawGraphic(xstart/2, ystart/2, el2gfx(properties_element));
2382
2383   /* copy the whole stuff to the definitive location */
2384   XCopyArea(display, drawto, drawto, gc,
2385             SX + xstart * MINI_TILEX - MINI_TILEX/2,
2386             SY + ystart * MINI_TILEY - MINI_TILEY,
2387             2 * TILEX, 2 * TILEY,
2388             SX + xstart * MINI_TILEX - MINI_TILEX/2,
2389             SY + ystart * MINI_TILEY - MINI_TILEY/2);
2390
2391   DrawTextF((xstart + 3) * MINI_TILEX, (ystart + 1) * MINI_TILEY,
2392             font_color, element_info[properties_element]);
2393
2394   num_elements_in_level = 0;
2395   for (y=0; y<lev_fieldy; y++) 
2396     for (x=0; x<lev_fieldx; x++)
2397       if (Feld[x][y] == properties_element)
2398         num_elements_in_level++;
2399   percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
2400
2401   DrawTextF(ED_SETTINGS_XPOS, 5 * TILEY, font_color, "In this level:");
2402   DrawTextF(ED_SETTINGS_XPOS + 15 * FONT2_XSIZE, 5 * TILEY, FC_YELLOW,
2403             "%d (%.2f%%)", num_elements_in_level, percentage);
2404
2405   /* check if there are elements where a score can be chosen for */
2406   for (i=0; elements_with_counter[i].element != -1; i++)
2407   {
2408     if (elements_with_counter[i].element == properties_element)
2409     {
2410       int x = counterbutton_info[ED_COUNTER_ID_ELEM_SCORE].x + xoffset_right;
2411       int y = counterbutton_info[ED_COUNTER_ID_ELEM_SCORE].y + yoffset_right;
2412
2413       gadget_elem_score_value = elements_with_counter[i].counter_value;
2414
2415       DrawTextF(x, y, font_color, elements_with_counter[i].text);
2416       ModifyEditorCounter(ED_COUNTER_ID_ELEM_SCORE, *gadget_elem_score_value);
2417       MapCounterButtons(ED_COUNTER_ID_ELEM_SCORE);
2418       break;
2419     }
2420   }
2421
2422   if (HAS_CONTENT(properties_element))
2423   {
2424
2425 #if 1
2426     /* draw stickybutton gadget */
2427     i = ED_CHECKBUTTON_ID_STICK_ELEMENT;
2428     x = checkbutton_info[i].x + xoffset_right2;
2429     y = checkbutton_info[i].y + yoffset_right2;
2430
2431     DrawTextF(x, y, font_color, checkbutton_info[i].text);
2432     ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
2433                  GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
2434     MapCheckbuttonGadget(i);
2435 #endif
2436
2437
2438     if (IS_AMOEBOID(properties_element))
2439       DrawAmoebaContentArea();
2440     else
2441       DrawElementContentAreas();
2442   }
2443 }
2444
2445 static void swap_numbers(int *i1, int *i2)
2446 {
2447   int help = *i1;
2448
2449   *i1 = *i2;
2450   *i2 = help;
2451 }
2452
2453 static void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
2454 {
2455   int help_x = *x1;
2456   int help_y = *y1;
2457
2458   *x1 = *x2;
2459   *x2 = help_x;
2460
2461   *y1 = *y2;
2462   *y2 = help_y;
2463 }
2464
2465 static void DrawLineElement(int sx, int sy, int element, boolean change_level)
2466 {
2467   int lx = sx + level_xpos;
2468   int ly = sy + level_ypos;
2469
2470   DrawMiniElement(sx, sy, (element < 0 ? Feld[lx][ly] : element));
2471
2472   if (change_level)
2473     Feld[lx][ly] = element;
2474 }
2475
2476 static void DrawLine(int from_x, int from_y, int to_x, int to_y,
2477                      int element, boolean change_level)
2478 {
2479   if (from_y == to_y)                   /* horizontal line */
2480   {
2481     int x;
2482     int y = from_y;
2483
2484     if (from_x > to_x)
2485       swap_numbers(&from_x, &to_x);
2486
2487     for (x=from_x; x<=to_x; x++)
2488       DrawLineElement(x, y, element, change_level);
2489   }
2490   else if (from_x == to_x)              /* vertical line */
2491   {
2492     int x = from_x;
2493     int y;
2494
2495     if (from_y > to_y)
2496       swap_numbers(&from_y, &to_y);
2497
2498     for (y=from_y; y<=to_y; y++)
2499       DrawLineElement(x, y, element, change_level);
2500   }
2501   else                                  /* diagonal line */
2502   {
2503     int len_x = ABS(to_x - from_x);
2504     int len_y = ABS(to_y - from_y);
2505     int x, y;
2506
2507     if (len_y < len_x)                  /* a < 1 */
2508     {
2509       float a = (float)len_y / (float)len_x;
2510
2511       if (from_x > to_x)
2512         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
2513
2514       for (x=0; x<=len_x; x++)
2515       {
2516         y = (int)(a * x + 0.5) * (to_y < from_y ? -1 : +1);
2517         DrawLineElement(from_x + x, from_y + y, element, change_level);
2518       }
2519     }
2520     else                                /* a >= 1 */
2521     {
2522       float a = (float)len_x / (float)len_y;
2523
2524       if (from_y > to_y)
2525         swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
2526
2527       for (y=0; y<=len_y; y++)
2528       {
2529         x = (int)(a * y + 0.5) * (to_x < from_x ? -1 : +1);
2530         DrawLineElement(from_x + x, from_y + y, element, change_level);
2531       }
2532     }
2533   }
2534 }
2535
2536 static void DrawRectangle(int from_x, int from_y, int to_x, int to_y,
2537                           int element, boolean change_level)
2538 {
2539   DrawLine(from_x, from_y, from_x, to_y, element, change_level);
2540   DrawLine(from_x, to_y, to_x, to_y, element, change_level);
2541   DrawLine(to_x, to_y, to_x, from_y, element, change_level);
2542   DrawLine(to_x, from_y, from_x, from_y, element, change_level);
2543 }
2544
2545 static void DrawFilledBox(int from_x, int from_y, int to_x, int to_y,
2546                           int element, boolean change_level)
2547 {
2548   int y;
2549
2550   if (from_y > to_y)
2551     swap_number_pairs(&from_x, &from_y, &to_x, &to_y);
2552
2553   for (y=from_y; y<=to_y; y++)
2554     DrawLine(from_x, y, to_x, y, element, change_level);
2555 }
2556
2557 static void DrawArcExt(int from_x, int from_y, int to_x2, int to_y2,
2558                        int element, boolean change_level)
2559 {
2560   int to_x = to_x2 - (to_x2 > from_x ? +1 : -1);
2561   int to_y = to_y2 - (to_y2 > from_y ? +1 : -1);
2562   int len_x = ABS(to_x - from_x);
2563   int len_y = ABS(to_y - from_y);
2564   int radius, x, y;
2565
2566   radius = (int)(sqrt((float)(len_x * len_x + len_y * len_y)) + 0.5);
2567
2568   /* not optimal (some points get drawn twice) but simple,
2569      and fast enough for the few points we are drawing */
2570
2571   for (x=0; x<=radius; x++)
2572   {
2573     int sx, sy, lx, ly;
2574
2575     y = (int)(sqrt((float)(radius * radius - x * x)) + 0.5);
2576
2577     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
2578     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
2579     lx = sx + level_xpos;
2580     ly = sy + level_ypos;
2581
2582     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
2583       DrawLineElement(sx, sy, element, change_level);
2584   }
2585
2586   for (y=0; y<=radius; y++)
2587   {
2588     int sx, sy, lx, ly;
2589
2590     x = (int)(sqrt((float)(radius * radius - y * y)) + 0.5);
2591
2592     sx = from_x + x * (from_x < to_x2 ? +1 : -1);
2593     sy = from_y + y * (from_y < to_y2 ? +1 : -1);
2594     lx = sx + level_xpos;
2595     ly = sy + level_ypos;
2596
2597     if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
2598       DrawLineElement(sx, sy, element, change_level);
2599   }
2600 }
2601
2602 static void DrawArc(int from_x, int from_y, int to_x, int to_y,
2603                     int element, boolean change_level)
2604 {
2605   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
2606   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
2607
2608   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
2609 }
2610
2611 #define DRAW_CIRCLES_BUTTON_AVAILABLE   0
2612 #if DRAW_CIRCLES_BUTTON_AVAILABLE
2613 static void DrawCircle(int from_x, int from_y, int to_x, int to_y,
2614                        int element, boolean change_level)
2615 {
2616   int to_x2 = to_x + (to_x < from_x ? -1 : +1);
2617   int to_y2 = to_y + (to_y > from_y ? +1 : -1);
2618   int mirror_to_x2 = from_x - (to_x2 - from_x);
2619   int mirror_to_y2 = from_y - (to_y2 - from_y);
2620
2621   DrawArcExt(from_x, from_y, to_x2, to_y2, element, change_level);
2622   DrawArcExt(from_x, from_y, mirror_to_x2, to_y2, element, change_level);
2623   DrawArcExt(from_x, from_y, to_x2, mirror_to_y2, element, change_level);
2624   DrawArcExt(from_x, from_y, mirror_to_x2, mirror_to_y2, element,change_level);
2625 }
2626 #endif
2627
2628 static void DrawAreaBorder(int from_x, int from_y, int to_x, int to_y)
2629 {
2630   int from_sx, from_sy;
2631   int to_sx, to_sy;
2632
2633   if (from_x > to_x)
2634     swap_numbers(&from_x, &to_x);
2635
2636   if (from_y > to_y)
2637     swap_numbers(&from_y, &to_y);
2638
2639   from_sx = SX + from_x * MINI_TILEX;
2640   from_sy = SY + from_y * MINI_TILEY;
2641   to_sx = SX + to_x * MINI_TILEX + MINI_TILEX - 1;
2642   to_sy = SY + to_y * MINI_TILEY + MINI_TILEY - 1;
2643
2644   XSetForeground(display, gc, WhitePixel(display, screen));
2645
2646   XDrawLine(display, drawto, gc, from_sx, from_sy, to_sx, from_sy);
2647   XDrawLine(display, drawto, gc, to_sx, from_sy, to_sx, to_sy);
2648   XDrawLine(display, drawto, gc, to_sx, to_sy, from_sx, to_sy);
2649   XDrawLine(display, drawto, gc, from_sx, to_sy, from_sx, from_sy);
2650
2651   XSetForeground(display, gc, BlackPixel(display, screen));
2652
2653   if (from_x == to_x && from_y == to_y)
2654     MarkTileDirty(from_x/2, from_y/2);
2655   else
2656     redraw_mask |= REDRAW_FIELD;
2657 }
2658
2659 static void SelectArea(int from_x, int from_y, int to_x, int to_y,
2660                        int element, boolean change_level)
2661 {
2662   if (element == -1 || change_level)
2663     DrawRectangle(from_x, from_y, to_x, to_y, -1, FALSE);
2664   else
2665     DrawAreaBorder(from_x, from_y, to_x, to_y);
2666 }
2667
2668 /* values for CopyBrushExt() */
2669 #define CB_AREA_TO_BRUSH        0
2670 #define CB_BRUSH_TO_CURSOR      1
2671 #define CB_BRUSH_TO_LEVEL       2
2672 #define CB_DELETE_OLD_CURSOR    3
2673
2674 static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
2675                          int button, int mode)
2676 {
2677   static short brush_buffer[MAX_ED_FIELDX][MAX_ED_FIELDY];
2678   static int brush_width, brush_height;
2679   static int last_cursor_x = -1, last_cursor_y = -1;
2680   static boolean delete_old_brush;
2681   int new_element;
2682   int x, y;
2683
2684   if (mode == CB_DELETE_OLD_CURSOR && !delete_old_brush)
2685     return;
2686
2687   new_element = (button == 1 ? new_element1 :
2688                  button == 2 ? new_element2 :
2689                  button == 3 ? new_element3 : 0);
2690
2691   if (mode == CB_AREA_TO_BRUSH)
2692   {
2693     int from_lx, from_ly;
2694
2695     if (from_x > to_x)
2696       swap_numbers(&from_x, &to_x);
2697
2698     if (from_y > to_y)
2699       swap_numbers(&from_y, &to_y);
2700
2701     brush_width = to_x - from_x + 1;
2702     brush_height = to_y - from_y + 1;
2703
2704     from_lx = from_x + level_xpos;
2705     from_ly = from_y + level_ypos;
2706
2707     for (y=0; y<brush_height; y++)
2708     {
2709       for (x=0; x<brush_width; x++)
2710       {
2711         brush_buffer[x][y] = Feld[from_lx + x][from_ly + y];
2712
2713         if (button != 1)
2714           DrawLineElement(from_x + x, from_y + y, new_element, TRUE);
2715       }
2716     }
2717
2718     if (button != 1)
2719       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
2720
2721     delete_old_brush = FALSE;
2722   }
2723   else if (mode == CB_BRUSH_TO_CURSOR || mode == CB_DELETE_OLD_CURSOR ||
2724            mode == CB_BRUSH_TO_LEVEL)
2725   {
2726     int cursor_x = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_x : from_x);
2727     int cursor_y = (mode == CB_DELETE_OLD_CURSOR ? last_cursor_y : from_y);
2728     int cursor_from_x = cursor_x - brush_width / 2;
2729     int cursor_from_y = cursor_y - brush_height / 2;
2730     int border_from_x = cursor_x, border_from_y = cursor_y;
2731     int border_to_x = cursor_x, border_to_y = cursor_y;
2732
2733     if (mode != CB_DELETE_OLD_CURSOR && delete_old_brush)
2734       CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
2735
2736     if (!IN_ED_FIELD(cursor_x, cursor_y) ||
2737         !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos))
2738     {
2739       delete_old_brush = FALSE;
2740       return;
2741     }
2742
2743     for (y=0; y<brush_height; y++)
2744     {
2745       for (x=0; x<brush_width; x++)
2746       {
2747         int sx = cursor_from_x + x;
2748         int sy = cursor_from_y + y;
2749         int lx = sx + level_xpos;
2750         int ly = sy + level_ypos;
2751         boolean change_level = (mode == CB_BRUSH_TO_LEVEL);
2752         int element = (mode == CB_DELETE_OLD_CURSOR ? -1 :
2753                        mode == CB_BRUSH_TO_CURSOR || button == 1 ?
2754                        brush_buffer[x][y] : new_element);
2755
2756         if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
2757         {
2758           if (sx < border_from_x)
2759             border_from_x = sx;
2760           else if (sx > border_to_x)
2761             border_to_x = sx;
2762           if (sy < border_from_y)
2763             border_from_y = sy;
2764           else if (sy > border_to_y)
2765             border_to_y = sy;
2766
2767           DrawLineElement(sx, sy, element, change_level);
2768         }
2769       }
2770     }
2771
2772     /*
2773     printf("%d, %d - %d, %d in level and screen\n",
2774            border_from_x, border_from_y, border_to_x, border_to_y);
2775     */
2776
2777     if (mode != CB_DELETE_OLD_CURSOR)
2778       DrawAreaBorder(border_from_x, border_from_y, border_to_x, border_to_y);
2779
2780     /*
2781     if (mode == CB_BRUSH_TO_LEVEL)
2782       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
2783     */
2784
2785     last_cursor_x = cursor_x;
2786     last_cursor_y = cursor_y;
2787     delete_old_brush = TRUE;
2788   }
2789 }
2790
2791 static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y,
2792                             int button)
2793 {
2794   CopyBrushExt(from_x, from_y, to_x, to_y, button, CB_AREA_TO_BRUSH);
2795 }
2796
2797 static void CopyBrushToLevel(int x, int y, int button)
2798 {
2799   CopyBrushExt(x, y, 0, 0, button, CB_BRUSH_TO_LEVEL);
2800 }
2801
2802 static void CopyBrushToCursor(int x, int y)
2803 {
2804   CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR);
2805 }
2806
2807 static void DeleteBrushFromCursor()
2808 {
2809   CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR);
2810 }
2811
2812 static void FloodFill(int from_x, int from_y, int fill_element)
2813 {
2814   int i,x,y;
2815   int old_element;
2816   static int check[4][2] = { {-1,0}, {0,-1}, {1,0}, {0,1} };
2817   static int safety = 0;
2818
2819   /* check if starting field still has the desired content */
2820   if (Feld[from_x][from_y] == fill_element)
2821     return;
2822
2823   safety++;
2824
2825   if (safety > lev_fieldx*lev_fieldy)
2826     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
2827
2828   old_element = Feld[from_x][from_y];
2829   Feld[from_x][from_y] = fill_element;
2830
2831   for(i=0;i<4;i++)
2832   {
2833     x = from_x + check[i][0];
2834     y = from_y + check[i][1];
2835
2836     if (IN_LEV_FIELD(x,y) && Feld[x][y] == old_element)
2837       FloodFill(x, y, fill_element);
2838   }
2839
2840   safety--;
2841 }
2842
2843 /* values for DrawLevelText() modes */
2844 #define TEXT_INIT               0
2845 #define TEXT_SETCURSOR          1
2846 #define TEXT_WRITECHAR          2
2847 #define TEXT_BACKSPACE          3
2848 #define TEXT_NEWLINE            4
2849 #define TEXT_END                5
2850 #define TEXT_QUERY_TYPING       6
2851
2852 static int DrawLevelText(int sx, int sy, char letter, int mode)
2853 {
2854   static short delete_buffer[MAX_LEV_FIELDX];
2855   static int start_sx, start_sy;
2856   static int last_sx, last_sy;
2857   static boolean typing = FALSE;
2858   int letter_element = EL_CHAR_ASCII0 + letter;
2859   int lx = 0, ly = 0;
2860
2861   /* map lower case letters to upper case and convert special characters */
2862   if (letter >= 'a' && letter <= 'z')
2863     letter_element = EL_CHAR_ASCII0 + letter + (int)('A' - 'a');
2864   else if (letter == 'ä' || letter == 'Ä')
2865     letter_element = EL_CHAR_AE;
2866   else if (letter == 'ö' || letter == 'Ö')
2867     letter_element = EL_CHAR_OE;
2868   else if (letter == 'ü' || letter == 'Ãœ')
2869     letter_element = EL_CHAR_UE;
2870   else if (letter == '^')
2871     letter_element = EL_CHAR_COPY;
2872   else
2873     letter_element = EL_CHAR_ASCII0 + letter;
2874
2875   if (mode != TEXT_INIT)
2876   {
2877     if (!typing)
2878       return FALSE;
2879
2880     if (mode != TEXT_SETCURSOR)
2881     {
2882       sx = last_sx;
2883       sy = last_sy;
2884     }
2885
2886     lx = last_sx + level_xpos;
2887     ly = last_sy + level_ypos;
2888   }
2889
2890   switch (mode)
2891   {
2892     case TEXT_INIT:
2893       if (typing)
2894         DrawLevelText(0, 0, 0, TEXT_END);
2895
2896       typing = TRUE;
2897       start_sx = last_sx = sx;
2898       start_sy = last_sy = sy;
2899       DrawLevelText(sx, sy, 0, TEXT_SETCURSOR);
2900       break;
2901
2902     case TEXT_SETCURSOR:
2903       DrawMiniElement(last_sx, last_sy, Feld[lx][ly]);
2904       DrawAreaBorder(sx, sy, sx, sy);
2905       last_sx = sx;
2906       last_sy = sy;
2907       break;
2908
2909     case TEXT_WRITECHAR:
2910       if (letter_element >= EL_CHAR_START && letter_element <= EL_CHAR_END)
2911       {
2912         delete_buffer[sx - start_sx] = Feld[lx][ly];
2913         Feld[lx][ly] = letter_element;
2914
2915         if (sx + 1 < ed_fieldx && lx + 1 < lev_fieldx)
2916           DrawLevelText(sx + 1, sy, 0, TEXT_SETCURSOR);
2917         else if (sy + 1 < ed_fieldy && ly + 1 < lev_fieldy)
2918           DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
2919         else
2920           DrawLevelText(0, 0, 0, TEXT_END);
2921       }
2922       break;
2923
2924     case TEXT_BACKSPACE:
2925       if (sx > start_sx)
2926       {
2927         Feld[lx - 1][ly] = delete_buffer[sx - start_sx - 1];
2928         DrawMiniElement(sx - 1, sy, Feld[lx - 1][ly]);
2929         DrawLevelText(sx - 1, sy, 0, TEXT_SETCURSOR);
2930       }
2931       break;
2932
2933     case TEXT_NEWLINE:
2934       if (sy + 1 < ed_fieldy - 1 && ly + 1 < lev_fieldy - 1)
2935         DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
2936       else
2937         DrawLevelText(0, 0, 0, TEXT_END);
2938       break;
2939
2940     case TEXT_END:
2941       CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
2942       DrawMiniElement(sx, sy, Feld[lx][ly]);
2943       typing = FALSE;
2944       break;
2945
2946     case TEXT_QUERY_TYPING:
2947       break;
2948
2949     default:
2950       break;
2951   }
2952
2953   return typing;
2954 }
2955
2956 static void SetTextCursor(int unused_sx, int unused_sy, int sx, int sy,
2957                           int element, boolean change_level)
2958 {
2959   int lx = sx + level_xpos;
2960   int ly = sy + level_ypos;
2961
2962   if (element == -1)
2963     DrawMiniElement(sx, sy, Feld[lx][ly]);
2964   else
2965     DrawAreaBorder(sx, sy, sx, sy);
2966 }
2967
2968 static void CopyLevelToUndoBuffer(int mode)
2969 {
2970   static boolean accumulated_undo = FALSE;
2971   boolean new_undo_buffer_position = TRUE;
2972   int last_border_element;
2973   int x, y;
2974
2975   switch (mode)
2976   {
2977     case UNDO_IMMEDIATE:
2978       accumulated_undo = FALSE;
2979       break;
2980
2981     case UNDO_ACCUMULATE:
2982       if (accumulated_undo)
2983         new_undo_buffer_position = FALSE;
2984       accumulated_undo = TRUE;
2985       break;
2986
2987     default:
2988       break;
2989   }
2990
2991   if (new_undo_buffer_position)
2992   {
2993     /* new position in undo buffer ring */
2994     undo_buffer_position = (undo_buffer_position + 1) % NUM_UNDO_STEPS;
2995
2996     if (undo_buffer_steps < NUM_UNDO_STEPS - 1)
2997       undo_buffer_steps++;
2998   }
2999
3000   for(x=0; x<lev_fieldx; x++)
3001     for(y=0; y<lev_fieldy; y++)
3002       UndoBuffer[undo_buffer_position][x][y] = Feld[x][y];
3003
3004   /* check if change of border style was forced by drawing operation */
3005   last_border_element = BorderElement;
3006   SetBorderElement();
3007   if (BorderElement != last_border_element)
3008     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3009
3010 #if 0
3011 #ifdef DEBUG
3012   printf("level saved to undo buffer\n");
3013 #endif
3014 #endif
3015 }
3016
3017 static void RandomPlacement(int new_element)
3018 {
3019   static boolean free_position[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
3020   int num_free_positions;
3021   int num_percentage;
3022   int num_elements;
3023   int x, y;
3024
3025   /* determine number of free positions for the new elements */
3026   /* (maybe this statement should be formatted a bit more readable...) */
3027   num_free_positions = 0;
3028   for (x=0; x<lev_fieldx; x++)
3029     for (y=0; y<lev_fieldy; y++)
3030       if ((free_position[x][y] =
3031            ((random_placement_background_restricted &&
3032              Feld[x][y] == random_placement_background_element) ||
3033             (!random_placement_background_restricted &&
3034              Feld[x][y] != new_element))) == TRUE)
3035         num_free_positions++;
3036
3037   /* determine number of new elements to place there */
3038   num_percentage = num_free_positions * random_placement_value / 100;
3039   num_elements = (random_placement_method == RANDOM_USE_PERCENTAGE ?
3040                   num_percentage : random_placement_value);
3041
3042   /* if not more free positions than elements to place, fill whole level */
3043   if (num_elements >= num_free_positions)
3044   {
3045     for (x=0; x<lev_fieldx; x++)
3046       for (y=0; y<lev_fieldy; y++)
3047         Feld[x][y] = new_element;
3048
3049     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3050     CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3051     return;
3052   }
3053
3054   while (num_elements > 0)
3055   {
3056     x = RND(lev_fieldx);
3057     y = RND(lev_fieldy);
3058
3059     /* don't place element at the same position twice */
3060     if (free_position[x][y])
3061     {
3062       free_position[x][y] = FALSE;
3063       Feld[x][y] = new_element;
3064       num_elements--;
3065     }
3066   }
3067
3068   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3069   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3070 }
3071
3072 void WrapLevel(int dx, int dy)
3073 {
3074   int wrap_dx = lev_fieldx - dx;
3075   int wrap_dy = lev_fieldy - dy;
3076   int x, y;
3077
3078   for(x=0; x<lev_fieldx; x++)
3079     for(y=0; y<lev_fieldy; y++)
3080       FieldBackup[x][y] = Feld[x][y];
3081
3082   for(x=0; x<lev_fieldx; x++)
3083     for(y=0; y<lev_fieldy; y++)
3084       Feld[x][y] =
3085         FieldBackup[(x + wrap_dx) % lev_fieldx][(y + wrap_dy) % lev_fieldy];
3086
3087   DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3088   CopyLevelToUndoBuffer(UNDO_ACCUMULATE);
3089 }
3090
3091 static void HandleDrawingAreas(struct GadgetInfo *gi)
3092 {
3093   static boolean started_inside_drawing_area = FALSE;
3094   int id = gi->custom_id;
3095   boolean button_press_event;
3096   boolean button_release_event;
3097   boolean inside_drawing_area = !gi->event.off_borders;
3098   boolean draw_level = (id == GADGET_ID_DRAWING_LEVEL);
3099   int actual_drawing_function;
3100   int button = gi->event.button;
3101   int new_element = BUTTON_ELEMENT(button);
3102   int sx = gi->event.x, sy = gi->event.y;
3103   int min_sx = 0, min_sy = 0;
3104   int max_sx = gi->drawing.area_xsize - 1, max_sy = gi->drawing.area_ysize - 1;
3105   int lx = 0, ly = 0;
3106   int min_lx = 0, min_ly = 0;
3107   int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
3108   int x, y;
3109
3110   /* handle info callback for each invocation of action callback */
3111   gi->callback_info(gi);
3112
3113   /*
3114   if (edit_mode != ED_MODE_DRAWING)
3115     return;
3116   */
3117
3118   button_press_event = (gi->event.type == GD_EVENT_PRESSED);
3119   button_release_event = (gi->event.type == GD_EVENT_RELEASED);
3120
3121   /* make sure to stay inside drawing area boundaries */
3122   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
3123   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
3124
3125   if (draw_level)
3126   {
3127     /* get positions inside level field */
3128     lx = sx + level_xpos;
3129     ly = sy + level_ypos;
3130
3131     if (!IN_LEV_FIELD(lx, ly))
3132       inside_drawing_area = FALSE;
3133
3134     /* make sure to stay inside level field boundaries */
3135     lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
3136     ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
3137
3138     /* correct drawing area positions accordingly */
3139     sx = lx - level_xpos;
3140     sy = ly - level_ypos;
3141   }
3142
3143   if (button_press_event)
3144     started_inside_drawing_area = inside_drawing_area;
3145
3146   if (!started_inside_drawing_area)
3147     return;
3148
3149   if (!button && !button_release_event)
3150     return;
3151
3152
3153 #if 0
3154   if (button_release_event)
3155     button = 0;
3156 #endif
3157
3158 #if 0
3159   if (!draw_level && drawing_function != GADGET_ID_SINGLE_ITEMS)
3160     return;
3161 #endif
3162
3163   /* automatically switch to 'single item' drawing mode, if needed */
3164   actual_drawing_function =
3165     (draw_level ? drawing_function : GADGET_ID_SINGLE_ITEMS);
3166
3167   switch (actual_drawing_function)
3168   {
3169     case GADGET_ID_SINGLE_ITEMS:
3170       if (draw_level)
3171       {
3172         if (button_release_event)
3173         {
3174           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3175
3176           if (edit_mode == ED_MODE_DRAWING && draw_with_brush &&
3177               !inside_drawing_area)
3178             DeleteBrushFromCursor();
3179         }
3180
3181         if (!button)
3182           break;
3183
3184         if (draw_with_brush)
3185         {
3186           if (!button_release_event)
3187             CopyBrushToLevel(sx, sy, button);
3188         }
3189         else if (new_element != Feld[lx][ly])
3190         {
3191           if (new_element == EL_SPIELFIGUR)
3192           {
3193             /* remove player at old position */
3194             for(y=0; y<lev_fieldy; y++)
3195             {
3196               for(x=0; x<lev_fieldx; x++)
3197               {
3198                 if (Feld[x][y] == EL_SPIELFIGUR || Feld[x][y] == EL_SPIELER1)
3199                 {
3200                   Feld[x][y] = EL_LEERRAUM;
3201                   if (x - level_xpos >= 0 && x - level_xpos < ed_fieldx &&
3202                       y - level_ypos >= 0 && y - level_ypos < ed_fieldy)
3203                     DrawMiniElement(x - level_xpos, y - level_ypos,
3204                                     EL_LEERRAUM);
3205                 }
3206               }
3207             }
3208           }
3209
3210           Feld[lx][ly] = new_element;
3211           DrawMiniElement(sx, sy, new_element);
3212         }
3213       }
3214       else
3215       {
3216         DrawMiniGraphicExt(drawto, gc,
3217                            gi->x + sx * MINI_TILEX,
3218                            gi->y + sy * MINI_TILEY,
3219                            el2gfx(new_element));
3220         DrawMiniGraphicExt(window, gc,
3221                            gi->x + sx * MINI_TILEX,
3222                            gi->y + sy * MINI_TILEY,
3223                            el2gfx(new_element));
3224
3225         if (id == GADGET_ID_AMOEBA_CONTENT)
3226           level.amoebe_inhalt = new_element;
3227         else if (id == GADGET_ID_RANDOM_BACKGROUND)
3228           random_placement_background_element = new_element;
3229         else if (id >= GADGET_ID_ELEM_CONTENT_0 &&
3230                  id <= GADGET_ID_ELEM_CONTENT_7)
3231           level.mampfer_inhalt[id - GADGET_ID_ELEM_CONTENT_0][sx][sy] =
3232             new_element;
3233       }
3234       break;
3235
3236     case GADGET_ID_CONNECTED_ITEMS:
3237       {
3238         static int last_sx = -1;
3239         static int last_sy = -1;
3240
3241         if (button_release_event)
3242           CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3243
3244         if (button)
3245         {
3246           if (!button_press_event)
3247             DrawLine(last_sx, last_sy, sx, sy, new_element, TRUE);
3248
3249           last_sx = sx;
3250           last_sy = sy;
3251         }
3252       }
3253       break;
3254
3255     case GADGET_ID_LINE:
3256     case GADGET_ID_ARC:
3257     case GADGET_ID_RECTANGLE:
3258     case GADGET_ID_FILLED_BOX:
3259     case GADGET_ID_GRAB_BRUSH:
3260     case GADGET_ID_TEXT:
3261       {
3262         static int last_sx = -1;
3263         static int last_sy = -1;
3264         static int start_sx = -1;
3265         static int start_sy = -1;
3266         void (*draw_func)(int, int, int, int, int, boolean);
3267
3268         if (drawing_function == GADGET_ID_LINE)
3269           draw_func = DrawLine;
3270         else if (drawing_function == GADGET_ID_ARC)
3271           draw_func = DrawArc;
3272         else if (drawing_function == GADGET_ID_RECTANGLE)
3273           draw_func = DrawRectangle;
3274         else if (drawing_function == GADGET_ID_FILLED_BOX)
3275           draw_func = DrawFilledBox;
3276         else if (drawing_function == GADGET_ID_GRAB_BRUSH)
3277           draw_func = SelectArea;
3278         else /* (drawing_function == GADGET_ID_TEXT) */
3279           draw_func = SetTextCursor;
3280
3281         if (button_press_event)
3282         {
3283           draw_func(sx, sy, sx, sy, new_element, FALSE);
3284           start_sx = last_sx = sx;
3285           start_sy = last_sy = sy;
3286
3287           if (drawing_function == GADGET_ID_TEXT)
3288             DrawLevelText(0, 0, 0, TEXT_END);
3289         }
3290         else if (button_release_event)
3291         {
3292           draw_func(start_sx, start_sy, sx, sy, new_element, TRUE);
3293           if (drawing_function == GADGET_ID_GRAB_BRUSH)
3294           {
3295             CopyAreaToBrush(start_sx, start_sy, sx, sy, button);
3296             CopyBrushToCursor(sx, sy);
3297             ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS],MB_LEFT);
3298             draw_with_brush = TRUE;
3299           }
3300           else if (drawing_function == GADGET_ID_TEXT)
3301             DrawLevelText(sx, sy, 0, TEXT_INIT);
3302           else
3303             CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3304         }
3305         else if (last_sx != sx || last_sy != sy)
3306         {
3307           draw_func(start_sx, start_sy, last_sx, last_sy, -1, FALSE);
3308           draw_func(start_sx, start_sy, sx, sy, new_element, FALSE);
3309           last_sx = sx;
3310           last_sy = sy;
3311         }
3312       }
3313       break;
3314
3315     case GADGET_ID_FLOOD_FILL:
3316       if (button_press_event && Feld[lx][ly] != new_element)
3317       {
3318         FloodFill(lx, ly, new_element);
3319         DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3320         CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
3321       }
3322       break;
3323
3324     case GADGET_ID_PICK_ELEMENT:
3325
3326       /*
3327       if (button_press_event)
3328         PickDrawingElement(button, Feld[lx][ly]);
3329       */
3330
3331       if (button_release_event)
3332         ClickOnGadget(level_editor_gadget[last_drawing_function], MB_LEFT);
3333       else
3334         PickDrawingElement(button, Feld[lx][ly]);
3335
3336       break;
3337
3338     default:
3339       break;
3340   }
3341 }
3342
3343 static void HandleCounterButtons(struct GadgetInfo *gi)
3344 {
3345   int id = gi->custom_id;
3346   int button = gi->event.button;
3347   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
3348
3349   switch (id)
3350   {
3351     case GADGET_ID_ELEM_SCORE_DOWN:
3352     case GADGET_ID_ELEM_SCORE_UP:
3353       step *= (id == GADGET_ID_ELEM_SCORE_DOWN ? -1 : 1);
3354       ModifyEditorCounter(ED_COUNTER_ID_ELEM_SCORE,
3355                           *gadget_elem_score_value + step);
3356       break;
3357     case GADGET_ID_ELEM_SCORE_TEXT:
3358       *gadget_elem_score_value = gi->text.number_value;
3359       break;
3360
3361     case GADGET_ID_ELEM_CONTENT_DOWN:
3362     case GADGET_ID_ELEM_CONTENT_UP:
3363       step *= (id == GADGET_ID_ELEM_CONTENT_DOWN ? -1 : 1);
3364       ModifyEditorCounter(ED_COUNTER_ID_ELEM_CONTENT,
3365                           *gadget_elem_content_value + step);
3366       DrawElementContentAreas();
3367       break;
3368     case GADGET_ID_ELEM_CONTENT_TEXT:
3369       *gadget_elem_content_value = gi->text.number_value;
3370       DrawElementContentAreas();
3371       break;
3372
3373     case GADGET_ID_LEVEL_XSIZE_DOWN:
3374     case GADGET_ID_LEVEL_XSIZE_UP:
3375       step *= (id == GADGET_ID_LEVEL_XSIZE_DOWN ? -1 : 1);
3376       ModifyEditorCounter(ED_COUNTER_ID_LEVEL_XSIZE,
3377                           *gadget_level_xsize_value + step);
3378       level.fieldx = lev_fieldx;
3379       break;
3380     case GADGET_ID_LEVEL_XSIZE_TEXT:
3381       *gadget_level_xsize_value = gi->text.number_value;
3382       level.fieldx = lev_fieldx;
3383       break;
3384
3385     case GADGET_ID_LEVEL_YSIZE_DOWN:
3386     case GADGET_ID_LEVEL_YSIZE_UP:
3387       step *= (id == GADGET_ID_LEVEL_YSIZE_DOWN ? -1 : 1);
3388       ModifyEditorCounter(ED_COUNTER_ID_LEVEL_YSIZE,
3389                           *gadget_level_ysize_value + step);
3390       level.fieldy = lev_fieldy;
3391       break;
3392     case GADGET_ID_LEVEL_YSIZE_TEXT:
3393       *gadget_level_ysize_value = gi->text.number_value;
3394       level.fieldy = lev_fieldy;
3395       break;
3396
3397     case GADGET_ID_LEVEL_RANDOM_DOWN:
3398     case GADGET_ID_LEVEL_RANDOM_UP:
3399       step *= (id == GADGET_ID_LEVEL_RANDOM_DOWN ? -1 : 1);
3400       ModifyEditorCounter(ED_COUNTER_ID_LEVEL_RANDOM,
3401                           *gadget_level_random_value + step);
3402       break;
3403     case GADGET_ID_LEVEL_RANDOM_TEXT:
3404       *gadget_level_random_value = gi->text.number_value;
3405       break;
3406
3407     case GADGET_ID_LEVEL_COLLECT_DOWN:
3408     case GADGET_ID_LEVEL_COLLECT_UP:
3409       step *= (id == GADGET_ID_LEVEL_COLLECT_DOWN ? -1 : 1);
3410       ModifyEditorCounter(ED_COUNTER_ID_LEVEL_COLLECT,
3411                           *gadget_level_collect_value + step);
3412       break;
3413     case GADGET_ID_LEVEL_COLLECT_TEXT:
3414       *gadget_level_collect_value = gi->text.number_value;
3415       break;
3416
3417     case GADGET_ID_LEVEL_TIMELIMIT_DOWN:
3418     case GADGET_ID_LEVEL_TIMELIMIT_UP:
3419       step *= (id == GADGET_ID_LEVEL_TIMELIMIT_DOWN ? -1 : 1);
3420       ModifyEditorCounter(ED_COUNTER_ID_LEVEL_TIMELIMIT,
3421                           *gadget_level_timelimit_value + step);
3422       break;
3423     case GADGET_ID_LEVEL_TIMELIMIT_TEXT:
3424       *gadget_level_timelimit_value = gi->text.number_value;
3425       break;
3426
3427     case GADGET_ID_LEVEL_TIMESCORE_DOWN:
3428     case GADGET_ID_LEVEL_TIMESCORE_UP:
3429       step *= (id == GADGET_ID_LEVEL_TIMESCORE_DOWN ? -1 : 1);
3430       ModifyEditorCounter(ED_COUNTER_ID_LEVEL_TIMESCORE,
3431                           *gadget_level_timescore_value + step);
3432       break;
3433     case GADGET_ID_LEVEL_TIMESCORE_TEXT:
3434       *gadget_level_timescore_value = gi->text.number_value;
3435       break;
3436
3437     default:
3438       break;
3439   }
3440 }
3441
3442 static void HandleTextInputGadgets(struct GadgetInfo *gi)
3443 {
3444   int id = gi->custom_id;
3445
3446   switch (id)
3447   {
3448     case GADGET_ID_LEVEL_NAME:
3449       strcpy(level.name, gi->text.value);
3450       break;
3451
3452     case GADGET_ID_LEVEL_AUTHOR:
3453       strcpy(level.author, gi->text.value);
3454       break;
3455
3456     default:
3457       break;
3458   }
3459 }
3460
3461 static void HandleControlButtons(struct GadgetInfo *gi)
3462 {
3463   int id = gi->custom_id;
3464   int button = gi->event.button;
3465   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
3466   int new_element = BUTTON_ELEMENT(button);
3467   int player_present = FALSE;
3468   int level_changed = FALSE;
3469   int i, x, y;
3470
3471   if (edit_mode == ED_MODE_DRAWING && drawing_function == GADGET_ID_TEXT)
3472     DrawLevelText(0, 0, 0, TEXT_END);
3473
3474   if (id < ED_NUM_CTRL1_BUTTONS && id != GADGET_ID_PROPERTIES &&
3475       edit_mode != ED_MODE_DRAWING)
3476   {
3477     DrawDrawingWindow();
3478     edit_mode = ED_MODE_DRAWING;
3479   }
3480
3481   switch (id)
3482   {
3483     case GADGET_ID_SCROLL_LEFT:
3484       if (level_xpos >= 0)
3485       {
3486         int gadget_id = GADGET_ID_SCROLL_HORIZONTAL;
3487         struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3488
3489         if (lev_fieldx < ed_fieldx - 2)
3490           break;
3491
3492         level_xpos -= step;
3493         if (level_xpos < -1)
3494           level_xpos = -1;
3495         if (button == 1)
3496           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_RIGHT);
3497         else
3498           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3499
3500         ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
3501       }
3502       break;
3503
3504     case GADGET_ID_SCROLL_RIGHT:
3505       if (level_xpos <= lev_fieldx - ed_fieldx)
3506       {
3507         int gadget_id = GADGET_ID_SCROLL_HORIZONTAL;
3508         struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3509
3510         if (lev_fieldx < ed_fieldx - 2)
3511           break;
3512
3513         level_xpos += step;
3514         if (level_xpos > lev_fieldx - ed_fieldx + 1)
3515           level_xpos = lev_fieldx - ed_fieldx + 1;
3516         if (button == 1)
3517           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_LEFT);
3518         else
3519           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3520
3521         ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_xpos + 1, GDI_END);
3522       }
3523       break;
3524
3525     case GADGET_ID_SCROLL_UP:
3526       if (level_ypos >= 0)
3527       {
3528         int gadget_id = GADGET_ID_SCROLL_VERTICAL;
3529         struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3530
3531         if (lev_fieldy < ed_fieldy - 2)
3532           break;
3533
3534         level_ypos -= step;
3535         if (level_ypos < -1)
3536           level_ypos = -1;
3537         if (button == 1)
3538           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_DOWN);
3539         else
3540           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3541
3542         ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
3543       }
3544       break;
3545
3546     case GADGET_ID_SCROLL_DOWN:
3547       if (level_ypos <= lev_fieldy - ed_fieldy)
3548       {
3549         int gadget_id = GADGET_ID_SCROLL_VERTICAL;
3550         struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3551
3552         if (lev_fieldy < ed_fieldy - 2)
3553           break;
3554
3555         level_ypos += step;
3556         if (level_ypos > lev_fieldy - ed_fieldy + 1)
3557           level_ypos = lev_fieldy - ed_fieldy + 1;
3558         if (button == 1)
3559           ScrollMiniLevel(level_xpos, level_ypos, ED_SCROLL_UP);
3560         else
3561           DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3562
3563         ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, level_ypos + 1, GDI_END);
3564       }
3565       break;
3566
3567     case GADGET_ID_SCROLL_HORIZONTAL:
3568       level_xpos = gi->event.item_position - 1;
3569       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3570       break;
3571
3572     case GADGET_ID_SCROLL_VERTICAL:
3573       level_ypos = gi->event.item_position - 1;
3574       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3575       break;
3576
3577     case GADGET_ID_ELEMENTLIST_UP:
3578     case GADGET_ID_ELEMENTLIST_DOWN:
3579       step *= (id == GADGET_ID_ELEMENTLIST_UP ? -1 : +1);
3580       element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
3581
3582       if (element_shift < 0)
3583         element_shift = 0;
3584       if (element_shift > elements_in_list - ED_NUM_ELEMENTLIST_BUTTONS)
3585         element_shift = elements_in_list - ED_NUM_ELEMENTLIST_BUTTONS;
3586
3587       for (i=0; i<ED_NUM_ELEMENTLIST_BUTTONS; i++)
3588       {
3589         int gadget_id = GADGET_ID_ELEMENTLIST_FIRST + i;
3590         struct GadgetInfo *gi = level_editor_gadget[gadget_id];
3591         struct GadgetDesign *gd = &gi->deco.design;
3592         int element = editor_element[element_shift + i];
3593
3594         UnmapGadget(gi);
3595         getMiniGraphicSource(el2gfx(element), &gd->pixmap, &gd->x, &gd->y);
3596         ModifyGadget(gi, GDI_INFO_TEXT, element_info[element], GDI_END);
3597         MapGadget(gi);
3598       }
3599       break;
3600
3601     case GADGET_ID_WRAP_LEFT:
3602       WrapLevel(-step, 0);
3603       break;
3604
3605     case GADGET_ID_WRAP_RIGHT:
3606       WrapLevel(step, 0);
3607       break;
3608
3609     case GADGET_ID_WRAP_UP:
3610       WrapLevel(0, -step);
3611       break;
3612
3613     case GADGET_ID_WRAP_DOWN:
3614       WrapLevel(0, step);
3615       break;
3616
3617     case GADGET_ID_SINGLE_ITEMS:
3618     case GADGET_ID_CONNECTED_ITEMS:
3619     case GADGET_ID_LINE:
3620     case GADGET_ID_ARC:
3621     case GADGET_ID_TEXT:
3622     case GADGET_ID_RECTANGLE:
3623     case GADGET_ID_FILLED_BOX:
3624     case GADGET_ID_FLOOD_FILL:
3625     case GADGET_ID_GRAB_BRUSH:
3626     case GADGET_ID_PICK_ELEMENT:
3627       last_drawing_function = drawing_function;
3628       drawing_function = id;
3629       draw_with_brush = FALSE;
3630       break;
3631
3632     case GADGET_ID_RANDOM_PLACEMENT:
3633       RandomPlacement(new_element);
3634       break;
3635
3636     case GADGET_ID_PROPERTIES:
3637       if (edit_mode != ED_MODE_PROPERTIES)
3638       {
3639         properties_element = new_element;
3640         DrawPropertiesWindow();
3641         edit_mode = ED_MODE_PROPERTIES;
3642       }
3643       else
3644       {
3645         DrawDrawingWindow();
3646         edit_mode = ED_MODE_DRAWING;
3647       }
3648       break;
3649
3650     case GADGET_ID_UNDO:
3651       if (undo_buffer_steps == 0)
3652       {
3653         Request("Undo buffer empty !", REQ_CONFIRM);
3654         break;
3655       }
3656
3657       undo_buffer_position =
3658         (undo_buffer_position - 1 + NUM_UNDO_STEPS) % NUM_UNDO_STEPS;
3659       undo_buffer_steps--;
3660
3661       for(x=0; x<lev_fieldx; x++)
3662         for(y=0; y<lev_fieldy; y++)
3663           Feld[x][y] = UndoBuffer[undo_buffer_position][x][y];
3664       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos,level_ypos);
3665       break;
3666
3667     case GADGET_ID_INFO:
3668       if (edit_mode != ED_MODE_INFO)
3669       {
3670         DrawLevelInfoWindow();
3671         edit_mode = ED_MODE_INFO;
3672       }
3673       else
3674       {
3675         DrawDrawingWindow();
3676         edit_mode = ED_MODE_DRAWING;
3677       }
3678       break;
3679
3680     case GADGET_ID_CLEAR:
3681       for(x=0; x<MAX_LEV_FIELDX; x++) 
3682         for(y=0; y<MAX_LEV_FIELDY; y++) 
3683           Feld[x][y] = (button == 1 ? EL_LEERRAUM : new_element);
3684       CopyLevelToUndoBuffer(GADGET_ID_CLEAR);
3685
3686       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
3687       break;
3688
3689     case GADGET_ID_SAVE:
3690       if (leveldir[leveldir_nr].readonly)
3691       {
3692         Request("This level is read only !", REQ_CONFIRM);
3693         break;
3694       }
3695
3696       for(y=0; y<lev_fieldy; y++) 
3697         for(x=0; x<lev_fieldx; x++)
3698           if (Feld[x][y] != Ur[x][y])
3699             level_changed = TRUE;
3700
3701       if (0 && !level_changed)
3702       {
3703         Request("Level has not changed !", REQ_CONFIRM);
3704         break;
3705       }
3706
3707       for(y=0; y<lev_fieldy; y++) 
3708         for(x=0; x<lev_fieldx; x++)
3709           if (Feld[x][y] == EL_SPIELFIGUR ||
3710               Feld[x][y] == EL_SPIELER1 ||
3711               Feld[x][y] == EL_SP_MURPHY) 
3712             player_present = TRUE;
3713
3714       if (!player_present)
3715         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
3716       else
3717       {
3718         if (Request("Save this level and kill the old ?", REQ_ASK))
3719         {
3720           for(x=0; x<lev_fieldx; x++)
3721             for(y=0; y<lev_fieldy; y++) 
3722               Ur[x][y] = Feld[x][y];
3723           SaveLevel(level_nr);
3724         }
3725       }
3726       break;
3727
3728     case GADGET_ID_TEST:
3729       for(y=0; y<lev_fieldy; y++) 
3730         for(x=0; x<lev_fieldx; x++)
3731           if (Feld[x][y] == EL_SPIELFIGUR ||
3732               Feld[x][y] == EL_SPIELER1 ||
3733               Feld[x][y] == EL_SP_MURPHY) 
3734             player_present = TRUE;
3735
3736       if (!player_present)
3737         Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
3738       else
3739       {
3740         for(x=0; x<lev_fieldx; x++)
3741           for(y=0; y<lev_fieldy; y++)
3742             FieldBackup[x][y] = Ur[x][y];
3743
3744         for(x=0; x<lev_fieldx; x++)
3745           for(y=0; y<lev_fieldy; y++)
3746             Ur[x][y] = Feld[x][y];
3747
3748         UnmapLevelEditorGadgets();
3749
3750         /* draw smaller door */
3751         XCopyArea(display, pix[PIX_DOOR], drawto, gc,
3752                   DOOR_GFX_PAGEX7, 64,
3753                   108, 64,
3754                   EX - 4, EY - 12);
3755         redraw_mask |= REDRAW_ALL;
3756
3757         CloseDoor(DOOR_CLOSE_ALL);
3758
3759         DrawCompleteVideoDisplay();
3760
3761         if (setup.autorecord)
3762           TapeStartRecording();
3763
3764         level_editor_test_game = TRUE;
3765         game_status = PLAYING;
3766
3767         InitGame();
3768       }
3769       break;
3770
3771     case GADGET_ID_EXIT:
3772       for(y=0; y<lev_fieldy; y++) 
3773         for(x=0; x<lev_fieldx; x++)
3774           if (Feld[x][y] != Ur[x][y])
3775             level_changed = TRUE;
3776
3777       if (!level_changed ||
3778           Request("Level has changed! Exit without saving ?",
3779                   REQ_ASK | REQ_STAY_OPEN))
3780       {
3781         CloseDoor(DOOR_CLOSE_1);
3782
3783         /*
3784         CloseDoor(DOOR_CLOSE_ALL);
3785         */
3786
3787         /* draw smaller door */
3788         XCopyArea(display, pix[PIX_DOOR], drawto, gc,
3789                   DOOR_GFX_PAGEX7, 64,
3790                   108, 64,
3791                   EX - 4, EY - 12);
3792         redraw_mask |= REDRAW_ALL;
3793
3794         game_status = MAINMENU;
3795         DrawMainMenu();
3796       }
3797       else
3798       {
3799         CloseDoor(DOOR_CLOSE_1);
3800         XCopyArea(display, pix[PIX_DB_DOOR], pix[PIX_DB_DOOR], gc,
3801                   DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
3802                   DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3803         OpenDoor(DOOR_OPEN_1);
3804       }
3805       break;
3806
3807     case GADGET_ID_RANDOM_PERCENTAGE:
3808       *radiobutton_info[ED_RADIOBUTTON_ID_PERCENTAGE].value =
3809         radiobutton_info[ED_RADIOBUTTON_ID_PERCENTAGE].checked_value;
3810       break;
3811
3812     case GADGET_ID_RANDOM_QUANTITY:
3813       *radiobutton_info[ED_RADIOBUTTON_ID_QUANTITY].value =
3814         radiobutton_info[ED_RADIOBUTTON_ID_QUANTITY].checked_value;
3815       break;
3816
3817     case GADGET_ID_RANDOM_RESTRICTED:
3818       *checkbutton_info[ED_CHECKBUTTON_ID_RANDOM_RESTRICTED].value ^= TRUE;
3819       break;
3820
3821     case GADGET_ID_DOUBLE_SPEED:
3822       *checkbutton_info[ED_CHECKBUTTON_ID_DOUBLE_SPEED].value ^= TRUE;
3823       break;
3824
3825     case GADGET_ID_GRAVITY:
3826       *checkbutton_info[ED_CHECKBUTTON_ID_GRAVITY].value ^= TRUE;
3827       break;
3828
3829     case GADGET_ID_STICK_ELEMENT:
3830       *checkbutton_info[ED_CHECKBUTTON_ID_STICK_ELEMENT].value ^= TRUE;
3831       break;
3832
3833     default:
3834       if (id >= GADGET_ID_ELEMENTLIST_FIRST &&
3835           id <= GADGET_ID_ELEMENTLIST_LAST)
3836       {
3837         int element_position = id - GADGET_ID_ELEMENTLIST_FIRST;
3838         int new_element = editor_element[element_position + element_shift];
3839
3840         PickDrawingElement(button, new_element);
3841
3842         if (!HAS_CONTENT(properties_element) ||
3843             !stick_element_properties_window)
3844         {
3845           properties_element = new_element;
3846           if (edit_mode == ED_MODE_PROPERTIES)
3847             DrawPropertiesWindow();
3848         }
3849
3850         if (drawing_function == GADGET_ID_PICK_ELEMENT)
3851           ClickOnGadget(level_editor_gadget[last_drawing_function], MB_LEFT);
3852       }
3853 #ifdef DEBUG
3854       else if (gi->event.type == GD_EVENT_PRESSED)
3855         printf("default: HandleControlButtons: GD_EVENT_PRESSED(%d)\n", id);
3856       else if (gi->event.type == GD_EVENT_RELEASED)
3857         printf("default: HandleControlButtons: GD_EVENT_RELEASED(%d)\n", id);
3858       else if (gi->event.type == GD_EVENT_MOVING)
3859         printf("default: HandleControlButtons: GD_EVENT_MOVING(%d)\n", id);
3860       else
3861         printf("default: HandleControlButtons: ? (id == %d)\n", id);
3862 #endif
3863       break;
3864   }
3865 }
3866
3867 void HandleLevelEditorKeyInput(KeySym key)
3868 {
3869   char letter = getCharFromKeySym(key);
3870   int button = MB_LEFT;
3871
3872   if (drawing_function == GADGET_ID_TEXT &&
3873       DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE)
3874   {
3875     if (letter)
3876       DrawLevelText(0, 0, letter, TEXT_WRITECHAR);
3877     else if (key == XK_Delete || key == XK_BackSpace)
3878       DrawLevelText(0, 0, 0, TEXT_BACKSPACE);
3879     else if (key == XK_Return)
3880       DrawLevelText(0, 0, 0, TEXT_NEWLINE);
3881   }
3882   else if (button_status == MB_RELEASED)
3883   {
3884     int i, id;
3885
3886     switch (key)
3887     {
3888       case XK_Left:
3889         id = GADGET_ID_SCROLL_LEFT;
3890         break;
3891       case XK_Right:
3892         id = GADGET_ID_SCROLL_RIGHT;
3893         break;
3894       case XK_Up:
3895         id = GADGET_ID_SCROLL_UP;
3896         break;
3897       case XK_Down:
3898         id = GADGET_ID_SCROLL_DOWN;
3899         break;
3900       case XK_Page_Up:
3901         id = GADGET_ID_ELEMENTLIST_UP;
3902         button = 3;
3903         break;
3904       case XK_Page_Down:
3905         id = GADGET_ID_ELEMENTLIST_DOWN;
3906         button = 3;
3907         break;
3908
3909       default:
3910         id = GADGET_ID_NONE;
3911         break;
3912     }
3913
3914     if (id != GADGET_ID_NONE)
3915       ClickOnGadget(level_editor_gadget[id], button);
3916     else if (letter == '.')
3917       ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
3918     else
3919       for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
3920         if (letter && letter == control_info[i].shortcut)
3921           if (!anyTextGadgetActive())
3922             ClickOnGadget(level_editor_gadget[i], button);
3923   }
3924 }
3925
3926 void ClearEditorGadgetInfoText()
3927 {
3928   XFillRectangle(display, drawto, gc,
3929                  INFOTEXT_XPOS, INFOTEXT_YPOS, INFOTEXT_XSIZE, INFOTEXT_YSIZE);
3930   redraw_mask |= REDRAW_FIELD;
3931 }
3932
3933 void HandleEditorGadgetInfoText(void *ptr)
3934 {
3935   struct GadgetInfo *gi = (struct GadgetInfo *)ptr;
3936   char infotext[MAX_INFOTEXT_LEN + 1];
3937   char shortcut[20];
3938
3939   ClearEditorGadgetInfoText();
3940
3941   /* misuse this function to delete brush cursor, if needed */
3942   if (edit_mode == ED_MODE_DRAWING && draw_with_brush)
3943     DeleteBrushFromCursor();
3944
3945   if (gi == NULL || gi->info_text == NULL)
3946     return;
3947
3948   strncpy(infotext, gi->info_text, MAX_INFOTEXT_LEN);
3949   infotext[MAX_INFOTEXT_LEN] = '\0';
3950
3951   if (gi->custom_id < ED_NUM_CTRL_BUTTONS)
3952   {
3953     int key = control_info[gi->custom_id].shortcut;
3954
3955     if (key)
3956     {
3957       sprintf(shortcut, " ('%s%c')",
3958               (key >= 'A' && key <= 'Z' ? "Shift-" :
3959                gi->custom_id == GADGET_ID_SINGLE_ITEMS ? ".' or '" : ""),
3960               key);
3961
3962       if (strlen(infotext) + strlen(shortcut) <= MAX_INFOTEXT_LEN)
3963         strcat(infotext, shortcut);
3964     }
3965   }
3966
3967   DrawText(INFOTEXT_XPOS, INFOTEXT_YPOS, infotext, FS_SMALL, FC_YELLOW);
3968 }
3969
3970 static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
3971 {
3972   static int start_lx, start_ly;
3973   char *infotext;
3974   int id = gi->custom_id;
3975   int sx = gi->event.x;
3976   int sy = gi->event.y;
3977   int lx = sx + level_xpos;
3978   int ly = sy + level_ypos;
3979   int min_sx = 0, min_sy = 0;
3980   int max_sx = gi->drawing.area_xsize - 1;
3981   int max_sy = gi->drawing.area_ysize - 1;
3982
3983   ClearEditorGadgetInfoText();
3984
3985   /* make sure to stay inside drawing area boundaries */
3986   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
3987   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
3988
3989   if (id == GADGET_ID_DRAWING_LEVEL)
3990   {
3991     if (button_status)
3992     {
3993       int min_lx = 0, min_ly = 0;
3994       int max_lx = lev_fieldx - 1, max_ly = lev_fieldy - 1;
3995
3996       /* get positions inside level field */
3997       lx = sx + level_xpos;
3998       ly = sy + level_ypos;
3999
4000       /* make sure to stay inside level field boundaries */
4001       lx = (lx < min_lx ? min_lx : lx > max_lx ? max_lx : lx);
4002       ly = (ly < min_ly ? min_ly : ly > max_ly ? max_ly : ly);
4003
4004       /* correct drawing area positions accordingly */
4005       sx = lx - level_xpos;
4006       sy = ly - level_ypos;
4007     }
4008
4009     if (IN_ED_FIELD(sx,sy) && IN_LEV_FIELD(lx, ly))
4010     {
4011       if (button_status)        /* if (gi->state == GD_BUTTON_PRESSED) */
4012       {
4013         if (gi->event.type == GD_EVENT_PRESSED)
4014         {
4015           start_lx = lx;
4016           start_ly = ly;
4017         }
4018
4019         switch (drawing_function)
4020         {
4021           case GADGET_ID_SINGLE_ITEMS:
4022             infotext = "Drawing single items";
4023             break;
4024           case GADGET_ID_CONNECTED_ITEMS:
4025             infotext = "Drawing connected items";
4026             break;
4027           case GADGET_ID_LINE:
4028             infotext = "Drawing line";
4029             break;
4030           case GADGET_ID_ARC:
4031             infotext = "Drawing arc";
4032             break;
4033           case GADGET_ID_TEXT:
4034             infotext = "Setting text cursor";
4035             break;
4036           case GADGET_ID_RECTANGLE:
4037             infotext = "Drawing rectangle";
4038             break;
4039           case GADGET_ID_FILLED_BOX:
4040             infotext = "Drawing filled box";
4041             break;
4042           case GADGET_ID_FLOOD_FILL:
4043             infotext = "Flood fill";
4044             break;
4045           case GADGET_ID_GRAB_BRUSH:
4046             infotext = "Grabbing brush";
4047             break;
4048           case GADGET_ID_PICK_ELEMENT:
4049             infotext = "Picking element";
4050             break;
4051
4052           default:
4053             infotext = "Drawing position";
4054             break;
4055         }
4056
4057         if (drawing_function == GADGET_ID_PICK_ELEMENT)
4058           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4059                     "%s: %d, %d", infotext, lx, ly);
4060         else
4061           DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4062                     "%s: %d, %d", infotext,
4063                     ABS(lx - start_lx) + 1, ABS(ly - start_ly) + 1);
4064       }
4065       else if (drawing_function == GADGET_ID_PICK_ELEMENT)
4066         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4067                   "%s", element_info[Feld[lx][ly]]);
4068       else
4069         DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4070                   "Level position: %d, %d", lx, ly);
4071     }
4072
4073     /* misuse this function to draw brush cursor, if needed */
4074     if (edit_mode == ED_MODE_DRAWING && draw_with_brush && !button_status)
4075     {
4076       if (IN_ED_FIELD(sx, sy) && IN_LEV_FIELD(lx, ly))
4077         CopyBrushToCursor(sx, sy);
4078       else
4079         DeleteBrushFromCursor();
4080     }
4081   }
4082   else if (id == GADGET_ID_AMOEBA_CONTENT)
4083     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4084               "Amoeba content");
4085   else if (id == GADGET_ID_RANDOM_BACKGROUND)
4086     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4087               "Random placement background");
4088   else
4089     DrawTextF(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY, FC_YELLOW,
4090               "Content area %d position: %d, %d",
4091               id - GADGET_ID_ELEM_CONTENT_0 + 1, sx, sy);
4092 }