]> git.sesse.net Git - vlc/blob - activex/plugin.cpp
all: reworked ActiveX plugin, which now works properly with .NET (tested with Visual...
[vlc] / activex / plugin.cpp
1 /*****************************************************************************
2  * plugin.cpp: ActiveX control for VLC
3  *****************************************************************************
4  * Copyright (C) 2005 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., 59 Temple Place - Suite 330, Boston, MA  02111, 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 "viewobject.h"
37 #include "dataobject.h"
38
39 #include "utils.h"
40
41 #include <string.h>
42 #include <winreg.h>
43 #include <winuser.h>
44 #include <servprov.h>
45 #include <shlwapi.h>
46 #include <wininet.h>
47
48 using namespace std;
49
50 ////////////////////////////////////////////////////////////////////////
51 //class factory
52
53 // {E23FE9C6-778E-49d4-B537-38FCDE4887D8}
54 //const GUID CLSID_VLCPlugin = 
55 //    { 0xe23fe9c6, 0x778e, 0x49d4, { 0xb5, 0x37, 0x38, 0xfc, 0xde, 0x48, 0x87, 0xd8 } };
56
57 static LRESULT CALLBACK VLCInPlaceClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
58     switch( uMsg )
59     {
60         case WM_ERASEBKGND:
61             return 1L;
62
63         case WM_PAINT:
64             PAINTSTRUCT ps;
65             if( GetUpdateRect(hWnd, NULL, FALSE) )
66             {
67                 BeginPaint(hWnd, &ps);
68                 EndPaint(hWnd, &ps);
69             }
70             return 0L;
71
72         default:
73             return DefWindowProc(hWnd, uMsg, wParam, lParam);
74     }
75 };
76
77 static LRESULT CALLBACK VLCVideoClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
78     VLCPlugin *p_instance = reinterpret_cast<VLCPlugin *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
79
80     switch( uMsg )
81     {
82         case WM_ERASEBKGND:
83             return 1L;
84
85         case WM_PAINT:
86             PAINTSTRUCT ps;
87             RECT pr;
88             if( GetUpdateRect(hWnd, &pr, FALSE) )
89             {
90                 RECT bounds;
91                 GetClientRect(hWnd, &bounds);
92                 BeginPaint(hWnd, &ps);
93                 p_instance->onPaint(ps.hdc, bounds, pr);
94                 EndPaint(hWnd, &ps);
95             }
96             return 0L;
97
98         default:
99             return DefWindowProc(hWnd, uMsg, wParam, lParam);
100     }
101 };
102
103 VLCPluginClass::VLCPluginClass(LONG *p_class_ref, HINSTANCE hInstance) :
104     _p_class_ref(p_class_ref),
105     _hinstance(hInstance)
106 {
107     WNDCLASS wClass;
108
109     if( ! GetClassInfo(hInstance, getInPlaceWndClassName(), &wClass) )
110     {
111         wClass.style          = CS_NOCLOSE|CS_DBLCLKS;
112         wClass.lpfnWndProc    = VLCInPlaceClassWndProc;
113         wClass.cbClsExtra     = 0;
114         wClass.cbWndExtra     = 0;
115         wClass.hInstance      = hInstance;
116         wClass.hIcon          = NULL;
117         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
118         wClass.hbrBackground  = NULL;
119         wClass.lpszMenuName   = NULL;
120         wClass.lpszClassName  = getInPlaceWndClassName();
121        
122         _inplace_wndclass_atom = RegisterClass(&wClass);
123     }
124     else
125     {
126         _inplace_wndclass_atom = 0;
127     }
128
129     if( ! GetClassInfo(hInstance, getVideoWndClassName(), &wClass) )
130     {
131         wClass.style          = CS_NOCLOSE;
132         wClass.lpfnWndProc    = VLCVideoClassWndProc;
133         wClass.cbClsExtra     = 0;
134         wClass.cbWndExtra     = 0;
135         wClass.hInstance      = hInstance;
136         wClass.hIcon          = NULL;
137         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
138         wClass.hbrBackground  = NULL;
139         wClass.lpszMenuName   = NULL;
140         wClass.lpszClassName  = getVideoWndClassName();
141        
142         _video_wndclass_atom = RegisterClass(&wClass);
143     }
144     else
145     {
146         _video_wndclass_atom = 0;
147     }
148
149     HBITMAP hbitmap = (HBITMAP)LoadImage(getHInstance(), TEXT("INPLACE-PICT"), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
150     if( NULL != hbitmap )
151     {
152         PICTDESC pictDesc;
153
154         pictDesc.cbSizeofstruct = sizeof(PICTDESC);
155         pictDesc.picType        = PICTYPE_BITMAP;
156         pictDesc.bmp.hbitmap    = hbitmap;
157         pictDesc.bmp.hpal       = NULL;
158
159         if( FAILED(OleCreatePictureIndirect(&pictDesc, IID_IPicture, TRUE, reinterpret_cast<LPVOID*>(&_inplace_picture))) )
160             _inplace_picture = NULL;
161     }
162     AddRef();
163 };
164
165 VLCPluginClass::~VLCPluginClass()
166 {
167     if( 0 != _inplace_wndclass_atom )
168         UnregisterClass(MAKEINTATOM(_inplace_wndclass_atom), _hinstance);
169
170     if( 0 != _video_wndclass_atom )
171         UnregisterClass(MAKEINTATOM(_video_wndclass_atom), _hinstance);
172
173     if( NULL != _inplace_picture )
174         _inplace_picture->Release();
175 };
176
177 STDMETHODIMP VLCPluginClass::QueryInterface(REFIID riid, void **ppv)
178 {
179     if( NULL == ppv )
180         return E_INVALIDARG;
181
182     if( (IID_IUnknown == riid) || (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         plugin->Release();
227         return hr;
228     }
229     return E_OUTOFMEMORY;
230 };
231
232 STDMETHODIMP VLCPluginClass::LockServer(BOOL fLock)
233 {
234     if( fLock )
235         AddRef();
236     else 
237         Release();
238
239     return S_OK;
240 };
241
242 ////////////////////////////////////////////////////////////////////////
243
244 VLCPlugin::VLCPlugin(VLCPluginClass *p_class, LPUNKNOWN pUnkOuter) :
245     _inplacewnd(NULL),
246     _p_class(p_class),
247     _i_ref(1UL),
248     _i_codepage(CP_ACP),
249     _b_usermode(TRUE),
250     _bstr_mrl(NULL),
251     _b_autoplay(TRUE),
252     _b_autoloop(FALSE),
253     _b_visible(TRUE),
254     _b_mute(FALSE),
255     _i_vlc(0)
256 {
257     p_class->AddRef();
258
259     vlcOleControl = new VLCOleControl(this);
260     vlcOleInPlaceObject = new VLCOleInPlaceObject(this);
261     vlcOleInPlaceActiveObject = new VLCOleInPlaceActiveObject(this);
262     vlcPersistStorage = new VLCPersistStorage(this);
263     vlcPersistStreamInit = new VLCPersistStreamInit(this);
264     vlcPersistPropertyBag = new VLCPersistPropertyBag(this);
265     vlcProvideClassInfo = new VLCProvideClassInfo(this);
266     vlcConnectionPointContainer = new VLCConnectionPointContainer(this);
267     vlcObjectSafety = new VLCObjectSafety(this);
268     vlcControl = new VLCControl(this);
269     vlcViewObject = new VLCViewObject(this);
270     vlcDataObject = new VLCDataObject(this);
271     vlcOleObject = new VLCOleObject(this);
272
273     // configure controlling IUnknown interface for implemented interfaces
274     this->pUnkOuter = (NULL != pUnkOuter) ? pUnkOuter : dynamic_cast<LPUNKNOWN>(this);
275
276     // default picure
277     _p_pict = p_class->getInPlacePict();
278
279     // set default/preferred size (320x240) pixels in HIMETRIC
280     HDC hDC = CreateDevDC(NULL);
281     _extent.cx = 320;
282     _extent.cy = 240;
283     HimetricFromDP(hDC, (LPPOINT)&_extent, 1);
284     DeleteDC(hDC);
285 };
286
287 VLCPlugin::~VLCPlugin()
288 {
289     delete vlcOleObject;
290     delete vlcDataObject;
291     delete vlcViewObject;
292     delete vlcControl;
293     delete vlcConnectionPointContainer;
294     delete vlcProvideClassInfo;
295     delete vlcPersistPropertyBag;
296     delete vlcPersistStreamInit;
297     delete vlcPersistStorage;
298     delete vlcOleInPlaceActiveObject;
299     delete vlcOleInPlaceObject;
300     delete vlcObjectSafety;
301
302     delete vlcOleControl;
303     if( _p_pict )
304         _p_pict->Release();
305
306     SysFreeString(_bstr_mrl),
307
308     _p_class->Release();
309 };
310
311 STDMETHODIMP VLCPlugin::QueryInterface(REFIID riid, void **ppv)
312 {
313     if( NULL == ppv )
314         return E_INVALIDARG;
315
316     if( IID_IUnknown == riid )
317         *ppv = reinterpret_cast<LPVOID>(this);
318     else if( IID_IOleObject == riid )
319         *ppv = reinterpret_cast<LPVOID>(vlcOleObject);
320     else if( IID_IOleControl == riid )
321         *ppv = reinterpret_cast<LPVOID>(vlcOleControl);
322     else if( IID_IOleWindow == riid )
323         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
324     else if( IID_IOleInPlaceObject == riid )
325         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
326     else if( IID_IOleInPlaceActiveObject == riid )
327         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceActiveObject);
328     else if( IID_IPersist == riid )
329         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
330     else if( IID_IPersistStreamInit == riid )
331         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
332     else if( IID_IPersistStorage == riid )
333         *ppv = reinterpret_cast<LPVOID>(vlcPersistStorage);
334     else if( IID_IPersistPropertyBag == riid )
335         *ppv = reinterpret_cast<LPVOID>(vlcPersistPropertyBag);
336     else if( IID_IProvideClassInfo == riid )
337         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
338     else if( IID_IProvideClassInfo2 == riid )
339         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
340     else if( IID_IConnectionPointContainer == riid )
341         *ppv = reinterpret_cast<LPVOID>(vlcConnectionPointContainer);
342     else if( IID_IObjectSafety == riid )
343         *ppv = reinterpret_cast<LPVOID>(vlcObjectSafety);
344     else if( IID_IDispatch == riid )
345         *ppv = reinterpret_cast<LPVOID>(vlcControl);
346     else if( IID_IVLCControl == riid )
347         *ppv = reinterpret_cast<LPVOID>(vlcControl);
348     else if( IID_IViewObject == riid )
349         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
350     else if( IID_IViewObject2 == riid )
351         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
352     else if( IID_IDataObject == riid )
353         *ppv = reinterpret_cast<LPVOID>(vlcDataObject);
354     else
355     {
356         *ppv = NULL;
357         return E_NOINTERFACE;
358     }
359     ((LPUNKNOWN)*ppv)->AddRef();
360     return NOERROR;
361 };
362
363 STDMETHODIMP_(ULONG) VLCPlugin::AddRef(void)
364 {
365     return InterlockedIncrement((LONG *)&_i_ref);
366 };
367
368 STDMETHODIMP_(ULONG) VLCPlugin::Release(void)
369 {
370     if( ! InterlockedDecrement((LONG *)&_i_ref) )
371     {
372         delete this;
373         return 0;
374     }
375     return _i_ref;
376 };
377
378 //////////////////////////////////////
379
380 /*
381 ** we use a window to represent plugin viewport,
382 ** whose geometry is limited by the clipping rectangle
383 ** all drawing within this window must follow must
384 ** follow coordinates system described in lprPosRect
385 */
386
387 static void getViewportCoords(LPRECT lprPosRect, LPRECT lprClipRect)
388 {
389     RECT bounds;
390     bounds.right  = lprPosRect->right-lprPosRect->left;
391
392     if( lprClipRect->left <= lprPosRect->left )
393     {
394         // left side is not clipped out
395         bounds.left = 0;
396
397         if( lprClipRect->right >= lprPosRect->right )
398         {
399             // right side is not clipped out, no change
400         }
401         else if( lprClipRect->right >= lprPosRect->left )
402         {
403             // right side is clipped out
404             lprPosRect->right = lprClipRect->right;
405         }
406         else
407         {
408             // outside of clipping rectange, not visible
409             lprPosRect->right = lprPosRect->left;
410         }
411     }
412     else
413     {
414         // left side is clipped out
415         bounds.left = lprPosRect->left-lprClipRect->left;
416         bounds.right += bounds.left;
417
418         lprPosRect->left = lprClipRect->left;
419         if( lprClipRect->right >= lprPosRect->right )
420         {
421             // right side is not clipped out
422         }
423         else
424         {
425             // right side is clipped out
426             lprPosRect->right = lprClipRect->right;
427         }
428     }
429
430     bounds.bottom = lprPosRect->bottom-lprPosRect->top;
431
432     if( lprClipRect->top <= lprPosRect->top )
433     {
434         // top side is not clipped out
435         bounds.top = 0;
436
437         if( lprClipRect->bottom >= lprPosRect->bottom )
438         {
439             // bottom side is not clipped out, no change
440         }
441         else if( lprClipRect->bottom >= lprPosRect->top )
442         {
443             // bottom side is clipped out
444             lprPosRect->bottom = lprClipRect->bottom;
445         }
446         else
447         {
448             // outside of clipping rectange, not visible
449             lprPosRect->right = lprPosRect->left;
450         }
451     }
452     else
453     {
454         bounds.top = lprPosRect->top-lprClipRect->top;
455         bounds.bottom += bounds.top;
456
457         lprPosRect->top = lprClipRect->top;
458         if( lprClipRect->bottom >= lprPosRect->bottom )
459         {
460             // bottom side is not clipped out
461         }
462         else
463         {
464             // bottom side is clipped out
465             lprPosRect->bottom = lprClipRect->bottom;
466         }
467     }
468     *lprClipRect = *lprPosRect;
469     *lprPosRect  = bounds;
470 };
471
472 HRESULT VLCPlugin::onInit(void)
473 {
474     if( 0 == _i_vlc )
475     {
476 //#ifdef ACTIVEX_DEBUG
477 #if 1
478         char *ppsz_argv[] = { "vlc", "-vv", "--fast-mutex", "--win9x-cv-method=1" };
479 #else
480         char *ppsz_argv[] = { "vlc", "-vv" };
481 #endif
482         HKEY h_key;
483         DWORD i_type, i_data = MAX_PATH + 1;
484         char p_data[MAX_PATH + 1];
485         if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
486                           0, KEY_READ, &h_key ) == ERROR_SUCCESS )
487         {
488              if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
489                                   (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
490              {
491                  if( i_type == REG_SZ )
492                  {
493                      strcat( p_data, "\\vlc" );
494                      ppsz_argv[0] = p_data;
495                  }
496              }
497              RegCloseKey( h_key );
498         }
499
500 #if 1
501         ppsz_argv[0] = "C:\\cygwin\\home\\Damien_Fouilleul\\dev\\videolan\\vlc-trunk\\vlc";
502 #endif
503
504         _i_vlc = VLC_Create();
505
506         if( VLC_Init(_i_vlc, sizeof(ppsz_argv)/sizeof(char*), ppsz_argv) )
507         {
508             VLC_Destroy(_i_vlc);
509             _i_vlc = 0;
510             return E_FAIL;
511         }
512         return S_OK;
513     }
514     return CO_E_ALREADYINITIALIZED;
515 };
516
517 HRESULT VLCPlugin::onLoad(void)
518 {
519     if( _b_mute )
520         VLC_VolumeMute(_i_vlc);
521
522     if( NULL != _bstr_mrl )
523     {
524         /*
525         ** try to combine MRL with client site moniker, which for Internet Explorer
526         ** is the URL of the page the plugin is embedded into. Hence, if the MRL
527         ** is a relative URL, we should end up with an absolute URL
528         */
529         IOleClientSite *pClientSite;
530         if( SUCCEEDED(vlcOleObject->GetClientSite(&pClientSite)) )
531         {
532             IBindCtx *pBC = 0;
533             if( SUCCEEDED(CreateBindCtx(0, &pBC)) )
534             {
535                 LPMONIKER pContMoniker = NULL;
536                 if( SUCCEEDED(pClientSite->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
537                                 OLEWHICHMK_CONTAINER, &pContMoniker)) )
538                 {
539                     LPOLESTR name;
540                     if( SUCCEEDED(pContMoniker->GetDisplayName(pBC, NULL, &name)) )
541                     {
542                         if( UrlIsW(name, URLIS_URL) )
543                         {
544                             LPOLESTR url = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*INTERNET_MAX_URL_LENGTH);
545                             if( NULL != url )
546                             {
547                                 DWORD len = INTERNET_MAX_URL_LENGTH;
548                                 if( SUCCEEDED(UrlCombineW(name, _bstr_mrl, url, &len,
549                                                 URL_ESCAPE_UNSAFE)) )
550                                 {
551                                     SysFreeString(_bstr_mrl);
552                                     _bstr_mrl = SysAllocString(url);
553                                 }
554                                 CoTaskMemFree(url);
555                             }
556                         }
557                         CoTaskMemFree(name);
558                     }
559                     pContMoniker->Release();
560                 }
561                 pBC->Release();
562             }
563             pClientSite->Release();
564         }
565
566         char *psz_mrl = CStrFromBSTR(CP_UTF8, _bstr_mrl);
567         if( NULL != psz_mrl )
568         {
569             // add default target to playlist
570             char *cOptions[1];
571             int  cOptionsCount = 0;
572
573             if( _b_autoloop )
574             {
575                 cOptions[cOptionsCount++] = "loop";
576             }
577             VLC_AddTarget(_i_vlc, psz_mrl, (const char **)&cOptions, cOptionsCount, PLAYLIST_APPEND, PLAYLIST_END);
578             CoTaskMemFree(psz_mrl);
579         }
580     }
581     setDirty(FALSE);
582     return S_OK;
583 };
584
585 HRESULT VLCPlugin::onAmbientChanged(LPUNKNOWN pContainer, DISPID dispID)
586 {
587     VARIANT v;
588     switch( dispID )
589     {
590         case DISPID_AMBIENT_BACKCOLOR:
591             break;
592         case DISPID_AMBIENT_DISPLAYNAME:
593             break;
594         case DISPID_AMBIENT_FONT:
595             break;
596         case DISPID_AMBIENT_FORECOLOR:
597             break;
598         case DISPID_AMBIENT_LOCALEID:
599             break;
600         case DISPID_AMBIENT_MESSAGEREFLECT:
601             break;
602         case DISPID_AMBIENT_SCALEUNITS:
603             break;
604         case DISPID_AMBIENT_TEXTALIGN:
605             break;
606         case DISPID_AMBIENT_USERMODE:
607             VariantInit(&v);
608             V_VT(&v) = VT_BOOL;
609             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
610             {
611                 setUserMode(V_BOOL(&v) != VARIANT_FALSE);
612                 VariantClear(&v);
613             }
614             break;
615         case DISPID_AMBIENT_UIDEAD:
616             break;
617         case DISPID_AMBIENT_SHOWGRABHANDLES:
618             break;
619         case DISPID_AMBIENT_SHOWHATCHING:
620             break;
621         case DISPID_AMBIENT_DISPLAYASDEFAULT:
622             break;
623         case DISPID_AMBIENT_SUPPORTSMNEMONICS:
624             break;
625         case DISPID_AMBIENT_AUTOCLIP:
626             break;
627         case DISPID_AMBIENT_APPEARANCE:
628             break;
629         case DISPID_AMBIENT_CODEPAGE:
630             VariantInit(&v);
631             V_VT(&v) = VT_I4;
632             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
633             {
634                 setCodePage(V_I4(&v));
635             }
636             break;
637         case DISPID_AMBIENT_PALETTE:
638             break;
639         case DISPID_AMBIENT_CHARSET:
640             break;
641         case DISPID_AMBIENT_RIGHTTOLEFT:
642             break;
643         case DISPID_AMBIENT_TOPTOBOTTOM:
644             break;
645         case DISPID_UNKNOWN:
646             VariantInit(&v);
647             V_VT(&v) = VT_BOOL;
648             if( SUCCEEDED(GetObjectProperty(pContainer, DISPID_AMBIENT_USERMODE, v)) )
649             {
650                 setUserMode(V_BOOL(&v) != VARIANT_FALSE);
651                 VariantClear(&v);
652             }
653             VariantInit(&v);
654             V_VT(&v) = VT_I4;
655             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
656             {
657                 setCodePage(V_I4(&v));
658             }
659             break;
660     }
661     return S_OK;
662 };
663
664 HRESULT VLCPlugin::onClose(DWORD dwSaveOption)
665 {
666     if( _i_vlc )
667     {
668         int i_vlc = _i_vlc;
669
670         _i_vlc = 0;
671         if( isInPlaceActive() )
672         {
673             onInPlaceDeactivate();
674         }
675         vlcDataObject->onClose();
676
677         VLC_CleanUp(i_vlc);
678         VLC_Destroy(i_vlc);
679     }
680     return S_OK;
681 };
682
683 BOOL VLCPlugin::isInPlaceActive(void)
684 {
685     return (NULL != _inplacewnd);
686 };
687
688 HRESULT VLCPlugin::onActivateInPlace(LPMSG lpMesg, HWND hwndParent, LPCRECT lprcPosRect, LPCRECT lprcClipRect)
689 {
690     RECT posRect = *lprcPosRect;
691     RECT clipRect = *lprcClipRect;
692
693     /*
694     ** record keeping of control geometry within container
695     */ 
696     _posRect = posRect;
697
698     /*
699     ** convert posRect & clipRect to match control viewport coordinates
700     */
701     getViewportCoords(&posRect, &clipRect);
702
703     /*
704     ** Create a window for in place activated control.
705     ** the window geometry represents the control viewport
706     ** so that embedded video is always properly clipped.
707     */
708     _inplacewnd = CreateWindow(_p_class->getInPlaceWndClassName(),
709             "VLC Plugin In-Place Window",
710             WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
711             clipRect.left,
712             clipRect.top,
713             clipRect.right-clipRect.left,
714             clipRect.bottom-clipRect.top,
715             hwndParent,
716             0,
717             _p_class->getHInstance(),
718             NULL
719            );
720
721     if( NULL == _inplacewnd )
722         return E_FAIL;
723
724     SetWindowLongPtr(_inplacewnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
725
726     /*
727     ** VLC embedded video geometry automatically matches parent window.
728     ** hence create a child window so that video position and size
729     ** is always correct relative to the viewport bounds
730     */
731     _videownd = CreateWindow(_p_class->getVideoWndClassName(),
732             "VLC Plugin Video Window",
733             WS_CHILD|WS_CLIPCHILDREN|WS_VISIBLE,
734             posRect.left,
735             posRect.top,
736             posRect.right-posRect.left,
737             posRect.bottom-posRect.top,
738             _inplacewnd,
739             0,
740             _p_class->getHInstance(),
741             NULL
742            );
743
744     if( NULL == _videownd )
745     {
746         DestroyWindow(_inplacewnd);
747         return E_FAIL;
748     }
749
750     SetWindowLongPtr(_videownd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
751
752     if( getVisible() )
753         ShowWindow(_inplacewnd, SW_SHOWNORMAL);
754
755     /* horrible cast there */
756     vlc_value_t val;
757     val.i_int = reinterpret_cast<int>(_videownd);
758     VLC_VariableSet(_i_vlc, "drawable", val);
759     val.i_int = posRect.right-posRect.left;
760     VLC_VariableSet(_i_vlc, "width", val);
761     val.i_int = posRect.bottom-posRect.top;
762     VLC_VariableSet(_i_vlc, "height", val);
763
764     if( _b_usermode && _b_autoplay & (VLC_PlaylistNumberOfItems(_i_vlc) > 0) )
765     {
766         VLC_Play(_i_vlc);
767         fireOnPlayEvent();
768     }
769     return S_OK;
770 };
771
772 HRESULT VLCPlugin::onInPlaceDeactivate(void)
773 {
774     VLC_Stop(_i_vlc);
775     fireOnStopEvent();
776
777     DestroyWindow(_videownd);
778     _videownd = NULL;
779     DestroyWindow(_inplacewnd);
780     _inplacewnd = NULL;
781  
782     return S_OK;
783 };
784
785 void VLCPlugin::setVisible(BOOL fVisible)
786 {
787     _b_visible = fVisible;
788     if( isInPlaceActive() )
789         ShowWindow(_inplacewnd, fVisible ? SW_SHOWNORMAL : SW_HIDE);
790     firePropChangedEvent(DISPID_Visible);
791 };
792
793 void VLCPlugin::setFocus(BOOL fFocus)
794 {
795     if( fFocus )
796         SetActiveWindow(_inplacewnd);
797 };
798
799 BOOL VLCPlugin::hasFocus(void)
800 {
801     return GetActiveWindow() == _inplacewnd;
802 };
803
804 void VLCPlugin::onDraw(DVTARGETDEVICE * ptd, HDC hicTargetDev,
805         HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds)
806 {
807     if( getVisible() )
808     {
809         long width = lprcBounds->right-lprcBounds->left;
810         long height = lprcBounds->bottom-lprcBounds->top;
811
812         RECT bounds = { lprcBounds->left, lprcBounds->top, lprcBounds->right, lprcBounds->bottom };
813         FillRect(hdcDraw, &bounds, (HBRUSH)GetStockObject(WHITE_BRUSH));
814
815         LPPICTURE pict = getPicture();
816         if( NULL != pict )
817         {
818             OLE_XSIZE_HIMETRIC picWidth;
819             OLE_YSIZE_HIMETRIC picHeight;
820
821             pict->get_Width(&picWidth);
822             pict->get_Height(&picHeight);
823
824             SIZEL picSize = { picWidth, picHeight };
825
826             if( NULL != hicTargetDev )
827             {
828                 DPFromHimetric(hicTargetDev, (LPPOINT)&picSize, 1);
829             }
830             else if( NULL != (hicTargetDev = CreateDevDC(ptd)) )
831             {
832                 DPFromHimetric(hicTargetDev, (LPPOINT)&picSize, 1);
833                 DeleteDC(hicTargetDev);
834             }
835
836             if( picSize.cx > width-4 )
837                 picSize.cx = width-4;
838             if( picSize.cy > height-4 )
839                 picSize.cy = height-4;
840
841             LONG dstX = lprcBounds->left+(width-picSize.cx)/2;
842             LONG dstY = lprcBounds->top+(height-picSize.cy)/2;
843
844             if( NULL != lprcWBounds )
845             {
846                 RECT wBounds = { lprcWBounds->left, lprcWBounds->top, lprcWBounds->right, lprcWBounds->bottom };
847                 pict->Render(hdcDraw, dstX, dstY, picSize.cx, picSize.cy,
848                         0L, picHeight, picWidth, -picHeight, &wBounds);
849             }
850             else 
851                 pict->Render(hdcDraw, dstX, dstY, picSize.cx, picSize.cy,
852                         0L, picHeight, picWidth, -picHeight, NULL);
853
854             pict->Release();
855         }
856
857         SelectObject(hdcDraw, GetStockObject(BLACK_BRUSH));
858
859         MoveToEx(hdcDraw, bounds.left, bounds.top, NULL);
860         LineTo(hdcDraw, bounds.left+width-1, bounds.top);
861         LineTo(hdcDraw, bounds.left+width-1, bounds.top+height-1);
862         LineTo(hdcDraw, bounds.left, bounds.top+height-1);
863         LineTo(hdcDraw, bounds.left, bounds.top);
864     }
865 };
866
867 void VLCPlugin::onPaint(HDC hdc, const RECT &bounds, const RECT &clipRect)
868 {
869     if( getVisible() )
870     {
871         /** if VLC is playing, it may not display any VIDEO content 
872         ** hence, draw control logo*/
873         HDC hdcDraw = CreateCompatibleDC(hdc);
874         if( NULL != hdcDraw )
875         {
876             SIZEL size = getExtent();
877             DPFromHimetric(hdc, (LPPOINT)&size, 1);
878             RECTL posRect = { 0, 0, size.cx, size.cy };
879
880             int width = bounds.right-bounds.left;
881             int height = bounds.bottom-bounds.top;
882
883             HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
884             if( NULL != hBitmap )
885             {
886                 HBITMAP oldBmp = (HBITMAP)SelectObject(hdcDraw, hBitmap);
887
888                 if( (size.cx != width) || (size.cx != height) )
889                 {
890                     // needs to scale canvas
891                     SetMapMode(hdcDraw, MM_ANISOTROPIC);
892                     SetWindowExtEx(hdcDraw, size.cx, size.cy, NULL);
893                     SetViewportExtEx(hdcDraw, width, height, NULL);
894                 }
895
896                 onDraw(NULL, hdc, hdcDraw, &posRect, NULL);
897
898                 SetMapMode(hdcDraw, MM_TEXT);
899                 BitBlt(hdc, bounds.left, bounds.top,
900                         width, height,
901                         hdcDraw, 0, 0,
902                         SRCCOPY);
903
904                 SelectObject(hdcDraw, oldBmp);
905                 DeleteObject(hBitmap);
906             }
907             DeleteDC(hdcDraw);
908         }
909     }
910 };
911
912 void VLCPlugin::onPositionChange(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
913 {
914     RECT clipRect = *lprcClipRect;
915     RECT posRect  = *lprcPosRect;
916
917     //RedrawWindow(GetParent(_inplacewnd), &_posRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
918
919     /*
920     ** record keeping of control geometry within container
921     */
922     _posRect = posRect;
923
924     /*
925     ** convert posRect & clipRect to match control viewport coordinates
926     */
927     getViewportCoords(&posRect, &clipRect);
928
929     /*
930     ** change in-place window geometry to match clipping region
931     */
932     SetWindowPos(_inplacewnd, NULL,
933             clipRect.left,
934             clipRect.top,
935             clipRect.right-clipRect.left,
936             clipRect.bottom-clipRect.top,
937             SWP_NOACTIVATE|
938             SWP_NOCOPYBITS|
939             SWP_NOZORDER|
940             SWP_NOOWNERZORDER );
941
942     /*
943     ** change video window geometry to match object bounds within clipping region
944     */
945     SetWindowPos(_videownd, NULL,
946             posRect.left,
947             posRect.top,
948             posRect.right-posRect.left,
949             posRect.bottom-posRect.top,
950             SWP_NOACTIVATE|
951             SWP_NOCOPYBITS|
952             SWP_NOZORDER|
953             SWP_NOOWNERZORDER );
954
955     //RedrawWindow(_videownd, &posRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
956     vlc_value_t val;
957     val.i_int = posRect.right-posRect.left;
958     VLC_VariableSet(_i_vlc, "width", val);
959     val.i_int = posRect.bottom-posRect.top;
960     VLC_VariableSet(_i_vlc, "height", val);
961 };
962
963 void VLCPlugin::freezeEvents(BOOL freeze)
964 {
965     vlcConnectionPointContainer->freezeEvents(freeze);
966 };
967
968 void VLCPlugin::firePropChangedEvent(DISPID dispid)
969 {
970     vlcConnectionPointContainer->firePropChangedEvent(dispid); 
971 };
972
973 void VLCPlugin::fireOnPlayEvent(void)
974 {
975     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
976     vlcConnectionPointContainer->fireEvent(DISPID_PlayEvent, &dispparamsNoArgs); 
977 };
978
979 void VLCPlugin::fireOnPauseEvent(void)
980 {
981     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
982     vlcConnectionPointContainer->fireEvent(DISPID_PauseEvent, &dispparamsNoArgs); 
983 };
984
985 void VLCPlugin::fireOnStopEvent(void)
986 {
987     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
988     vlcConnectionPointContainer->fireEvent(DISPID_StopEvent, &dispparamsNoArgs); 
989 };
990