]> Pileus Git - grits/blob - src/objects/grits-object.c
Add grits_object_destroy functions and fix memory leaks
[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                 grits_viewer_queue_draw(object->viewer);
167 }
168
169 void grits_object_set_cursor(GritsObject *object, GdkCursorType cursor)
170 {
171         // Used by grits OpenGL
172         object->cursor = gdk_cursor_new(cursor);
173 }
174
175 void grits_object_destroy(GritsObject *object)
176 {
177         if (object->viewer)
178                 grits_viewer_remove(object->viewer, object);
179         g_object_unref(object);
180 }
181
182 /* Event handling */
183 void grits_object_pick(GritsObject *object, GritsOpenGL *opengl)
184 {
185         grits_object_pickdraw(object, opengl, TRUE);
186 }
187
188 gboolean grits_object_set_pointer(GritsObject *object, GdkEvent *event, gboolean selected)
189 {
190         gboolean rval = FALSE;
191         if (selected) {
192                 if (!object->state.selected)
193                         g_signal_emit(object, signals[SIG_ENTER], 0, event, &rval);
194                 object->state.selected = TRUE;
195         } else {
196                 if (object->state.selected)
197                         g_signal_emit(object, signals[SIG_LEAVE], 0, event, &rval);
198                 object->state.selected = FALSE;
199         }
200         return rval;
201 }
202
203 gboolean grits_object_event(GritsObject *object, GdkEvent *event)
204 {
205         const int map[GDK_EVENT_LAST] = {
206                 [GDK_BUTTON_PRESS  ] SIG_BUTTON_PRESS,
207                 [GDK_2BUTTON_PRESS ] SIG_BUTTON_PRESS,
208                 [GDK_3BUTTON_PRESS ] SIG_BUTTON_PRESS,
209                 [GDK_BUTTON_RELEASE] SIG_BUTTON_RELEASE,
210                 [GDK_KEY_PRESS     ] SIG_KEY_PRESS,
211                 [GDK_KEY_RELEASE   ] SIG_KEY_RELEASE,
212                 [GDK_MOTION_NOTIFY ] SIG_MOTION,
213         };
214         if (!object->state.selected)
215                 return FALSE;
216         guint sig = map[event->type];
217         gboolean rval = FALSE;
218
219         /* Handle button click */
220         if (sig == SIG_BUTTON_PRESS)
221                 object->state.clicking  = GRITS_CLICK_THRESHOLD;
222         if (sig == SIG_MOTION && object->state.clicking)
223                 object->state.clicking -= 1;
224         if (sig == SIG_BUTTON_RELEASE && object->state.clicking)
225                 g_signal_emit(object, signals[SIG_CLICKED], 0, event, &rval);
226         if (sig == SIG_BUTTON_RELEASE)
227                 object->state.clicking  = 0;
228
229         /* Emit this signal */
230         if (rval == FALSE) {
231                 if (!g_signal_has_handler_pending(object, signals[sig], 0, FALSE))
232                         return FALSE;
233                 g_signal_emit(object, signals[sig], 0, event, &rval);
234         }
235
236         if (rval == TRUE)
237                 g_debug("GritsObject: breaking chained event");
238         return rval;
239 }
240
241 /* GObject stuff */
242 G_DEFINE_ABSTRACT_TYPE(GritsObject, grits_object, G_TYPE_OBJECT);
243 static void grits_object_init(GritsObject *object)
244 {
245         object->center.lat  =  0;
246         object->center.lon  =  0;
247         object->center.elev = -EARTH_R;
248 }
249
250 static void grits_object_class_init(GritsObjectClass *klass)
251 {
252         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
253
254         /**
255          * GritsObject::enter:
256          * @object: the object.
257          *
258          * The ::enter signal is emitted when the pointer moves over the object
259          */
260         signals[SIG_ENTER] = g_signal_new(
261                         "enter",
262                         G_TYPE_FROM_CLASS(gobject_class),
263                         G_SIGNAL_RUN_LAST,
264                         0,
265                         NULL,
266                         NULL,
267                         grits_cclosure_marshal_BOOLEAN__POINTER,
268                         G_TYPE_BOOLEAN,
269                         1,
270                         G_TYPE_POINTER);
271
272         /**
273          * GritsViewer::leave:
274          * @object: the object.
275          *
276          * The ::leave signal is emitted when the pointer moves away from the
277          * object
278          */
279         signals[SIG_LEAVE] = g_signal_new(
280                         "leave",
281                         G_TYPE_FROM_CLASS(gobject_class),
282                         G_SIGNAL_RUN_LAST,
283                         0,
284                         NULL,
285                         NULL,
286                         grits_cclosure_marshal_BOOLEAN__POINTER,
287                         G_TYPE_BOOLEAN,
288                         1,
289                         G_TYPE_POINTER);
290
291         /**
292          * GritsViewer::clicked:
293          * @object: the object.
294          *
295          * The ::clicked signal is emitted when the user clicks on the object
296          */
297         signals[SIG_CLICKED] = g_signal_new(
298                         "clicked",
299                         G_TYPE_FROM_CLASS(gobject_class),
300                         G_SIGNAL_RUN_LAST,
301                         0,
302                         NULL,
303                         NULL,
304                         grits_cclosure_marshal_BOOLEAN__POINTER,
305                         G_TYPE_BOOLEAN,
306                         1,
307                         G_TYPE_POINTER);
308
309         /**
310          * GritsViewer::button-press:
311          * @object: the object.
312          * @event:  the GdkEventButton which triggered this signal
313          *
314          * The ::button-press signal is emitted when a button (typically from a
315          * mouse) is pressed.
316          */
317         signals[SIG_BUTTON_PRESS] = g_signal_new(
318                         "button-press",
319                         G_TYPE_FROM_CLASS(gobject_class),
320                         G_SIGNAL_RUN_LAST,
321                         0,
322                         NULL,
323                         NULL,
324                         grits_cclosure_marshal_BOOLEAN__POINTER,
325                         G_TYPE_BOOLEAN,
326                         1,
327                         G_TYPE_POINTER);
328
329         /**
330          * GritsViewer::button-release:
331          * @object: the object.
332          * @event:  the GdkEventButton which triggered this signal
333          *
334          * The ::button-release signal is emitted when a button (typically from
335          * a mouse) is released.
336          */
337         signals[SIG_BUTTON_RELEASE] = g_signal_new(
338                         "button-release",
339                         G_TYPE_FROM_CLASS(gobject_class),
340                         G_SIGNAL_RUN_LAST,
341                         0,
342                         NULL,
343                         NULL,
344                         grits_cclosure_marshal_BOOLEAN__POINTER,
345                         G_TYPE_BOOLEAN,
346                         1,
347                         G_TYPE_POINTER);
348
349         /**
350          * GritsViewer::key-press:
351          * @object: the object.
352          * @event:  the GdkEventKey which triggered this signal
353          *
354          * The ::key-press signal is emitted when a key is pressed.
355          */
356         signals[SIG_KEY_PRESS] = g_signal_new(
357                         "key-press",
358                         G_TYPE_FROM_CLASS(gobject_class),
359                         G_SIGNAL_RUN_LAST,
360                         0,
361                         NULL,
362                         NULL,
363                         grits_cclosure_marshal_BOOLEAN__POINTER,
364                         G_TYPE_BOOLEAN,
365                         1,
366                         G_TYPE_POINTER);
367
368         /**
369          * GritsViewer::key-release:
370          * @object: the object.
371          * @event:  the GdkEventKey which triggered this signal
372          *
373          * The ::key-release signal is emitted when a key is released.
374          */
375         signals[SIG_KEY_RELEASE] = g_signal_new(
376                         "key-release",
377                         G_TYPE_FROM_CLASS(gobject_class),
378                         G_SIGNAL_RUN_LAST,
379                         0,
380                         NULL,
381                         NULL,
382                         grits_cclosure_marshal_BOOLEAN__POINTER,
383                         G_TYPE_BOOLEAN,
384                         1,
385                         G_TYPE_POINTER);
386
387         /**
388          * GritsViewer::motion:
389          * @object: the object.
390          * @event:  the GdkEventMotion which triggered this signal
391          *
392          * The ::motion signal is emitted the pointer moves over the object
393          */
394         signals[SIG_MOTION] = g_signal_new(
395                         "motion",
396                         G_TYPE_FROM_CLASS(gobject_class),
397                         G_SIGNAL_RUN_LAST,
398                         0,
399                         NULL,
400                         NULL,
401                         grits_cclosure_marshal_BOOLEAN__POINTER,
402                         G_TYPE_BOOLEAN,
403                         1,
404                         G_TYPE_POINTER);
405 }