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