]> git.decadent.org.uk Git - ion3.git/blob - ioncore/frame-pointer.c
[svn-upgrade] Tagging new upstream version, ion3 (20070927)
[ion3.git] / ioncore / frame-pointer.c
1 /*
2  * ion/ioncore/frame-pointer.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
11 #include <libtu/objp.h>
12
13 #include "common.h"
14 #include "global.h"
15 #include "pointer.h"
16 #include "cursor.h"
17 #include "focus.h"
18 #include "attach.h"
19 #include "resize.h"
20 #include "grab.h"
21 #include "frame.h"
22 #include "framep.h"
23 #include "frame-pointer.h"
24 #include "frame-draw.h"
25 #include "bindmaps.h"
26 #include "infowin.h"
27 #include "rectangle.h"
28 #include "xwindow.h"
29 #include "names.h"
30 #include "presize.h"
31 #include "llist.h"
32
33
34 static int p_tab_x=0, p_tab_y=0, p_tabnum=-1;
35 static WInfoWin *tabdrag_infowin=NULL;
36
37
38 /*{{{ Frame press */
39
40
41 static WRegion *sub_at_tab(WFrame *frame)
42 {
43     return mplex_mx_nth((WMPlex*)frame, p_tabnum);
44 }
45
46
47 int frame_press(WFrame *frame, XButtonEvent *ev, WRegion **reg_ret)
48 {
49     WRegion *sub=NULL;
50     WRectangle g;
51     
52     p_tabnum=-1;
53
54     window_p_resize_prepare((WWindow*)frame, ev);
55     
56     /* Check tab */
57     
58     frame_bar_geom(frame, &g);
59     
60     /* Borders act like tabs at top of the parent region */
61     if(REGION_GEOM(frame).y==0){
62         g.h+=g.y;
63         g.y=0;
64     }
65
66     if(frame->barmode!=FRAME_BAR_NONE && 
67        rectangle_contains(&g, ev->x, ev->y)){
68         p_tabnum=frame_tab_at_x(frame, ev->x);
69
70         region_rootpos((WRegion*)frame, &p_tab_x, &p_tab_y);
71         p_tab_x+=frame_nth_tab_x(frame, p_tabnum);
72         p_tab_y+=g.y;
73         
74         sub=mplex_mx_nth(&(frame->mplex), p_tabnum);
75
76         if(reg_ret!=NULL)
77             *reg_ret=sub;
78         
79         return FRAME_AREA_TAB;
80     }else{
81         WLListIterTmp tmp;
82         FRAME_MX_FOR_ALL(sub, frame, tmp){
83             p_tabnum++;
84             if(sub==FRAME_CURRENT(frame))
85                 break;
86         }
87         
88         if(sub!=NULL){
89             p_tab_x=ev->x_root-frame_nth_tab_w(frame, p_tabnum)/2;
90             p_tab_y=ev->y_root-frame->bar_h/2;
91         }else{
92             p_tabnum=-1;
93         }
94     }
95     
96
97     /* Check border */
98     
99     frame_border_inner_geom(frame, &g);
100     
101     if(rectangle_contains(&g, ev->x, ev->y))
102         return FRAME_AREA_CLIENT;
103     
104     return FRAME_AREA_BORDER;
105 }
106
107
108 /*}}}*/
109
110
111 /*{{{ Tab drag */
112
113
114 static ExtlExportedFn *tabdrag_safe_fns[]={
115     (ExtlExportedFn*)&mplex_switch_nth,
116     (ExtlExportedFn*)&mplex_switch_next,
117     (ExtlExportedFn*)&mplex_switch_prev,
118     NULL
119 };
120
121 static ExtlSafelist tabdrag_safelist=EXTL_SAFELIST_INIT(tabdrag_safe_fns);
122
123
124 #define BUTTONS_MASK \
125     (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
126
127
128 static bool tabdrag_kbd_handler(WRegion *reg, XEvent *xev)
129 {
130     XKeyEvent *ev=&xev->xkey;
131     WBinding *binding=NULL;
132     WBindmap **bindptr;
133     
134     if(ev->type==KeyRelease)
135         return FALSE;
136     
137     assert(reg!=NULL);
138
139     binding=bindmap_lookup_binding(ioncore_screen_bindmap, BINDING_KEYPRESS,
140                                    ev->state&~BUTTONS_MASK, ev->keycode);
141     
142     if(binding!=NULL && binding->func!=extl_fn_none()){
143         extl_protect(&tabdrag_safelist);
144         extl_call(binding->func, "o", NULL, region_screen_of(reg));
145         extl_unprotect(&tabdrag_safelist);
146     }
147     
148     return FALSE;
149 }
150
151
152 static void setup_dragwin(WFrame *frame, uint tab)
153 {
154     WRectangle g;
155     WRootWin *rw;
156     WFitParams fp;
157     const char *tab_style=framemode_get_tab_style(frame->mode);
158     
159     assert(tabdrag_infowin==NULL);
160     
161     rw=region_rootwin_of((WRegion*)frame);
162     
163     fp.mode=REGION_FIT_EXACT;
164     fp.g.x=p_tab_x;
165     fp.g.y=p_tab_y;
166     fp.g.w=frame_nth_tab_w(frame, tab);
167     fp.g.h=frame->bar_h;
168     
169     tabdrag_infowin=create_infowin((WWindow*)rw, &fp, tab_style);
170     
171     if(tabdrag_infowin==NULL)
172         return;
173     
174     frame_setup_dragwin_style(frame, infowin_stylespec(tabdrag_infowin), tab);
175     
176     if(frame->titles[tab].text!=NULL){
177         char *buf=INFOWIN_BUFFER(tabdrag_infowin);
178         strncpy(buf, frame->titles[tab].text, INFOWIN_BUFFER_LEN-1);
179         buf[INFOWIN_BUFFER_LEN-1]='\0';
180     }
181 }
182
183
184 static void p_tabdrag_motion(WFrame *frame, XMotionEvent *ev,
185                              int dx, int dy)
186 {
187     WRootWin *rootwin=region_rootwin_of((WRegion*)frame);
188
189     p_tab_x+=dx;
190     p_tab_y+=dy;
191     
192     if(tabdrag_infowin!=NULL){
193         WRectangle g;
194         g.x=p_tab_x;
195         g.y=p_tab_y;
196         g.w=REGION_GEOM(tabdrag_infowin).w;
197         g.h=REGION_GEOM(tabdrag_infowin).h;
198         region_fit((WRegion*)tabdrag_infowin, &g, REGION_FIT_EXACT);
199     }
200 }
201
202
203 static void p_tabdrag_begin(WFrame *frame, XMotionEvent *ev,
204                             int dx, int dy)
205 {
206     WRootWin *rootwin=region_rootwin_of((WRegion*)frame);
207
208     if(p_tabnum<0)
209         return;
210     
211     ioncore_change_grab_cursor(IONCORE_CURSOR_DRAG);
212         
213     setup_dragwin(frame, p_tabnum);
214     
215     frame->tab_dragged_idx=p_tabnum;
216     frame_update_attr_nth(frame, p_tabnum);
217
218     frame_draw_bar(frame, FALSE);
219     
220     p_tabdrag_motion(frame, ev, dx, dy);
221     
222     if(tabdrag_infowin!=NULL)
223         window_map((WWindow*)tabdrag_infowin);
224 }
225
226
227 static WRegion *fnd(Window root, int x, int y)
228 {
229     Window win=root;
230     int dstx, dsty;
231     WRegion *reg=NULL;
232     WWindow *w=NULL;
233     WScreen *scr;
234     
235     FOR_ALL_SCREENS(scr){
236         if(region_root_of((WRegion*)scr)==root &&
237            rectangle_contains(&REGION_GEOM(scr), x, y)){
238             break;
239         }
240     }
241     
242     w=(WWindow*)scr;
243     
244     while(w!=NULL){
245         if(HAS_DYN(w, region_handle_drop))
246             reg=(WRegion*)w;
247         
248         if(!XTranslateCoordinates(ioncore_g.dpy, root, w->win,
249                                   x, y, &dstx, &dsty, &win)){
250             break;
251         }
252         
253         w=XWINDOW_REGION_OF_T(win, WWindow);
254         /*x=dstx;
255         y=dsty;*/
256     }
257
258     return reg;
259 }
260
261
262 static bool drop_ok(WRegion *mgr, WRegion *reg)
263 {
264     WRegion *reg2=mgr;
265     for(reg2=mgr; reg2!=NULL; reg2=region_manager(reg2)){
266         if(reg2==reg)
267             goto err;
268     }
269     
270     for(reg2=REGION_PARENT_REG(mgr); reg2!=NULL; reg2=REGION_PARENT_REG(reg2)){
271         if(reg2==reg)
272             goto err;
273     }
274     
275     return TRUE;
276     
277 err:
278     warn(TR("Attempt to make region %s manage its ancestor %s."),
279          region_name(mgr), region_name(reg));
280     return FALSE;
281 }
282
283
284 static void tabdrag_deinit(WFrame *frame)
285 {
286     int idx=frame->tab_dragged_idx;
287     frame->tab_dragged_idx=-1;
288     frame_update_attr_nth(frame, idx);
289     
290     if(tabdrag_infowin!=NULL){
291         destroy_obj((Obj*)tabdrag_infowin);
292         tabdrag_infowin=NULL;
293     }
294 }
295
296
297 static void tabdrag_killed(WFrame *frame)
298 {
299     tabdrag_deinit(frame);
300     if(!OBJ_IS_BEING_DESTROYED(frame))
301         frame_draw_bar(frame, TRUE);
302 }
303
304
305 static void p_tabdrag_end(WFrame *frame, XButtonEvent *ev)
306 {
307     WRegion *sub=NULL;
308     WRegion *dropped_on;
309     Window win=None;
310
311     sub=sub_at_tab(frame);
312     
313     tabdrag_deinit(frame);
314     
315     /* Must be same root window */
316     if(sub==NULL || ev->root!=region_root_of(sub))
317         return;
318     
319     dropped_on=fnd(ev->root, ev->x_root, ev->y_root);
320
321     if(dropped_on==NULL || dropped_on==(WRegion*)frame || 
322        dropped_on==sub || !drop_ok(dropped_on, sub)){
323         frame_draw_bar(frame, TRUE);
324         return;
325     }
326     
327     if(region_handle_drop(dropped_on, p_tab_x, p_tab_y, sub))
328         region_goto(dropped_on);
329     else
330         frame_draw_bar(frame, TRUE);
331 }
332
333
334 /*EXTL_DOC
335  * Start dragging the tab that the user pressed on with the pointing device.
336  * This function should only be used by binding it to \emph{mpress} or
337  * \emph{mdrag} action with area \codestr{tab}.
338  */
339 EXTL_EXPORT_MEMBER
340 void frame_p_tabdrag(WFrame *frame)
341 {
342     if(p_tabnum<0)
343         return;
344     
345     ioncore_set_drag_handlers((WRegion*)frame,
346                         (WMotionHandler*)p_tabdrag_begin,
347                         (WMotionHandler*)p_tabdrag_motion,
348                         (WButtonHandler*)p_tabdrag_end,
349                         tabdrag_kbd_handler,
350                         (GrabKilledHandler*)tabdrag_killed);
351 }
352
353
354 /*}}}*/
355
356
357 /*{{{ switch_tab */
358
359
360 /*EXTL_DOC
361  * Display the region corresponding to the tab that the user pressed on.
362  * This function should only be used by binding it to a mouse action.
363  */
364 EXTL_EXPORT_MEMBER
365 void frame_p_switch_tab(WFrame *frame)
366 {
367     /*WRegion *sub;*/
368     
369     if(ioncore_pointer_grab_region()!=(WRegion*)frame)
370         return;
371     
372     /*
373     sub=sub_at_tab(frame);
374     if(sub!=NULL){
375         bool mcf=region_may_control_focus((WRegion*)frame);
376         region_goto_flags(sub, (mcf 
377                                 ? REGION_GOTO_FOCUS|REGION_GOTO_NOWARP 
378                                 : 0));
379     }
380     */
381     
382     mplex_switch_nth((WMPlex*)frame, p_tabnum);
383 }
384
385
386 /*}}}*/
387