]> Pileus Git - grits/blob - src/objects/grits-object.c
Add parameters and return values to GritsObject events
[grits] / src / objects / grits-object.c
1 /*
2  * Copyright (C) 2009-2011 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
34 #include "gtkgl.h"
35 #include "grits-object.h"
36 #include "grits-marshal.h"
37
38 /* Constants */
39 enum {
40         SIG_ENTER,
41         SIG_LEAVE,
42         SIG_CLICKED,
43         SIG_BUTTON_PRESS,
44         SIG_BUTTON_RELEASE,
45         SIG_KEY_PRESS,
46         SIG_KEY_RELEASE,
47         SIG_MOTION,
48         NUM_SIGNALS,
49 };
50 static guint signals[NUM_SIGNALS];
51
52 void grits_object_pickdraw(GritsObject *object, GritsOpenGL *opengl, gboolean pick)
53 {
54         GritsObjectClass *klass = GRITS_OBJECT_GET_CLASS(object);
55
56         if (!klass->draw) {
57                 g_warning("GritsObject: draw - Unimplemented");
58                 return;
59         }
60
61         /* Skip hidden objects */
62         if (object->hidden)
63                 return;
64
65         /* Skip object with no signals when picking */
66         for (int i = 0; pick; i++) {
67                 if (i == NUM_SIGNALS)
68                         return;
69                 if (g_signal_has_handler_pending(object, signals[i], 0, FALSE))
70                         break;
71         }
72
73         /* Support GritsTester */
74         if (!GRITS_IS_OPENGL(opengl)) {
75                 g_debug("GritsObject: draw - drawing raw object");
76                 klass->draw(object, opengl);
77                 return;
78         }
79
80         /* Calculate distance for LOD and horizon tests */
81         GritsPoint *center = &object->center;
82         if ((!(object->skip & GRITS_SKIP_LOD) ||
83              !(object->skip & GRITS_SKIP_HORIZON)) &&
84             (center->elev != -EARTH_R)) {
85                 /* LOD test */
86                 gdouble eye[3], obj[3];
87                 grits_viewer_get_location(GRITS_VIEWER(opengl),
88                                 &eye[0], &eye[1], &eye[2]);
89                 gdouble elev = eye[2];
90                 lle2xyz(eye[0], eye[1], eye[2],
91                                 &eye[0], &eye[1], &eye[2]);
92                 lle2xyz(center->lat, center->lon, center->elev,
93                                 &obj[0], &obj[1], &obj[2]);
94                 gdouble dist = distd(obj, eye);
95
96                 /* Level of detail test */
97                 if (!(object->skip & GRITS_SKIP_LOD)
98                                 && object->lod > 0) {
99                         if (object->lod < dist)
100                                 return;
101                 }
102
103                 /* Horizon test */
104                 if (!(object->skip & GRITS_SKIP_HORIZON)) {
105                         gdouble c = EARTH_R+elev;
106                         gdouble a = EARTH_R;
107                         gdouble horizon = sqrt(c*c - a*a);
108                         if (dist > horizon)
109                                 return;
110                 }
111         }
112
113         /* Save state, draw, restore state */
114         g_mutex_lock(opengl->sphere_lock);
115         if (!(object->skip & GRITS_SKIP_STATE)) {
116                 glPushAttrib(GL_ALL_ATTRIB_BITS);
117                 glMatrixMode(GL_PROJECTION); glPushMatrix();
118                 glMatrixMode(GL_MODELVIEW);  glPushMatrix();
119         }
120
121         if (!(object->skip & GRITS_SKIP_CENTER))
122                 grits_viewer_center_position(GRITS_VIEWER(opengl),
123                                 object->center.lat,
124                                 object->center.lon,
125                                 object->center.elev);
126
127         if (pick && klass->pick)
128                 klass->pick(object, opengl);
129         else
130                 klass->draw(object, opengl);
131
132         if (!(object->skip & GRITS_SKIP_STATE)) {
133                 glPopAttrib();
134                 glMatrixMode(GL_PROJECTION); glPopMatrix();
135                 glMatrixMode(GL_MODELVIEW);  glPopMatrix();
136         }
137         g_mutex_unlock(opengl->sphere_lock);
138 }
139
140 /**
141  * grits_object_draw:
142  * @object: the object
143  * @opengl: the viewer the object is being displayed in
144  *
145  * Perform any OpenGL commands necessasairy to draw the object.
146  *
147  * The GL_PROJECTION and GL_MODELVIEW matricies and GL_ALL_ATTRIB_BITS will be
148  * restored to the default state after the call to draw.
149  */
150 void grits_object_draw(GritsObject *object, GritsOpenGL *opengl)
151 {
152         grits_object_pickdraw(object, opengl, FALSE);
153 }
154
155 void grits_object_hide(GritsObject *object, gboolean hidden)
156 {
157         GritsObjectClass *klass = GRITS_OBJECT_GET_CLASS(object);
158         object->hidden = hidden;
159         if (klass->hide)
160                 klass->hide(object, hidden);
161 }
162
163 void grits_object_queue_draw(GritsObject *object)
164 {
165         if (object->viewer)
166                 gtk_widget_queue_draw(GTK_WIDGET(object->viewer));
167 }
168
169 /* Event handling */
170 void grits_object_pick(GritsObject *object, GritsOpenGL *opengl)
171 {
172         grits_object_pickdraw(object, opengl, TRUE);
173 }
174
175 gboolean grits_object_set_pointer(GritsObject *object, GdkEvent *event, gboolean selected)
176 {
177         gboolean rval = FALSE;
178         if (selected) {
179                 if (!object->state.selected)
180                         g_signal_emit(object, signals[SIG_ENTER], 0, event, &rval);
181                 object->state.selected = TRUE;
182         } else {
183                 if (object->state.selected)
184                         g_signal_emit(object, signals[SIG_LEAVE], 0, event, &rval);
185                 object->state.selected = FALSE;
186         }
187         return rval;
188 }
189
190 gboolean grits_object_event(GritsObject *object, GdkEvent *event)
191 {
192         const int map[GDK_EVENT_LAST] = {
193                 [GDK_BUTTON_PRESS  ] SIG_BUTTON_PRESS,
194                 [GDK_2BUTTON_PRESS ] SIG_BUTTON_PRESS,
195                 [GDK_3BUTTON_PRESS ] SIG_BUTTON_PRESS,
196                 [GDK_BUTTON_RELEASE] SIG_BUTTON_RELEASE,
197                 [GDK_KEY_PRESS     ] SIG_KEY_PRESS,
198                 [GDK_KEY_RELEASE   ] SIG_KEY_RELEASE,
199                 [GDK_MOTION_NOTIFY ] SIG_MOTION,
200         };
201         if (!object->state.selected)
202                 return FALSE;
203         guint sig = map[event->type];
204         gboolean rval = FALSE;
205
206         /* Handle button click */
207         if (sig == SIG_BUTTON_PRESS)
208                 object->state.clicking = TRUE;
209         if (sig == SIG_BUTTON_RELEASE && object->state.clicking)
210                 g_signal_emit(object, signals[SIG_CLICKED], 0, event, &rval);
211         if (sig == SIG_BUTTON_RELEASE || sig == SIG_MOTION)
212                 object->state.clicking = FALSE;
213
214         /* Emit this signal */
215         if (rval == FALSE) {
216                 if (!g_signal_has_handler_pending(object, signals[sig], 0, FALSE))
217                         return FALSE;
218                 g_signal_emit(object, signals[sig], 0, event, &rval);
219         }
220
221         if (rval == TRUE)
222                 g_debug("GritsObject: breaking chained event");
223         return rval;
224 }
225
226 /* GObject stuff */
227 G_DEFINE_ABSTRACT_TYPE(GritsObject, grits_object, G_TYPE_OBJECT);
228 static void grits_object_init(GritsObject *object)
229 {
230         object->center.lat  =  0;
231         object->center.lon  =  0;
232         object->center.elev = -EARTH_R;
233 }
234
235 static void grits_object_class_init(GritsObjectClass *klass)
236 {
237         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
238
239         /**
240          * GritsObject::enter:
241          * @object: the object.
242          *
243          * The ::enter signal is emitted when the pointer moves over the object
244          */
245         signals[SIG_ENTER] = g_signal_new(
246                         "enter",
247                         G_TYPE_FROM_CLASS(gobject_class),
248                         G_SIGNAL_RUN_LAST,
249                         0,
250                         NULL,
251                         NULL,
252                         grits_cclosure_marshal_BOOLEAN__POINTER,
253                         G_TYPE_BOOLEAN,
254                         1,
255                         G_TYPE_POINTER);
256
257         /**
258          * GritsViewer::leave:
259          * @object: the object.
260          *
261          * The ::leave signal is emitted when the pointer moves away from the
262          * object
263          */
264         signals[SIG_LEAVE] = g_signal_new(
265                         "leave",
266                         G_TYPE_FROM_CLASS(gobject_class),
267                         G_SIGNAL_RUN_LAST,
268                         0,
269                         NULL,
270                         NULL,
271                         grits_cclosure_marshal_BOOLEAN__POINTER,
272                         G_TYPE_BOOLEAN,
273                         1,
274                         G_TYPE_POINTER);
275
276         /**
277          * GritsViewer::clicked:
278          * @object: the object.
279          *
280          * The ::clicked signal is emitted when the user clicks on the object
281          */
282         signals[SIG_CLICKED] = g_signal_new(
283                         "clicked",
284                         G_TYPE_FROM_CLASS(gobject_class),
285                         G_SIGNAL_RUN_LAST,
286                         0,
287                         NULL,
288                         NULL,
289                         grits_cclosure_marshal_BOOLEAN__POINTER,
290                         G_TYPE_BOOLEAN,
291                         1,
292                         G_TYPE_POINTER);
293
294         /**
295          * GritsViewer::button-press:
296          * @object: the object.
297          * @event:  the GdkEventButton which triggered this signal
298          *
299          * The ::button-press signal is emitted when a button (typically from a
300          * mouse) is pressed.
301          */
302         signals[SIG_BUTTON_PRESS] = g_signal_new(
303                         "button-press",
304                         G_TYPE_FROM_CLASS(gobject_class),
305                         G_SIGNAL_RUN_LAST,
306                         0,
307                         NULL,
308                         NULL,
309                         grits_cclosure_marshal_BOOLEAN__POINTER,
310                         G_TYPE_BOOLEAN,
311                         1,
312                         G_TYPE_POINTER);
313
314         /**
315          * GritsViewer::button-release:
316          * @object: the object.
317          * @event:  the GdkEventButton which triggered this signal
318          *
319          * The ::button-release signal is emitted when a button (typically from
320          * a mouse) is released.
321          */
322         signals[SIG_BUTTON_RELEASE] = g_signal_new(
323                         "button-release",
324                         G_TYPE_FROM_CLASS(gobject_class),
325                         G_SIGNAL_RUN_LAST,
326                         0,
327                         NULL,
328                         NULL,
329                         grits_cclosure_marshal_BOOLEAN__POINTER,
330                         G_TYPE_BOOLEAN,
331                         1,
332                         G_TYPE_POINTER);
333
334         /**
335          * GritsViewer::key-press:
336          * @object: the object.
337          * @event:  the GdkEventKey which triggered this signal
338          *
339          * The ::key-press signal is emitted when a key is pressed.
340          */
341         signals[SIG_KEY_PRESS] = g_signal_new(
342                         "key-press",
343                         G_TYPE_FROM_CLASS(gobject_class),
344                         G_SIGNAL_RUN_LAST,
345                         0,
346                         NULL,
347                         NULL,
348                         grits_cclosure_marshal_BOOLEAN__POINTER,
349                         G_TYPE_BOOLEAN,
350                         1,
351                         G_TYPE_POINTER);
352
353         /**
354          * GritsViewer::key-release:
355          * @object: the object.
356          * @event:  the GdkEventKey which triggered this signal
357          *
358          * The ::key-release signal is emitted when a key is released.
359          */
360         signals[SIG_KEY_RELEASE] = g_signal_new(
361                         "key-release",
362                         G_TYPE_FROM_CLASS(gobject_class),
363                         G_SIGNAL_RUN_LAST,
364                         0,
365                         NULL,
366                         NULL,
367                         grits_cclosure_marshal_BOOLEAN__POINTER,
368                         G_TYPE_BOOLEAN,
369                         1,
370                         G_TYPE_POINTER);
371
372         /**
373          * GritsViewer::motion:
374          * @object: the object.
375          * @event:  the GdkEventMotion which triggered this signal
376          *
377          * The ::motion signal is emitted the pointer moves over the object
378          */
379         signals[SIG_MOTION] = g_signal_new(
380                         "motion",
381                         G_TYPE_FROM_CLASS(gobject_class),
382                         G_SIGNAL_RUN_LAST,
383                         0,
384                         NULL,
385                         NULL,
386                         grits_cclosure_marshal_BOOLEAN__POINTER,
387                         G_TYPE_BOOLEAN,
388                         1,
389                         G_TYPE_POINTER);
390 }