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