* drag & drop for X11 skins. Still mysterious problems but it's better
[vlc.git] / modules / gui / skins / x11 / x11_window.cpp
1 /*****************************************************************************
2  * x11_window.cpp: X11 implementation of the Window class
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: x11_window.cpp,v 1.18 2003/06/09 00:07:09 asmax Exp $
6  *
7  * Authors: Cyril Deguet     <asmax@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (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
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111,
22  * USA.
23  *****************************************************************************/
24
25 #ifdef X11_SKINS
26
27 //--- GENERAL ---------------------------------------------------------------
28 //#include <math.h>
29
30 //--- VLC -------------------------------------------------------------------
31 #include <vlc/intf.h>
32
33 //--- X11 -------------------------------------------------------------------
34 #include <X11/Xlib.h>
35 #include <X11/Xatom.h>
36 #include <X11/extensions/shape.h>
37
38 //--- SKIN ------------------------------------------------------------------
39 #include "../os_api.h"
40 #include "../src/anchor.h"
41 #include "../controls/generic.h"
42 #include "../src/window.h"
43 #include "../os_window.h"
44 #include "../src/event.h"
45 #include "../os_event.h"
46 #include "../src/graphics.h"
47 #include "../os_graphics.h"
48 #include "../src/skin_common.h"
49 #include "../src/theme.h"
50 #include "../os_theme.h"
51 #include "x11_timer.h"
52
53
54 bool ToolTipCallback( void *data );
55
56
57 //---------------------------------------------------------------------------
58 // Skinable Window
59 //---------------------------------------------------------------------------
60 X11Window::X11Window( intf_thread_t *p_intf, Window wnd, int x, int y,
61     bool visible, int transition, int normalalpha, int movealpha,
62     bool dragdrop, string name )
63     : SkinWindow( p_intf, x, y, visible, transition, normalalpha, movealpha,
64               dragdrop )
65 {
66     // Set handles
67     Wnd         = wnd;
68     display     = p_intf->p_sys->display;
69     int screen  = DefaultScreen( display );
70     Name        = name;
71     LButtonDown = false;
72     RButtonDown = false;
73
74     // Creation of a graphic context that doesn't generate a GraphicsExpose
75     // event when using functions like XCopyArea
76     XGCValues gcVal;
77     gcVal.graphics_exposures = False;
78     XLOCK;
79     Gc = XCreateGC( display, wnd, GCGraphicsExposures, &gcVal );
80     XUNLOCK;
81
82     // Removing fading effect
83     Transition  = 0;
84
85     if( DragDrop )
86     {
87         // register the listview as a drop target
88         DropObject = new X11DropObject( p_intf, Wnd );
89
90         Atom xdndAtom = XInternAtom( display, "XdndAware", False );
91         char xdndVersion = 4;
92         XLOCK;
93         XChangeProperty( display, wnd, xdndAtom, XA_ATOM, 32, 
94                          PropModeReplace, (unsigned char *)&xdndVersion, 1);
95         XUNLOCK;
96     }
97
98     // Create Tool Tip window
99     XColor color;
100     color.red = 0xffff;
101     color.green = 0xffff;
102     color.blue = 0xa000;
103     Colormap cm = DefaultColormap( display, screen );
104     Window root = DefaultRootWindow( display );
105
106     XLOCK;
107     XAllocColor( display, cm, &color );
108     XSetWindowAttributes attr;
109     attr.background_pixel = color.pixel;
110     attr.override_redirect = True;
111     ToolTip.window = XCreateWindow( display, root, 0, 0, 1, 1, 1, 0,
112                                     InputOutput, CopyFromParent,
113                                     CWBackPixel|CWOverrideRedirect, &attr );
114     ToolTip.font = XLoadFont( display,
115                               "-*-helvetica-bold-r-*-*-*-80-*-*-*-*-*-*" );
116     gcVal.font = ToolTip.font;
117     gcVal.foreground = 0;
118     gcVal.background = color.pixel;
119     ToolTip.gc = XCreateGC( display, ToolTip.window, 
120                             GCBackground|GCForeground|GCFont, &gcVal );
121     XUNLOCK;
122
123     ToolTip.display = display;
124     X11Timer *timer = new X11Timer( p_intf, 500000, ToolTipCallback, &ToolTip );
125     ToolTip.p_intf = p_intf;
126     ToolTip.timer = timer;
127
128     // Double-click handling
129     ClickedX = 0;
130     ClickedY = 0;
131     ClickedTime = 0;
132     // TODO: can be retrieved somewhere ?
133     DblClickDelay = 400;
134    
135 }
136 //---------------------------------------------------------------------------
137 X11Window::~X11Window()
138 {
139 /*    delete CursorPos;
140     delete WindowPos;
141
142     if( hWnd != NULL )
143     {
144         DestroyWindow( hWnd );
145     }*/
146
147     XFreeGC( display, Gc );
148     XDestroyWindow( display, ToolTip.window );
149
150     /*
151     if( DragDrop )
152     {
153         // Remove the listview from the list of drop targets
154         RevokeDragDrop( hWnd );
155         DropTarget->Release();
156         // Uninitialize the OLE library
157         OleUninitialize();
158     }*/
159 }
160 //---------------------------------------------------------------------------
161 void X11Window::OSShow( bool show )
162 {
163     XLOCK;
164     XResizeWindow( display, Wnd, 1, 1 ); // Avoid flicker
165     XUNLOCK;
166
167     if( show )
168     {
169         // We do the call to XShapeCombineRegion() here because the window
170         // must be unmapped for this to work.
171         Drawable drawable = (( X11Graphics* )Image )->GetImage();
172
173         XLOCK;
174         XImage *image = XGetImage( display, drawable, 0, 0, Width, Height, 
175                                    AllPlanes, ZPixmap );
176         if( image )
177         {
178             // Mask for transparency
179             Region region = XCreateRegion();
180             region = XCreateRegion();
181             for( int line = 0; line < Height; line++ )
182             {
183                 int start = 0, end = 0;
184                 while( start < Width )
185                 {
186                     while( start < Width && XGetPixel( image, start, line )
187                            == 0 )
188                     {
189                         start++;
190                     }
191                     end = start;
192                     while( end < Width && XGetPixel( image, end, line ) != 0)
193                     {
194                         end++;
195                     }
196                     XRectangle rect;
197                     rect.x = start;
198                     rect.y = line;
199                     rect.width = end - start + 1;
200                     rect.height = 1;
201                     Region newRegion = XCreateRegion();
202                     XUnionRectWithRegion( &rect, region, newRegion );
203                     XDestroyRegion( region );
204                     region = newRegion;
205                     start = end + 1;
206                 }
207             }
208
209             XShapeCombineRegion( display, Wnd, ShapeBounding, 0, 0, region,
210                                  ShapeSet );
211             XDestroyRegion( region );
212
213         }
214         else
215         {
216             msg_Err( p_intf, "X11Window::OSShow XShapeCombineRegion() failed");
217         }
218
219         XMapWindow( display, Wnd );
220         XMoveResizeWindow( display, Wnd, Left, Top, Width, Height );
221         XUNLOCK;
222     }
223     else
224     {
225         XLOCK;
226         XUnmapWindow( display, Wnd );
227         XUNLOCK;
228     }
229 }
230 //---------------------------------------------------------------------------
231 bool X11Window::ProcessOSEvent( Event *evt )
232 {
233     unsigned int msg = evt->GetMessage();
234     unsigned int p1  = evt->GetParam1();
235     int          p2  = evt->GetParam2();
236     int          time;
237     int          posX, posY;
238
239     switch( msg )
240     {
241         case Expose:
242             RefreshFromImage( 0, 0, Width, Height );
243             return true;
244
245         case MotionNotify:
246             if( LButtonDown )
247                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
248                            (int)( (XMotionEvent *)p2 )->y, 1 );
249             else if( RButtonDown )
250                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
251                            (int)( (XMotionEvent *)p2 )->y, 2 );
252             else
253                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
254                            (int)( (XMotionEvent *)p2 )->y, 0 );
255             return true;
256
257
258         case ButtonPress:
259             // Raise all the windows
260             for( list<SkinWindow *>::const_iterator win = 
261                     p_intf->p_sys->p_theme->WindowList.begin();
262                     win != p_intf->p_sys->p_theme->WindowList.end(); win++ )
263             {
264                 XLOCK;
265                 XRaiseWindow( display, ( (X11Window *)(*win) )->GetHandle() );
266                 XUNLOCK;
267             }
268
269             switch( ( (XButtonEvent *)p2 )->button )
270             {
271                 case 1:
272                     // Left button
273                     time = OSAPI_GetTime();
274                     OSAPI_GetMousePos( posX, posY );
275                     if( time - ClickedTime < DblClickDelay && 
276                         posX == ClickedX && posY == ClickedY )
277                     {
278                         // Double-click
279                         ClickedTime = 0; 
280                         MouseDblClick( (int)( (XButtonEvent *)p2 )->x,
281                                        (int)( (XButtonEvent *)p2 )->y, 1 );
282                     }
283                     else
284                     {
285                         ClickedTime = time;
286                         ClickedX = posX;
287                         ClickedY = posY;
288                         LButtonDown = true;
289                         MouseDown( (int)( (XButtonEvent *)p2 )->x,
290                                    (int)( (XButtonEvent *)p2 )->y, 1 );
291                     }
292                     break;
293
294                 case 3:
295                     // Right button
296                     RButtonDown = true;
297                     MouseDown( (int)( (XButtonEvent *)p2 )->x,
298                                (int)( (XButtonEvent *)p2 )->y, 2 );
299                     break;
300
301                 default:
302                     break;
303             }
304             return true;
305
306         case ButtonRelease:
307             switch( ( (XButtonEvent *)p2 )->button )
308             {
309                 case 1:
310                     // Left button
311                     LButtonDown = false;
312                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
313                              (int)( (XButtonEvent *)p2 )->y, 1 );
314                     break;
315
316                 case 3:
317                     // Right button
318                     RButtonDown = false;
319                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
320                              (int)( (XButtonEvent *)p2 )->y, 2 );
321                     break; 
322
323                 case 4:
324                     // Scroll up
325                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
326                                  (int)( (XButtonEvent *)p2 )->y,
327                                  MOUSE_SCROLL_UP);
328                     break;
329  
330                 case 5:
331                     // Scroll down
332                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
333                                  (int)( (XButtonEvent *)p2 )->y,
334                                  MOUSE_SCROLL_DOWN);
335                     break;
336
337                 default:
338                     break;
339             }
340             return true;
341
342         case LeaveNotify:
343             OSAPI_PostMessage( this, WINDOW_LEAVE, 0, 0 );
344             return true;
345
346         case ClientMessage:
347             {
348             XLOCK;
349             string type = XGetAtomName( display, ( (XClientMessageEvent*)
350                                                     p2 )->message_type );
351             XUNLOCK;
352             if( type == "XdndEnter" )
353             {
354                 DropObject->DndEnter( ((XClientMessageEvent*)p2)->data.l );
355                 return true;
356             }
357             else if( type == "XdndPosition" )
358             {
359                 DropObject->DndPosition( ((XClientMessageEvent*)p2)->data.l );
360                 return true;
361             }
362             else if( type == "XdndLeave" )
363             {
364                 DropObject->DndLeave( ((XClientMessageEvent*)p2)->data.l );
365                 return true;
366             }
367             else if( type == "XdndDrop" )
368             {
369                 DropObject->DndDrop( ((XClientMessageEvent*)p2)->data.l );
370                 return true;
371             }
372             }
373             return false;
374             
375         default:
376             return false;
377     }
378 }
379 //---------------------------------------------------------------------------
380 void X11Window::SetTransparency( int Value )
381 {
382 /*    if( Value > -1 )
383         Alpha = Value;
384     SetLayeredWindowAttributes( hWnd, 0, Alpha, LWA_ALPHA | LWA_COLORKEY );
385     UpdateWindow( hWnd );*/
386 }
387 //---------------------------------------------------------------------------
388 void X11Window::RefreshFromImage( int x, int y, int w, int h )
389 {
390     Drawable drawable = (( X11Graphics* )Image )->GetImage();
391
392     XLOCK;
393     XCopyArea( display, drawable, Wnd, Gc, x, y, w, h, x, y );
394     XSync( display, 0);
395     XUNLOCK;
396 }
397 //---------------------------------------------------------------------------
398 void X11Window::WindowManualMove()
399 {
400     // Get mouse cursor position
401     int x, y;
402     OSAPI_GetMousePos( x, y );
403
404     // Move window and chek for magnetism
405     p_intf->p_sys->p_theme->MoveSkinMagnet( this,
406         WindowX + x - CursorX, WindowY + y - CursorY );
407
408 }
409 //---------------------------------------------------------------------------
410 void X11Window::WindowManualMoveInit()
411 {
412     OSAPI_GetMousePos( CursorX, CursorY );
413     WindowX = Left;
414     WindowY = Top;
415 }
416 //---------------------------------------------------------------------------
417 void X11Window::Move( int left, int top )
418 {
419     Left = left;
420     Top  = top;
421     XMoveWindow( display, Wnd, left, top );
422 }
423 //---------------------------------------------------------------------------
424 void X11Window::Size( int width, int height )
425 {
426     Width  = width;
427     Height = height;
428     XResizeWindow( display, Wnd, width, height );
429 }
430 //---------------------------------------------------------------------------
431
432
433 bool ToolTipCallback( void *data )
434 {
435     int direction, fontAscent, fontDescent;
436
437     Display *disp = ((tooltip_t*)data)->display;
438     Window win = ((tooltip_t*)data)->window;
439     Font font = ((tooltip_t*)data)->font;
440     GC gc = ((tooltip_t*)data)->gc;
441     string text = ((tooltip_t*)data)->text;
442     int curX = ((tooltip_t*)data)->curX;
443     int curY = ((tooltip_t*)data)->curY;
444  
445     XLOCK;
446     XClearWindow( disp, win );
447     XCharStruct overall;
448     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
449                        &fontAscent, &fontDescent, &overall );
450     int w = overall.rbearing - overall.lbearing;
451     int h = overall.ascent + overall.descent;
452     XMapRaised( disp, win );
453     XMoveWindow( disp, win, curX - w/4, curY + 20 );
454     XResizeWindow( disp, win, w+8, h+8 );
455     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
456                  text.size() );
457     XSync( disp, 0 );
458     XUNLOCK;
459     
460     return False;
461 }
462
463
464 void X11Window::ChangeToolTipText( string text )
465 {
466     if( text == "none" )
467     {
468         if( ToolTipText != "none" )
469         {
470             ToolTipText = "none";
471             XLOCK;
472             // Hide the tooltip window
473             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
474             timerManager->removeTimer( ToolTip.timer );
475             XUnmapWindow( display, ToolTip.window );
476             XResizeWindow( display, ToolTip.window, 1, 1 );
477             XSync( display, 0 );
478             XUNLOCK;
479         }
480     }
481     else
482     {
483         if( text != ToolTipText )
484         {
485             ToolTipText = text;
486             ToolTip.text = text;
487             OSAPI_GetMousePos( ToolTip.curX, ToolTip.curY );
488             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
489             timerManager->addTimer( ToolTip.timer );
490         }
491     }
492 }
493 //---------------------------------------------------------------------------
494
495 #endif