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