]> Pileus Git - grits/blob - src/objects/grits-object.c
Add mouse, keyboard, and motion events to GritsObject
[grits] / src / objects / grits-object.c
1 /*
2  * Copyright (C) 2009-2010 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /**
19  * SECTION:grits-object
20  * @short_description: Base class for drawing operations
21  *
22  * Objects in grits are things which can be added to the viewer and will be
23  * displayed to the user. Each object has information such as it's location and
24  * level of detail which are used by the viewer to determine which objects
25  * should be drawn.
26  *
27  * Each #GritsObject is also a #GObject, but not every GObject in grits is a
28  * GritsObject. The "Object" part of the name is just coincidence.
29  */
30
31 #include <config.h>
32 #include <math.h>
33 #include <GL/gl.h>
34
35 #include "grits-object.h"
36
37 /* Constants */
38 enum {
39         SIG_ENTER,
40         SIG_LEAVE,
41         SIG_BUTTON_PRESS,
42         SIG_BUTTON_RELEASE,
43         SIG_KEY_PRESS,
44         SIG_KEY_RELEASE,
45         SIG_MOTION,
46         NUM_SIGNALS,
47 };
48 static guint signals[NUM_SIGNALS];
49
50 void grits_object_pickdraw(GritsObject *object, GritsOpenGL *opengl, gboolean pick)
51 {
52         GritsObjectClass *klass = GRITS_OBJECT_GET_CLASS(object);
53         if (!klass->draw) {
54                 g_warning("GritsObject: draw - Unimplemented");
55                 return;
56         }
57
58         /* Skip hidden objects */
59         if (object->hidden)
60                 return;
61
62         /* Support GritsTester */
63         if (!GRITS_IS_OPENGL(opengl)) {
64                 g_debug("GritsObject: draw - drawing raw object");
65                 klass->draw(object, opengl);
66                 return;
67         }
68
69         /* Calculae distance for LOD and horizon tests */
70         GritsPoint *center = &object->center;
71         if ((!(object->skip & GRITS_SKIP_LOD) ||
72              !(object->skip & GRITS_SKIP_HORIZON)) &&
73             (center->elev != -EARTH_R)) {
74                 /* LOD test */
75                 gdouble eye[3], obj[3];
76                 grits_viewer_get_location(GRITS_VIEWER(opengl),
77                                 &eye[0], &eye[1], &eye[2]);
78                 gdouble elev = eye[2];
79                 lle2xyz(eye[0], eye[1], eye[2],
80                                 &eye[0], &eye[1], &eye[2]);
81                 lle2xyz(center->lat, center->lon, center->elev,
82                                 &obj[0], &obj[1], &obj[2]);
83                 gdouble dist = distd(obj, eye);
84
85                 /* Level of detail test */
86                 if (!(object->skip & GRITS_SKIP_LOD)
87                                 && object->lod > 0) {
88                         if (object->lod < dist)
89                                 return;
90                 }
91
92                 /* Horizon test */
93                 if (!(object->skip & GRITS_SKIP_HORIZON)) {
94                         gdouble c = EARTH_R+elev;
95                         gdouble a = EARTH_R;
96                         gdouble horizon = sqrt(c*c - a*a);
97                         if (dist > horizon)
98                                 return;
99                 }
100         }
101
102         /* Save state, draw, restore state */
103         g_mutex_lock(opengl->sphere_lock);
104         if (!(object->skip & GRITS_SKIP_STATE)) {
105                 glPushAttrib(GL_ALL_ATTRIB_BITS);
106                 glMatrixMode(GL_PROJECTION); glPushMatrix();
107                 glMatrixMode(GL_MODELVIEW);  glPushMatrix();
108         }
109
110         if (!(object->skip & GRITS_SKIP_CENTER))
111                 grits_viewer_center_position(GRITS_VIEWER(opengl),
112                                 object->center.lat,
113                                 object->center.lon,
114                                 object->center.elev);
115
116         if (pick && klass->pick)
117                 klass->pick(object, opengl);
118         else
119                 klass->draw(object, opengl);
120
121         if (!(object->skip & GRITS_SKIP_STATE)) {
122                 glPopAttrib();
123                 glMatrixMode(GL_PROJECTION); glPopMatrix();
124                 glMatrixMode(GL_MODELVIEW);  glPopMatrix();
125         }
126         g_mutex_unlock(opengl->sphere_lock);
127 }
128
129 /**
130  * grits_object_draw:
131  * @object: the object
132  * @opengl: the viewer the object is being displayed in
133  *
134  * Perform any OpenGL commands necessasairy to draw the object.
135  *
136  * The GL_PROJECTION and GL_MODELVIEW matricies and GL_ALL_ATTRIB_BITS will be
137  * restored to the default state after the call to draw.
138  */
139 void grits_object_draw(GritsObject *object, GritsOpenGL *opengl)
140 {
141         grits_object_pickdraw(object, opengl, FALSE);
142 }
143
144 void grits_object_hide(GritsObject *object, gboolean hidden)
145 {
146         GritsObjectClass *klass = GRITS_OBJECT_GET_CLASS(object);
147         object->hidden = hidden;
148         if (klass->hide)
149                 klass->hide(object, hidden);
150 }
151
152 void grits_object_queue_draw(GritsObject *object)
153 {
154         if (object->viewer)
155                 gtk_widget_queue_draw(GTK_WIDGET(object->viewer));
156 }
157
158 /* Event handling */
159 void grits_object_pick_begin(GritsObject *object, GritsOpenGL *opengl)
160 {
161         object->state.picked = FALSE;
162
163         /* Check for connected signals */
164         for (int i = 0; i < NUM_SIGNALS; i++) {
165                 if (g_signal_has_handler_pending(object, signals[i], 0, FALSE)) {
166                         /* Someone is watching, render the object _once_ */
167                         glPushName((guint)object);
168                         grits_object_pickdraw(object, opengl, TRUE);
169                         glPopName();
170                         return;
171                 }
172         }
173 }
174
175 void grits_object_pick_pointer(GritsObject *object, double x, double y)
176 {
177         object->state.picked = TRUE;
178 }
179
180 void grits_object_pick_end(GritsObject *object)
181 {
182         if (object->state.picked) {
183                 if (!object->state.selected)
184                         g_signal_emit(object, signals[SIG_ENTER], 0);
185                 object->state.selected = TRUE;
186         } else {
187                 if (object->state.selected)
188                         g_signal_emit(object, signals[SIG_LEAVE], 0);
189                 object->state.selected = FALSE;
190         }
191 }
192
193 void grits_object_event(GritsObject *object, GdkEvent *event)
194 {
195         const int map[GDK_EVENT_LAST] = {
196                 [GDK_BUTTON_PRESS  ] SIG_BUTTON_PRESS,
197                 [GDK_2BUTTON_PRESS ] SIG_BUTTON_PRESS,
198                 [GDK_3BUTTON_PRESS ] SIG_BUTTON_PRESS,
199                 [GDK_BUTTON_RELEASE] SIG_BUTTON_RELEASE,
200                 [GDK_KEY_PRESS     ] SIG_KEY_PRESS,
201                 [GDK_KEY_RELEASE   ] SIG_KEY_RELEASE,
202                 [GDK_MOTION_NOTIFY ] SIG_MOTION,
203         };
204         if (!object->state.selected)
205                 return;
206         guint sig = signals[map[event->type]];
207         if (!g_signal_has_handler_pending(object, sig, 0, FALSE))
208                 return;
209         g_signal_emit(object, sig, 0, event);
210 }
211
212 /* GObject stuff */
213 G_DEFINE_ABSTRACT_TYPE(GritsObject, grits_object, G_TYPE_OBJECT);
214 static void grits_object_init(GritsObject *object)
215 {
216         object->center.lat  =  0;
217         object->center.lon  =  0;
218         object->center.elev = -EARTH_R;
219 }
220
221 static void grits_object_class_init(GritsObjectClass *klass)
222 {
223         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
224
225         /**
226          * GritsObject::enter:
227          * @object: the object.
228          *
229          * The ::enter signal is emitted when the pointer moves over the object
230          */
231         signals[SIG_ENTER] = g_signal_new(
232                         "enter",
233                         G_TYPE_FROM_CLASS(gobject_class),
234                         G_SIGNAL_RUN_LAST,
235                         0,
236                         NULL,
237                         NULL,
238                         g_cclosure_marshal_VOID__VOID,
239                         G_TYPE_NONE,
240                         0);
241
242         /**
243          * GritsViewer::leave:
244          * @object: the object.
245          *
246          * The ::leave signal is emitted when the pointer moves away from the
247          * object
248          */
249         signals[SIG_LEAVE] = g_signal_new(
250                         "leave",
251                         G_TYPE_FROM_CLASS(gobject_class),
252                         G_SIGNAL_RUN_LAST,
253                         0,
254                         NULL,
255                         NULL,
256                         g_cclosure_marshal_VOID__VOID,
257                         G_TYPE_NONE,
258                         0);
259
260         /**
261          * GritsViewer::button-press:
262          * @object: the object.
263          * @event:  the GdkEventButton which triggered this signal
264          *
265          * The ::button-press signal is emitted when a button (typically from a
266          * mouse) is pressed.
267          */
268         signals[SIG_BUTTON_PRESS] = g_signal_new(
269                         "button-press",
270                         G_TYPE_FROM_CLASS(gobject_class),
271                         G_SIGNAL_RUN_LAST,
272                         0,
273                         NULL,
274                         NULL,
275                         g_cclosure_marshal_VOID__POINTER,
276                         G_TYPE_NONE,
277                         1,
278                         G_TYPE_POINTER);
279
280         /**
281          * GritsViewer::button-release:
282          * @object: the object.
283          * @event:  the GdkEventButton which triggered this signal
284          *
285          * The ::button-release signal is emitted when a button (typically from
286          * a mouse) is released.
287          */
288         signals[SIG_BUTTON_RELEASE] = g_signal_new(
289                         "button-release",
290                         G_TYPE_FROM_CLASS(gobject_class),
291                         G_SIGNAL_RUN_LAST,
292                         0,
293                         NULL,
294                         NULL,
295                         g_cclosure_marshal_VOID__POINTER,
296                         G_TYPE_NONE,
297                         1,
298                         G_TYPE_POINTER);
299
300         /**
301          * GritsViewer::key-press:
302          * @object: the object.
303          * @event:  the GdkEventKey which triggered this signal
304          *
305          * The ::key-press signal is emitted when a key is pressed.
306          */
307         signals[SIG_KEY_PRESS] = g_signal_new(
308                         "key-press",
309                         G_TYPE_FROM_CLASS(gobject_class),
310                         G_SIGNAL_RUN_LAST,
311                         0,
312                         NULL,
313                         NULL,
314                         g_cclosure_marshal_VOID__POINTER,
315                         G_TYPE_NONE,
316                         1,
317                         G_TYPE_POINTER);
318
319         /**
320          * GritsViewer::key-release:
321          * @object: the object.
322          * @event:  the GdkEventKey which triggered this signal
323          *
324          * The ::key-release signal is emitted when a key is released.
325          */
326         signals[SIG_KEY_RELEASE] = g_signal_new(
327                         "key-release",
328                         G_TYPE_FROM_CLASS(gobject_class),
329                         G_SIGNAL_RUN_LAST,
330                         0,
331                         NULL,
332                         NULL,
333                         g_cclosure_marshal_VOID__POINTER,
334                         G_TYPE_NONE,
335                         1,
336                         G_TYPE_POINTER);
337
338         /**
339          * GritsViewer::motion:
340          * @object: the object.
341          * @event:  the GdkEventMotion which triggered this signal
342          *
343          * The ::motion signal is emitted the pointer moves over the object
344          */
345         signals[SIG_MOTION] = g_signal_new(
346                         "motion",
347                         G_TYPE_FROM_CLASS(gobject_class),
348                         G_SIGNAL_RUN_LAST,
349                         0,
350                         NULL,
351                         NULL,
352                         g_cclosure_marshal_VOID__POINTER,
353                         G_TYPE_NONE,
354                         1,
355                         G_TYPE_POINTER);
356 }