* tooltips are now updated during scrolling
[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.24 2003/06/17 18:13:18 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 static bool ToolTipCallback( void *data );
55 static void DrawToolTipText( tooltip_t *tooltip );
56
57
58 //---------------------------------------------------------------------------
59 // Skinable Window
60 //---------------------------------------------------------------------------
61 X11Window::X11Window( intf_thread_t *p_intf, Window wnd, int x, int y,
62     bool visible, int transition, int normalalpha, int movealpha,
63     bool dragdrop, string name )
64     : SkinWindow( p_intf, x, y, visible, transition, normalalpha, movealpha,
65               dragdrop )
66 {
67     // Set handles
68     Wnd         = wnd;
69     display     = p_intf->p_sys->display;
70     int screen  = DefaultScreen( display );
71     Name        = name;
72     LButtonDown = false;
73     RButtonDown = false;
74
75     // Creation of a graphic context that doesn't generate a GraphicsExpose
76     // event when using functions like XCopyArea
77     XGCValues gcVal;
78     gcVal.graphics_exposures = False;
79     XLOCK;
80     Gc = XCreateGC( display, wnd, GCGraphicsExposures, &gcVal );
81     XUNLOCK;
82
83     // Removing fading effect
84     Transition  = 0;
85
86     if( DragDrop )
87     {
88         // register the listview as a drop target
89         DropObject = new X11DropObject( p_intf, Wnd );
90
91         Atom xdndAtom = XInternAtom( display, "XdndAware", False );
92         char xdndVersion = 4;
93         XLOCK;
94         XChangeProperty( display, wnd, xdndAtom, XA_ATOM, 32, 
95                          PropModeReplace, (unsigned char *)&xdndVersion, 1);
96         XUNLOCK;
97     }
98
99     // Associate vlc icon to the window
100     XLOCK;
101     XWMHints *hints = XGetWMHints( display, Wnd );
102     if( !hints)
103     {
104         hints = XAllocWMHints();
105     }
106     if( p_intf->p_sys->iconPixmap != None )
107     {
108         hints->icon_pixmap = p_intf->p_sys->iconPixmap;
109         hints->flags |= IconPixmapHint;
110     }
111     if( p_intf->p_sys->iconMask != None )
112     {
113         hints->icon_mask = p_intf->p_sys->iconMask;
114         hints->flags |= IconMaskHint;
115     }
116     XSetWMHints( display, Wnd, hints );
117     XFree( hints );
118     XUNLOCK;
119
120     // Create Tool Tip window
121     XColor color;
122     color.red = 0xffff;
123     color.green = 0xffff;
124     color.blue = 0xa000;
125     Colormap cm = DefaultColormap( display, screen );
126     Window root = DefaultRootWindow( display );
127
128     XLOCK;
129     XAllocColor( display, cm, &color );
130     XSetWindowAttributes attr;
131     attr.background_pixel = color.pixel;
132     attr.override_redirect = True;
133     ToolTip.window = XCreateWindow( display, root, 0, 0, 1, 1, 1, 0,
134                                     InputOutput, CopyFromParent,
135                                     CWBackPixel|CWOverrideRedirect, &attr );
136     ToolTip.font = XLoadFont( display,
137                               "-*-helvetica-bold-r-*-*-*-80-*-*-*-*-*-*" );
138     gcVal.font = ToolTip.font;
139     gcVal.foreground = 0;
140     gcVal.background = color.pixel;
141     ToolTip.gc = XCreateGC( display, ToolTip.window, 
142                             GCBackground|GCForeground|GCFont, &gcVal );
143     XUNLOCK;
144
145     ToolTip.display = display;
146     X11Timer *timer = new X11Timer( p_intf, 500000, ToolTipCallback, &ToolTip );
147     ToolTip.p_intf = p_intf;
148     ToolTip.timer = timer;
149     ToolTip.active = False;
150
151     // Double-click handling
152     ClickedX = 0;
153     ClickedY = 0;
154     ClickedTime = 0;
155     // TODO: can be retrieved somewhere ?
156     DblClickDelay = 400;
157    
158 }
159 //---------------------------------------------------------------------------
160 X11Window::~X11Window()
161 {
162     if( DragDrop )
163     {
164         delete DropObject;
165     }
166     delete ToolTip.timer;
167     XLOCK;
168     XFreeGC( display, ToolTip.gc );
169     XFreeGC( display, Gc );
170     XDestroyWindow( display, ToolTip.window );
171     XDestroyWindow( display, Wnd );
172     XUNLOCK;
173 }
174 //---------------------------------------------------------------------------
175 void X11Window::OSShow( bool show )
176 {
177     XLOCK;
178     XResizeWindow( display, Wnd, 1, 1 ); // Avoid flicker
179     XUNLOCK;
180
181     if( show )
182     {
183         // We do the call to XShapeCombineRegion() here because the window
184         // must be unmapped for this to work.
185         Drawable drawable = (( X11Graphics* )Image )->GetImage();
186
187         XLOCK;
188         XImage *image = XGetImage( display, drawable, 0, 0, Width, Height, 
189                                    AllPlanes, ZPixmap );
190         if( image )
191         {
192             // Mask for transparency
193             Region region = XCreateRegion();
194             for( int line = 0; line < Height; line++ )
195             {
196                 int start = 0, end = 0;
197                 while( start < Width )
198                 {
199                     while( start < Width && XGetPixel( image, start, line )
200                            == 0 )
201                     {
202                         start++;
203                     }
204                     end = start;
205                     while( end < Width && XGetPixel( image, end, line ) != 0)
206                     {
207                         end++;
208                     }
209                     XRectangle rect;
210                     rect.x = start;
211                     rect.y = line;
212                     rect.width = end - start + 1;
213                     rect.height = 1;
214                     Region newRegion = XCreateRegion();
215                     XUnionRectWithRegion( &rect, region, newRegion );
216                     XDestroyRegion( region );
217                     region = newRegion;
218                     start = end + 1;
219                 }
220             }
221             XDestroyImage( image );
222
223             XShapeCombineRegion( display, Wnd, ShapeBounding, 0, 0, region,
224                                  ShapeSet );
225             XDestroyRegion( region );
226
227         }
228         else
229         {
230             msg_Err( p_intf, "X11Window::OSShow XShapeCombineRegion() failed");
231         }
232
233         XMapWindow( display, Wnd );
234         XMoveResizeWindow( display, Wnd, Left, Top, Width, Height );
235         XUNLOCK;
236     }
237     else
238     {
239         XLOCK;
240         XUnmapWindow( display, Wnd );
241         XUNLOCK;
242     }
243 }
244 //---------------------------------------------------------------------------
245 bool X11Window::ProcessOSEvent( Event *evt )
246 {
247     unsigned int msg = evt->GetMessage();
248     //unsigned int p1  = evt->GetParam1();
249     int          p2  = evt->GetParam2();
250     int          time;
251     int          posX, posY;
252     string       type;
253     int          button;
254
255     switch( msg )
256     {
257         case Expose:
258             RefreshFromImage( 0, 0, Width, Height );
259             return true;
260
261         case MotionNotify:
262             if( LButtonDown )
263                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
264                            (int)( (XMotionEvent *)p2 )->y, 1 );
265             else if( RButtonDown )
266                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
267                            (int)( (XMotionEvent *)p2 )->y, 2 );
268             else
269                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
270                            (int)( (XMotionEvent *)p2 )->y, 0 );
271             return true;
272
273
274         case ButtonPress:
275             // Raise all the windows
276             for( list<SkinWindow *>::const_iterator win = 
277                     p_intf->p_sys->p_theme->WindowList.begin();
278                     win != p_intf->p_sys->p_theme->WindowList.end(); win++ )
279             {
280                 XLOCK;
281                 XRaiseWindow( display, ( (X11Window *)(*win) )->GetHandle() );
282                 XUNLOCK;
283             }
284             
285             button = 0;
286             if( ((XButtonEvent *)p2 )->state & ControlMask )
287             {
288                 button |= KEY_CTRL;
289             } 
290             if( ((XButtonEvent *)p2 )->state & ShiftMask )
291             {
292                 button |= KEY_SHIFT;
293             }
294
295             switch( ( (XButtonEvent *)p2 )->button )
296             {
297                 case 1:
298                     // Left button
299                     button |= MOUSE_LEFT;
300                     time = OSAPI_GetTime();
301                     OSAPI_GetMousePos( posX, posY );
302                     if( time - ClickedTime < DblClickDelay && 
303                         posX == ClickedX && posY == ClickedY )
304                     {
305                         // Double-click
306                         ClickedTime = 0; 
307                         MouseDblClick( (int)( (XButtonEvent *)p2 )->x,
308                                        (int)( (XButtonEvent *)p2 )->y, button );
309                     }
310                     else
311                     {
312                         ClickedTime = time;
313                         ClickedX = posX;
314                         ClickedY = posY;
315                         LButtonDown = true;
316                         MouseDown( (int)( (XButtonEvent *)p2 )->x,
317                                    (int)( (XButtonEvent *)p2 )->y, button );
318                     }
319                     break;
320
321                 case 3:
322                     // Right button
323                     button |= MOUSE_RIGHT;
324                     RButtonDown = true;
325                     MouseDown( (int)( (XButtonEvent *)p2 )->x,
326                                (int)( (XButtonEvent *)p2 )->y, button );
327                     break;
328
329                 default:
330                     break;
331             }
332             return true;
333
334         case ButtonRelease:
335             button = 0;
336             if( ((XButtonEvent *)p2 )->state & ControlMask )
337             {
338                 button |= KEY_CTRL;
339             }
340             if( ((XButtonEvent *)p2 )->state & ShiftMask )
341             {
342                 button |= KEY_SHIFT;
343             }
344
345             switch( ( (XButtonEvent *)p2 )->button )
346             {
347                 case 1:
348                     // Left button
349                     button |= MOUSE_LEFT;
350                     LButtonDown = false;
351                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
352                              (int)( (XButtonEvent *)p2 )->y, button );
353                     break;
354
355                 case 3:
356                     button |= MOUSE_RIGHT;
357                     // Right button
358                     RButtonDown = false;
359                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
360                              (int)( (XButtonEvent *)p2 )->y, button );
361                     break; 
362
363                 case 4:
364                     // Scroll up
365                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
366                                  (int)( (XButtonEvent *)p2 )->y,
367                                  MOUSE_SCROLL_UP);
368                     break;
369  
370                 case 5:
371                     // Scroll down
372                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
373                                  (int)( (XButtonEvent *)p2 )->y,
374                                  MOUSE_SCROLL_DOWN);
375                     break;
376
377                 default:
378                     break;
379             }
380             return true;
381
382         case LeaveNotify:
383             OSAPI_PostMessage( this, WINDOW_LEAVE, 0, 0 );
384             return true;
385
386         case ClientMessage:
387             XLOCK;
388             type = XGetAtomName( display, ( (XClientMessageEvent*)
389                                              p2 )->message_type );
390             XUNLOCK;
391             if( type == "XdndEnter" )
392             {
393                 DropObject->DndEnter( ((XClientMessageEvent*)p2)->data.l );
394                 return true;
395             }
396             else if( type == "XdndPosition" )
397             {
398                 DropObject->DndPosition( ((XClientMessageEvent*)p2)->data.l );
399                 return true;
400             }
401             else if( type == "XdndLeave" )
402             {
403                 DropObject->DndLeave( ((XClientMessageEvent*)p2)->data.l );
404                 return true;
405             }
406             else if( type == "XdndDrop" )
407             {
408                 DropObject->DndDrop( ((XClientMessageEvent*)p2)->data.l );
409                 return true;
410             }
411             return false;
412             
413         default:
414             return false;
415     }
416 }
417 //---------------------------------------------------------------------------
418 void X11Window::SetTransparency( int Value )
419 {
420 /*    if( Value > -1 )
421         Alpha = Value;
422     SetLayeredWindowAttributes( hWnd, 0, Alpha, LWA_ALPHA | LWA_COLORKEY );
423     UpdateWindow( hWnd );*/
424 }
425 //---------------------------------------------------------------------------
426 void X11Window::RefreshFromImage( int x, int y, int w, int h )
427 {
428     Drawable drawable = (( X11Graphics* )Image )->GetImage();
429
430     XLOCK;
431     XCopyArea( display, drawable, Wnd, Gc, x, y, w, h, x, y );
432     XSync( display, 0);
433     XUNLOCK;
434 }
435 //---------------------------------------------------------------------------
436 void X11Window::WindowManualMove()
437 {
438     // Get mouse cursor position
439     int x, y;
440     OSAPI_GetMousePos( x, y );
441
442     // Move window and chek for magnetism
443     p_intf->p_sys->p_theme->MoveSkinMagnet( this,
444         WindowX + x - CursorX, WindowY + y - CursorY );
445
446 }
447 //---------------------------------------------------------------------------
448 void X11Window::WindowManualMoveInit()
449 {
450     OSAPI_GetMousePos( CursorX, CursorY );
451     WindowX = Left;
452     WindowY = Top;
453 }
454 //---------------------------------------------------------------------------
455 void X11Window::Move( int left, int top )
456 {
457     Left = left;
458     Top  = top;
459     XMoveWindow( display, Wnd, left, top );
460 }
461 //---------------------------------------------------------------------------
462 void X11Window::Size( int width, int height )
463 {
464     Width  = width;
465     Height = height;
466     XResizeWindow( display, Wnd, width, height );
467 }
468 //---------------------------------------------------------------------------
469
470
471 bool ToolTipCallback( void *data )
472 {
473     int direction, fontAscent, fontDescent;
474
475     Display *disp = ((tooltip_t*)data)->display;
476     Window win = ((tooltip_t*)data)->window;
477     Font font = ((tooltip_t*)data)->font;
478     GC gc = ((tooltip_t*)data)->gc;
479     string text = ((tooltip_t*)data)->text;
480     int curX = ((tooltip_t*)data)->curX;
481     int curY = ((tooltip_t*)data)->curY;
482  
483     XLOCK;
484     XClearWindow( disp, win );
485     XCharStruct overall;
486     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
487                        &fontAscent, &fontDescent, &overall );
488     int w = overall.rbearing - overall.lbearing;
489     int h = overall.ascent + overall.descent;
490     XMapRaised( disp, win );
491     XMoveWindow( disp, win, curX - w/4, curY + 20 );
492     XResizeWindow( disp, win, w+8, h+8 );
493     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
494                  text.size() );
495     XSync( disp, 0 );
496     ((tooltip_t*)data)->active = True;
497     XUNLOCK;
498     
499     return False;
500 }
501
502
503
504 void DrawToolTipText( tooltip_t *tooltip )
505 {
506     int direction, fontAscent, fontDescent;
507
508     Display *disp = tooltip->display;
509     Window win = tooltip->window;
510     Font font = tooltip->font;
511     GC gc = tooltip->gc;
512     string text = tooltip->text;
513     int curX = tooltip->curX;
514     int curY = tooltip->curY;
515  
516     XLOCK;
517     XClearWindow( disp, win );
518     XCharStruct overall;
519     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
520                        &fontAscent, &fontDescent, &overall );
521     int w = overall.rbearing - overall.lbearing;
522     int h = overall.ascent + overall.descent;
523     XMoveWindow( disp, win, curX - w/4, curY + 20 );
524     XResizeWindow( disp, win, w+8, h+8 );
525     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
526                  text.size() );
527     XSync( disp, 0 );
528     XUNLOCK;
529 }
530
531
532 void X11Window::ChangeToolTipText( string text )
533 {
534     if( text == "none" )
535     {
536         if( ToolTipText != "none" )
537         {
538             ToolTipText = "none";
539             XLOCK;
540             // Hide the tooltip window
541             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
542             timerManager->removeTimer( ToolTip.timer );
543             XUnmapWindow( display, ToolTip.window );
544             XResizeWindow( display, ToolTip.window, 1, 1 );
545             XSync( display, 0 );
546             ToolTip.active = False;
547             XUNLOCK;
548         }
549     }
550     else
551     {
552         if( text != ToolTipText )
553         {
554             ToolTipText = text;
555             ToolTip.text = text;
556             if( !ToolTip.active )
557             {
558                 // Create the tooltip
559                 OSAPI_GetMousePos( ToolTip.curX, ToolTip.curY );
560                 X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
561                 timerManager->addTimer( ToolTip.timer );
562             }
563             else
564             {
565                 // Refresh the tooltip
566                 DrawToolTipText( &ToolTip );
567             }
568         }
569     }
570 }
571 //---------------------------------------------------------------------------
572
573 #endif