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