Nilorea Library

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

All Data Structures Files Functions Variables Typedefs Macros Groups
lexmenu.c
Go to the documentation of this file.
1
15 #include "lexmenu.h"
16
17 LexMenu* lexMenuCreate(BITMAP *bg) {
18 LexMenu *menu = calloc(1, sizeof(LexMenu));
19 if (menu) {
20 menu->bg = bg;
21 menu->begin = calloc(1, sizeof(LexMenuItem));
22 menu->end = calloc(1, sizeof(LexMenuItem));
23
24 menu->begin->next = menu->end;
25 menu->end->prev = menu->begin;
26 }
27 return menu;
28 }
29
30 LexMenu* lexMenuDestroy(LexMenu* menu) {
31 if (menu) {
32 LexMenuItemItor *itor = menu->begin;
33 while (itor != menu->end) {
34 if (itor->data) {
35 if (itor->data->autoDestroyResources) {
36 int i= 0;
37 for (i = 0; i < LEX_MENU_IMG_COUNT; ++i) {
38 /* It's ok to pass NULL to destroy bitmap */
39 destroy_bitmap(itor->data->img[i]);
40 }
41 for (i = 0; i < LEX_MENU_SND_COUNT; ++i) {
42 /* It's ok to pass NULL to destroy bitmap */
43 destroy_sample(itor->data->snd[i]);
44 }
45 }
46 if (itor->data->autoDestroy) {
47 free(itor->data);
48 }
49 }
50 itor = itor->next;
51 free(itor->prev);
52 }
53 free(menu->end);
54
55 if (menu->autoDestroyResources) {
56 destroy_bitmap(menu->bg);
57 }
58 free(menu);
59 menu = NULL;
60 }
61 return menu;
62 }
63
64 LexMenuItem *lexMenuCreateItem() {
65 LexMenuItem *item = calloc(1, sizeof(LexMenuItem));
66 item->autoDestroy = 1;
67
68 return item;
69 }
70
71 BITMAP *lexMenu__getImg(LexMenuItem *item) {
72 BITMAP *img = NULL;
73 int active = item->state == LEX_MENU_ACTIVE ||
74 item->state == LEX_MENU_PRESSED;
75 if (item->img[item->state]) {
76 img = item->img[item->state];
77 } else {
78 if (active && item->img[LEX_MENU_ACTIVE]) {
79 img = item->img[LEX_MENU_ACTIVE];
80 } else {
81 img = item->img[LEX_MENU_NORMAL];
82 }
83 }
84 return img;
85 }
86
87
88 int lexMenu__getX(LexMenuItem *item) {
89 BITMAP *img;
90 int x;
91
92 img = lexMenu__getImg(item);
93 if (!img) {
94 return item->x;
95 }
96
97 switch (item->align) {
98 case LEX_MENU_ALIGN_LEFT:
99 x = item->x;
100 break;
101 case LEX_MENU_ALIGN_CENTER:
102 x = item->x - img->w/2;
103 break;
104 case LEX_MENU_ALIGN_RIGHT:
105 x = item->x - img->w;
106 break;
107 }
108 return x;
109 }
110
111 int lexMenu__getY(LexMenuItem *item) {
112 BITMAP *img = lexMenu__getImg(item);
113 int y;
114 if (!img) {
115 return item->y;
116 }
117 switch (item->align) {
118 case LEX_MENU_ALIGN_TOP:
119 y = item->y;
120 break;
121 case LEX_MENU_ALIGN_CENTER:
122 y = item->y - img->h/2;
123 break;
124 case LEX_MENU_ALIGN_BOTTOM:
125 y = item->x - img->h;
126 break;
127 }
128 return y;
129 }
130
131
132 void lexMenu__fireEvent(LexMenuEvent *event) {
133
134 if (event->source->cbf[event->type]) {
135 /* Execute callback */
136 event->source->cbf[event->type](event);
137 }
138 if (event->type < LEX_MENU_SND_COUNT &&
139 event->source->snd[event->type]) {
140 play_sample(event->source->snd[event->type], 255, 128, 1000, 0);
141 }
142 }
143
144 void lexMenuAddItem(LexMenu *menu, LexMenuItem *item) {
145 LexMenuItemItor *itor = calloc(1, sizeof(LexMenuItemItor));
146 itor->data = item;
147
148 itor->prev = menu->end->prev;
149 itor->next = menu->end;
150
151 menu->end->prev = itor;
152 itor->prev->next = itor;
153 }
154
155 void lexMenuSetCallback(LexMenuItem *item, int callbackID, LexMenuCallback func) {
156 if (callbackID >= 0 && callbackID < LEX_MENU_CBF_COUNT) {
157 item->cbf[callbackID] = func;
158 }
159 }
160
161 void lexMenuSetImage(LexMenuItem *item, int state, BITMAP* img) {
162 if (state >= 0 && state < LEX_MENU_IMG_COUNT) {
163 item->img[state] = img;
164 }
165 }
166
167 void lexMenuSetSample(LexMenuItem *item, int event, SAMPLE* sample) {
168 if (event >= 0 && event < LEX_MENU_SND_COUNT) {
169 item->snd[event] = sample;
170 }
171 }
172
173
174 int lexMenuRun(LexMenu* menu, BITMAP* dst) {
175 int result = -1;
176 int running = 1;
177
178 int x,y,dx,dy;
179 int mx = 0;
180 int my = 0;
181 int mb = 0;
182 int lastX = 0;
183 int lastY = 0;
184 int lastB = 0;
185 int mode = menu->inputMode != 0 ? menu->inputMode : LEX_MENU_MOUSE_AND_KB;
186
187 int moveX = 0;
188 int moveY = 0;
189 int shouldMove = 0;
190 int wasDragged = 0;
191
192 LexMenuItemItor *itor = NULL;
193 LexMenuItem *item = NULL;
194 LexMenuItem *active = NULL;
195 LexMenuItem *next = NULL;
196 LexMenuItem *grab = NULL;
197
198 LexMenuItem *lastActive = NULL;
199
200 LexMenuEvent event;
201
202 BITMAP *img;
203
204 if (dst == NULL) {
205 dst = screen;
206 }
207
208 event.menu = menu;
209
210 if (!(mode & LEX_MENU_MOUSE)) {
211 active = menu->begin->next->data;
212 if (active) {
213 active->state = LEX_MENU_ACTIVE;
214 lastActive = active;
215 }
216 show_mouse(NULL);
217 } else if (menu->cursor){
218 show_mouse(NULL);
219 } else {
220 show_mouse(screen);
221 }
222
223 if (menu->begin && menu->begin->next) {
224 lastActive = menu->begin->next->data;
225 }
226
227
228 do {
229 if (menu->bg) {
230 blit(menu->bg, dst, 0, 0, 0, 0, menu->bg->w, menu->bg->h);
231 } else {
232 clear(dst);
233 }
234
235 if (mode & LEX_MENU_MOUSE) {
236 lastX = mx;
237 lastY = my;
238 lastB = mb;
239
240 mx = mouse_x;
241 my = mouse_y;
242 mb = mouse_b;
243
244 dx = mx - lastX;
245 dy = my - lastY;
246
247 event.x = mx;
248 event.y = my;
249
250 if (active && (mx != lastX || my != lastY)) {
251
252 if (grab) {
253 event.source = grab;
254 event.type = LEX_MENU_ON_DRAG;
255 event.dx = dx;
256 event.dy = dy;
257 lexMenu__fireEvent(&event);
258
259 wasDragged = 1;
260 }
261
262 x = lexMenu__getX(active);
263 y = lexMenu__getY(active);
264
265 img = lexMenu__getImg(active);
266 if (img) {
267 if ( (mx >= x && mx < x + img->w) &&
268 (my >= y && my < y + img->h)
269 ){
270 /* OnMove: will behandled below */
271 } else {
272 event.source = active;
273 event.type = LEX_MENU_ON_LEAVE;
274 active->state = LEX_MENU_NORMAL;
275
276 lexMenu__fireEvent(&event);
277 active = NULL;
278 }
279 }
280 }
281 }
282
283 itor = menu->begin;
284 while (itor != menu->end) {
285 item = itor->data;
286 if (item) {
287 img = lexMenu__getImg(item);
288 x = lexMenu__getX(item);
289 y = lexMenu__getY(item);
290
291 event.source = item;
292 if (item->cbf[LEX_MENU_ON_DRAW]) {
293 event.type = LEX_MENU_ON_DRAW;
294 lexMenu__fireEvent(&event);
295 } else if (img) {
296 draw_sprite(dst, img, x, y);
297 }
298 event.type = LEX_MENU_ON_DRAW_DONE;
299 lexMenu__fireEvent(&event);
300
301 if (img && (mode & LEX_MENU_MOUSE && (mx != lastX || my != lastY || mb != lastB))) {
302 if (mx >= x && mx < x + img->w &&
303 my >= y && my < y + img->h) {
304
305 if (grab && item != grab) {
306 grab->state = LEX_MENU_NORMAL;
307 event.type = LEX_MENU_ON_LEAVE;
308 active = NULL;
309 event.source = grab;
310 lexMenu__fireEvent(&event);
311 }
312
313 event.source = item;
314 if (active != item && grab == NULL) {
315
316 if (active != NULL) {
317 active->state = LEX_MENU_NORMAL;
318 event.type = LEX_MENU_ON_LEAVE;
319 lexMenu__fireEvent(&event);
320 }
321
322 item->state = LEX_MENU_ACTIVE;
323 active = item;
324 lastActive = item;
325
326 event.type = LEX_MENU_ON_ENTER;
327 lexMenu__fireEvent(&event);
328 } else {
329 if (mb) {
330 if (item->state != LEX_MENU_PRESSED && grab == NULL) {
331 item->state = LEX_MENU_PRESSED;
332
333 grab = item;
334 wasDragged = 0;
335 event.type = LEX_MENU_ON_PRESS;
336 lexMenu__fireEvent(&event);
337 }
338 } else {
339 if (grab && item != grab) {
340 event.source = grab;
341 event.type = LEX_MENU_ON_RELEASE;
342 lexMenu__fireEvent(&event);
343
344 event.source = item;
345 }
346 if (item->state == LEX_MENU_PRESSED) {
347 item->state = LEX_MENU_NORMAL;
348 if (wasDragged) {
349 event.type = LEX_MENU_ON_RELEASE;
350 } else {
351 event.type = LEX_MENU_ON_FIRE;
352 running = 0;
353 }
354 lexMenu__fireEvent(&event);
355 }
356 if (lastX != mx || lastY != my) {
357 event.dx = mx - lastX;
358 event.dy = my - lastY;
359
360 event.type = LEX_MENU_ON_MOVE;
361 lexMenu__fireEvent(&event);
362 }
363 wasDragged = 0;
364 grab = NULL;
365 }
366 }
367 }
368 }
369 }
370 itor = itor->next;
371 }
372
373 if (key[KEY_ESC]) {
374 active = NULL;
375 running = 0;
376 }
377 shouldMove = 0;
378 if ((mode & LEX_MENU_KEYBOARD) && (active || lastActive)) {
379 if (key[KEY_LEFT]) {
380 if (moveX != -1) {
381 moveX = -1;
382 shouldMove = 1;
383 }
384 } else if (key[KEY_RIGHT]) {
385 if (moveX != 1) {
386 moveX = 1;
387 shouldMove = 1;
388 }
389 } else {
390 moveX = 0;
391 }
392 if (key[KEY_DOWN]) {
393 if (moveY != 1) {
394 moveY = 1;
395 shouldMove = 1;
396 }
397 } else if (key[KEY_UP]) {
398 if (moveY != -1) {
399 moveY= -1;
400 shouldMove = 1;
401 }
402 } else {
403 moveY = 0;
404 }
405
406 if (key[KEY_ENTER] || key[KEY_SPACE]) {
407
408 if (active==NULL && lastActive != NULL) {
409 active = lastActive;
410 }
411 running = 0;
412
413 event.type = LEX_MENU_ON_PRESS;
414 event.source = active;
415 lexMenu__fireEvent(&event);
416
417 event.type = LEX_MENU_ON_FIRE;
418 lexMenu__fireEvent(&event);
419 }
420 } else {
421 moveX = moveY = 0;
422 }
423
424 if (shouldMove) {
425 int dist = 0;
426 int min = 0xffffff;
427 int ox = 0;
428 int oy = 0;
429
430 if (active==NULL && lastActive != NULL) {
431 active = lastActive;
432 }
433 ox = lexMenu__getX(active) + lexMenu__getImg(active)->w/2;
434 oy = lexMenu__getY(active) + lexMenu__getImg(active)->h/2;
435
436 next = NULL;
437 itor = menu->begin;
438 while (itor != menu->end) {
439 item = itor->data;
440 if (item && item != active && item->state != LEX_MENU_DISABLED) {
441 img = lexMenu__getImg(item);
442 x = lexMenu__getX(item) + img->w/2;
443 y = lexMenu__getY(item) + img->h/2;
444
445 if (moveX > 0) {
446 if (x > ox) {
447 dist = ABS(ox-x);
448 } else {
449 dist = 0xffffff;
450 }
451 } else if (moveX < 0) {
452 if (x < ox) {
453 dist = ABS(ox-x);
454 } else {
455 dist = 0xffffff;
456 }
457 } else if (moveY > 0) {
458 if (y > oy) {
459 dist = ABS(oy-y);
460 } else {
461 dist = 0xffffff;
462 }
463 } else if (moveY < 0) {
464 if (y < oy) {
465 dist = ABS(oy-y);
466 } else {
467 dist = 0xffffff;
468 }
469 }
470 if (dist < min) {
471 dist = min;
472 next= item;
473 }
474 }
475 itor = itor->next;
476 }
477 if (next) {
478 event.type = LEX_MENU_ON_LEAVE;
479 active->state = LEX_MENU_NORMAL;
480 event.source = active;
481 lexMenu__fireEvent(&event);
482
483 active = next;
484 lastActive = active;
485 active->state = LEX_MENU_ACTIVE;
486 event.type = LEX_MENU_ON_ENTER;
487 event.source = active;
488 lexMenu__fireEvent(&event);
489 }
490 }
491
492 if (menu->cursor) {
493 draw_sprite(dst, menu->cursor, mx- menu->hotX, my- menu->hotY);
494 }
495
496 if (menu->showMenu) {
497 menu->showMenu(dst);
498 } else {
499 if (dst != screen) {
500 scare_mouse();
501 blit(dst, screen, 0, 0, 0, 0, dst->w, dst->h);
502 unscare_mouse();
503 }
504 }
505 } while (running);
506
507 if (active) {
508 result = active->id;
509 } else {
510 result = -1;
511 }
512
513 return result;
514 }
515
516 #include "lexmenu.h"
517 #include <stdio.h>
518 #include <strings.h>
519
520 char* lex__createFileName(const char *filename, const char* postfix) {
521 int len = strlen(filename);
522 int postfixLen = strlen(postfix);
523 int i =0;
524 char* buf = NULL;
525 char* cursor = NULL;
526
527 i = len;
528 while (filename[i] != '.' && i >= 0) {
529 --i;
530 }
531 if (i >=0 && filename[i] == '.') {
532 buf = calloc(len + postfixLen+1, 1);
533
534 cursor = buf;
535 strncpy(cursor, filename, i);
536 cursor += i;
537 strncpy(cursor, postfix, postfixLen);
538 cursor+=postfixLen;
539 strncpy(cursor, filename+i, len-i);
540
541 return buf;
542 } else {
543 return NULL;
544 }
545 }
546
547 BITMAP* lex__loadStateImage(const char* value, const char* postfix) {
548 char *imageName = NULL;
549 BITMAP* image = NULL;
550
551 imageName = lex__createFileName(value, postfix);
552 if (imageName != NULL) {
553 image = load_bitmap(imageName, NULL);
554 free(imageName);
555 }
556 return image;
557 }
558
559 SAMPLE* lex__loadStateSample(const char* value, const char* postfix) {
560 char *sampleName = NULL;
561 SAMPLE* sample = NULL;
562
563 sampleName = lex__createFileName(value, postfix);
564 if (sampleName != NULL) {
565 sample = load_sample(sampleName);
566 free(sampleName);
567 }
568 return sample;
569 }
570
571 LexMenuItem* lexLoadItemFromSection(const char* itemSectionName, int id) {
572 LexMenuItem* item = NULL;
573 BITMAP* image = NULL;
574 const char* value;
575
576 /* The minimum requirement is that the item has a position */
577 /* So, we first check if the x and y entries are present */
578 if (get_config_string(itemSectionName,"x", NULL) == NULL
579 || get_config_string(itemSectionName,"y", NULL) == NULL ) {
580 return NULL;
581 }
582
583 item = lexMenuCreateItem();
584 if (item != NULL) {
585 item->autoDestroyResources = TRUE;
586 item->id = id;
587
588 /* Position */
589 item->x = get_config_int(itemSectionName,"x", 0);
590 item->y = get_config_int(itemSectionName,"y", 0);
591
592 /* Alignment */
593 value = get_config_string(itemSectionName,"h_align", "center");
594 if (stricmp("center", value) == 0) {
595 item->align = LEX_MENU_ALIGN_CENTER;
596 } else if (stricmp("left", value) == 0) {
597 item->align = LEX_MENU_ALIGN_LEFT;
598 } else if (stricmp("right", value) == 0) {
599 item->align = LEX_MENU_ALIGN_RIGHT;
600 }
601
602 value = get_config_string(itemSectionName,"h_align", "center");
603 if (stricmp("center", value) == 0) {
604 item->valign = LEX_MENU_ALIGN_CENTER;
605 } else if (stricmp("top", value) == 0) {
606 item->valign = LEX_MENU_ALIGN_TOP;
607 } else if (stricmp("bottom", value) == 0) {
608 item->valign = LEX_MENU_ALIGN_BOTTOM;
609 }
610
611 /* Images */
612 value = get_config_string(itemSectionName, "image", NULL);
613
614 if (value != NULL) {
615 image = load_bitmap(value, NULL);
616 if (image != NULL) {
617 lexMenuSetImage(item, LEX_MENU_NORMAL, image);
618 lexMenuSetImage(item, LEX_MENU_ACTIVE, lex__loadStateImage(value, "_hot"));
619 lexMenuSetImage(item, LEX_MENU_PRESSED, lex__loadStateImage(value, "_down"));
620 lexMenuSetImage(item, LEX_MENU_DISABLED, lex__loadStateImage(value, "_gray"));
621 }
622 }
623 value = get_config_string(itemSectionName, "sound", NULL);
624 if (value != NULL) {
625 lexMenuSetSample(item, LEX_MENU_ON_FIRE, load_sample(value));
626 lexMenuSetSample(item, LEX_MENU_ON_ENTER, lex__loadStateSample(value, "_hot"));
627 lexMenuSetSample(item, LEX_MENU_ON_LEAVE, lex__loadStateSample(value, "_cold"));
628 }
629
630 }
631 return item;
632 }
633
634 LexMenu* lexLoadMenuFromFile(const char* filename, const char* screenname) {
635 int curItem = 0;
636 int countItems = 0;
637 int ok = FALSE;
638 int len = 0, max=0;
639 char buf[9];
640 const char* value=NULL;
641 char *itemSectionName = NULL;
642 LexMenu* menu = NULL;
643 LexMenuItem* item =NULL;
644 BITMAP *bg = NULL;
645
646 push_config_state();
647 set_config_file(filename);
648
649 /* check how many items we have */
650 ok = TRUE;
651 while (ok && countItems < 9999) { /* Check is here to ensure that buf won't overflow */
652 sprintf(buf, "item%d", countItems);
653 value = get_config_string(screenname, buf, NULL);
654 if (value == NULL) {
655 ok = FALSE;
656 } else {
657 len = strlen(value);
658 max = MAX(max, len);
659 ++countItems;
660 }
661 }
662
663 /* Yeah, we have some work todo */
664 if (countItems > 0) {
665 value = get_config_string(screenname, "bg", NULL);
666 if (value) {
667 bg = load_bitmap(value, NULL);
668 }
669 menu = lexMenuCreate(bg);
670 if (menu != NULL) {
671 menu->autoDestroyResources= TRUE;
672
673 /* Need to create the section header name dynamically.
674 * So we need the space for screenname + '.' + itenname + string terminator.
675 * We know the length of the longest item name, so we'll just create
676 * one stringbuffer and reuse it.
677 */
678 itemSectionName = calloc(strlen(screenname) + max +2,1);
679 for (curItem = 0; curItem < countItems; ++curItem) {
680 sprintf(buf, "item%d", curItem);
681 sprintf(itemSectionName, "%s.%s", screenname, get_config_string(screenname, buf, "#"));
682
683 item = lexLoadItemFromSection(itemSectionName, curItem);
684 if (item != NULL) {
685 lexMenuAddItem(menu, item);
686 }
687 }
688 free(itemSectionName);
689 }
690 }
691
692
693 pop_config_state();
694 return menu;
695 }
696
697
698 BITMAP *lexMenuGetBitmap( LexMenu* menu , int item , int whichone )
699 {
700 LexMenuItemItor *item_ptr = NULL;
701
702 if( !menu )
703 return NULL;
704 if( !menu -> begin )
705 return NULL ;
706
707 item_ptr = menu -> begin ;
708
709 if( whichone < 0 || whichone > 3 )
710 return NULL ;
711
712 while( item_ptr )
713 {
714 if( item_ptr -> data )
715 {
716 if( item_ptr -> data -> id == item )
717 return item_ptr -> data -> img[ whichone ];
718 }
719
720 item_ptr = item_ptr -> next ;
721
722 }
723
724 return NULL;
725 }