1 /*  Part of XPCE --- The SWI-Prolog GUI toolkit
2 
3     Author:        Jan Wielemaker and Anjo Anjewierden
4     E-mail:        jan@swi.psy.uva.nl
5     WWW:           http://www.swi.psy.uva.nl/projects/xpce/
6     Copyright (c)  1985-2002, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 
38 static status
initialisePopupGesture(PopupGesture g,PopupObj popup,Name button,Modifier modifier)39 initialisePopupGesture(PopupGesture g, PopupObj popup,
40 		       Name button, Modifier modifier)
41 { if ( isDefault(popup) )
42     popup = NIL;
43 
44   initialiseGesture((Gesture) g, button, modifier);
45   assign(g, popup, popup);
46 
47   succeed;
48 }
49 
50 
51 static status
cancelPopupGesture(PopupGesture g,EventObj ev)52 cancelPopupGesture(PopupGesture g, EventObj ev)
53 { assign(g, current, NIL);
54   assign(g, context, NIL);
55 
56   return cancelGesture((Gesture)g, ev);
57 }
58 
59 
60 static status
updatePopupGesture(PopupGesture g,EventObj ev)61 updatePopupGesture(PopupGesture g, EventObj ev)
62 { PopupObj p;
63   Any rec = getMasterEvent(ev);
64 
65   DEBUG(NAME_popup, Cprintf("updatePopupGesture(): rec=%s\n", pp(rec)));
66 
67   if ( notNil(g->popup) )
68   { if ( instanceOfObject(g->popup, ClassFunction) )
69     { TRY( p = getForwardReceiverFunction((Function) g->popup, rec,
70 				  rec, ev, EAV) );
71       TRY( p = checkType(p, nameToType(NAME_popup), g));
72     } else
73       p = g->popup;
74   } else
75   { if ( !(p = get(rec, NAME_popup, EAV)) ||
76 	 !instanceOfObject(p, ClassPopup) )
77       fail;
78   }
79 
80   assign(g, current, p);
81   if ( isNil(g->context) )
82     assign(g, context, notNil(p->context) ? p->context : rec);
83   send(p, NAME_update, g->context, EAV);
84 
85   if ( p->active == OFF || emptyChain(p->members) )
86   { send(g, NAME_cancel, ev, EAV);
87     fail;
88   }
89 
90   succeed;
91 }
92 
93 
94 static status
eventPopupGesture(PopupGesture g,EventObj ev)95 eventPopupGesture(PopupGesture g, EventObj ev)
96 { if ( g->status == NAME_active && isUpEvent(ev) )
97   { PceWindow sw;
98 
99     if ( !(sw = getWindowGraphical(ev->receiver)) )
100       sw = ev->window;
101 
102     if ( notNil(g->current) && g->current->displayed == OFF )
103     { send(g->current, NAME_open, ev->receiver,
104 	   getAreaPositionEvent(ev, DEFAULT), EAV);
105       attributeObject(g, NAME_Stayup, ON);
106       grabPointerWindow(sw, ON);
107       focusWindow(sw, ev->receiver, (Recogniser) g, g->cursor, NIL);
108     } else if ( valInt(getClickTimeEvent(ev)) < 400 &&
109 		getAttributeObject(g, NAME_Stayup) != ON )
110     { attributeObject(g, NAME_Stayup, ON);
111       grabPointerWindow(sw, ON);
112       focusWindow(sw, ev->receiver, (Recogniser) g, g->cursor, NIL);
113     } else
114     { send(g, NAME_terminate, EAV);
115       if ( isNil(g->current) )
116       { grabPointerWindow(sw, OFF);
117 	focusWindow(sw, NIL, NIL, NIL, NIL);
118 	deleteAttributeObject(g, NAME_Stayup);
119 	assign(g, status, NAME_inactive);
120       }
121     }
122 
123     succeed;
124   } else if ( notNil(g->current) && g->current->displayed == ON )
125     return postEvent(ev, (Graphical) g->current, DEFAULT);
126 
127   if ( eventGesture(g, ev) )
128     succeed;
129 
130   if ( g->status == NAME_active && isAEvent(ev, NAME_keyboard) )
131   { Name key;
132 
133     TRY(updatePopupGesture(g, ev));
134     key = characterName(getIdEvent(ev));
135 
136     if ( send(g->current, NAME_key, key, EAV) )
137     { Any context = g->context;
138       PopupObj current = g->current;
139 
140       assign(g, context, NIL);
141       assign(g, current, NIL);
142 
143       send(current, NAME_execute, context, EAV);
144       succeed;
145     } else
146       send(g, NAME_cancel, ev, EAV);
147   }
148 
149   fail;
150 }
151 
152 
153 		/********************************
154 		*       GESTURE BEHAVIOUR	*
155 		********************************/
156 
157 static status
verifyPopupGesture(PopupGesture g,EventObj ev)158 verifyPopupGesture(PopupGesture g, EventObj ev)
159 { return updatePopupGesture(g, ev);
160 }
161 
162 
163 static status
initiatePopupGesture(PopupGesture g,EventObj ev)164 initiatePopupGesture(PopupGesture g, EventObj ev)
165 { if ( isNil(g->max_drag_distance) )
166   { send(g->current, NAME_open, ev->receiver,
167 	 getAreaPositionEvent(ev, DEFAULT), EAV);
168     postEvent(ev, (Graphical) g->current, DEFAULT);
169   }
170 
171   succeed;
172 }
173 
174 
175 static status
dragPopupGesture(PopupGesture g,EventObj ev)176 dragPopupGesture(PopupGesture g, EventObj ev)
177 { if ( notNil(g->current) && g->current->displayed == ON )
178   { DEBUG(NAME_popup, Cprintf("Posting drag to %s\n", pp(g->current)));
179     return postEvent(ev, (Graphical) g->current, DEFAULT);
180   } else
181   { if ( notNil(g->max_drag_distance) )
182     { PceWindow sw;
183 
184       if ( instanceOfObject((sw=ev->window), ClassWindow) &&
185 	   valInt(getDistanceEvent(sw->focus_event, ev)) >
186 	   valInt(g->max_drag_distance) )
187 	send(g, NAME_cancel, ev, EAV);
188     }
189   }
190 
191   fail;
192 }
193 
194 
195 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
196 To avoid dangling references, the context and current are first copied
197 to local variables.
198 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
199 
200 static status
terminatePopupGesture(PopupGesture g,EventObj ev)201 terminatePopupGesture(PopupGesture g, EventObj ev)
202 { Any context = g->context;
203   PopupObj current = g->current;
204 
205   if ( notNil(current) )
206   { postEvent(ev, (Graphical) current, DEFAULT);
207 
208     if ( current->displayed == OFF )	/* for stayup */
209     { PceWindow sw;
210 
211       if ( !(sw = getWindowGraphical(ev->receiver)) )
212 	sw = ev->window;
213 
214       assign(g, context, NIL);
215       assign(g, current, NIL);
216 
217       grabPointerWindow(sw, OFF);
218       send(current, NAME_execute, context, EAV);
219       focusWindow(sw, NIL, NIL, NIL, NIL);
220     }
221   }
222 
223   succeed;
224 }
225 
226 
227 		 /*******************************
228 		 *	 CLASS DECLARATION	*
229 		 *******************************/
230 
231 /* Type declarations */
232 
233 static char *T_initialise[] =
234         { "popup=[popup|function]", "button=[button_name]", "modifier=[modifier]" };
235 
236 /* Instance Variables */
237 
238 static vardecl var_popupGesture[] =
239 { IV(NAME_popup, "popup|function*", IV_BOTH,
240      NAME_popup, "Popup displayed"),
241   IV(NAME_current, "popup*", IV_NONE,
242      NAME_popup, "Currently visible popup"),
243   IV(NAME_context, "any", IV_BOTH,
244      NAME_context, "Context to be send with the ->execute"),
245   IV(NAME_maxDragDistance, "int*", IV_BOTH,
246      NAME_cancel, "Cancel after dragging this far")
247 };
248 
249 /* Send Methods */
250 
251 static senddecl send_popupGesture[] =
252 { SM(NAME_drag, 1, "event", dragPopupGesture,
253      DEFAULT, "Pass drag events to popup"),
254   SM(NAME_initialise, 3, T_initialise, initialisePopupGesture,
255      DEFAULT, "Create from popup, button and modifier"),
256   SM(NAME_initiate, 1, "event", initiatePopupGesture,
257      DEFAULT, "Show popup"),
258   SM(NAME_terminate, 1, "event", terminatePopupGesture,
259      DEFAULT, "Unshow popup and execute selected item"),
260   SM(NAME_verify, 1, "event", verifyPopupGesture,
261      DEFAULT, "Verify popup can be activated"),
262   SM(NAME_cancel, 1, "event", cancelPopupGesture,
263      NAME_cancel, "Cancel this gesture and try the next"),
264   SM(NAME_event, 1, "event", eventPopupGesture,
265      NAME_accelerator, "Handle accelerators")
266 };
267 
268 /* Get Methods */
269 
270 #define get_popupGesture NULL
271 /*
272 static getdecl get_popupGesture[] =
273 {
274 };
275 */
276 
277 /* Resources */
278 
279 static classvardecl rc_popupGesture[] =
280 { RC(NAME_button, "button_name", "right",
281      "Active on which button (right)"),
282   RC(NAME_cursor, "cursor", "right_ptr",
283      "Cursor while active"),
284   RC(NAME_modifier, "modifier", "",
285      "Condition on shift, control and meta"),
286   RC(NAME_maxDragDistance, "int*", "5",
287      "Cancel after dragging this far")
288 };
289 
290 /* Class Declaration */
291 
292 static Name popupGesture_termnames[] = { NAME_popup, NAME_button, NAME_modifier };
293 
294 ClassDecl(popupGesture_decls,
295           var_popupGesture, send_popupGesture,
296 	  get_popupGesture, rc_popupGesture,
297           3, popupGesture_termnames,
298           "$Rev$");
299 
300 status
makeClassPopupGesture(Class class)301 makeClassPopupGesture(Class class)
302 { return declareClass(class, &popupGesture_decls);
303 }
304 
305 
306 
307 Recogniser
popupGesture()308 popupGesture()
309 { if ( GESTURE_popup == NULL )
310     GESTURE_popup = globalObject(NAME_PopupGesture, ClassPopupGesture, EAV);
311 
312   return (Recogniser) GESTURE_popup;
313 }
314 
315