1 /* viewport.c */
2
3 /* Viewport routines */
4
5 /* fsv - 3D File System Visualizer
6 * Copyright (C)1999 Daniel Richard G. <skunk@mit.edu>
7 * Copyright (C)2009-2011 Yury P. Fedorchenko <yuryfdr@users.sf.net>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24
25 #include "common.h"
26 #include "fsvwindow.h"
27 #include "viewport.h"
28
29 #include <GL/gl.h> /* GLuint */
30
31 #include "about.h"
32 #include "camera.h"
33 #include "filelist.h" /* filelist_show_entry( ) */
34 #include "geometry.h"
35 #include "ogl.h"
36
37
38 /* Sensitivity factor used for manual camera control */
39 #define MOUSE_SENSITIVITY 0.5
40
41
42 /* The node table, used to find a node by its ID number */
43 static GNode **node_table = NULL;
44
45 /* The currently highlighted (indicated) node */
46 static GNode *indicated_node = NULL;
47
48 /* Previous mouse pointer coordinates */
49 static int prev_x = 0, prev_y = 0;
50
51
52 /* Receives a newly created node table from scanfs( ) */
53 void
viewport_pass_node_table(GNode ** new_node_table)54 viewport_pass_node_table( GNode **new_node_table )
55 {
56 if (node_table != NULL)
57 xfree( node_table );
58
59 node_table = new_node_table;
60 }
61
62
63 /* This returns the node (if any) that is visible at viewport location
64 * (x,y) (where (0,0) indicates the upper-left corner). The ID number of
65 * the particular face being pointed at is stored in face_id */
66 static GNode *
node_at_location(int x,int y,unsigned int * face_id)67 node_at_location( int x, int y, unsigned int *face_id )
68 {
69 const GLuint *hit_records;
70 unsigned int name_count, z1, z2, name1, name2;
71 unsigned int min_z1;
72 unsigned int node_id = 0;
73 int hit_count;
74 int i = 0;
75
76 *face_id = 0;
77
78 hit_count = ogl_select( x, y, &hit_records );
79 if (hit_count > 0) {
80 /* Process selection hit records: find nearest hit
81 * (i.e. hit with smallest window-coordinate z value) */
82 min_z1 = 4294967295U; /* 2^32 - 1 */
83 while (hit_count > 0) {
84 name_count = hit_records[i++];
85 z1 = hit_records[i++];
86 z2 = hit_records[i++];
87 name1 = hit_records[i++];
88 if (name_count == 2)
89 name2 = hit_records[i++];
90 else
91 name2 = 0; /* default */
92 if (z1 < min_z1) {
93 node_id = name1;
94 *face_id = name2;
95 min_z1 = z1;
96 }
97 z2 = z2; /* z2 not used */
98 --hit_count;
99 }
100
101 if (node_id != 0)
102 return node_table[node_id];
103 }
104
105 return NULL;
106 }
107
108
109 /* This callback catches all events for the viewport */
110 int
viewport_cb(GtkWidget * gl_area_w,GdkEvent * event)111 viewport_cb( GtkWidget *gl_area_w, GdkEvent *event )
112 {
113 GdkEventButton *ev_button;
114 GdkEventMotion *ev_motion;
115 GNode *node;
116 double dx, dy;
117 unsigned int face_id;
118 int x, y;
119 boolean btn1, btn2, btn3;
120 boolean ctrl_key;
121
122 /* Handle low-level GL area widget events */
123 switch (event->type) {
124 case GDK_EXPOSE:
125 ogl_refresh( );
126 return FALSE;
127
128 case GDK_CONFIGURE:
129 ogl_resize( );
130 return FALSE;
131
132 default:
133 /* Event is probably coming from the mouse */
134 break;
135 }
136
137 if (event->type == GDK_BUTTON_PRESS) {
138 /* Exit the About presentation if it is up */
139 if (about( ABOUT_END )) {
140 indicated_node = NULL;
141 return FALSE;
142 }
143 }
144
145 /* If we're in splash screen mode, proceed no further */
146 if (globalsc.fsv_mode == FSV_SPLASH)
147 return FALSE;
148
149 /* Mouse-related events */
150 switch (event->type) {
151 case GDK_BUTTON_PRESS:
152 ev_button = (GdkEventButton *)event;
153 btn1 = ev_button->button == 1;
154 btn2 = ev_button->button == 2;
155 btn3 = ev_button->button == 3;
156 ctrl_key = ev_button->state & GDK_CONTROL_MASK;
157 x = (int)ev_button->x;
158 y = (int)ev_button->y;
159 if (camera_moving( )) {
160 /* Yipe! Impatient user */
161 camera_pan_finish( );
162 indicated_node = NULL;
163 }
164 else if (!ctrl_key) {
165 if (btn2)
166 indicated_node = NULL;
167 else
168 indicated_node = node_at_location( x, y, &face_id );
169 if (indicated_node == NULL) {
170 geometry_highlight_node( NULL, FALSE );
171 window_statusbar( SB_RIGHT, "" );
172 }
173 else {
174 if (geometry_should_highlight( indicated_node, face_id ) || btn1)
175 geometry_highlight_node( indicated_node, btn1 );
176 else
177 geometry_highlight_node( NULL, FALSE );
178 window_statusbar( SB_RIGHT, node_absname( indicated_node ) );
179 if (btn3) {
180 /* Bring up context-sensitive menu */
181 context_menu( indicated_node, ev_button );
182 filelist_show_entry( indicated_node );
183 }
184 }
185 }
186 prev_x = x;
187 prev_y = y;
188 break;
189
190 case GDK_2BUTTON_PRESS:
191 /* Ignore second click of a double-click */
192 break;
193
194 case GDK_BUTTON_RELEASE:
195 ev_button = (GdkEventButton *)event;
196 btn1 = ev_button->state & GDK_BUTTON1_MASK;
197 ctrl_key = ev_button->state & GDK_CONTROL_MASK;
198 if (btn1 && !ctrl_key && !camera_moving( ) && (indicated_node != NULL))
199 camera_look_at( indicated_node );
200 gui_cursor( gl_area_w, -1 );
201 break;
202
203 case GDK_MOTION_NOTIFY:
204 ev_motion = (GdkEventMotion *)event;
205 btn1 = ev_motion->state & GDK_BUTTON1_MASK;
206 btn2 = ev_motion->state & GDK_BUTTON2_MASK;
207 btn3 = ev_motion->state & GDK_BUTTON3_MASK;
208 ctrl_key = ev_motion->state & GDK_CONTROL_MASK;
209 x = (int)ev_motion->x;
210 y = (int)ev_motion->y;
211 if (!camera_moving( ) && !gtk_events_pending( )) {
212 if (btn2) {
213 /* Dolly the camera */
214 gui_cursor( gl_area_w, GDK_DOUBLE_ARROW );
215 dy = MOUSE_SENSITIVITY * (y - prev_y);
216 camera_dolly( - dy );
217 indicated_node = NULL;
218 }
219 else if (ctrl_key && btn1) {
220 /* Revolve the camera */
221 gui_cursor( gl_area_w, GDK_FLEUR );
222 dx = MOUSE_SENSITIVITY * (x - prev_x);
223 dy = MOUSE_SENSITIVITY * (y - prev_y);
224 camera_revolve( dx, dy );
225 indicated_node = NULL;
226 }
227 else if (!ctrl_key && (btn1 || btn3)) {
228 /* Pointless dragging */
229 if (indicated_node != NULL) {
230 node = node_at_location( x, y, &face_id );
231 if (node != indicated_node)
232 indicated_node = NULL;
233 }
234 }
235 else
236 indicated_node = node_at_location( x, y, &face_id );
237 /* Update node highlighting */
238 if (indicated_node == NULL) {
239 geometry_highlight_node( NULL, FALSE );
240 window_statusbar( SB_RIGHT, "" );
241 }
242 else {
243 if (geometry_should_highlight( indicated_node, face_id ) || btn1)
244 geometry_highlight_node( indicated_node, btn1 );
245 else
246 geometry_highlight_node( NULL, FALSE);
247 window_statusbar( SB_RIGHT, node_absname( indicated_node ) );
248 }
249 prev_x = x;
250 prev_y = y;
251 }
252 break;
253
254 case GDK_LEAVE_NOTIFY:
255 /* The mouse has left the viewport */
256 geometry_highlight_node( NULL, FALSE );
257 window_statusbar( SB_RIGHT, "" );
258 gui_cursor( gl_area_w, -1 );
259 indicated_node = NULL;
260 break;
261
262 default:
263 /* Ignore event */
264 break;
265 }
266
267 return FALSE;
268 }
269
270
271 /* end viewport.c */
272