]> git.sesse.net Git - vlc/blob - activex/plugin.cpp
- activex: added configuration interface
[vlc] / activex / plugin.cpp
1 /*****************************************************************************
2  * plugin.cpp: ActiveX control for VLC
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  *
6  * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #include "plugin.h"
24
25 #include "oleobject.h"
26 #include "olecontrol.h"
27 #include "oleinplaceobject.h"
28 #include "oleinplaceactiveobject.h"
29 #include "persistpropbag.h"
30 #include "persiststreaminit.h"
31 #include "persiststorage.h"
32 #include "provideclassinfo.h"
33 #include "connectioncontainer.h"
34 #include "objectsafety.h"
35 #include "vlccontrol.h"
36 #include "vlccontrol2.h"
37 #include "viewobject.h"
38 #include "dataobject.h"
39
40 #include "utils.h"
41
42 #include <string.h>
43 #include <winreg.h>
44 #include <winuser.h>
45 #include <servprov.h>
46 #include <shlwapi.h>
47 #include <wininet.h>
48
49 using namespace std;
50
51 ////////////////////////////////////////////////////////////////////////
52 //class factory
53
54 static LRESULT CALLBACK VLCInPlaceClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
55     switch( uMsg )
56     {
57         case WM_ERASEBKGND:
58             return 1L;
59
60         case WM_PAINT:
61             PAINTSTRUCT ps;
62             if( GetUpdateRect(hWnd, NULL, FALSE) )
63             {
64                 BeginPaint(hWnd, &ps);
65                 EndPaint(hWnd, &ps);
66             }
67             return 0L;
68
69         default:
70             return DefWindowProc(hWnd, uMsg, wParam, lParam);
71     }
72 };
73
74 static LRESULT CALLBACK VLCVideoClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
75     VLCPlugin *p_instance = reinterpret_cast<VLCPlugin *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
76
77     switch( uMsg )
78     {
79         case WM_ERASEBKGND:
80             return 1L;
81
82         case WM_PAINT:
83             PAINTSTRUCT ps;
84             RECT pr;
85             if( GetUpdateRect(hWnd, &pr, FALSE) )
86             {
87                 RECT bounds;
88                 GetClientRect(hWnd, &bounds);
89                 BeginPaint(hWnd, &ps);
90                 p_instance->onPaint(ps.hdc, bounds, pr);
91                 EndPaint(hWnd, &ps);
92             }
93             return 0L;
94
95         default:
96             return DefWindowProc(hWnd, uMsg, wParam, lParam);
97     }
98 };
99
100 VLCPluginClass::VLCPluginClass(LONG *p_class_ref, HINSTANCE hInstance, REFCLSID rclsid) :
101     _p_class_ref(p_class_ref),
102     _hinstance(hInstance),
103     _classid(rclsid),
104     _inplace_picture(NULL)
105 {
106     WNDCLASS wClass;
107
108     if( ! GetClassInfo(hInstance, getInPlaceWndClassName(), &wClass) )
109     {
110         wClass.style          = CS_NOCLOSE|CS_DBLCLKS;
111         wClass.lpfnWndProc    = VLCInPlaceClassWndProc;
112         wClass.cbClsExtra     = 0;
113         wClass.cbWndExtra     = 0;
114         wClass.hInstance      = hInstance;
115         wClass.hIcon          = NULL;
116         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
117         wClass.hbrBackground  = NULL;
118         wClass.lpszMenuName   = NULL;
119         wClass.lpszClassName  = getInPlaceWndClassName();
120        
121         _inplace_wndclass_atom = RegisterClass(&wClass);
122     }
123     else
124     {
125         _inplace_wndclass_atom = 0;
126     }
127
128     if( ! GetClassInfo(hInstance, getVideoWndClassName(), &wClass) )
129     {
130         wClass.style          = CS_NOCLOSE;
131         wClass.lpfnWndProc    = VLCVideoClassWndProc;
132         wClass.cbClsExtra     = 0;
133         wClass.cbWndExtra     = 0;
134         wClass.hInstance      = hInstance;
135         wClass.hIcon          = NULL;
136         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
137         wClass.hbrBackground  = NULL;
138         wClass.lpszMenuName   = NULL;
139         wClass.lpszClassName  = getVideoWndClassName();
140        
141         _video_wndclass_atom = RegisterClass(&wClass);
142     }
143     else
144     {
145         _video_wndclass_atom = 0;
146     }
147
148     HBITMAP hbitmap = (HBITMAP)LoadImage(getHInstance(), TEXT("INPLACE-PICT"), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
149     if( NULL != hbitmap )
150     {
151         PICTDESC pictDesc;
152
153         pictDesc.cbSizeofstruct = sizeof(PICTDESC);
154         pictDesc.picType        = PICTYPE_BITMAP;
155         pictDesc.bmp.hbitmap    = hbitmap;
156         pictDesc.bmp.hpal       = NULL;
157
158         if( FAILED(OleCreatePictureIndirect(&pictDesc, IID_IPicture, TRUE, reinterpret_cast<LPVOID*>(&_inplace_picture))) )
159             _inplace_picture = NULL;
160     }
161     AddRef();
162 };
163
164 VLCPluginClass::~VLCPluginClass()
165 {
166     if( 0 != _inplace_wndclass_atom )
167         UnregisterClass(MAKEINTATOM(_inplace_wndclass_atom), _hinstance);
168
169     if( 0 != _video_wndclass_atom )
170         UnregisterClass(MAKEINTATOM(_video_wndclass_atom), _hinstance);
171
172     if( NULL != _inplace_picture )
173         _inplace_picture->Release();
174 };
175
176 STDMETHODIMP VLCPluginClass::QueryInterface(REFIID riid, void **ppv)
177 {
178     if( NULL == ppv )
179         return E_INVALIDARG;
180
181     if( (IID_IUnknown == riid)
182      || (IID_IClassFactory == riid) )
183     {
184         AddRef();
185         *ppv = reinterpret_cast<LPVOID>(this);
186
187         return NOERROR;
188     }
189
190     *ppv = NULL;
191
192     return E_NOINTERFACE;
193 };
194
195 STDMETHODIMP_(ULONG) VLCPluginClass::AddRef(void)
196 {
197     return InterlockedIncrement(_p_class_ref);
198 };
199
200 STDMETHODIMP_(ULONG) VLCPluginClass::Release(void)
201 {
202     ULONG refcount = InterlockedDecrement(_p_class_ref);
203     if( 0 == refcount )
204     {
205         delete this;
206         return 0;
207     }
208     return refcount;
209 };
210
211 STDMETHODIMP VLCPluginClass::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppv)
212 {
213     if( NULL == ppv )
214         return E_POINTER;
215
216     *ppv = NULL;
217
218     if( (NULL != pUnkOuter) && (IID_IUnknown != riid) ) {
219         return CLASS_E_NOAGGREGATION;
220     }
221
222     VLCPlugin *plugin = new VLCPlugin(this, pUnkOuter);
223     if( NULL != plugin )
224     {
225         HRESULT hr = plugin->QueryInterface(riid, ppv);
226         // the following will destroy the object if QueryInterface() failed
227         plugin->Release();
228         return hr;
229     }
230     return E_OUTOFMEMORY;
231 };
232
233 STDMETHODIMP VLCPluginClass::LockServer(BOOL fLock)
234 {
235     if( fLock )
236         AddRef();
237     else 
238         Release();
239
240     return S_OK;
241 };
242
243 ////////////////////////////////////////////////////////////////////////
244
245 VLCPlugin::VLCPlugin(VLCPluginClass *p_class, LPUNKNOWN pUnkOuter) :
246     _inplacewnd(NULL),
247     _videownd(NULL),
248     _p_class(p_class),
249     _i_ref(1UL),
250     _p_libvlc(NULL),
251     _i_codepage(CP_ACP),
252     _b_usermode(TRUE)
253 {
254     p_class->AddRef();
255
256     vlcOleControl = new VLCOleControl(this);
257     vlcOleInPlaceObject = new VLCOleInPlaceObject(this);
258     vlcOleInPlaceActiveObject = new VLCOleInPlaceActiveObject(this);
259     vlcPersistStorage = new VLCPersistStorage(this);
260     vlcPersistStreamInit = new VLCPersistStreamInit(this);
261     vlcPersistPropertyBag = new VLCPersistPropertyBag(this);
262     vlcProvideClassInfo = new VLCProvideClassInfo(this);
263     vlcConnectionPointContainer = new VLCConnectionPointContainer(this);
264     vlcObjectSafety = new VLCObjectSafety(this);
265     vlcControl = new VLCControl(this);
266     vlcControl2 = new VLCControl2(this);
267     vlcViewObject = new VLCViewObject(this);
268     vlcDataObject = new VLCDataObject(this);
269     vlcOleObject = new VLCOleObject(this);
270
271     // configure controlling IUnknown interface for implemented interfaces
272     this->pUnkOuter = (NULL != pUnkOuter) ? pUnkOuter : dynamic_cast<LPUNKNOWN>(this);
273
274     // default picure
275     _p_pict = p_class->getInPlacePict();
276
277     // make sure that persistable properties are initialized
278     onInit();
279 };
280
281 VLCPlugin::~VLCPlugin()
282 {
283     delete vlcOleObject;
284     delete vlcDataObject;
285     delete vlcViewObject;
286     delete vlcControl2;
287     delete vlcControl;
288     delete vlcConnectionPointContainer;
289     delete vlcProvideClassInfo;
290     delete vlcPersistPropertyBag;
291     delete vlcPersistStreamInit;
292     delete vlcPersistStorage;
293     delete vlcOleInPlaceActiveObject;
294     delete vlcOleInPlaceObject;
295     delete vlcObjectSafety;
296
297     delete vlcOleControl;
298     if( _p_pict )
299         _p_pict->Release();
300
301     SysFreeString(_bstr_mrl);
302
303     _p_class->Release();
304 };
305
306 STDMETHODIMP VLCPlugin::QueryInterface(REFIID riid, void **ppv)
307 {
308     if( NULL == ppv )
309         return E_INVALIDARG;
310
311     if( IID_IUnknown == riid )
312         *ppv = reinterpret_cast<LPVOID>(this);
313     else if( IID_IOleObject == riid )
314         *ppv = reinterpret_cast<LPVOID>(vlcOleObject);
315     else if( IID_IOleControl == riid )
316         *ppv = reinterpret_cast<LPVOID>(vlcOleControl);
317     else if( IID_IOleWindow == riid )
318         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
319     else if( IID_IOleInPlaceObject == riid )
320         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
321     else if( IID_IOleInPlaceActiveObject == riid )
322         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceActiveObject);
323     else if( IID_IPersist == riid )
324         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
325     else if( IID_IPersistStreamInit == riid )
326         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
327     else if( IID_IPersistStorage == riid )
328         *ppv = reinterpret_cast<LPVOID>(vlcPersistStorage);
329     else if( IID_IPersistPropertyBag == riid )
330         *ppv = reinterpret_cast<LPVOID>(vlcPersistPropertyBag);
331     else if( IID_IProvideClassInfo == riid )
332         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
333     else if( IID_IProvideClassInfo2 == riid )
334         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
335     else if( IID_IConnectionPointContainer == riid )
336         *ppv = reinterpret_cast<LPVOID>(vlcConnectionPointContainer);
337     else if( IID_IObjectSafety == riid )
338         *ppv = reinterpret_cast<LPVOID>(vlcObjectSafety);
339     else if( IID_IDispatch == riid )
340         *ppv = (CLSID_VLCPlugin2 == getClassID()) ?
341                 reinterpret_cast<LPVOID>(vlcControl2) :
342                 reinterpret_cast<LPVOID>(vlcControl);
343     else if( IID_IVLCControl == riid )
344         *ppv = reinterpret_cast<LPVOID>(vlcControl);
345     else if( IID_IVLCControl2 == riid )
346         *ppv = reinterpret_cast<LPVOID>(vlcControl2);
347     else if( IID_IViewObject == riid )
348         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
349     else if( IID_IViewObject2 == riid )
350         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
351     else if( IID_IDataObject == riid )
352         *ppv = reinterpret_cast<LPVOID>(vlcDataObject);
353     else
354     {
355         *ppv = NULL;
356         return E_NOINTERFACE;
357     }
358     ((LPUNKNOWN)*ppv)->AddRef();
359     return NOERROR;
360 };
361
362 STDMETHODIMP_(ULONG) VLCPlugin::AddRef(void)
363 {
364     return InterlockedIncrement((LONG *)&_i_ref);
365 };
366
367 STDMETHODIMP_(ULONG) VLCPlugin::Release(void)
368 {
369     if( ! InterlockedDecrement((LONG *)&_i_ref) )
370     {
371         delete this;
372         return 0;
373     }
374     return _i_ref;
375 };
376
377 //////////////////////////////////////
378
379 /*
380 ** we use a window to represent plugin viewport,
381 ** whose geometry is limited by the clipping rectangle
382 ** all drawing within this window must follow must
383 ** follow coordinates system described in lprPosRect
384 */
385
386 static void getViewportCoords(LPRECT lprPosRect, LPRECT lprClipRect)
387 {
388     RECT bounds;
389     bounds.right  = lprPosRect->right-lprPosRect->left;
390
391     if( lprClipRect->left <= lprPosRect->left )
392     {
393         // left side is not clipped out
394         bounds.left = 0;
395
396         if( lprClipRect->right >= lprPosRect->right )
397         {
398             // right side is not clipped out, no change
399         }
400         else if( lprClipRect->right >= lprPosRect->left )
401         {
402             // right side is clipped out
403             lprPosRect->right = lprClipRect->right;
404         }
405         else
406         {
407             // outside of clipping rectange, not visible
408             lprPosRect->right = lprPosRect->left;
409         }
410     }
411     else
412     {
413         // left side is clipped out
414         bounds.left = lprPosRect->left-lprClipRect->left;
415         bounds.right += bounds.left;
416
417         lprPosRect->left = lprClipRect->left;
418         if( lprClipRect->right >= lprPosRect->right )
419         {
420             // right side is not clipped out
421         }
422         else
423         {
424             // right side is clipped out
425             lprPosRect->right = lprClipRect->right;
426         }
427     }
428
429     bounds.bottom = lprPosRect->bottom-lprPosRect->top;
430
431     if( lprClipRect->top <= lprPosRect->top )
432     {
433         // top side is not clipped out
434         bounds.top = 0;
435
436         if( lprClipRect->bottom >= lprPosRect->bottom )
437         {
438             // bottom side is not clipped out, no change
439         }
440         else if( lprClipRect->bottom >= lprPosRect->top )
441         {
442             // bottom side is clipped out
443             lprPosRect->bottom = lprClipRect->bottom;
444         }
445         else
446         {
447             // outside of clipping rectange, not visible
448             lprPosRect->right = lprPosRect->left;
449         }
450     }
451     else
452     {
453         bounds.top = lprPosRect->top-lprClipRect->top;
454         bounds.bottom += bounds.top;
455
456         lprPosRect->top = lprClipRect->top;
457         if( lprClipRect->bottom >= lprPosRect->bottom )
458         {
459             // bottom side is not clipped out
460         }
461         else
462         {
463             // bottom side is clipped out
464             lprPosRect->bottom = lprClipRect->bottom;
465         }
466     }
467     *lprClipRect = *lprPosRect;
468     *lprPosRect  = bounds;
469 };
470
471 HRESULT VLCPlugin::onInit(void)
472 {
473     if( NULL == _p_libvlc )
474     {
475         // initialize persistable properties
476         _b_autoplay = TRUE;
477         _b_autoloop = FALSE;
478         _bstr_baseurl = NULL;
479         _bstr_mrl = NULL;
480         _b_visible = TRUE;
481         _b_mute = FALSE;
482         _i_volume = 50;
483         _i_time   = 0;
484         // set default/preferred size (320x240) pixels in HIMETRIC
485         HDC hDC = CreateDevDC(NULL);
486         _extent.cx = 320;
487         _extent.cy = 240;
488         HimetricFromDP(hDC, (LPPOINT)&_extent, 1);
489         DeleteDC(hDC);
490
491         return S_OK;
492     }
493     return CO_E_ALREADYINITIALIZED;
494 };
495
496 HRESULT VLCPlugin::onLoad(void)
497 {
498     if( SysStringLen(_bstr_baseurl) == 0 )
499     {
500         /*
501         ** try to retreive the base URL using the client site moniker, which for Internet Explorer
502         ** is the URL of the page the plugin is embedded into. 
503         */
504         LPOLECLIENTSITE pClientSite;
505         if( SUCCEEDED(vlcOleObject->GetClientSite(&pClientSite)) && (NULL != pClientSite) )
506         {
507             IBindCtx *pBC = 0;
508             if( SUCCEEDED(CreateBindCtx(0, &pBC)) )
509             {
510                 LPMONIKER pContMoniker = NULL;
511                 if( SUCCEEDED(pClientSite->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
512                                 OLEWHICHMK_CONTAINER, &pContMoniker)) )
513                 {
514                     LPOLESTR base_url;
515                     if( SUCCEEDED(pContMoniker->GetDisplayName(pBC, NULL, &base_url)) )
516                     {
517                         /*
518                         ** check that the moniker name is a URL
519                         */
520                         if( UrlIsW(base_url, URLIS_URL) )
521                         {
522                             /* copy base URL */
523                             _bstr_mrl = SysAllocString(base_url);
524                         }
525                         CoTaskMemFree(base_url);
526                     }
527                 }
528             }
529         }
530     }
531     setDirty(FALSE);
532     return S_OK;
533 };
534
535 HRESULT VLCPlugin::getVLCObject(int* i_vlc)
536 {
537     libvlc_instance_t *p_libvlc;
538     HRESULT result = getVLC(&p_libvlc);
539     if( SUCCEEDED(result) )
540     {
541         *i_vlc = libvlc_get_vlc_id(p_libvlc);
542     }
543     else
544     {
545         *i_vlc = 0;
546     }
547     return result;
548 }
549
550 HRESULT VLCPlugin::getVLC(libvlc_instance_t** pp_libvlc)
551 {
552     if( ! isRunning() )
553     {
554         /*
555         ** default initialization options
556         */
557         char *ppsz_argv[32] = { "vlc" };
558         int   ppsz_argc = 1;
559
560         HKEY h_key;
561         DWORD i_type, i_data = MAX_PATH + 1;
562         char p_data[MAX_PATH + 1];
563         if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
564                           0, KEY_READ, &h_key ) == ERROR_SUCCESS )
565         {
566              if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
567                                   (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
568              {
569                  if( i_type == REG_SZ )
570                  {
571                      strcat( p_data, "\\vlc" );
572                      ppsz_argv[0] = p_data;
573                  }
574              }
575              RegCloseKey( h_key );
576         }
577
578 #if 0
579         ppsz_argv[0] = "C:\\cygwin\\home\\damienf\\vlc-trunk\\vlc";
580 #endif
581
582         // make sure plugin isn't affected with VLC single instance mode
583         ppsz_argv[ppsz_argc++] = "--no-one-instance";
584
585         /* common settings */
586         ppsz_argv[ppsz_argc++] = "-vv";
587         ppsz_argv[ppsz_argc++] = "--no-stats";
588         ppsz_argv[ppsz_argc++] = "--no-media-library";
589         ppsz_argv[ppsz_argc++] = "--intf";
590         ppsz_argv[ppsz_argc++] = "dummy";
591
592         // loop mode is a configuration option only
593         if( _b_autoloop )
594             ppsz_argv[ppsz_argc++] = "--loop";
595
596         // initial volume setting
597         char volBuffer[16];
598         ppsz_argv[ppsz_argc++] = "--volume";
599         if( _b_mute )
600         {
601            ppsz_argv[ppsz_argc++] = "0";
602         }
603         else
604         {
605             snprintf(volBuffer, sizeof(volBuffer), "%d", _i_volume);
606             ppsz_argv[ppsz_argc++] = volBuffer;
607         }
608             
609         if( IsDebuggerPresent() )
610         {
611             /*
612             ** VLC default threading mechanism is designed to be as compatible
613             ** with POSIX as possible. However when debugged on win32, threads
614             ** lose signals and eventually VLC get stuck during initialization.
615             ** threading support can be configured to be more debugging friendly
616             ** but it will be less compatible with POSIX.
617             ** This is done by initializing with the following options:
618             */
619             ppsz_argv[ppsz_argc++] = "--fast-mutex";
620             ppsz_argv[ppsz_argc++] = "--win9x-cv-method=1";
621         }
622
623         _p_libvlc = libvlc_new(ppsz_argc, ppsz_argv, NULL);
624         if( NULL == _p_libvlc )
625         {
626             *pp_libvlc = NULL;
627             return E_FAIL;
628         }
629
630         if( SysStringLen(_bstr_mrl) > 0 )
631         {
632             char *psz_mrl = NULL;
633             DWORD len = INTERNET_MAX_URL_LENGTH;
634             LPOLESTR abs_url = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*len);
635             if( NULL != abs_url )
636             {
637                 /*
638                 ** if the MRL a relative URL, we should end up with an absolute URL
639                 */
640                 if( SUCCEEDED(UrlCombineW(_bstr_baseurl, _bstr_mrl, abs_url, &len,
641                                 URL_ESCAPE_UNSAFE|URL_PLUGGABLE_PROTOCOL)) )
642                 {
643                     psz_mrl = CStrFromBSTR(CP_UTF8, abs_url);
644                 }
645                 else
646                 {
647                     psz_mrl = CStrFromBSTR(CP_UTF8, _bstr_mrl);
648                 }
649                 CoTaskMemFree(abs_url);
650             }
651             if( NULL != psz_mrl )
652             {
653                 const char *options[1];
654                 int i_options = 0;
655
656                 char timeBuffer[32];
657                 if( _i_time )
658                 {
659                     snprintf(timeBuffer, sizeof(timeBuffer), ":start-time=%d", _i_time);
660                     options[i_options++] = timeBuffer;
661                 }
662                 // add default target to playlist
663                 libvlc_playlist_add_extended(_p_libvlc, psz_mrl, NULL, i_options, options, NULL);
664                 CoTaskMemFree(psz_mrl);
665             }
666         }
667     }
668     *pp_libvlc = _p_libvlc;
669     return S_OK;
670 };
671
672 HRESULT VLCPlugin::onAmbientChanged(LPUNKNOWN pContainer, DISPID dispID)
673 {
674     VARIANT v;
675     switch( dispID )
676     {
677         case DISPID_AMBIENT_BACKCOLOR:
678             break;
679         case DISPID_AMBIENT_DISPLAYNAME:
680             break;
681         case DISPID_AMBIENT_FONT:
682             break;
683         case DISPID_AMBIENT_FORECOLOR:
684             break;
685         case DISPID_AMBIENT_LOCALEID:
686             break;
687         case DISPID_AMBIENT_MESSAGEREFLECT:
688             break;
689         case DISPID_AMBIENT_SCALEUNITS:
690             break;
691         case DISPID_AMBIENT_TEXTALIGN:
692             break;
693         case DISPID_AMBIENT_USERMODE:
694             VariantInit(&v);
695             V_VT(&v) = VT_BOOL;
696             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
697             {
698                 setUserMode(V_BOOL(&v) != VARIANT_FALSE);
699             }
700             break;
701         case DISPID_AMBIENT_UIDEAD:
702             break;
703         case DISPID_AMBIENT_SHOWGRABHANDLES:
704             break;
705         case DISPID_AMBIENT_SHOWHATCHING:
706             break;
707         case DISPID_AMBIENT_DISPLAYASDEFAULT:
708             break;
709         case DISPID_AMBIENT_SUPPORTSMNEMONICS:
710             break;
711         case DISPID_AMBIENT_AUTOCLIP:
712             break;
713         case DISPID_AMBIENT_APPEARANCE:
714             break;
715         case DISPID_AMBIENT_CODEPAGE:
716             VariantInit(&v);
717             V_VT(&v) = VT_I4;
718             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
719             {
720                 setCodePage(V_I4(&v));
721             }
722             break;
723         case DISPID_AMBIENT_PALETTE:
724             break;
725         case DISPID_AMBIENT_CHARSET:
726             break;
727         case DISPID_AMBIENT_RIGHTTOLEFT:
728             break;
729         case DISPID_AMBIENT_TOPTOBOTTOM:
730             break;
731         case DISPID_UNKNOWN:
732             /*
733             ** multiple property change, look up the ones we are interested in
734             */
735             VariantInit(&v);
736             V_VT(&v) = VT_BOOL;
737             if( SUCCEEDED(GetObjectProperty(pContainer, DISPID_AMBIENT_USERMODE, v)) )
738             {
739                 setUserMode(V_BOOL(&v) != VARIANT_FALSE);
740             }
741             VariantInit(&v);
742             V_VT(&v) = VT_I4;
743             if( SUCCEEDED(GetObjectProperty(pContainer, DISPID_AMBIENT_CODEPAGE, v)) )
744             {
745                 setCodePage(V_I4(&v));
746             }
747             break;
748     }
749     return S_OK;
750 };
751
752 HRESULT VLCPlugin::onClose(DWORD dwSaveOption)
753 {
754     if( isInPlaceActive() )
755     {
756         onInPlaceDeactivate();
757     }
758     if( isRunning() )
759     {
760         libvlc_instance_t* p_libvlc = _p_libvlc;
761
762         _p_libvlc = NULL;
763         vlcDataObject->onClose();
764
765         libvlc_destroy(p_libvlc, NULL );
766     }
767     return S_OK;
768 };
769
770 BOOL VLCPlugin::isInPlaceActive(void)
771 {
772     return (NULL != _inplacewnd);
773 };
774
775 HRESULT VLCPlugin::onActivateInPlace(LPMSG lpMesg, HWND hwndParent, LPCRECT lprcPosRect, LPCRECT lprcClipRect)
776 {
777     RECT posRect = *lprcPosRect;
778     RECT clipRect = *lprcClipRect;
779
780     /*
781     ** record keeping of control geometry within container
782     */ 
783     _posRect = posRect;
784
785     /*
786     ** convert posRect & clipRect to match control viewport coordinates
787     */
788     getViewportCoords(&posRect, &clipRect);
789
790     /*
791     ** Create a window for in place activated control.
792     ** the window geometry matches the control viewport
793     ** within container so that embedded video is always
794     ** properly clipped.
795     */
796     _inplacewnd = CreateWindow(_p_class->getInPlaceWndClassName(),
797             "VLC Plugin In-Place Window",
798             WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
799             clipRect.left,
800             clipRect.top,
801             clipRect.right-clipRect.left,
802             clipRect.bottom-clipRect.top,
803             hwndParent,
804             0,
805             _p_class->getHInstance(),
806             NULL
807            );
808
809     if( NULL == _inplacewnd )
810         return E_FAIL;
811
812     SetWindowLongPtr(_inplacewnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
813
814     /*
815     ** VLC embedded video automatically grows to cover client
816     ** area of parent window.
817     ** hence create a such a 'parent' window whose geometry
818     ** is always correct relative to the viewport bounds
819     */
820     _videownd = CreateWindow(_p_class->getVideoWndClassName(),
821             "VLC Plugin Video Window",
822             WS_CHILD|WS_CLIPCHILDREN|WS_VISIBLE,
823             posRect.left,
824             posRect.top,
825             posRect.right-posRect.left,
826             posRect.bottom-posRect.top,
827             _inplacewnd,
828             0,
829             _p_class->getHInstance(),
830             NULL
831            );
832
833     if( NULL == _videownd )
834     {
835         DestroyWindow(_inplacewnd);
836         return E_FAIL;
837     }
838
839     SetWindowLongPtr(_videownd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
840
841     if( _b_usermode )
842     {
843         /* will run vlc if not done already */
844         libvlc_instance_t* p_libvlc;
845         HRESULT result = getVLC(&p_libvlc);
846         if( FAILED(result) )
847             return result;
848
849         /* set internal video width and height */
850         libvlc_video_set_size(p_libvlc,
851             posRect.right-posRect.left,
852             posRect.bottom-posRect.top,
853             NULL );
854
855         /* set internal video parent window */
856         libvlc_video_set_parent(p_libvlc,
857             reinterpret_cast<libvlc_drawable_t>(_videownd), NULL);
858
859         if( _b_autoplay & (libvlc_playlist_items_count(p_libvlc, NULL) > 0) )
860         {
861             libvlc_playlist_play(p_libvlc, 0, 0, NULL, NULL);
862             fireOnPlayEvent();
863         }
864     }
865
866     if( isVisible() )
867         ShowWindow(_inplacewnd, SW_SHOW);
868
869     return S_OK;
870 };
871
872 HRESULT VLCPlugin::onInPlaceDeactivate(void)
873 {
874     if( isRunning() )
875     {
876         libvlc_playlist_stop(_p_libvlc, NULL);
877         fireOnStopEvent();
878     }
879
880     DestroyWindow(_videownd);
881     _videownd = NULL;
882     DestroyWindow(_inplacewnd);
883     _inplacewnd = NULL;
884  
885     return S_OK;
886 };
887
888 void VLCPlugin::setVisible(BOOL fVisible)
889 {
890     if( fVisible != _b_visible )
891     {
892         _b_visible = fVisible;
893         if( isInPlaceActive() )
894         {
895             ShowWindow(_inplacewnd, fVisible ? SW_SHOW : SW_HIDE);
896             if( fVisible )
897                 InvalidateRect(_videownd, NULL, TRUE);
898         }
899         setDirty(TRUE);
900         firePropChangedEvent(DISPID_Visible);
901     }
902 };
903
904 void VLCPlugin::setVolume(int volume)
905 {
906     if( volume < 0 )
907         volume = 0;
908     else if( volume > 200 )
909         volume = 200;
910
911     if( volume != _i_volume )
912     {
913         _i_volume = volume;
914         if( isRunning() )
915         {
916             libvlc_audio_set_volume(_p_libvlc, _i_volume, NULL);
917         }
918         setDirty(TRUE);
919     }
920 };
921
922 void VLCPlugin::setTime(int seconds)
923 {
924     if( seconds < 0 )
925         seconds = 0;
926
927     if( seconds != _i_time )
928     {
929         setStartTime(_i_time);
930         if( isRunning() )
931         {
932             libvlc_input_t *p_input = libvlc_playlist_get_input(_p_libvlc, NULL);
933             if( NULL != p_input )
934             {
935                 libvlc_input_set_time(p_input, _i_time, NULL);
936                 libvlc_input_free(p_input);
937             }
938         }
939     }
940 };
941
942 void VLCPlugin::setFocus(BOOL fFocus)
943 {
944     if( fFocus )
945         SetActiveWindow(_inplacewnd);
946 };
947
948 BOOL VLCPlugin::hasFocus(void)
949 {
950     return GetActiveWindow() == _inplacewnd;
951 };
952
953 void VLCPlugin::onDraw(DVTARGETDEVICE * ptd, HDC hicTargetDev,
954         HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds)
955 {
956     if( isVisible() )
957     {
958         long width = lprcBounds->right-lprcBounds->left;
959         long height = lprcBounds->bottom-lprcBounds->top;
960
961         RECT bounds = { lprcBounds->left, lprcBounds->top, lprcBounds->right, lprcBounds->bottom };
962         FillRect(hdcDraw, &bounds, (HBRUSH)GetStockObject(WHITE_BRUSH));
963
964         LPPICTURE pict = getPicture();
965         if( NULL != pict )
966         {
967             OLE_XSIZE_HIMETRIC picWidth;
968             OLE_YSIZE_HIMETRIC picHeight;
969
970             pict->get_Width(&picWidth);
971             pict->get_Height(&picHeight);
972
973             SIZEL picSize = { picWidth, picHeight };
974
975             if( NULL != hicTargetDev )
976             {
977                 DPFromHimetric(hicTargetDev, (LPPOINT)&picSize, 1);
978             }
979             else if( NULL != (hicTargetDev = CreateDevDC(ptd)) )
980             {
981                 DPFromHimetric(hicTargetDev, (LPPOINT)&picSize, 1);
982                 DeleteDC(hicTargetDev);
983             }
984
985             if( picSize.cx > width-4 )
986                 picSize.cx = width-4;
987             if( picSize.cy > height-4 )
988                 picSize.cy = height-4;
989
990             LONG dstX = lprcBounds->left+(width-picSize.cx)/2;
991             LONG dstY = lprcBounds->top+(height-picSize.cy)/2;
992
993             if( NULL != lprcWBounds )
994             {
995                 RECT wBounds = { lprcWBounds->left, lprcWBounds->top, lprcWBounds->right, lprcWBounds->bottom };
996                 pict->Render(hdcDraw, dstX, dstY, picSize.cx, picSize.cy,
997                         0L, picHeight, picWidth, -picHeight, &wBounds);
998             }
999             else 
1000                 pict->Render(hdcDraw, dstX, dstY, picSize.cx, picSize.cy,
1001                         0L, picHeight, picWidth, -picHeight, NULL);
1002
1003             pict->Release();
1004         }
1005
1006         SelectObject(hdcDraw, GetStockObject(BLACK_BRUSH));
1007
1008         MoveToEx(hdcDraw, bounds.left, bounds.top, NULL);
1009         LineTo(hdcDraw, bounds.left+width-1, bounds.top);
1010         LineTo(hdcDraw, bounds.left+width-1, bounds.top+height-1);
1011         LineTo(hdcDraw, bounds.left, bounds.top+height-1);
1012         LineTo(hdcDraw, bounds.left, bounds.top);
1013     }
1014 };
1015
1016 void VLCPlugin::onPaint(HDC hdc, const RECT &bounds, const RECT &clipRect)
1017 {
1018     if( isVisible() )
1019     {
1020         /** if VLC is playing, it may not display any VIDEO content 
1021         ** hence, draw control logo*/
1022         HDC hdcDraw = CreateCompatibleDC(hdc);
1023         if( NULL != hdcDraw )
1024         {
1025             SIZEL size = getExtent();
1026             DPFromHimetric(hdc, (LPPOINT)&size, 1);
1027             RECTL posRect = { 0, 0, size.cx, size.cy };
1028
1029             int width = bounds.right-bounds.left;
1030             int height = bounds.bottom-bounds.top;
1031
1032             HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
1033             if( NULL != hBitmap )
1034             {
1035                 HBITMAP oldBmp = (HBITMAP)SelectObject(hdcDraw, hBitmap);
1036
1037                 if( (size.cx != width) || (size.cy != height) )
1038                 {
1039                     // needs to scale canvas
1040                     SetMapMode(hdcDraw, MM_ANISOTROPIC);
1041                     SetWindowExtEx(hdcDraw, size.cx, size.cy, NULL);
1042                     SetViewportExtEx(hdcDraw, width, height, NULL);
1043                 }
1044
1045                 onDraw(NULL, hdc, hdcDraw, &posRect, NULL);
1046
1047                 SetMapMode(hdcDraw, MM_TEXT);
1048                 BitBlt(hdc, bounds.left, bounds.top,
1049                         width, height,
1050                         hdcDraw, 0, 0,
1051                         SRCCOPY);
1052
1053                 SelectObject(hdcDraw, oldBmp);
1054                 DeleteObject(hBitmap);
1055             }
1056             DeleteDC(hdcDraw);
1057         }
1058     }
1059 };
1060
1061 void VLCPlugin::onPositionChange(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
1062 {
1063     RECT clipRect = *lprcClipRect;
1064     RECT posRect  = *lprcPosRect;
1065
1066     //RedrawWindow(GetParent(_inplacewnd), &_posRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
1067
1068     /*
1069     ** record keeping of control geometry within container
1070     */
1071     _posRect = posRect;
1072
1073     /*
1074     ** convert posRect & clipRect to match control viewport coordinates
1075     */
1076     getViewportCoords(&posRect, &clipRect);
1077
1078     /*
1079     ** change in-place window geometry to match clipping region
1080     */
1081     SetWindowPos(_inplacewnd, NULL,
1082             clipRect.left,
1083             clipRect.top,
1084             clipRect.right-clipRect.left,
1085             clipRect.bottom-clipRect.top,
1086             SWP_NOACTIVATE|
1087             SWP_NOCOPYBITS|
1088             SWP_NOZORDER|
1089             SWP_NOOWNERZORDER );
1090
1091     /*
1092     ** change video window geometry to match object bounds within clipping region
1093     */
1094     SetWindowPos(_videownd, NULL,
1095             posRect.left,
1096             posRect.top,
1097             posRect.right-posRect.left,
1098             posRect.bottom-posRect.top,
1099             SWP_NOACTIVATE|
1100             SWP_NOCOPYBITS|
1101             SWP_NOZORDER|
1102             SWP_NOOWNERZORDER );
1103
1104     //RedrawWindow(_videownd, &posRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
1105     if( isRunning() )
1106     {
1107         libvlc_video_set_size(_p_libvlc,
1108             posRect.right-posRect.left,
1109             posRect.bottom-posRect.top,
1110             NULL );
1111     }
1112 };
1113
1114 void VLCPlugin::freezeEvents(BOOL freeze)
1115 {
1116     vlcConnectionPointContainer->freezeEvents(freeze);
1117 };
1118
1119 void VLCPlugin::firePropChangedEvent(DISPID dispid)
1120 {
1121     vlcConnectionPointContainer->firePropChangedEvent(dispid); 
1122 };
1123
1124 void VLCPlugin::fireOnPlayEvent(void)
1125 {
1126     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1127     vlcConnectionPointContainer->fireEvent(DISPID_PlayEvent, &dispparamsNoArgs); 
1128 };
1129
1130 void VLCPlugin::fireOnPauseEvent(void)
1131 {
1132     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1133     vlcConnectionPointContainer->fireEvent(DISPID_PauseEvent, &dispparamsNoArgs); 
1134 };
1135
1136 void VLCPlugin::fireOnStopEvent(void)
1137 {
1138     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1139     vlcConnectionPointContainer->fireEvent(DISPID_StopEvent, &dispparamsNoArgs); 
1140 };
1141