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