1 /*
2 * GRacer
3 *
4 * Copyright (C) 1999 Takashi Matsuda <matsu@users.sourceforge.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21
22 #include <GL/glut.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <math.h>
26 #include <common/gr_memory.h>
27 #include <common/gr_debug.h>
28 #include "glutwidgets.h"
29 #include "glbind.h"
30 #include "sound.h"
31
32 static void default_label_display (GlutObject);
33
34 static void default_button_display (GlutObject);
35 static void default_button_keyboard (GlutObject, unsigned char, int, int);
36 static void default_button_mouse (GlutObject, int, int, int, int);
37
38 static void default_entry_display (GlutObject);
39 static void default_entry_keyboard (GlutObject, unsigned char, int, int);
40 static void default_entry_mouse (GlutObject, int, int, int, int);
41
42 static int glut_widget_check_bbox (GlutObject obj, int x, int y);
43 static void glut_widget_calc_bbox (GlutObject);
44 static int glut_widget_state (GlutObject);
45
46 static void glut_bitmap_string (void *font, char *string, int length);
47
48 GrSList *glut_objects;
49 GlutObject grab_widget;
50 GlutObject focus_widget;
51 GLuint widget_dl;
52 int widget_changed;
53
54 int screen_width = 300;
55 int screen_height = 300;
56
57 static GLfloat default_fg_color[GR_NUM_STATE][4] = {
58 {0.7, 0.7, 1, 1},
59 {0.7, 0.7, 1, 1},
60 {0.5, 0.5, 0.8, 1},
61 {1, 1, 0, 1},
62 };
63
64 static GLfloat default_bg_color[GR_NUM_STATE][4] = {
65 {0.6, 0.6, 0.6, 1},
66 {0.8, 0.8, 1.0, 1},
67 {0.8, 0.8, 1.0, 1},
68 {0.8, 0.8, 1.0, 1},
69 };
70
71 GlutFontInfo bitmap_font_infos[] = {
72 {GLUT_BITMAP_8_BY_13, 13, 11, 2},
73 {GLUT_BITMAP_9_BY_15, 15, 13, 2},
74 {GLUT_BITMAP_TIMES_ROMAN_10, 10, 7, 3},
75 {GLUT_BITMAP_TIMES_ROMAN_24, 24, 18, 6},
76 {GLUT_BITMAP_HELVETICA_10, 10, 8, 2},
77 {GLUT_BITMAP_HELVETICA_12, 12, 9, 3},
78 {GLUT_BITMAP_HELVETICA_18, 18, 15, 3},
79 {NULL, -1, 0, 0},
80 };
81
82 void
glut_post_redisplay(void)83 glut_post_redisplay (void)
84 {
85 widget_changed = 1;
86 glutPostRedisplay ();
87 }
88
89 void
glut_forget_all(void)90 glut_forget_all (void)
91 {
92 GlutRoot *root;
93
94 while (glut_objects) {
95 root = glut_objects->data;
96 gr_DECREF (root);
97 glut_objects = gr_slist_remove (glut_objects);
98 }
99 grab_widget = NULL;
100 focus_widget = NULL;
101 }
102
103 GlutFontInfo *
glut_get_bitmap_font_info(void * font)104 glut_get_bitmap_font_info (void *font)
105 {
106 int i;
107
108 for (i=0; bitmap_font_infos[i].height > 0; i++) {
109 if (font == bitmap_font_infos[i].font) {
110 return bitmap_font_infos + i;
111 }
112 }
113 return NULL;
114 }
115
116 void
glut_append_object(GlutObject object)117 glut_append_object (GlutObject object)
118 {
119 GlutWidget *widget = object;
120
121 glut_objects = gr_slist_append (glut_objects, object);
122
123 if (!focus_widget) {
124 glut_widget_set_focus (widget);
125 }
126
127 glut_post_redisplay ();
128 }
129
130 void
glut_prepend_object(GlutObject object)131 glut_prepend_object (GlutObject object)
132 {
133 GlutWidget *widget = object;
134
135 glut_objects = gr_slist_prepend (glut_objects, object);
136
137 if (!focus_widget) {
138 glut_widget_set_focus (widget);
139 }
140
141 glut_post_redisplay ();
142 }
143
144 void
glut_insert_object(GlutObject object,GlutObject after)145 glut_insert_object (GlutObject object, GlutObject after)
146 {
147 GrSList *list;
148 GlutWidget *widget = object;
149
150 if (!after) {
151 glut_prepend_object (object);
152 return;
153 }
154
155 for (list=glut_objects; list!=NULL; list=list->next) {
156 if (list->data == after) {
157 list->next = gr_slist_prepend (list->next, object);
158 if (!focus_widget) {
159 glut_widget_set_focus (widget);
160 }
161 return;
162 }
163 }
164 /* after not found */
165 glut_prepend_object (object);
166
167 glut_post_redisplay ();
168 }
169
170 void
glut_remove_object(GlutObject object)171 glut_remove_object (GlutObject object)
172 {
173 GrSList *list;
174
175 if (!glut_objects)
176 return;
177
178 if (focus_widget == object)
179 focus_widget = NULL;
180 if (grab_widget == object)
181 grab_widget = NULL;
182
183
184 if (glut_objects->data == object) {
185 glut_objects = gr_slist_remove (glut_objects);
186 } else {
187 list = glut_objects;
188 while (list->next) {
189 if (list->next->data == object) {
190 list->next = gr_slist_remove (list->next);
191 break;
192 }
193 list = list->next;
194 }
195 }
196
197 glut_post_redisplay ();
198 }
199
200 void
glut_display_func(void)201 glut_display_func (void)
202 {
203 GrSList *list;
204 GlutRoot *root;
205
206 GL_CHECK(glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
207 GL_CHECK(glEnable (GL_CULL_FACE));
208 GL_CHECK(glEnable (GL_COLOR_MATERIAL));
209 GL_CHECK(glEnable (GL_LIGHTING));
210 GL_CHECK(glEnable (GL_LIGHT0));
211 GL_CHECK(glEnable (GL_DEPTH_TEST));
212
213 if (glut_objects) {
214 GL_CHECK(glMatrixMode (GL_PROJECTION));
215 GL_CHECK(glLoadIdentity ());
216 GL_CHECK(glOrtho (-screen_width/2, screen_width/2,
217 -screen_height/2, screen_height/2, -10, 10));
218 GL_CHECK(glMatrixMode (GL_MODELVIEW));
219 GL_CHECK(glLoadIdentity ());
220 }
221
222 list = glut_objects;
223 gr_FOREACH (list, root) {
224 if (root->display_func) {
225 (*root->display_func) (root);
226 }
227 }
228
229 tcl_DisplayFunc ();
230
231 glutSwapBuffers ();
232
233 gr_sound_update ();
234 }
235
236 void
glut_reshape_func(int width,int height)237 glut_reshape_func (int width, int height)
238 {
239 GrSList *list;
240 GlutRoot *root;
241 GlutWidget *widget;
242
243 list = glut_objects;
244 gr_FOREACH (list, widget) {
245 if (widget->core.type == GR_GLUT_ROOT_WIDGET) {
246 switch (widget->anchor & GR_HORIZONTAL_MASK) {
247 case GR_LEFT:
248 widget->x -= (width - screen_width) / 2;
249 break;
250 case GR_RIGHT:
251 widget->x += (width - screen_width) / 2;
252 break;
253 case GR_LEFT|GR_RIGHT:
254 widget->x -= (width - screen_width) / 2;
255 widget->width += (width - screen_width);
256 break;
257 }
258 switch (widget->anchor & GR_VERTICAL_MASK) {
259 case GR_BOTTOM:
260 widget->y -= (height - screen_height) / 2;
261 break;
262 case GR_TOP:
263 widget->y += (height - screen_height) / 2;
264 break;
265 case GR_LEFT|GR_RIGHT:
266 widget->y -= (height - screen_height) / 2;
267 widget->height += (width - screen_height);
268 break;
269 }
270 glut_widget_calc_bbox (widget);
271 }
272 }
273
274 screen_width = width;
275 screen_height = height;
276
277 list = glut_objects;
278 gr_FOREACH (list, root) {
279 if (root->reshape_func) {
280 (*root->reshape_func) (root, width, height);
281 }
282 }
283
284 tcl_ReshapeFunc (width, height);
285
286 glutPostRedisplay ();
287 }
288
289 void
glut_keyboard_func(unsigned char key,int x,int y)290 glut_keyboard_func (unsigned char key, int x, int y)
291 {
292 GrSList *list;
293 GlutWidget *widget;
294 int done = 0;
295 int modifiers;
296 int wx, wy;
297
298 wx = x - screen_width / 2;
299 wy = screen_height / 2 - y;
300
301 if (focus_widget) {
302 widget = focus_widget;
303 (widget->keyboard_func) (widget, key, wx, wy);
304 done = 1;
305 } else {
306 list = glut_objects;
307 gr_FOREACH (list, widget) {
308 if (glut_widget_check_bbox (widget, wx, wy)) {
309 if (widget->keyboard_func) {
310 (*widget->keyboard_func) (widget, key, wx, wy);
311 done = 1;
312 }
313 }
314 }
315 }
316 if (!done) {
317 switch (key) {
318 case '\t':
319 modifiers = glutGetModifiers ();
320 if (modifiers) {
321 glut_widget_set_focus (glut_previous_focus_widget (NULL));
322 } else {
323 glut_widget_set_focus (glut_next_focus_widget (NULL));
324 }
325 break;
326 }
327 }
328
329 tcl_KeyboardFunc (key, x, y);
330 }
331
332 static GlutObject
find_left_focusable_widget(GlutObject obj)333 find_left_focusable_widget (GlutObject obj)
334 {
335 GlutWidget *widget = obj;
336 GlutWidget *w;
337 GlutWidget *candidate = NULL;
338 GrSList *list;
339 int distance = 0, new_distance;
340
341 if (!glut_widget_is_focusable (widget))
342 return glut_next_focus_widget (NULL);
343
344 list = glut_objects;
345 gr_FOREACH (list, w) {
346 if (!glut_widget_is_focusable (w))
347 continue;
348
349 if (!(widget->x0 >= w->x1))
350 continue;
351
352 new_distance = fabs (w->x1 - widget->x0) + fabs (w->y0 - widget->y0) * 5;
353 if (candidate == NULL || distance > new_distance) {
354 candidate = w;
355 distance = new_distance;
356 }
357 }
358 return candidate;
359 }
360
361 static GlutObject
find_right_focusable_widget(GlutObject obj)362 find_right_focusable_widget (GlutObject obj)
363 {
364 GlutWidget *widget = obj;
365 GlutWidget *w;
366 GlutWidget *candidate = NULL;
367 GrSList *list;
368 int distance = 0, new_distance;
369
370 if (!glut_widget_is_focusable (widget))
371 return glut_next_focus_widget (NULL);
372
373 list = glut_objects;
374 gr_FOREACH (list, w) {
375 if (!glut_widget_is_focusable (w))
376 continue;
377
378 if (!(widget->x1 <= w->x0))
379 continue;
380
381 new_distance = fabs (w->x0 - widget->x1) + fabs (w->y0 - widget->y0) * 5;
382 if (candidate == NULL || distance > new_distance) {
383 candidate = w;
384 distance = new_distance;
385 }
386 }
387 return candidate;
388 }
389
390 static GlutObject
find_upper_focusable_widget(GlutObject obj)391 find_upper_focusable_widget (GlutObject obj)
392 {
393 GlutWidget *widget = obj;
394 GlutWidget *w;
395 GlutWidget *candidate = NULL;
396 GrSList *list;
397 int distance = 0, new_distance;
398
399 if (!glut_widget_is_focusable (widget))
400 return glut_next_focus_widget (NULL);
401
402 list = glut_objects;
403 gr_FOREACH (list, w) {
404 if (!glut_widget_is_focusable (w))
405 continue;
406
407 if (!(widget->y1 <= w->y0))
408 continue;
409
410 new_distance = fabs (w->x0 - widget->x0) * 2 + fabs (w->y0 - widget->y1);
411 if (candidate == NULL || distance > new_distance) {
412 candidate = w;
413 distance = new_distance;
414 }
415 }
416 return candidate;
417 }
418
419 static GlutObject
find_lower_focusable_widget(GlutObject obj)420 find_lower_focusable_widget (GlutObject obj)
421 {
422 GlutWidget *widget = obj;
423 GlutWidget *w;
424 GlutWidget *candidate = NULL;
425 GrSList *list;
426 int distance = 0, new_distance;
427
428 if (!glut_widget_is_focusable (widget))
429 return glut_next_focus_widget (NULL);
430
431 list = glut_objects;
432 gr_FOREACH (list, w) {
433 if (!glut_widget_is_focusable (w))
434 continue;
435
436 if (!(widget->y0 >= w->y1))
437 continue;
438
439 new_distance = fabs (w->x0 - widget->x0) * 2 + fabs (w->y1 - widget->y0);
440 if (candidate == NULL || distance > new_distance) {
441 candidate = w;
442 distance = new_distance;
443 }
444 }
445 return candidate;
446 }
447
448 void
glut_special_func(int key,int x,int y)449 glut_special_func (int key, int x, int y)
450 {
451 GrSList *list;
452 GlutWidget *widget;
453 int done = 0;
454 int wx, wy;
455
456 wx = x - screen_width / 2;
457 wy = screen_height / 2 - y;
458
459 if (focus_widget) {
460 widget = focus_widget;
461 if (widget->special_func) {
462 (widget->special_func) (widget, key, wx, wy);
463 done = 1;
464 }
465 } else {
466 list = glut_objects;
467 gr_FOREACH (list, widget) {
468 if (glut_widget_check_bbox (widget, wx, wy)) {
469 if (widget->special_func) {
470 (*widget->special_func) (widget, key, wx, wy);
471 done = 1;
472 }
473 }
474 }
475 }
476 if (!done) {
477 switch (key) {
478 case GLUT_KEY_LEFT:
479 if ((widget = find_left_focusable_widget (focus_widget))) {
480 glut_widget_set_focus (widget);
481 }
482 break;
483
484 case GLUT_KEY_RIGHT:
485 if ((widget = find_right_focusable_widget (focus_widget))) {
486 glut_widget_set_focus (widget);
487 }
488 break;
489
490 case GLUT_KEY_UP:
491 if ((widget = find_upper_focusable_widget (focus_widget))) {
492 glut_widget_set_focus (widget);
493 }
494 break;
495
496 case GLUT_KEY_DOWN:
497 if ((widget = find_lower_focusable_widget (focus_widget))) {
498 glut_widget_set_focus (widget);
499 }
500 break;
501 }
502 }
503
504 tcl_SpecialFunc (key, x, y);
505 }
506
507 void
glut_mouse_func(int button,int state,int x,int y)508 glut_mouse_func (int button, int state, int x, int y)
509 {
510 GrSList *list;
511 GlutWidget *widget;
512 int wx, wy;
513
514 wx = x - screen_width / 2;
515 wy = screen_height / 2 - y;
516
517 if (state == GLUT_DOWN)
518 grab_widget = NULL;
519
520 list = glut_objects;
521 gr_FOREACH (list, widget) {
522 if (glut_widget_check_bbox (widget, wx, wy)) {
523 if (widget->mouse_func) {
524 (*widget->mouse_func) (widget, button, state, wx, wy);
525 }
526 }
527 }
528 if (state == GLUT_UP)
529 grab_widget = NULL;
530
531 tcl_MouseFunc (button, state, x, y);
532 }
533
534 void
glut_passive_motion_func(int x,int y)535 glut_passive_motion_func (int x, int y)
536 {
537 GrSList *list;
538 GlutWidget *widget;
539 int redraw = 0;
540 int wx, wy;
541
542 wx = x - screen_width / 2;
543 wy = screen_height / 2 - y;
544
545 list = glut_objects;
546 gr_FOREACH (list, widget) {
547 if (widget->core.type != GR_GLUT_ROOT_WIDGET)
548 continue;
549
550 if (glut_widget_check_bbox (widget, wx, wy)) {
551 if (!widget->active) {
552 redraw = 1;
553 widget->active = 1;
554 }
555 } else {
556 if (widget->active) {
557 redraw = 1;
558 widget->active = 0;
559 }
560 }
561 }
562 if (redraw) {
563 glut_post_redisplay ();
564 }
565
566 tcl_PassiveMotionFunc (x, y);
567 }
568
569 int
glut_bitmap_string_width(void * font,char * string,int length)570 glut_bitmap_string_width (void *font, char *string, int length)
571 {
572 int i;
573 int width = 0;
574
575 if (!string)
576 return 0;
577
578 for (i=0; i<length; i++) {
579 width += glutBitmapWidth (font, string[i]);
580 }
581 return width;
582 }
583
584 static void
glut_widgets_initialize(void)585 glut_widgets_initialize (void)
586 {
587 int i;
588 GlutFontInfo *finfo;
589
590 for (i=0; bitmap_font_infos[i].font != NULL; i++) {
591 finfo = bitmap_font_infos + i;
592
593 finfo->dl = glGenLists (128);
594 }
595 }
596
597 void
glut_root_destroy(GlutObject obj)598 glut_root_destroy (GlutObject obj)
599 {
600 GlutRoot *root = obj;
601
602 glDeleteLists (root->dl, 1);
603 free (root);
604 }
605
606 void
glut_root_init(GlutObject obj)607 glut_root_init (GlutObject obj)
608 {
609 static int do_init = 1;
610 GlutRoot *root = obj;
611
612 if (do_init) {
613 glut_widgets_initialize ();
614 do_init = 0;
615 }
616
617 gr_FREE_FUNC (obj, glut_root_destroy);
618
619 GL_CHECK(root->dl = glGenLists (1));
620 root->changed = 1;
621
622 root->type = GR_GLUT_ROOT;
623 }
624
625 GlutObject
glut_root_new(void)626 glut_root_new (void)
627 {
628 GlutRoot *root;
629
630 root = gr_new0 (GlutRoot, 1);
631 glut_root_init (root);
632
633 return root;
634 }
635
636 void
glut_widget_destroy(GlutObject obj)637 glut_widget_destroy (GlutObject obj)
638 {
639 GlutWidget *widget = obj;
640
641 glut_root_destroy ((GlutRoot *) widget);
642 }
643
644 void
glut_widget_init(GlutObject obj)645 glut_widget_init (GlutObject obj)
646 {
647 GlutWidget *widget = obj;
648
649 glut_root_init (&widget->core);
650
651 gr_FREE_FUNC (obj, glut_root_destroy);
652 widget->core.type = GR_GLUT_ROOT_WIDGET;
653
654 widget->font = GLUT_BITMAP_HELVETICA_18;
655 memcpy (widget->fg_color, default_fg_color, sizeof (default_fg_color));
656 memcpy (widget->bg_color, default_bg_color, sizeof (default_bg_color));
657 }
658
659 GlutObject
glut_widget_new(void)660 glut_widget_new (void)
661 {
662 GlutWidget *widget;
663
664 widget = gr_new0 (GlutWidget, 1);
665 glut_widget_init (widget);
666
667 return widget;
668 }
669
670 static void
glut_widget_calc_bbox(GlutObject obj)671 glut_widget_calc_bbox (GlutObject obj)
672 {
673 GlutWidget *widget = obj;
674
675 switch (widget->anchor & GR_HORIZONTAL_MASK) {
676 case GR_CENTER:
677 widget->x0 = widget->x - widget->width / 2;
678 widget->x1 = widget->x + widget->width / 2;
679 break;
680
681 default:
682 case GR_LEFT:
683 widget->x0 = widget->x;
684 widget->x1 = widget->x + widget->width;
685 break;
686
687 case GR_RIGHT:
688 widget->x0 = widget->x - widget->width;
689 widget->x1 = widget->x;
690 break;
691 }
692
693 switch (widget->anchor & GR_VERTICAL_MASK) {
694 case GR_CENTER:
695 widget->y0 = widget->y - widget->height / 2;
696 widget->y1 = widget->y + widget->height / 2;
697 break;
698
699 default:
700 case GR_TOP:
701 widget->y0 = widget->y - widget->height;
702 widget->y1 = widget->y;
703 break;
704
705 case GR_BOTTOM:
706 widget->y0 = widget->y;
707 widget->y1 = widget->y + widget->height;
708 break;
709 }
710 }
711
712 static int
glut_widget_check_bbox(GlutObject obj,int x,int y)713 glut_widget_check_bbox (GlutObject obj, int x, int y)
714 {
715 GlutWidget *widget = obj;
716
717 if (widget->core.type != GR_GLUT_ROOT_WIDGET)
718 return 0;
719
720 return (x >= widget->x0 &&
721 y >= widget->y0 &&
722 x <= widget->x1 &&
723 y <= widget->y1);
724 }
725
726 static void
glut_widget_check_size(GlutObject obj)727 glut_widget_check_size (GlutObject obj)
728 {
729 GlutWidget *widget = obj;
730
731 if (widget->check_size) {
732 (*widget->check_size) (obj);
733 }
734
735 glut_widget_calc_bbox (obj);
736 }
737
738 void
glut_widget_set_position(GlutObject obj,int x,int y,int anchor)739 glut_widget_set_position (GlutObject obj, int x, int y, int anchor)
740 {
741 GlutWidget *widget = obj;
742
743 switch (anchor & GR_HORIZONTAL_MASK) {
744 case GR_LEFT|GR_RIGHT:
745 case GR_LEFT:
746 widget->x = -screen_width / 2 + x;
747 break;
748 case GR_RIGHT:
749 widget->x = screen_width / 2 + x;
750 break;
751 default:
752 widget->x = x;
753 }
754 switch (anchor & GR_VERTICAL_MASK) {
755 case GR_BOTTOM|GR_TOP:
756 case GR_BOTTOM:
757 widget->y = -screen_height / 2 + y;
758 break;
759 case GR_TOP:
760 widget->y = screen_height / 2 + y;
761 break;
762 default:
763 widget->y = y;
764 }
765 widget->anchor = anchor;
766
767 glut_widget_check_size (obj);
768 }
769
770 void
glut_widget_set_size(GlutObject obj,int width,int height)771 glut_widget_set_size (GlutObject obj, int width, int height)
772 {
773 GlutWidget *widget = obj;
774
775 widget->width = width;
776 widget->height = height;
777
778 glut_widget_check_size (obj);
779 }
780
781 void
glut_widget_move(GlutObject obj,int dx,int dy)782 glut_widget_move (GlutObject obj, int dx, int dy)
783 {
784 GlutWidget *widget = obj;
785
786 widget->x += dx;
787 widget->y += dy;
788
789 glut_widget_calc_bbox (obj);
790
791 glut_post_redisplay ();
792 }
793
794 void
glut_widget_set_font(GlutObject obj,void * font)795 glut_widget_set_font (GlutObject obj, void *font)
796 {
797 GlutWidget *widget = obj;
798
799 widget->font = font;
800 glut_widget_check_size (obj);
801 }
802
803 int
glut_widget_is_focusable(GlutObject obj)804 glut_widget_is_focusable (GlutObject obj)
805 {
806 GlutWidget *widget = obj;
807
808 return (widget && widget->core.type == GR_GLUT_ROOT_WIDGET &&
809 (widget->keyboard_func || widget->special_func));
810 }
811
812 void
glut_widget_set_focus(GlutObject obj)813 glut_widget_set_focus (GlutObject obj)
814 {
815 GlutWidget *widget = obj;
816
817 if (!obj) {
818 return;
819 }
820
821 if (!glut_widget_is_focusable (widget))
822 return;
823
824 if (focus_widget != widget) {
825 focus_widget = widget;
826 glut_post_redisplay ();
827 }
828 }
829
830 static int
glut_widget_state(GlutObject obj)831 glut_widget_state (GlutObject obj)
832 {
833 GlutWidget *widget = obj;
834
835 if (grab_widget == obj)
836 return GR_STATE_GRAB;
837 if (focus_widget == obj)
838 return GR_STATE_FOCUS;
839 if (widget->active)
840 return GR_STATE_ACTIVE;
841
842 return GR_STATE_NORMAL;
843 }
844
845 static int
glut_label_width(GlutObject obj)846 glut_label_width (GlutObject obj)
847 {
848 GlutLabel *label = obj;
849 int i, j, width = 0, new_width;
850
851 for (i=0; i<label->length; i++) {
852 for (j=i; j<label->length && label->title[j] != '\n'; j++) {}
853 new_width = glut_bitmap_string_width (label->core.font,
854 label->title + i, j - i);
855 if (new_width > width)
856 width = new_width;
857 i = j + 1;
858 }
859 return width;
860 }
861
862 static void
glut_label_check_size(GlutObject obj)863 glut_label_check_size (GlutObject obj)
864 {
865 GlutWidget *widget = obj;
866 GlutLabel *label = obj;
867 int width;
868 GlutFontInfo *finfo;
869
870 width = glut_label_width (obj);
871 finfo = glut_get_bitmap_font_info (widget->font);
872
873 widget->width = width;
874 label->str_width = width;
875
876 if (widget->height < finfo->height) {
877 widget->height = finfo->height;
878 }
879
880 }
881
882 void
glut_label_destroy(GlutObject obj)883 glut_label_destroy (GlutObject obj)
884 {
885 GlutLabel *label = obj;
886
887 free (label->title);
888 glut_widget_destroy (obj);
889 }
890
891 void
glut_label_init(GlutObject obj)892 glut_label_init (GlutObject obj)
893 {
894 GlutLabel *label = obj;
895
896 glut_widget_init (&label->core);
897
898 gr_FREE_FUNC (label, glut_label_destroy);
899 label->core.type = GR_GLUT_WIDGET_LABEL;
900 label->core.core.display_func = default_label_display;
901
902 label->core.check_size = glut_label_check_size;
903
904 label->justify = -1;
905 }
906
907 GlutObject
glut_label_new(void)908 glut_label_new (void)
909 {
910 GlutLabel *label;
911
912 label = gr_new0 (GlutLabel, 1);
913 glut_label_init (label);
914
915 return label;
916 }
917
918 void
glut_label_set_text(GlutObject obj,char * text)919 glut_label_set_text (GlutObject obj, char *text)
920 {
921 GlutLabel *label = obj;
922
923 if (label->title)
924 free (label->title);
925 label->title = NULL;
926
927 if (!text) {
928 label->length = 0;
929 } else {
930 label->length = strlen (text);
931 label->title = strdup (text);
932 }
933
934 glut_widget_check_size (obj);
935 }
936
937 void
glut_button_check_size(GlutObject obj)938 glut_button_check_size (GlutObject obj)
939 {
940 GlutWidget *widget = obj;
941 GlutButton *button = obj;
942 int width;
943 GlutFontInfo *finfo;
944
945 width = glut_bitmap_string_width (widget->font, button->title, button->length);
946 button->str_width = width;
947
948 if (widget->width < width + 10) {
949 widget->width = width + 10;
950 }
951
952 finfo = glut_get_bitmap_font_info (widget->font);
953
954 if (widget->height < finfo->height + 4) {
955 widget->height = finfo->height + 4;
956 }
957 }
958
959 void
glut_button_destroy(GlutObject obj)960 glut_button_destroy (GlutObject obj)
961 {
962 GlutButton *button = obj;
963
964 free (button->title);
965 glut_widget_destroy (obj);
966 }
967
968 void
glut_button_init(GlutObject obj)969 glut_button_init (GlutObject obj)
970 {
971 GlutButton *button = obj;
972
973 glut_widget_init (&button->core);
974
975 button->core.type = GR_GLUT_WIDGET_BUTTON;
976
977 gr_FREE_FUNC (button, glut_button_destroy);
978 button->core.core.display_func = default_button_display;
979 button->core.keyboard_func = default_button_keyboard;
980 button->core.mouse_func = default_button_mouse;
981
982 button->core.check_size = glut_button_check_size;
983 }
984
985 GlutObject
glut_button_new(void)986 glut_button_new (void)
987 {
988 GlutButton *button;
989
990 button = gr_new0 (GlutButton, 1);
991 glut_button_init (button);
992
993 return button;
994 }
995
996 void
glut_button_set_text(GlutObject obj,char * text)997 glut_button_set_text (GlutObject obj, char *text)
998 {
999 GlutButton *button = obj;
1000
1001 if (button->title) {
1002 free (button->title);
1003 button->title = NULL;
1004 }
1005
1006 if (!text) {
1007 button->length = 0;
1008 } else {
1009 button->length = strlen (text);
1010 button->title = strdup (text);
1011 }
1012 glut_widget_check_size (obj);
1013 }
1014
1015 void
glut_entry_check_size(GlutObject obj)1016 glut_entry_check_size (GlutObject obj)
1017 {
1018 GlutWidget *widget = obj;
1019 GlutFontInfo *finfo;
1020
1021 finfo = glut_get_bitmap_font_info (widget->font);
1022
1023 if (widget->height < finfo->height + finfo->descent) {
1024 widget->height = finfo->height + finfo->descent;
1025 }
1026 }
1027
1028 static void
button_timer_func(int value)1029 button_timer_func (int value)
1030 {
1031 GlutButton *button = grab_widget;
1032
1033 if (!button || button->core.core.type != GR_GLUT_ROOT_WIDGET ||
1034 button->core.type != GR_GLUT_WIDGET_BUTTON)
1035 return;
1036
1037 if (button->invoke_func) {
1038 (*button->invoke_func) (button);
1039 }
1040 grab_widget = NULL;
1041 glut_post_redisplay ();
1042 }
1043
1044 void
glut_button_invoke(GlutObject obj)1045 glut_button_invoke (GlutObject obj)
1046 {
1047 GlutButton *button = obj;
1048
1049 if (button->invoke_func) {
1050 grab_widget = obj;
1051 glut_post_redisplay ();
1052 glutTimerFunc (200, button_timer_func, 0);
1053 }
1054 }
1055
1056 void
glut_entry_destroy(GlutObject obj)1057 glut_entry_destroy (GlutObject obj)
1058 {
1059 glut_widget_destroy (obj);
1060 }
1061
1062 void
glut_entry_init(GlutObject obj)1063 glut_entry_init (GlutObject obj)
1064 {
1065 GlutEntry *entry = obj;
1066 glut_widget_init (&entry->core);
1067
1068 entry->core.type = GR_GLUT_WIDGET_ENTRY;
1069
1070 gr_FREE_FUNC (entry, glut_entry_destroy);
1071 entry->core.core.display_func = default_entry_display;
1072 entry->core.keyboard_func = default_entry_keyboard;
1073 entry->core.mouse_func = default_entry_mouse;
1074
1075 entry->core.check_size = glut_entry_check_size;
1076 }
1077
1078 GlutObject
glut_entry_new(void)1079 glut_entry_new (void)
1080 {
1081 GlutEntry *entry;
1082
1083 entry = gr_new0 (GlutEntry, 1);
1084 glut_entry_init (entry);
1085
1086 return entry;
1087 }
1088
1089 void
glut_entry_set_text(GlutObject obj,char * text)1090 glut_entry_set_text (GlutObject obj, char *text)
1091 {
1092 GlutEntry *entry = obj;
1093 int length;
1094
1095 if (!text) {
1096 entry->length = 0;
1097 entry->display_pos = 0;
1098 entry->calet_pos = 0;
1099 } else {
1100 length = strlen (text);
1101 if (length > 1024)
1102 length = 1024;
1103 entry->length = length;
1104 strncpy (entry->text, text, length);
1105 entry->display_pos = 0;
1106 if (entry->calet_pos > entry->length) {
1107 entry->calet_pos = entry->length;
1108 }
1109 }
1110
1111 glut_widget_check_size (obj);
1112 }
1113
1114 void
glut_entry_invoke(GlutObject obj)1115 glut_entry_invoke (GlutObject obj)
1116 {
1117 GlutEntry *entry = obj;
1118
1119 if (entry->invoke_func) {
1120 (*entry->invoke_func) (entry);
1121 }
1122 }
1123
1124 GlutObject
glut_last_object(void)1125 glut_last_object (void)
1126 {
1127 GrSList *list;
1128
1129 if (!glut_objects)
1130 return NULL;
1131
1132 for (list=glut_objects;list->next!=NULL; list=list->next) {}
1133
1134 return list->data;
1135 }
1136
1137 GlutObject
glut_previous_object(GlutObject from)1138 glut_previous_object (GlutObject from)
1139 {
1140 GrSList *list;
1141
1142 if (!glut_objects)
1143 return NULL;
1144
1145 if (glut_objects->data == from) {
1146 return glut_last_object ();
1147 } else {
1148 for (list=glut_objects;
1149 list->next && list->next->data != from;
1150 list=list->next)
1151 {}
1152 if (list) {
1153 return list->data;
1154 } else {
1155 return NULL;
1156 }
1157 }
1158 }
1159
1160 GlutObject
glut_next_object(GlutObject from)1161 glut_next_object (GlutObject from)
1162 {
1163 GrSList *list;
1164
1165 if (!glut_objects)
1166 return NULL;
1167
1168 for (list=glut_objects; list!=NULL; list=list->next) {
1169 if (list->data == from)
1170 break;
1171 }
1172 if (list && list->next) {
1173 return list->next->data;
1174 } else {
1175 return glut_objects->data;
1176 }
1177 }
1178
1179 GlutObject
glut_previous_focus_widget(GlutObject current)1180 glut_previous_focus_widget (GlutObject current)
1181 {
1182 GrSList *list;
1183 GlutWidget *widget;
1184 GlutObject candidate = NULL;
1185
1186 if (!current) {
1187 for (list=glut_objects; list!=NULL; list=list->next) {
1188 widget = list->data;
1189 if (glut_widget_is_focusable (widget)) {
1190 candidate = widget;
1191 }
1192 }
1193 return candidate;
1194 } else {
1195 widget = current;
1196 do {
1197 widget = glut_previous_object (widget);
1198 } while (!(widget == current || glut_widget_is_focusable (widget)));
1199
1200 return widget;
1201 }
1202 }
1203
1204 GlutObject
glut_next_focus_widget(GlutObject current)1205 glut_next_focus_widget (GlutObject current)
1206 {
1207 GrSList *list;
1208 GlutWidget *widget;
1209
1210 if (!current) {
1211 for (list=glut_objects; list!=NULL; list=list->next) {
1212 widget = list->data;
1213 if (glut_widget_is_focusable (widget)) {
1214 break;
1215 }
1216 }
1217 return (list)? list->data : NULL;
1218 } else {
1219 widget = current;
1220 do {
1221 widget = glut_next_object (widget);
1222 } while (!(widget == current || glut_widget_is_focusable (widget)));
1223
1224 return widget;
1225 }
1226 }
1227
1228 static void
default_button_display(GlutObject obj)1229 default_button_display (GlutObject obj)
1230 {
1231 GlutWidget *widget = obj;
1232 GlutButton *button = obj;
1233 int x, y;
1234 int dx, dy;
1235 int state;
1236 GlutFontInfo *finfo;
1237
1238 state = glut_widget_state (obj);
1239
1240 if (grab_widget == widget) {
1241 dx = dy = 2;
1242 } else {
1243 dx = dy = 0;
1244 }
1245
1246 finfo = glut_get_bitmap_font_info (widget->font);
1247
1248 glColor4fv (widget->bg_color[state]);
1249 glRecti (widget->x0+dx, widget->y0-dy, widget->x1+dx, widget->y1-dy);
1250
1251 if (button->length > 0) {
1252 x = widget->x0 + (widget->width - button->str_width) / 2 + dx;
1253 y = widget->y0 + (widget->height - finfo->height) / 2 + finfo->descent -dy;
1254
1255 glColor4f (widget->bg_color[state][0] * 0.8,
1256 widget->bg_color[state][1] * 0.8,
1257 widget->bg_color[state][2] * 0.8,
1258 widget->bg_color[state][3]);
1259 glRasterPos3i (x+2, y-2, 1);
1260 glut_bitmap_string (widget->font, button->title, button->length);
1261
1262 glColor4fv (widget->fg_color[state]);
1263 glRasterPos3i (x, y, 2);
1264 glut_bitmap_string (widget->font, button->title, button->length);
1265 }
1266
1267 glColor4f (widget->bg_color[state][0] * 0.6,
1268 widget->bg_color[state][1] * 0.6,
1269 widget->bg_color[state][2] * 0.6,
1270 widget->bg_color[state][3]);
1271 glRecti (widget->x0+4, widget->y0-4, widget->x1+4, widget->y1-4);
1272 }
1273
1274 static void
default_button_keyboard(GlutObject obj,unsigned char key,int x,int y)1275 default_button_keyboard (GlutObject obj, unsigned char key, int x, int y)
1276 {
1277 int modifiers;
1278
1279 switch (key) {
1280 case ' ':
1281 case '\r':
1282 glut_button_invoke (obj);
1283 break;
1284
1285 case '\t':
1286 modifiers = glutGetModifiers ();
1287 if (modifiers) {
1288 focus_widget = glut_previous_focus_widget (focus_widget);
1289 } else {
1290 focus_widget = glut_next_focus_widget (focus_widget);
1291 }
1292 glut_post_redisplay ();
1293 break;
1294 }
1295 }
1296
1297 static void
default_button_mouse(GlutObject obj,int button,int state,int x,int y)1298 default_button_mouse (GlutObject obj, int button, int state, int x, int y)
1299 {
1300 GlutButton *b = obj;
1301
1302 if (state == GLUT_DOWN) {
1303 grab_widget = obj;
1304 focus_widget = obj;
1305 glut_post_redisplay ();
1306 } else {
1307 if (b->invoke_func && grab_widget == b) {
1308 (*b->invoke_func) (obj);
1309 }
1310 grab_widget = NULL;
1311 glut_post_redisplay ();
1312 }
1313 }
1314
1315 static void
default_entry_display(GlutObject obj)1316 default_entry_display (GlutObject obj)
1317 {
1318 GlutWidget *widget = obj;
1319 GlutEntry *entry = obj;
1320 int width;
1321 GlutFontInfo *finfo;
1322 int state;
1323 int calet_width;
1324 int x, y;
1325 int i;
1326
1327 state = glut_widget_state (obj);
1328
1329 glColor4fv (widget->bg_color[state]);
1330 glBegin (GL_LINE_LOOP);
1331 glVertex3i (widget->x0, widget->y0, 2);
1332 glVertex3i (widget->x1, widget->y0, 2);
1333 glVertex3i (widget->x1, widget->y1, 2);
1334 glVertex3i (widget->x0, widget->y1, 2);
1335 glEnd ();
1336
1337 if (entry->calet_pos < entry->display_pos) {
1338 entry->display_pos = entry->calet_pos;
1339 }
1340 width = glut_bitmap_string_width (widget->font,
1341 entry->text + entry->display_pos,
1342 entry->calet_pos - entry->display_pos);
1343 if (width > widget->width - 10) {
1344 width -= widget->width - 10;
1345
1346 for (i=entry->display_pos; i<entry->length && width > 0; i++) {
1347 width -= glutBitmapWidth (widget->font, entry->text[i]);
1348 }
1349 entry->display_pos = i;
1350 }
1351
1352 finfo = glut_get_bitmap_font_info (widget->font);
1353 x = widget->x0 + 2;
1354 y = widget->y0 + finfo->descent +
1355 (widget->height - finfo->height) / 2;
1356
1357 if (state == GR_STATE_FOCUS) {
1358 if (entry->calet_pos >= entry->display_pos) {
1359 calet_width = glut_bitmap_string_width (widget->font,
1360 entry->text + entry->display_pos,
1361 entry->calet_pos - entry->display_pos);
1362 if (entry->calet_pos < entry->length) {
1363 width = glutBitmapWidth (widget->font, entry->text[entry->calet_pos]);
1364 } else {
1365 width = glutBitmapWidth (widget->font, ' ');
1366 }
1367
1368 GL_CHECK(glColor3f (1, 0, 0));
1369 GL_CHECK(glRecti (x + calet_width - 1, widget->y0 + 1,
1370 x + calet_width + width, widget->y1 - 1));
1371 }
1372 width = 4;
1373 GL_CHECK(glColor4fv (widget->fg_color[state]));
1374 GL_CHECK(glRasterPos3i (x, y, 1));
1375 for (i=entry->display_pos; i<entry->length; i++) {
1376 width += glutBitmapWidth (widget->font, entry->text[i]);
1377 if (width > widget->width)
1378 break;
1379 }
1380 glut_bitmap_string (widget->font, entry->text + entry->display_pos,
1381 i - entry->display_pos);
1382 } else {
1383 glColor4fv (widget->fg_color[state]);
1384 glRasterPos3i (x, y, 1);
1385 width = 10;
1386 for (i=entry->display_pos; i<entry->length; i++) {
1387 width += glutBitmapWidth (widget->font, entry->text[i]);
1388 if (width > widget->width)
1389 break;
1390 }
1391 glut_bitmap_string (widget->font, entry->text + entry->display_pos,
1392 i - entry->display_pos);
1393 }
1394 }
1395
1396
1397 #define CTRL(key) (key - 'a' + 1)
1398
1399 static void
default_entry_keyboard(GlutObject obj,unsigned char key,int x,int y)1400 default_entry_keyboard (GlutObject obj, unsigned char key, int x, int y)
1401 {
1402 GlutEntry *entry = obj;
1403 int modifiers;
1404
1405 modifiers = glutGetModifiers ();
1406 switch (key) {
1407 case '\r':
1408 glut_entry_invoke (obj);
1409 break;
1410
1411 case '\t':
1412 if (modifiers) {
1413 focus_widget = glut_previous_focus_widget (focus_widget);
1414 } else {
1415 focus_widget = glut_next_focus_widget (focus_widget);
1416 }
1417 glut_post_redisplay ();
1418 break;
1419
1420 case CTRL('a'):
1421 entry->calet_pos = 0;
1422 glut_post_redisplay ();
1423 break;
1424
1425 case CTRL('b'):
1426 if (entry->calet_pos > 0) {
1427 entry->calet_pos --;
1428 glut_post_redisplay ();
1429 }
1430 break;
1431
1432 case CTRL('d'):
1433 if (entry->calet_pos < entry->length) {
1434 memmove (entry->text + entry->calet_pos,
1435 entry->text + entry->calet_pos + 1,
1436 entry->length - entry->calet_pos - 1);
1437 entry->length --;
1438 if (entry->changed_func) {
1439 (*entry->changed_func) (entry);
1440 }
1441 glut_post_redisplay ();
1442 }
1443 break;
1444
1445 case CTRL('e'):
1446 entry->calet_pos = entry->length;
1447 glut_post_redisplay ();
1448 break;
1449
1450 case CTRL('f'):
1451 if (entry->calet_pos < entry->length) {
1452 entry->calet_pos ++;
1453 glut_post_redisplay ();
1454 }
1455 break;
1456
1457 case CTRL('h'):
1458 if (entry->calet_pos > 0) {
1459 memmove (entry->text + entry->calet_pos - 1,
1460 entry->text + entry->calet_pos,
1461 entry->length - entry->calet_pos);
1462 entry->length --;
1463 entry->calet_pos --;
1464 if (entry->changed_func) {
1465 (*entry->changed_func) (entry);
1466 }
1467 glut_post_redisplay ();
1468 }
1469 break;
1470
1471 case CTRL('k'):
1472 entry->length = entry->calet_pos;
1473 if (entry->changed_func) {
1474 (*entry->changed_func) (entry);
1475 }
1476 glut_post_redisplay ();
1477 break;
1478
1479 default:
1480 if (isprint (key)) {
1481 memmove (entry->text + entry->calet_pos + 1,
1482 entry->text + entry->calet_pos,
1483 entry->length - entry->calet_pos);
1484 entry->text[entry->calet_pos] = key;
1485 entry->length ++;
1486 entry->calet_pos ++;
1487
1488 if (entry->changed_func) {
1489 (*entry->changed_func) (entry);
1490 }
1491
1492 glut_post_redisplay ();
1493 }
1494 }
1495 }
1496
1497 static void
default_entry_mouse(GlutObject obj,int button,int state,int x,int y)1498 default_entry_mouse (GlutObject obj, int button, int state, int x, int y)
1499 {
1500 GlutWidget *widget = obj;
1501 GlutEntry *entry = obj;
1502 int pos;
1503 int width = 0;
1504 int i;
1505
1506 if (state == GLUT_DOWN) {
1507 focus_widget = obj;
1508
1509 pos = x - widget->x0 - 2;
1510 for (i=entry->display_pos; i<entry->length; i++) {
1511 width += glutBitmapWidth (widget->font, entry->text[i]);
1512 if (pos < width)
1513 break;
1514 }
1515 entry->calet_pos = i;
1516 glut_post_redisplay ();
1517 }
1518 }
1519
1520 static void
glut_bitmap_string(void * font,char * string,int length)1521 glut_bitmap_string (void *font, char *string, int length)
1522 {
1523 int i, c;
1524 GlutFontInfo *finfo = glut_get_bitmap_font_info (font);
1525
1526 if (!finfo)
1527 return;
1528
1529 for (i=0; i<length; i++) {
1530 c = string [i];
1531 if (finfo->compiled[c]) {
1532 glCallList (finfo->dl + c);
1533 } else {
1534 glNewList (finfo->dl + c, GL_COMPILE_AND_EXECUTE);
1535 glutBitmapCharacter (finfo->font, c);
1536 glEndList ();
1537 finfo->compiled[c] = 1;
1538 }
1539 }
1540 }
1541
1542 static void
default_label_display(GlutObject obj)1543 default_label_display (GlutObject obj)
1544 {
1545 GlutWidget *widget = obj;
1546 GlutLabel *label = obj;
1547 GlutFontInfo *finfo;
1548 int i, j, line;
1549 int width = 0;
1550 int x = 0, y;
1551
1552 if (label->justify == -1)
1553 label->justify = widget->anchor & GR_HORIZONTAL_MASK;
1554
1555 if (label->length > 0) {
1556 switch (label->justify) {
1557 case GR_LEFT:
1558 x = widget->x0;
1559 break;
1560
1561 case GR_RIGHT:
1562 width = glut_label_width (obj) / 2;
1563 break;
1564 }
1565
1566 glColor4fv (widget->fg_color[GR_STATE_NORMAL]);
1567
1568 finfo = glut_get_bitmap_font_info (widget->font);
1569 j=0;
1570 glRasterPos3i (widget->x0, widget->y0 + finfo->descent, 1);
1571 line = 0;
1572 for (i=0; i<label->length; i++) {
1573 for (j=i; j<label->length && label->title[j] != '\n'; j++) {}
1574 y = widget->y0 + finfo->descent - (finfo->height + finfo->descent) * line;
1575 if (y < screen_height / 2 && y > -screen_height / 2) {
1576 switch (label->justify) {
1577 case GR_CENTER:
1578 width = glut_bitmap_string_width (widget->font, label->title + i, j-i);
1579 x = widget->x0 + (widget->width - width) / 2;
1580 break;
1581
1582 case GR_RIGHT:
1583 x = widget->x1 - glut_bitmap_string_width (widget->font,
1584 label->title + i, j-i);
1585 break;
1586 }
1587 glRasterPos3i (x, widget->y0 + finfo->descent
1588 - (finfo->height + finfo->descent) * line, 1);
1589
1590 glut_bitmap_string (widget->font, label->title + i, j - i);
1591 }
1592
1593 i = j;
1594 line ++;
1595 }
1596 }
1597 }
1598