1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-98 Artsoft Entertainment *
8 * phone: ++49 +521 290471 *
9 * email: aeglos@valinor.owl.de *
10 *----------------------------------------------------------*
12 ***********************************************************/
22 /* some positions in the video tape control window */
23 #define VIDEO_DATE_LABEL_XPOS (VIDEO_DISPLAY1_XPOS)
24 #define VIDEO_DATE_LABEL_YPOS (VIDEO_DISPLAY1_YPOS)
25 #define VIDEO_DATE_LABEL_XSIZE (VIDEO_DISPLAY_XSIZE)
26 #define VIDEO_DATE_LABEL_YSIZE (VIDEO_DISPLAY_YSIZE)
27 #define VIDEO_DATE_XPOS (VIDEO_DISPLAY1_XPOS + 1)
28 #define VIDEO_DATE_YPOS (VIDEO_DISPLAY1_YPOS + 14)
29 #define VIDEO_DATE_XSIZE (VIDEO_DISPLAY_XSIZE)
30 #define VIDEO_DATE_YSIZE 16
31 #define VIDEO_REC_LABEL_XPOS (VIDEO_DISPLAY2_XPOS)
32 #define VIDEO_REC_LABEL_YPOS (VIDEO_DISPLAY2_YPOS)
33 #define VIDEO_REC_LABEL_XSIZE 20
34 #define VIDEO_REC_LABEL_YSIZE 12
35 #define VIDEO_REC_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS + 20)
36 #define VIDEO_REC_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
37 #define VIDEO_REC_SYMBOL_XSIZE 16
38 #define VIDEO_REC_SYMBOL_YSIZE 16
39 #define VIDEO_PLAY_LABEL_XPOS (VIDEO_DISPLAY2_XPOS + 65)
40 #define VIDEO_PLAY_LABEL_YPOS (VIDEO_DISPLAY2_YPOS)
41 #define VIDEO_PLAY_LABEL_XSIZE 22
42 #define VIDEO_PLAY_LABEL_YSIZE 12
43 #define VIDEO_PLAY_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS + 52)
44 #define VIDEO_PLAY_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
45 #define VIDEO_PLAY_SYMBOL_XSIZE 11
46 #define VIDEO_PLAY_SYMBOL_YSIZE 13
47 #define VIDEO_PAUSE_LABEL_XPOS (VIDEO_DISPLAY2_XPOS)
48 #define VIDEO_PAUSE_LABEL_YPOS (VIDEO_DISPLAY2_YPOS + 20)
49 #define VIDEO_PAUSE_LABEL_XSIZE 35
50 #define VIDEO_PAUSE_LABEL_YSIZE 8
51 #define VIDEO_PAUSE_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS + 35)
52 #define VIDEO_PAUSE_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
53 #define VIDEO_PAUSE_SYMBOL_XSIZE 17
54 #define VIDEO_PAUSE_SYMBOL_YSIZE 13
55 #define VIDEO_TIME_XPOS (VIDEO_DISPLAY2_XPOS + 38)
56 #define VIDEO_TIME_YPOS (VIDEO_DISPLAY2_YPOS + 14)
57 #define VIDEO_TIME_XSIZE 50
58 #define VIDEO_TIME_YSIZE 16
61 #define VIDEO_PBEND_LABEL_XPOS 6
62 #define VIDEO_PBEND_LABEL_YPOS 220
63 #define VIDEO_PBEND_LABEL_XSIZE 35
64 #define VIDEO_PBEND_LABEL_YSIZE 30
66 #define VIDEO_STATE_OFF (VIDEO_STATE_PLAY_OFF | \
67 VIDEO_STATE_REC_OFF | \
68 VIDEO_STATE_PAUSE_OFF | \
69 VIDEO_STATE_FFWD_OFF | \
70 VIDEO_STATE_PBEND_OFF | \
71 VIDEO_STATE_DATE_OFF | \
73 #define VIDEO_PRESS_OFF (VIDEO_PRESS_PLAY_OFF | \
74 VIDEO_PRESS_REC_OFF | \
75 VIDEO_PRESS_PAUSE_OFF | \
76 VIDEO_PRESS_STOP_OFF | \
77 VIDEO_PRESS_EJECT_OFF)
78 #define VIDEO_ALL_OFF (VIDEO_STATE_OFF | VIDEO_PRESS_OFF)
80 #define VIDEO_STATE_ON (VIDEO_STATE_PLAY_ON | \
81 VIDEO_STATE_REC_ON | \
82 VIDEO_STATE_PAUSE_ON | \
83 VIDEO_STATE_FFWD_ON | \
84 VIDEO_STATE_PBEND_ON | \
85 VIDEO_STATE_DATE_ON | \
87 #define VIDEO_PRESS_ON (VIDEO_PRESS_PLAY_ON | \
88 VIDEO_PRESS_REC_ON | \
89 VIDEO_PRESS_PAUSE_ON | \
90 VIDEO_PRESS_STOP_ON | \
92 #define VIDEO_ALL_ON (VIDEO_STATE_ON | VIDEO_PRESS_ON)
94 #define VIDEO_STATE (VIDEO_STATE_ON | VIDEO_STATE_OFF)
95 #define VIDEO_PRESS (VIDEO_PRESS_ON | VIDEO_PRESS_OFF)
96 #define VIDEO_ALL (VIDEO_ALL_ON | VIDEO_ALL_OFF)
99 void DrawVideoDisplay(unsigned long state, unsigned long value)
102 int part_label = 0, part_symbol = 1;
103 int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
104 static char *monatsname[12] =
106 "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
107 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
109 static int video_pos[5][2][4] =
111 {{ VIDEO_PLAY_LABEL_XPOS, VIDEO_PLAY_LABEL_YPOS,
112 VIDEO_PLAY_LABEL_XSIZE,VIDEO_PLAY_LABEL_YSIZE },
113 { VIDEO_PLAY_SYMBOL_XPOS, VIDEO_PLAY_SYMBOL_YPOS,
114 VIDEO_PLAY_SYMBOL_XSIZE,VIDEO_PLAY_SYMBOL_YSIZE }},
116 {{ VIDEO_REC_LABEL_XPOS, VIDEO_REC_LABEL_YPOS,
117 VIDEO_REC_LABEL_XSIZE,VIDEO_REC_LABEL_YSIZE },
118 { VIDEO_REC_SYMBOL_XPOS, VIDEO_REC_SYMBOL_YPOS,
119 VIDEO_REC_SYMBOL_XSIZE,VIDEO_REC_SYMBOL_YSIZE }},
121 {{ VIDEO_PAUSE_LABEL_XPOS, VIDEO_PAUSE_LABEL_YPOS,
122 VIDEO_PAUSE_LABEL_XSIZE,VIDEO_PAUSE_LABEL_YSIZE },
123 { VIDEO_PAUSE_SYMBOL_XPOS, VIDEO_PAUSE_SYMBOL_YPOS,
124 VIDEO_PAUSE_SYMBOL_XSIZE,VIDEO_PAUSE_SYMBOL_YSIZE }},
126 {{ VIDEO_DATE_LABEL_XPOS, VIDEO_DATE_LABEL_YPOS,
127 VIDEO_DATE_LABEL_XSIZE,VIDEO_DATE_LABEL_YSIZE },
128 { VIDEO_DATE_XPOS, VIDEO_DATE_YPOS,
129 VIDEO_DATE_XSIZE,VIDEO_DATE_YSIZE }},
133 { VIDEO_TIME_XPOS, VIDEO_TIME_YPOS,
134 VIDEO_TIME_XSIZE,VIDEO_TIME_YSIZE }}
137 if (state & VIDEO_STATE_PBEND_OFF)
139 int cx = DOOR_GFX_PAGEX3, cy = DOOR_GFX_PAGEY2;
141 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
142 cx + VIDEO_REC_LABEL_XPOS,
143 cy + VIDEO_REC_LABEL_YPOS,
144 VIDEO_PBEND_LABEL_XSIZE,
145 VIDEO_PBEND_LABEL_YSIZE,
146 VX + VIDEO_REC_LABEL_XPOS,
147 VY + VIDEO_REC_LABEL_YPOS);
154 int pos = i/2, cx, cy = DOOR_GFX_PAGEY2;
156 if (i%2) /* i ungerade => STATE_ON / PRESS_OFF */
157 cx = DOOR_GFX_PAGEX4;
159 cx = DOOR_GFX_PAGEX3; /* i gerade => STATE_OFF / PRESS_ON */
161 if (video_pos[pos][part_label][0] && value != VIDEO_DISPLAY_SYMBOL_ONLY)
162 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
163 cx + video_pos[pos][part_label][xpos],
164 cy + video_pos[pos][part_label][ypos],
165 video_pos[pos][part_label][xsize],
166 video_pos[pos][part_label][ysize],
167 VX + video_pos[pos][part_label][xpos],
168 VY + video_pos[pos][part_label][ypos]);
169 if (video_pos[pos][part_symbol][0] && value != VIDEO_DISPLAY_LABEL_ONLY)
170 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
171 cx + video_pos[pos][part_symbol][xpos],
172 cy + video_pos[pos][part_symbol][ypos],
173 video_pos[pos][part_symbol][xsize],
174 video_pos[pos][part_symbol][ysize],
175 VX + video_pos[pos][part_symbol][xpos],
176 VY + video_pos[pos][part_symbol][ypos]);
180 if (state & VIDEO_STATE_FFWD_ON)
182 int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY2;
184 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
185 cx + VIDEO_PLAY_SYMBOL_XPOS,
186 cy + VIDEO_PLAY_SYMBOL_YPOS,
187 VIDEO_PLAY_SYMBOL_XSIZE - 2,
188 VIDEO_PLAY_SYMBOL_YSIZE,
189 VX + VIDEO_PLAY_SYMBOL_XPOS - 9,
190 VY + VIDEO_PLAY_SYMBOL_YPOS);
193 if (state & VIDEO_STATE_PBEND_ON)
195 int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY1;
197 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
198 cx + VIDEO_PBEND_LABEL_XPOS,
199 cy + VIDEO_PBEND_LABEL_YPOS,
200 VIDEO_PBEND_LABEL_XSIZE,
201 VIDEO_PBEND_LABEL_YSIZE,
202 VX + VIDEO_REC_LABEL_XPOS,
203 VY + VIDEO_REC_LABEL_YPOS);
206 if (state & VIDEO_STATE_DATE_ON)
208 int tag = value % 100;
209 int monat = (value/100) % 100;
210 int jahr = (value/10000);
212 DrawText(VX+VIDEO_DATE_XPOS,VY+VIDEO_DATE_YPOS,
213 int2str(tag,2),FS_SMALL,FC_SPECIAL1);
214 DrawText(VX+VIDEO_DATE_XPOS+27,VY+VIDEO_DATE_YPOS,
215 monatsname[monat],FS_SMALL,FC_SPECIAL1);
216 DrawText(VX+VIDEO_DATE_XPOS+64,VY+VIDEO_DATE_YPOS,
217 int2str(jahr,2),FS_SMALL,FC_SPECIAL1);
220 if (state & VIDEO_STATE_TIME_ON)
222 int min = value / 60;
223 int sec = value % 60;
225 DrawText(VX+VIDEO_TIME_XPOS,VY+VIDEO_TIME_YPOS,
226 int2str(min,2),FS_SMALL,FC_SPECIAL1);
227 DrawText(VX+VIDEO_TIME_XPOS+27,VY+VIDEO_TIME_YPOS,
228 int2str(sec,2),FS_SMALL,FC_SPECIAL1);
231 if (state & VIDEO_STATE_DATE)
232 redraw_mask |= REDRAW_VIDEO_1;
233 if ((state & ~VIDEO_STATE_DATE) & VIDEO_STATE)
234 redraw_mask |= REDRAW_VIDEO_2;
235 if (state & VIDEO_PRESS)
236 redraw_mask |= REDRAW_VIDEO_3;
239 void DrawCompleteVideoDisplay()
241 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
242 DOOR_GFX_PAGEX3,DOOR_GFX_PAGEY2, VXSIZE,VYSIZE, VX,VY);
243 XCopyArea(display,pix[PIX_DOOR],drawto,gc,
244 DOOR_GFX_PAGEX4+VIDEO_CONTROL_XPOS,
245 DOOR_GFX_PAGEY2+VIDEO_CONTROL_YPOS,
246 VIDEO_CONTROL_XSIZE,VIDEO_CONTROL_YSIZE,
247 VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
249 DrawVideoDisplay(VIDEO_ALL_OFF,0);
250 if (tape.date && tape.length)
252 DrawVideoDisplay(VIDEO_STATE_DATE_ON,tape.date);
253 DrawVideoDisplay(VIDEO_STATE_TIME_ON,tape.length_seconds);
256 XCopyArea(display,drawto,pix[PIX_DB_DOOR],gc,
257 VX,VY, VXSIZE,VYSIZE, DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY2);
261 /* NEW GADGET STUFF -------------------------------------------------------- */
264 /* values for DrawGadget() */
265 #define DG_UNPRESSED 0
267 #define DG_BUFFERED 0
270 static struct GadgetInfo *gadget_list_first_entry = NULL;
271 static struct GadgetInfo *gadget_list_last_entry = NULL;
272 static int next_free_gadget_id = 1;
273 static boolean gadget_id_wrapped = FALSE;
275 static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
277 struct GadgetInfo *gi = gadget_list_first_entry;
279 while (gi && gi->id != id)
285 static int getNewGadgetID()
287 int id = next_free_gadget_id++;
289 if (next_free_gadget_id <= 0) /* counter overrun */
291 gadget_id_wrapped = TRUE; /* now we must check each ID */
292 next_free_gadget_id = 0;
295 if (gadget_id_wrapped)
297 next_free_gadget_id++;
298 while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
299 next_free_gadget_id++;
302 if (next_free_gadget_id <= 0) /* cannot get new gadget id */
303 Error(ERR_EXIT, "too much gadgets -- this should not happen");
308 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
310 struct GadgetInfo *gi = gadget_list_first_entry;
315 mx >= gi->x && mx < gi->x + gi->width &&
316 my >= gi->y && my < gi->y + gi->height)
325 static void default_callback_info(void *ptr)
327 if (game_status == LEVELED)
328 HandleEditorGadgetInfoText(ptr);
331 static void default_callback_action(void *ptr)
336 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
338 int state = (pressed ? 1 : 0);
339 struct GadgetDesign *gd = (gi->checked ?
340 &gi->alt_design[state] :
345 case GD_TYPE_NORMAL_BUTTON:
346 case GD_TYPE_CHECK_BUTTON:
347 case GD_TYPE_RADIO_BUTTON:
348 XCopyArea(display, gd->pixmap, drawto, gc,
349 gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
350 if (gi->deco.design.pixmap)
351 XCopyArea(display, gi->deco.design.pixmap, drawto, gc,
352 gi->deco.design.x, gi->deco.design.y,
353 gi->deco.width, gi->deco.height,
354 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
355 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
358 case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
359 case GD_TYPE_TEXTINPUT_NUMERIC:
363 char cursor_string[3];
364 char text[MAX_GADGET_TEXTSIZE + 1];
365 int font_type = gi->text.font_type;
366 int font_width = getFontWidth(FS_SMALL, font_type);
367 int border = gi->border.size;
368 strcpy(text, gi->text.value);
371 /* left part of gadget */
372 XCopyArea(display, gd->pixmap, drawto, gc,
373 gd->x, gd->y, border, gi->height, gi->x, gi->y);
375 /* middle part of gadget */
376 for (i=0; i<=gi->text.size; i++)
377 XCopyArea(display, gd->pixmap, drawto, gc,
378 gd->x + border, gd->y, font_width, gi->height,
379 gi->x + border + i * font_width, gi->y);
381 /* right part of gadget */
382 XCopyArea(display, gd->pixmap, drawto, gc,
383 gd->x + gi->border.width - border, gd->y,
384 border, gi->height, gi->x + gi->width - border, gi->y);
386 /* gadget text value */
387 DrawText(gi->x + border, gi->y + border, text, FS_SMALL, font_type);
389 cursor_letter = gi->text.value[gi->text.cursor_position];
390 cursor_string[0] = '~';
391 cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
392 cursor_string[2] = '\0';
394 /* draw cursor, if active */
396 DrawText(gi->x + border + gi->text.cursor_position * font_width,
397 gi->y + border, cursor_string, FS_SMALL, font_type);
401 case GD_TYPE_SCROLLBAR_VERTICAL:
405 int ypos = gi->y + gi->scrollbar.position;
406 int design_full = gi->width;
407 int design_body = design_full - 2 * gi->border.size;
408 int size_full = gi->scrollbar.size;
409 int size_body = size_full - 2 * gi->border.size;
410 int num_steps = size_body / design_body;
411 int step_size_remain = size_body - num_steps * design_body;
413 /* clear scrollbar area */
414 XFillRectangle(display, backbuffer, gc,
415 gi->x, gi->y, gi->width, gi->height);
417 /* upper part of gadget */
418 XCopyArea(display, gd->pixmap, drawto, gc,
420 gi->width, gi->border.size,
423 /* middle part of gadget */
424 for (i=0; i<num_steps; i++)
425 XCopyArea(display, gd->pixmap, drawto, gc,
426 gd->x, gd->y + gi->border.size,
427 gi->width, design_body,
428 xpos, ypos + gi->border.size + i * design_body);
430 /* remaining middle part of gadget */
431 if (step_size_remain > 0)
432 XCopyArea(display, gd->pixmap, drawto, gc,
433 gd->x, gd->y + gi->border.size,
434 gi->width, step_size_remain,
435 xpos, ypos + gi->border.size + num_steps * design_body);
437 /* lower part of gadget */
438 XCopyArea(display, gd->pixmap, drawto, gc,
439 gd->x, gd->y + design_full - gi->border.size,
440 gi->width, gi->border.size,
441 xpos, ypos + size_full - gi->border.size);
445 case GD_TYPE_SCROLLBAR_HORIZONTAL:
448 int xpos = gi->x + gi->scrollbar.position;
450 int design_full = gi->height;
451 int design_body = design_full - 2 * gi->border.size;
452 int size_full = gi->scrollbar.size;
453 int size_body = size_full - 2 * gi->border.size;
454 int num_steps = size_body / design_body;
455 int step_size_remain = size_body - num_steps * design_body;
457 /* clear scrollbar area */
458 XFillRectangle(display, backbuffer, gc,
459 gi->x, gi->y, gi->width, gi->height);
461 /* left part of gadget */
462 XCopyArea(display, gd->pixmap, drawto, gc,
464 gi->border.size, gi->height,
467 /* middle part of gadget */
468 for (i=0; i<num_steps; i++)
469 XCopyArea(display, gd->pixmap, drawto, gc,
470 gd->x + gi->border.size, gd->y,
471 design_body, gi->height,
472 xpos + gi->border.size + i * design_body, ypos);
474 /* remaining middle part of gadget */
475 if (step_size_remain > 0)
476 XCopyArea(display, gd->pixmap, drawto, gc,
477 gd->x + gi->border.size, gd->y,
478 step_size_remain, gi->height,
479 xpos + gi->border.size + num_steps * design_body, ypos);
481 /* right part of gadget */
482 XCopyArea(display, gd->pixmap, drawto, gc,
483 gd->x + design_full - gi->border.size, gd->y,
484 gi->border.size, gi->height,
485 xpos + size_full - gi->border.size, ypos);
494 XCopyArea(display, drawto, window, gc,
495 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
497 redraw_mask |= (gi->x < SX + SXSIZE ? REDRAW_FIELD :
498 gi->y < DY + DYSIZE ? REDRAW_DOOR_1 :
499 gi->y > VY ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
502 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
506 while (tag != GDI_END)
511 gi->custom_id = va_arg(ap, int);
514 case GDI_CUSTOM_TYPE_ID:
515 gi->custom_type_id = va_arg(ap, int);
520 int max_textsize = MAX_INFO_TEXTSIZE - 1;
522 strncpy(gi->info_text, va_arg(ap, char *), max_textsize);
523 gi->info_text[max_textsize] = '\0';
528 gi->x = va_arg(ap, int);
532 gi->y = va_arg(ap, int);
536 gi->width = va_arg(ap, int);
540 gi->height = va_arg(ap, int);
544 gi->type = va_arg(ap, unsigned long);
548 gi->state = va_arg(ap, unsigned long);
552 gi->checked = va_arg(ap, boolean);
556 gi->radio_nr = va_arg(ap, unsigned long);
559 case GDI_NUMBER_VALUE:
560 gi->text.number_value = va_arg(ap, long);
561 sprintf(gi->text.value, "%d", gi->text.number_value);
562 gi->text.cursor_position = strlen(gi->text.value);
566 gi->text.number_min = va_arg(ap, long);
567 if (gi->text.number_value < gi->text.number_min)
569 gi->text.number_value = gi->text.number_min;
570 sprintf(gi->text.value, "%d", gi->text.number_value);
575 gi->text.number_max = va_arg(ap, long);
576 if (gi->text.number_value > gi->text.number_max)
578 gi->text.number_value = gi->text.number_max;
579 sprintf(gi->text.value, "%d", gi->text.number_value);
585 int max_textsize = MAX_GADGET_TEXTSIZE;
588 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
590 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
591 gi->text.value[max_textsize] = '\0';
592 gi->text.cursor_position = strlen(gi->text.value);
598 int tag_value = va_arg(ap, int);
599 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
601 gi->text.size = max_textsize;
602 gi->text.value[max_textsize] = '\0';
607 gi->text.font_type = va_arg(ap, int);
610 case GDI_DESIGN_UNPRESSED:
611 gi->design[GD_BUTTON_UNPRESSED].pixmap = va_arg(ap, Pixmap);
612 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
613 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
616 case GDI_DESIGN_PRESSED:
617 gi->design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
618 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
619 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
622 case GDI_ALT_DESIGN_UNPRESSED:
623 gi->alt_design[GD_BUTTON_UNPRESSED].pixmap= va_arg(ap, Pixmap);
624 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
625 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
628 case GDI_ALT_DESIGN_PRESSED:
629 gi->alt_design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
630 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
631 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
634 case GDI_BORDER_SIZE:
635 gi->border.size = va_arg(ap, int);
638 case GDI_TEXTINPUT_DESIGN_WIDTH:
639 gi->border.width = va_arg(ap, int);
642 case GDI_DECORATION_DESIGN:
643 gi->deco.design.pixmap = va_arg(ap, Pixmap);
644 gi->deco.design.x = va_arg(ap, int);
645 gi->deco.design.y = va_arg(ap, int);
648 case GDI_DECORATION_POSITION:
649 gi->deco.x = va_arg(ap, int);
650 gi->deco.y = va_arg(ap, int);
653 case GDI_DECORATION_SIZE:
654 gi->deco.width = va_arg(ap, int);
655 gi->deco.height = va_arg(ap, int);
658 case GDI_DECORATION_SHIFTING:
659 gi->deco.xshift = va_arg(ap, int);
660 gi->deco.yshift = va_arg(ap, int);
664 gi->event_mask = va_arg(ap, unsigned long);
668 gi->drawing.area_xsize = va_arg(ap, int);
669 gi->drawing.area_ysize = va_arg(ap, int);
671 /* determine dependent values for drawing area gadget, if needed */
672 if (gi->width == 0 && gi->height == 0 &&
673 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
675 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
676 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
678 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
679 gi->width != 0 && gi->height != 0)
681 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
682 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
687 gi->drawing.item_xsize = va_arg(ap, int);
688 gi->drawing.item_ysize = va_arg(ap, int);
690 /* determine dependent values for drawing area gadget, if needed */
691 if (gi->width == 0 && gi->height == 0 &&
692 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
694 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
695 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
697 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
698 gi->width != 0 && gi->height != 0)
700 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
701 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
705 case GDI_SCROLLBAR_ITEMS_MAX:
706 gi->scrollbar.items_max = va_arg(ap, int);
709 case GDI_SCROLLBAR_ITEMS_VISIBLE:
710 gi->scrollbar.items_visible = va_arg(ap, int);
713 case GDI_SCROLLBAR_ITEM_POSITION:
714 gi->scrollbar.item_position = va_arg(ap, int);
717 case GDI_CALLBACK_INFO:
718 gi->callback_info = va_arg(ap, gadget_function);
721 case GDI_CALLBACK_ACTION:
722 gi->callback_action = va_arg(ap, gadget_function);
726 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
729 tag = va_arg(ap, int); /* read next tag */
732 /* check if gadget complete */
733 if (gi->type != GD_TYPE_DRAWING_AREA &&
734 (!gi->design[GD_BUTTON_UNPRESSED].pixmap ||
735 !gi->design[GD_BUTTON_PRESSED].pixmap))
736 Error(ERR_EXIT, "gadget incomplete (missing Pixmap)");
738 /* adjust gadget values in relation to other gadget values */
740 if (gi->type & GD_TYPE_TEXTINPUT)
742 int font_width = getFontWidth(FS_SMALL, gi->text.font_type);
743 int font_height = getFontHeight(FS_SMALL, gi->text.font_type);
745 gi->width = 2 * gi->border.size + (gi->text.size + 1) * font_width;
746 gi->height = 2 * gi->border.size + font_height;
749 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
751 struct GadgetTextInput *text = &gi->text;
752 int value = text->number_value;
754 text->number_value = (value < text->number_min ? text->number_min :
755 value > text->number_max ? text->number_max :
758 sprintf(text->value, "%d", text->number_value);
761 if (gi->type & GD_TYPE_SCROLLBAR)
763 struct GadgetScrollbar *gs = &gi->scrollbar;
765 if (gi->width == 0 || gi->height == 0 ||
766 gs->items_max == 0 || gs->items_visible == 0)
767 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
769 /* calculate internal scrollbar values */
770 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
771 gi->height : gi->width);
772 gs->size = gs->size_max * gs->items_visible / gs->items_max;
773 gs->position = gs->size_max * gs->item_position / gs->items_max;
774 gs->position_max = gs->size_max - gs->size;
775 gs->correction = gs->size_max / gs->items_max / 2;
777 /* finetuning for maximal right/bottom position */
778 if (gs->item_position == gs->items_max - gs->items_visible)
779 gs->position = gs->position_max;
783 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
787 va_start(ap, first_tag);
788 HandleGadgetTags(gi, first_tag, ap);
794 void RedrawGadget(struct GadgetInfo *gi)
797 DrawGadget(gi, gi->state, DG_DIRECT);
800 struct GadgetInfo *CreateGadget(int first_tag, ...)
802 struct GadgetInfo *new_gadget = checked_malloc(sizeof(struct GadgetInfo));
805 /* always start with reliable default values */
806 memset(new_gadget, 0, sizeof(struct GadgetInfo)); /* zero all fields */
807 new_gadget->id = getNewGadgetID();
808 new_gadget->callback_info = default_callback_info;
809 new_gadget->callback_action = default_callback_action;
811 va_start(ap, first_tag);
812 HandleGadgetTags(new_gadget, first_tag, ap);
815 /* insert new gadget into global gadget list */
816 if (gadget_list_last_entry)
818 gadget_list_last_entry->next = new_gadget;
819 gadget_list_last_entry = gadget_list_last_entry->next;
822 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
827 void FreeGadget(struct GadgetInfo *gi)
829 struct GadgetInfo *gi_previous = gadget_list_first_entry;
831 while (gi_previous && gi_previous->next != gi)
832 gi_previous = gi_previous->next;
834 if (gi == gadget_list_first_entry)
835 gadget_list_first_entry = gi->next;
837 if (gi == gadget_list_last_entry)
838 gadget_list_last_entry = gi_previous;
840 gi_previous->next = gi->next;
844 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
846 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
849 gi->text.number_value = atoi(gi->text.value);
851 if (gi->text.number_value < gi->text.number_min)
852 gi->text.number_value = gi->text.number_min;
853 if (gi->text.number_value > gi->text.number_max)
854 gi->text.number_value = gi->text.number_max;
856 sprintf(gi->text.value, "%d", gi->text.number_value);
858 if (gi->text.cursor_position < 0)
859 gi->text.cursor_position = 0;
860 else if (gi->text.cursor_position > strlen(gi->text.value))
861 gi->text.cursor_position = strlen(gi->text.value);
864 /* global pointer to gadget actually in use (when mouse button pressed) */
865 static struct GadgetInfo *last_gi = NULL;
867 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
869 if (gi == NULL || gi->mapped)
875 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
878 void MapGadget(struct GadgetInfo *gi)
880 MapGadgetExt(gi, TRUE);
883 void UnmapGadget(struct GadgetInfo *gi)
885 if (gi == NULL || !gi->mapped)
894 #define MAX_NUM_GADGETS 1024
895 #define MULTIMAP_UNMAP (1 << 0)
896 #define MULTIMAP_REMAP (1 << 1)
897 #define MULTIMAP_REDRAW (1 << 2)
898 #define MULTIMAP_PLAYFIELD (1 << 3)
899 #define MULTIMAP_DOOR_1 (1 << 4)
900 #define MULTIMAP_DOOR_2 (1 << 5)
901 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
905 static void MultiMapGadgets(int mode)
907 struct GadgetInfo *gi = gadget_list_first_entry;
908 static boolean map_state[MAX_NUM_GADGETS];
913 if ((mode & MULTIMAP_PLAYFIELD && gi->x < SX + SXSIZE) ||
914 (mode & MULTIMAP_DOOR_1 && gi->x >= DX && gi->y < DY + DYSIZE) ||
915 (mode & MULTIMAP_DOOR_2 && gi->x >= DX && gi->y > DY + DYSIZE) ||
916 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
918 if (mode & MULTIMAP_UNMAP)
920 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
925 if (map_state[map_count++ % MAX_NUM_GADGETS])
926 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
934 void UnmapAllGadgets()
936 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
939 void RemapAllGadgets()
941 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
944 boolean anyTextGadgetActive()
946 return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
949 void ClickOnGadget(struct GadgetInfo *gi, int button)
951 /* simulate releasing mouse button over last gadget, if still pressed */
953 HandleGadgets(-1, -1, 0);
955 /* simulate pressing mouse button over specified gadget */
956 HandleGadgets(gi->x, gi->y, button);
958 /* simulate releasing mouse button over specified gadget */
959 HandleGadgets(gi->x, gi->y, 0);
962 void HandleGadgets(int mx, int my, int button)
964 static struct GadgetInfo *last_info_gi = NULL;
965 static unsigned long pressed_delay = 0;
966 static int last_button = 0;
967 static int last_mx = 0, last_my = 0;
968 int scrollbar_mouse_pos = 0;
969 struct GadgetInfo *new_gi, *gi;
971 boolean release_event;
972 boolean mouse_moving;
973 boolean gadget_pressed;
974 boolean gadget_pressed_repeated;
975 boolean gadget_moving;
976 boolean gadget_moving_inside;
977 boolean gadget_moving_off_borders;
978 boolean gadget_released;
979 boolean gadget_released_inside;
980 boolean gadget_released_off_borders;
981 boolean changed_position = FALSE;
983 /* check if there are any gadgets defined */
984 if (gadget_list_first_entry == NULL)
987 /* check which gadget is under the mouse pointer */
988 new_gi = getGadgetInfoFromMousePosition(mx, my);
990 /* check if button state has changed since last invocation */
991 press_event = (button != 0 && last_button == 0);
992 release_event = (button == 0 && last_button != 0);
993 last_button = button;
995 /* check if mouse has been moved since last invocation */
996 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1000 /* special treatment for text and number input gadgets */
1001 if (anyTextGadgetActive() && button != 0 && !motion_status)
1003 struct GadgetInfo *gi = last_gi;
1005 if (new_gi == last_gi)
1007 /* if mouse button pressed inside activated text gadget, set cursor */
1008 gi->text.cursor_position =
1009 (mx - gi->x - gi->border.size) /
1010 getFontWidth(FS_SMALL, gi->text.font_type);
1012 if (gi->text.cursor_position < 0)
1013 gi->text.cursor_position = 0;
1014 else if (gi->text.cursor_position > strlen(gi->text.value))
1015 gi->text.cursor_position = strlen(gi->text.value);
1017 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1021 /* if mouse button pressed outside text input gadget, deactivate it */
1022 CheckRangeOfNumericInputGadget(gi);
1023 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1025 gi->event.type = GD_EVENT_TEXT_LEAVING;
1027 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1028 gi->callback_action(gi);
1035 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1036 gadget_pressed_repeated =
1037 (button != 0 && last_gi != NULL && new_gi == last_gi);
1039 gadget_released = (release_event && last_gi != NULL);
1040 gadget_released_inside = (gadget_released && new_gi == last_gi);
1041 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1043 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1044 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1045 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1047 /* if new gadget pressed, store this gadget */
1051 /* 'gi' is actually handled gadget */
1054 /* if gadget is scrollbar, choose mouse position value */
1055 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1056 scrollbar_mouse_pos =
1057 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1059 /* if mouse button released, no gadget needs to be handled anymore */
1060 if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
1063 /* modify event position values even if no gadget is pressed */
1064 if (button == 0 && !release_event)
1069 int last_x = gi->event.x;
1070 int last_y = gi->event.y;
1072 gi->event.x = mx - gi->x;
1073 gi->event.y = my - gi->y;
1075 if (gi->type == GD_TYPE_DRAWING_AREA)
1077 gi->event.x /= gi->drawing.item_xsize;
1078 gi->event.y /= gi->drawing.item_ysize;
1080 if (last_x != gi->event.x || last_y != gi->event.y)
1081 changed_position = TRUE;
1085 /* handle gadget popup info text */
1086 if (last_info_gi != new_gi ||
1087 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1089 last_info_gi = new_gi;
1091 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1093 new_gi->event.type = 0;
1094 new_gi->callback_info(new_gi);
1097 default_callback_info(NULL);
1102 if (gi->type == GD_TYPE_CHECK_BUTTON)
1104 gi->checked = !gi->checked;
1106 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1108 struct GadgetInfo *rgi = gadget_list_first_entry;
1113 rgi->type == GD_TYPE_RADIO_BUTTON &&
1114 rgi->radio_nr == gi->radio_nr &&
1117 rgi->checked = FALSE;
1118 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
1126 else if (gi->type & GD_TYPE_SCROLLBAR)
1130 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1141 if (mpos >= gpos + gi->scrollbar.position &&
1142 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1144 /* drag scrollbar */
1145 gi->scrollbar.drag_position =
1146 scrollbar_mouse_pos - gi->scrollbar.position;
1150 /* click scrollbar one scrollbar length up/left or down/right */
1152 struct GadgetScrollbar *gs = &gi->scrollbar;
1153 int old_item_position = gs->item_position;
1155 changed_position = FALSE;
1157 gs->item_position +=
1158 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1160 if (gs->item_position < 0)
1161 gs->item_position = 0;
1162 if (gs->item_position > gs->items_max - gs->items_visible)
1163 gs->item_position = gs->items_max - gs->items_visible;
1165 if (old_item_position != gs->item_position)
1167 gi->event.item_position = gs->item_position;
1168 changed_position = TRUE;
1171 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1174 gi->state = GD_BUTTON_UNPRESSED;
1175 gi->event.type = GD_EVENT_MOVING;
1176 gi->event.off_borders = FALSE;
1178 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1179 gi->callback_action(gi);
1181 /* don't handle this scrollbar anymore while mouse button pressed */
1188 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1190 gi->state = GD_BUTTON_PRESSED;
1191 gi->event.type = GD_EVENT_PRESSED;
1192 gi->event.button = button;
1193 gi->event.off_borders = FALSE;
1195 /* initialize delay counter */
1196 DelayReached(&pressed_delay, 0);
1198 if (gi->event_mask & GD_EVENT_PRESSED)
1199 gi->callback_action(gi);
1202 if (gadget_pressed_repeated)
1204 gi->event.type = GD_EVENT_PRESSED;
1206 if (gi->event_mask & GD_EVENT_REPEATED &&
1207 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1208 gi->callback_action(gi);
1213 if (gi->type & GD_TYPE_BUTTON)
1215 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1216 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1217 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1218 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1221 if (gi->type & GD_TYPE_SCROLLBAR)
1223 struct GadgetScrollbar *gs = &gi->scrollbar;
1224 int old_item_position = gs->item_position;
1226 gs->position = scrollbar_mouse_pos - gs->drag_position;
1228 if (gs->position < 0)
1230 if (gs->position > gs->position_max)
1231 gs->position = gs->position_max;
1234 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1236 if (gs->item_position < 0)
1237 gs->item_position = 0;
1238 if (gs->item_position > gs->items_max - 1)
1239 gs->item_position = gs->items_max - 1;
1241 if (old_item_position != gs->item_position)
1243 gi->event.item_position = gs->item_position;
1244 changed_position = TRUE;
1247 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1250 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1251 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1252 gi->event.type = GD_EVENT_MOVING;
1253 gi->event.off_borders = gadget_moving_off_borders;
1255 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1256 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1257 gi->callback_action(gi);
1260 if (gadget_released_inside)
1262 if (!(gi->type & GD_TYPE_TEXTINPUT))
1263 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1265 gi->state = GD_BUTTON_UNPRESSED;
1266 gi->event.type = GD_EVENT_RELEASED;
1268 if (gi->event_mask & GD_EVENT_RELEASED)
1269 gi->callback_action(gi);
1272 if (gadget_released_off_borders)
1274 if (gi->type & GD_TYPE_SCROLLBAR)
1275 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1277 gi->event.type = GD_EVENT_RELEASED;
1279 if (gi->event_mask & GD_EVENT_RELEASED &&
1280 gi->event_mask & GD_EVENT_OFF_BORDERS)
1281 gi->callback_action(gi);
1285 void HandleGadgetsKeyInput(KeySym key)
1287 struct GadgetInfo *gi = last_gi;
1288 char text[MAX_GADGET_TEXTSIZE];
1292 boolean legal_letter;
1294 if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
1297 text_length = strlen(gi->text.value);
1298 cursor_pos = gi->text.cursor_position;
1299 letter = getCharFromKeySym(key);
1300 legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1301 letter >= '0' && letter <= '9' :
1304 if (legal_letter && text_length < gi->text.size)
1306 strcpy(text, gi->text.value);
1307 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1308 gi->text.value[cursor_pos] = letter;
1309 gi->text.cursor_position++;
1310 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1312 else if (key == XK_Left && cursor_pos > 0)
1314 gi->text.cursor_position--;
1315 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1317 else if (key == XK_Right && cursor_pos < text_length)
1319 gi->text.cursor_position++;
1320 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1322 else if (key == XK_BackSpace && cursor_pos > 0)
1324 strcpy(text, gi->text.value);
1325 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1326 gi->text.cursor_position--;
1327 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1329 else if (key == XK_Delete && cursor_pos < text_length)
1331 strcpy(text, gi->text.value);
1332 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1333 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1335 else if (key == XK_Return)
1337 CheckRangeOfNumericInputGadget(gi);
1338 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1340 gi->event.type = GD_EVENT_TEXT_RETURN;
1342 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1343 gi->callback_action(gi);