4 * The geeky little puzzle game with a big noodly crunch!
6 * gPlanarity copyright (C) 2005 Monty <monty@xiph.org>
7 * Original Flash game by John Tantalo <john.tantalo@case.edu>
8 * Original game concept by Mary Radcliffe
10 * gPlanarity is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
15 * gPlanarity is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with Postfish; see the file COPYING. If not, write to the
22 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <gtk/gtkmain.h>
38 #include "gameboard.h"
39 #include "dialog_level.h"
41 /* does the given x/y fall inside an on-screen vertex? */
42 static void check_lit(Gameboard
*g
,int x
, int y
){
43 vertex
*v
= find_vertex(&g
->g
,x
,y
);
45 invalidate_vertex(g
,v
);
46 invalidate_vertex(g
,g
->lit_vertex
);
51 /* toplevel mouse motion handler */
52 gint
mouse_motion(GtkWidget
*widget
,
53 GdkEventMotion
*event
){
54 Gameboard
*g
= GAMEBOARD (widget
);
56 /* the level selection dialog implements its own icon mouse management */
57 if(g
->level_dialog_active
)
58 level_mouse_motion(g
,(int)event
->x
,(int)event
->y
);
60 /* similarly, if a vertex is grabbed, the only thing that can
61 happen is that we're dragging that vertex */
63 if(g
->grabbed_vertex
){
64 int x
= (int)event
->x
;
65 int y
= (int)event
->y
;
66 g
->dragx
= x
-g
->grabx
;
67 g
->dragy
= y
-g
->graby
;
69 invalidate_vertex(g
,g
->grabbed_vertex
);
70 invalidate_edges(widget
,g
->grabbed_vertex
,0,0);
71 move_vertex(&g
->g
,g
->grabbed_vertex
,x
+g
->graboffx
,y
+g
->graboffy
);
72 invalidate_vertex(g
,g
->grabbed_vertex
);
73 invalidate_edges(widget
,g
->grabbed_vertex
,0,0);
78 /* if the selection is grabbed, we can only be dragging a selection box */
79 if (g
->selection_grab
){
80 invalidate_selection(widget
);
82 g
->selectionx
= min(g
->grabx
, event
->x
);
83 g
->selectionw
= labs(g
->grabx
- event
->x
);
84 g
->selectiony
= min(g
->graby
, event
->y
);
85 g
->selectionh
= labs(g
->graby
- event
->y
);
86 select_verticies(&g
->g
,
87 g
->selectionx
,g
->selectiony
,
88 g
->selectionx
+g
->selectionw
-1,
89 g
->selectiony
+g
->selectionh
-1);
91 invalidate_selection(widget
);
95 /* if a selected vertex is grabbed (group drag) we're dragging
96 the whole selection and its lines */
99 invalidate_verticies_selection(widget
);
100 g
->dragx
= event
->x
- g
->grabx
;
101 g
->dragy
= event
->y
- g
->graby
;
103 /* don't allow the drag to put a vertex offscreen; bound the drag at the edge */
105 vertex
*v
=g
->g
.verticies
;
111 if(v
->x
+ g
->dragx
>= w
)
112 g
->dragx
=w
- v
->x
-1;
113 if(v
->x
+ g
->dragx
< 0 )
115 if(v
->y
+ g
->dragy
>= h
)
116 g
->dragy
=h
- v
->y
-1;
117 if(v
->y
+ g
->dragy
< 0 )
124 invalidate_verticies_selection(widget
);
128 /* A vertex rollover has priority over a button rollover. However,
129 a button grab disallows a rollover, as does the curtain. */
130 if(!g
->pushed_curtain
&& !g
->button_grabbed
)
131 check_lit(g
, (int)event
->x
,(int)event
->y
);
133 /* handle button rollovers and ongoing grabs; other grabs are
134 already disallowed, but also verify a vertex rollover isn't
135 already active (and that buttons are currently hot) */
136 if(!g
->lit_vertex
&& g
->b
.buttons_ready
){
137 buttonstate
*b
= find_button(g
,(int)event
->x
,(int)event
->y
);
140 /* a button is grabbed; a rollover sees only the grabbed
143 button_set_state(g
,b
,1,1);
145 button_set_state(g
,g
->b
.grabbed
,0,0);
147 /* no button is grabbed; any button may see a rollover */
149 button_set_state(g
,b
,1,0);
151 button_clear_state(g
);
158 /* toplevel mouse button press handler */
159 gboolean
mouse_press (GtkWidget
*widget
,
160 GdkEventButton
*event
){
162 if(event
->type
== GDK_BUTTON_PRESS
){ // filter out doubleclicks
164 Gameboard
*g
= GAMEBOARD (widget
);
166 vertex
*v
= find_vertex(&g
->g
,(int)event
->x
,(int)event
->y
);
167 buttonstate
*b
= find_button(g
,(int)event
->x
,(int)event
->y
);
168 int old_intersections
= g
->show_intersections
;
170 /* the level selection dialog implements its own icon mouse management */
171 if(g
->level_dialog_active
)
172 level_mouse_press(g
,(int)event
->x
,(int)event
->y
);
174 button_clear_state(g
);
176 set_show_intersections(g
,0);
178 /* presses that affect board elements other than buttons can only
179 happen when the curtain isn't pushed. */
180 if(!g
->pushed_curtain
){
182 /* case: shift-click addition of a single new vertex to selection */
183 if(v
&& event
->state
&GDK_SHIFT_MASK
){
186 g
->selection_active
--;
189 g
->selection_active
++;
191 invalidate_vertex(g
,g
->lit_vertex
);
195 /* case: shift-click drag of an additional region to add to selection */
196 if(event
->state
&GDK_SHIFT_MASK
){
198 g
->grabx
= (int)event
->x
;
199 g
->graby
= (int)event
->y
;
200 g
->selectionx
= g
->grabx
;
202 g
->selectiony
= g
->graby
;
207 /* case: drag the selected group of verticies to new location */
208 if(g
->selection_active
&& v
&& v
->selected
){
210 g
->grabx
= (int)event
->x
;
211 g
->graby
= (int)event
->y
;
214 // highlight vertex immediately; update the background after the
216 grab_selected(&g
->g
);
217 invalidate_verticies_selection(widget
);
219 update_full_delayed(g
);
223 /* if selection is active, we got this far, and we're not about to
224 take action based on pointing at a button, selection should be
226 if(g
->selection_active
&& (g
->lit_vertex
|| !b
)){
227 deselect_verticies(&g
->g
);
228 g
->selection_active
=0;
230 // potentially pull verticies out of background (can happen if
231 // mouse went offscreen during a grab)
235 /* case: grab/drag a single unselected vertex */
237 g
->grabbed_vertex
= g
->lit_vertex
;
238 g
->grabx
= (int)event
->x
;
239 g
->graby
= (int)event
->y
;
240 g
->graboffx
= g
->grabbed_vertex
->x
-g
->grabx
;
241 g
->graboffy
= g
->grabbed_vertex
->y
-g
->graby
;
243 grab_vertex(&g
->g
,g
->grabbed_vertex
);
244 invalidate_attached(widget
,g
->grabbed_vertex
);
245 invalidate_edges(widget
,g
->grabbed_vertex
,0,0);
247 // highlight vertex immediately; update the background after the
249 update_full_delayed(g
);
254 /* case: button click */
256 // if intersections were visible, a button press is the only
257 // click that wouldn't auto-hide them, so restore the flag to
258 // its state before entry.
259 g
->show_intersections
=old_intersections
;
261 button_set_state(g
,b
,1,1);
266 /* clicking anywhere else wtith no modifiers initiates a new
267 selection drag (assuming curtain isn't pushed) */
268 if(!g
->pushed_curtain
){
270 g
->grabx
= (int)event
->x
;
271 g
->graby
= (int)event
->y
;
272 g
->selectionx
= g
->grabx
;
274 g
->selectiony
= g
->graby
;
283 /* toplevel mouse button release handler */
284 gboolean
mouse_release (GtkWidget
*widget
,
285 GdkEventButton
*event
){
286 Gameboard
*g
= GAMEBOARD (widget
);
288 /* case: button grabbed */
290 buttonstate
*b
= find_button(g
,(int)event
->x
,(int)event
->y
);
291 if(b
&& g
->b
.grabbed
==b
){
292 button_set_state(g
,b
,1,0);
299 /* if the curtain is pushed, no sense checking for other grabs */
300 if(!g
->pushed_curtain
){
302 /* case: release a grabbed vertex */
303 if(g
->grabbed_vertex
){
304 ungrab_vertex(&g
->g
,g
->grabbed_vertex
);
305 update_add_vertex(g
,g
->grabbed_vertex
);
306 fade_attached(g
,g
->grabbed_vertex
);
308 g
->grabbed_vertex
= 0;
310 if(g
->g
.active_intersections
<=g
->g
.objective
)
317 /* case: release a selection grab */
318 if(g
->selection_grab
){
319 invalidate_selection(widget
);
322 /* are there selected verticies? If only one was selected, that's
323 usually due to an accidental drag while clicking. Treat a
324 single-vertex drag select as a nil selection */
325 if(num_selected_verticies(&g
->g
)<=1){
326 g
->selection_active
=0;
327 deselect_verticies(&g
->g
); // could have grabbed just one
329 commit_volatile_selection(&g
->g
);
330 g
->selection_active
=num_selected_verticies(&g
->g
);
334 /* case: release a group drag */
336 move_selected_verticies(&g
->g
,g
->dragx
,g
->dragy
);
338 update_add_selgroup(g
);
339 ungrab_verticies(&g
->g
);
344 if(g
->g
.active_intersections
<=g
->g
.objective
)
351 /* a release may result in a new mouse-over; look for it */
352 mouse_motion(widget
,(GdkEventMotion
*)event
); // the cast is safe in this case