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