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