On OSes other than Windows, Mac OS X and BeOS (so it's Linux) comply with the XDG...
[vlc.git] / modules / gui / skins2 / win32 / win32_factory.cpp
1 /*****************************************************************************
2  * win32_factory.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef WIN32_SKINS
26
27 #include "win32_factory.hpp"
28 #include "win32_graphics.hpp"
29 #include "win32_timer.hpp"
30 #include "win32_window.hpp"
31 #include "win32_tooltip.hpp"
32 #include "win32_popup.hpp"
33 #include "win32_loop.hpp"
34 #include "../src/theme.hpp"
35 #include "../src/window_manager.hpp"
36 #include "../commands/cmd_dialogs.hpp"
37 #include "../commands/cmd_minimize.hpp"
38
39 // Custom message for the notifications of the system tray
40 #define MY_WM_TRAYACTION (WM_APP + 1)
41
42
43 LRESULT CALLBACK Win32Proc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
44 {
45     // Get pointer to thread info: should only work with the parent window
46     intf_thread_t *p_intf = (intf_thread_t *)GetWindowLongPtr( hwnd,
47         GWLP_USERDATA );
48
49     // If doesn't exist, treat windows message normally
50     if( p_intf == NULL || p_intf->p_sys->p_osFactory == NULL )
51     {
52         return DefWindowProc( hwnd, uMsg, wParam, lParam );
53     }
54
55     // Here we know we are getting a message for the parent window, since it is
56     // the only one to store p_intf...
57     // Yes, it is a kludge :)
58
59 //Win32Factory *pFactory = (Win32Factory*)Win32Factory::instance( p_intf );
60 //msg_Err( p_intf, "Parent window %p %p %u %i\n", pFactory->m_hParentWindow, hwnd, uMsg, wParam );
61     // If Window is parent window
62     // XXX: this test isn't needed, see the kludge above...
63 //    if( hwnd == pFactory->m_hParentWindow )
64     {
65         if( uMsg == WM_SYSCOMMAND )
66         {
67             // If closing parent window
68             if( wParam == SC_CLOSE )
69             {
70                 Win32Loop *pLoop = (Win32Loop*)Win32Loop::instance( p_intf );
71                 pLoop->exit();
72                 return 0;
73             }
74             else
75             {
76                 msg_Dbg( p_intf, "WM_SYSCOMMAND %i", wParam );
77             }
78         }
79         // Handle systray notifications
80         else if( uMsg == MY_WM_TRAYACTION )
81         {
82             if( (UINT)lParam == WM_LBUTTONDOWN )
83             {
84                 p_intf->p_sys->p_theme->getWindowManager().raiseAll();
85             }
86             else if( (UINT)lParam == WM_RBUTTONDOWN )
87             {
88                 CmdDlgShowPopupMenu aCmdPopup( p_intf );
89                 aCmdPopup.execute();
90             }
91             else if( (UINT)lParam == WM_LBUTTONDBLCLK )
92             {
93                 CmdRestore aCmdRestore( p_intf );
94                 aCmdRestore.execute();
95             }
96         }
97     }
98
99     // If hwnd does not match any window or message not processed
100     return DefWindowProc( hwnd, uMsg, wParam, lParam );
101 }
102
103
104 Win32Factory::Win32Factory( intf_thread_t *pIntf ):
105     OSFactory( pIntf ), TransparentBlt( NULL ), AlphaBlend( NULL ),
106     SetLayeredWindowAttributes( NULL ), m_hParentWindow( NULL ),
107     m_dirSep( "\\" )
108 {
109     // see init()
110 }
111
112
113 bool Win32Factory::init()
114 {
115     // Get instance handle
116     m_hInst = GetModuleHandle( NULL );
117     if( m_hInst == NULL )
118     {
119         msg_Err( getIntf(), "Cannot get module handle" );
120     }
121
122     // Create window class
123     WNDCLASS skinWindowClass;
124     skinWindowClass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
125     skinWindowClass.lpfnWndProc = (WNDPROC) Win32Proc;
126     skinWindowClass.lpszClassName = _T("SkinWindowClass");
127     skinWindowClass.lpszMenuName = NULL;
128     skinWindowClass.cbClsExtra = 0;
129     skinWindowClass.cbWndExtra = 0;
130     skinWindowClass.hbrBackground = NULL;
131     skinWindowClass.hCursor = LoadCursor( NULL , IDC_ARROW );
132     skinWindowClass.hIcon = LoadIcon( m_hInst, _T("VLC_ICON") );
133     skinWindowClass.hInstance = m_hInst;
134
135     // Register class and check it
136     if( !RegisterClass( &skinWindowClass ) )
137     {
138         WNDCLASS wndclass;
139
140         // Check why it failed. If it's because the class already exists
141         // then fine, otherwise return with an error.
142         if( !GetClassInfo( m_hInst, _T("SkinWindowClass"), &wndclass ) )
143         {
144             msg_Err( getIntf(), "cannot register window class" );
145             return false;
146         }
147     }
148
149     // Create Window
150     m_hParentWindow = CreateWindowEx( WS_EX_TOOLWINDOW, _T("SkinWindowClass"),
151         _T("VLC media player"), WS_SYSMENU|WS_POPUP,
152         -200, -200, 0, 0, 0, 0, m_hInst, 0 );
153     if( m_hParentWindow == NULL )
154     {
155         msg_Err( getIntf(), "cannot create parent window" );
156         return false;
157     }
158
159     // Store with it a pointer to the interface thread
160     SetWindowLongPtr( m_hParentWindow, GWLP_USERDATA, (LONG_PTR)getIntf() );
161
162     // We do it this way otherwise CreateWindowEx will fail
163     // if WS_EX_LAYERED is not supported
164     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
165                       GetWindowLong( m_hParentWindow, GWL_EXSTYLE ) |
166                       WS_EX_LAYERED );
167
168     ShowWindow( m_hParentWindow, SW_SHOW );
169
170     // Initialize the systray icon
171     m_trayIcon.cbSize = sizeof( NOTIFYICONDATA );
172     m_trayIcon.hWnd = m_hParentWindow;
173     m_trayIcon.uID = 42;
174     m_trayIcon.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE;
175     m_trayIcon.uCallbackMessage = MY_WM_TRAYACTION;
176     m_trayIcon.hIcon = LoadIcon( m_hInst, _T("VLC_ICON") );
177     strcpy( m_trayIcon.szTip, "VLC media player" );
178
179     // Show the systray icon if needed
180     if( config_GetInt( getIntf(), "skins2-systray" ) )
181     {
182         addInTray();
183     }
184
185     // Show the task in the task bar if needed
186     if( config_GetInt( getIntf(), "skins2-taskbar" ) )
187     {
188         addInTaskBar();
189     }
190
191     // Initialize the OLE library (for drag & drop)
192     OleInitialize( NULL );
193
194     // We dynamically load msimg32.dll to get a pointer to TransparentBlt()
195     m_hMsimg32 = LoadLibrary( _T("msimg32.dll") );
196     if( !m_hMsimg32 ||
197         !( TransparentBlt =
198             (BOOL (WINAPI*)(HDC, int, int, int, int,
199                             HDC, int, int, int, int, unsigned int))
200             GetProcAddress( m_hMsimg32, _T("TransparentBlt") ) ) )
201     {
202         TransparentBlt = NULL;
203         msg_Dbg( getIntf(), "couldn't find TransparentBlt(), "
204                  "falling back to BitBlt()" );
205     }
206     if( !m_hMsimg32 ||
207         !( AlphaBlend =
208             (BOOL (WINAPI*)( HDC, int, int, int, int, HDC, int, int,
209                               int, int, BLENDFUNCTION ))
210             GetProcAddress( m_hMsimg32, _T("AlphaBlend") ) ) )
211     {
212         AlphaBlend = NULL;
213         msg_Dbg( getIntf(), "couldn't find AlphaBlend()" );
214     }
215
216     // Idem for user32.dll and SetLayeredWindowAttributes()
217     m_hUser32 = LoadLibrary( _T("user32.dll") );
218     if( !m_hUser32 ||
219         !( SetLayeredWindowAttributes =
220             (BOOL (WINAPI *)(HWND, COLORREF, BYTE, DWORD))
221             GetProcAddress( m_hUser32, _T("SetLayeredWindowAttributes") ) ) )
222     {
223         SetLayeredWindowAttributes = NULL;
224         msg_Dbg( getIntf(), "couldn't find SetLayeredWindowAttributes()" );
225     }
226
227     // Initialize the resource path
228     m_resourcePath.push_back( (string)getIntf()->p_libvlc->psz_datadir +
229                                "\\skins" );
230     m_resourcePath.push_back( (dstring)config_GetDataDir() +
231                               "\\skins" );
232     m_resourcePath.push_back( (string)config_GetDataDir() +
233                               "\\skins2" );
234     m_resourcePath.push_back( (string)config_GetDataDir() +
235                               "\\share\\skins" );
236     m_resourcePath.push_back( (string)config_GetDataDir() +
237                               "\\share\\skins2" );
238
239     // All went well
240     return true;
241 }
242
243
244 Win32Factory::~Win32Factory()
245 {
246     // Uninitialize the OLE library
247     OleUninitialize();
248
249     // Remove the systray icon
250     removeFromTray();
251
252     if( m_hParentWindow ) DestroyWindow( m_hParentWindow );
253
254     // Unload msimg32.dll and user32.dll
255     if( m_hMsimg32 )
256         FreeLibrary( m_hMsimg32 );
257     if( m_hUser32 )
258         FreeLibrary( m_hUser32 );
259 }
260
261
262 OSGraphics *Win32Factory::createOSGraphics( int width, int height )
263 {
264     return new Win32Graphics( getIntf(), width, height );
265 }
266
267
268 OSLoop *Win32Factory::getOSLoop()
269 {
270     return Win32Loop::instance( getIntf() );
271 }
272
273
274 void Win32Factory::destroyOSLoop()
275 {
276     Win32Loop::destroy( getIntf() );
277 }
278
279 void Win32Factory::minimize()
280 {
281     /* Make sure no tooltip is visible first */
282     getIntf()->p_sys->p_theme->getWindowManager().hideTooltip();
283
284     ShowWindow( m_hParentWindow, SW_MINIMIZE );
285 }
286
287 void Win32Factory::restore()
288 {
289     ShowWindow( m_hParentWindow, SW_RESTORE );
290 }
291
292 void Win32Factory::addInTray()
293 {
294     Shell_NotifyIcon( NIM_ADD, &m_trayIcon );
295 }
296
297 void Win32Factory::removeFromTray()
298 {
299     Shell_NotifyIcon( NIM_DELETE, &m_trayIcon );
300 }
301
302 void Win32Factory::addInTaskBar()
303 {
304     ShowWindow( m_hParentWindow, SW_HIDE );
305     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
306                       WS_EX_LAYERED|WS_EX_APPWINDOW );
307     ShowWindow( m_hParentWindow, SW_SHOW );
308 }
309
310 void Win32Factory::removeFromTaskBar()
311 {
312     ShowWindow( m_hParentWindow, SW_HIDE );
313     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
314                       WS_EX_LAYERED|WS_EX_TOOLWINDOW );
315     ShowWindow( m_hParentWindow, SW_SHOW );
316 }
317
318 OSTimer *Win32Factory::createOSTimer( CmdGeneric &rCmd )
319 {
320     return new Win32Timer( getIntf(), rCmd, m_hParentWindow );
321 }
322
323
324 OSWindow *Win32Factory::createOSWindow( GenericWindow &rWindow, bool dragDrop,
325                                         bool playOnDrop, OSWindow *pParent )
326 {
327     return new Win32Window( getIntf(), rWindow, m_hInst, m_hParentWindow,
328                             dragDrop, playOnDrop, (Win32Window*)pParent );
329 }
330
331
332 OSTooltip *Win32Factory::createOSTooltip()
333 {
334     return new Win32Tooltip( getIntf(), m_hInst, m_hParentWindow );
335 }
336
337
338 OSPopup *Win32Factory::createOSPopup()
339 {
340     // XXX FIXME: this way of getting the handle really sucks!
341     // In fact, the clean way would be to have in Builder::addPopup() a call
342     // to pPopup->associateToWindow() (to be written)... but the problem is
343     // that there is no way to access the OS-dependent window handle from a
344     // GenericWindow (we cannot even access the OSWindow).
345     if( m_windowMap.begin() == m_windowMap.end() )
346     {
347         msg_Err( getIntf(), "no window has been created before the popup!" );
348         return NULL;
349     }
350
351     return new Win32Popup( getIntf(), m_windowMap.begin()->first );
352 }
353
354
355 int Win32Factory::getScreenWidth() const
356 {
357     return GetSystemMetrics(SM_CXSCREEN);
358
359 }
360
361
362 int Win32Factory::getScreenHeight() const
363 {
364     return GetSystemMetrics(SM_CYSCREEN);
365 }
366
367
368 SkinsRect Win32Factory::getWorkArea() const
369 {
370     RECT r;
371     SystemParametersInfo( SPI_GETWORKAREA, 0, &r, 0 );
372     // Fill a Rect object
373     return  SkinsRect( r.left, r.top, r.right, r.bottom );
374 }
375
376
377 void Win32Factory::getMousePos( int &rXPos, int &rYPos ) const
378 {
379     POINT mousePos;
380     GetCursorPos( &mousePos );
381     rXPos = mousePos.x;
382     rYPos = mousePos.y;
383 }
384
385
386 void Win32Factory::changeCursor( CursorType_t type ) const
387 {
388     LPCTSTR id;
389     switch( type )
390     {
391         case kDefaultArrow:
392             id = IDC_ARROW;
393             break;
394         case kResizeNWSE:
395             id = IDC_SIZENWSE;
396             break;
397         case kResizeNS:
398             id = IDC_SIZENS;
399             break;
400         case kResizeWE:
401             id = IDC_SIZEWE;
402             break;
403         case kResizeNESW:
404             id = IDC_SIZENESW;
405             break;
406         default:
407             id = IDC_ARROW;
408             break;
409     }
410
411     HCURSOR hCurs = LoadCursor( NULL, id );
412     SetCursor( hCurs );
413 }
414
415
416 void Win32Factory::rmDir( const string &rPath )
417 {
418     WIN32_FIND_DATA find;
419     string file;
420     string findFiles = rPath + "\\*";
421     HANDLE handle    = FindFirstFile( findFiles.c_str(), &find );
422
423     while( handle != INVALID_HANDLE_VALUE )
424     {
425         // If file is neither "." nor ".."
426         if( strcmp( find.cFileName, "." ) && strcmp( find.cFileName, ".." ) )
427         {
428             // Set file name
429             file = rPath + "\\" + (string)find.cFileName;
430
431             // If file is a directory, delete it recursively
432             if( find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
433             {
434                 rmDir( file );
435             }
436             // Else, it is a file so simply delete it
437             else
438             {
439                 DeleteFile( file.c_str() );
440             }
441         }
442
443         // If no more file in directory, exit while
444         if( !FindNextFile( handle, &find ) )
445             break;
446     }
447
448     // Now directory is empty so can be removed
449     FindClose( handle );
450     RemoveDirectory( rPath.c_str() );
451 }
452
453 #endif