]> git.saurik.com Git - wxWidgets.git/blob - samples/flash/flash.cpp
Fix discrepancy between different ways of measuring text extents under Mac.
[wxWidgets.git] / samples / flash / flash.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: flash.cpp
3 // Purpose: Sample showing integration of Flash ActiveX control
4 // Author: Vadim Zeitlin
5 // Created: 2009-01-13
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Vadim Zeitlin
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 /*
12 Documentation for embedding Flash into anything other than a web browser is
13 not easy to find, here is the tech note which provided most of the
14 information used here: http://www.adobe.com/go/tn_12059
15 */
16
17 // ============================================================================
18 // declarations
19 // ============================================================================
20
21 // ----------------------------------------------------------------------------
22 // headers
23 // ----------------------------------------------------------------------------
24
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef __WXMSW__
32 #error "ActiveX controls are MSW-only"
33 #endif
34
35 #ifndef WX_PRECOMP
36 #include "wx/wx.h"
37 #endif
38
39 #include "wx/cmdline.h"
40 #include "wx/filename.h"
41
42 #if !defined(__WXMSW__) && !defined(__WXPM__)
43 #include "../sample.xpm"
44 #endif
45
46 #include "wx/msw/ole/activex.h"
47
48 // we currently use VC-specific extensions in this sample, it could be
49 // rewritten to avoid them if there is real interest in doing it but compiler
50 // COM support in MSVC makes the code much simpler to understand
51 #ifndef __VISUALC__
52 #error "This sample requires Microsoft Visual C++ compiler COM extensions"
53 #endif
54
55 // import Flash ActiveX control by using its (standard) type library UUID
56 //
57 // no_auto_exclude is needed to import IServiceProvider interface defined in
58 // this type library even though its name conflicts with a standard Windows
59 // interface with the same name
60 #import "libid:D27CDB6B-AE6D-11CF-96B8-444553540000" no_auto_exclude
61
62 using namespace ShockwaveFlashObjects;
63
64 const CLSID CLSID_ShockwaveFlash = __uuidof(ShockwaveFlash);
65 const IID IID_IShockwaveFlash = __uuidof(IShockwaveFlash);
66
67 inline wxString bstr2wx(const _bstr_t& bstr)
68 {
69 return wxString(static_cast<const wchar_t *>(bstr));
70 }
71
72 inline _bstr_t wx2bstr(const wxString& str)
73 {
74 return _bstr_t(str.wc_str());
75 }
76
77 // ----------------------------------------------------------------------------
78 // constants
79 // ----------------------------------------------------------------------------
80
81 // taken from type library
82 namespace
83 {
84
85 const int FLASH_DISPID_ONREADYSTATECHANGE = -609; // DISPID_ONREADYSTATECHANGE
86 const int FLASH_DISPID_ONPROGRESS = 0x7a6;
87 const int FLASH_DISPID_FSCOMMAND = 0x96;
88 const int FLASH_DISPID_FLASHCALL = 0xc5;
89
90 enum FlashState
91 {
92 FlashState_Unknown = -1,
93 FlashState_Loading,
94 FlashState_Uninitialized,
95 FlashState_Loaded,
96 FlashState_Interactive,
97 FlashState_Complete,
98 FlashState_Max
99 };
100
101 } // anonymous namespace
102
103 // ----------------------------------------------------------------------------
104 // private classes
105 // ----------------------------------------------------------------------------
106
107 // Define a new application type, each program should derive a class from wxApp
108 class FlashApp : public wxApp
109 {
110 public:
111 FlashApp() { }
112
113 virtual bool OnInit();
114
115 virtual void OnInitCmdLine(wxCmdLineParser& parser);
116 virtual bool OnCmdLineParsed(wxCmdLineParser& parser);
117
118 virtual bool OnExceptionInMainLoop();
119
120 private:
121 wxString m_swf;
122
123 wxDECLARE_NO_COPY_CLASS(FlashApp);
124 };
125
126 // Define a new frame type: this is going to be our main frame
127 class FlashFrame : public wxFrame
128 {
129 public:
130 // ctor takes ownership of the pointer which must be non-NULL and opens the
131 // given SWF file if it's non-empty
132 FlashFrame(IShockwaveFlash *flash, const wxString& swf);
133 virtual ~FlashFrame();
134
135 void SetMovie(const wxString& movie);
136
137 void Play();
138 void Stop();
139
140 private:
141 enum
142 {
143 Flash_Play = 100,
144 Flash_Get,
145 Flash_Set,
146 Flash_Call,
147 Flash_CallWithArg
148 };
149
150 void OnOpen(wxCommandEvent& event);
151 void OnQuit(wxCommandEvent& event);
152 void OnAbout(wxCommandEvent& event);
153
154 void OnPlay(wxCommandEvent&) { Play(); }
155 void OnStop(wxCommandEvent&) { Stop(); }
156 void OnBack(wxCommandEvent& event);
157 void OnForward(wxCommandEvent& event);
158 void OnInfo(wxCommandEvent& event);
159 void OnVarGet(wxCommandEvent& event);
160 void OnVarSet(wxCommandEvent& event);
161 void OnCall(wxCommandEvent& event);
162 void OnCallWithArg(wxCommandEvent& event);
163
164 void OnActiveXEvent(wxActiveXEvent& event);
165
166 // give an error message if hr is not S_OK
167 void CheckFlashCall(HRESULT hr, const char *func);
168
169 // return the name of the Flash control state
170 wxString GetFlashStateString(int state);
171
172 // call CallFunction() with a single argument of the type specified by
173 // argtype or without any arguments if it is empty
174 void CallFlashFunc(const wxString& argtype,
175 const wxString& func,
176 const wxString& arg = wxString());
177
178
179 const IShockwaveFlashPtr m_flash;
180 wxLog *m_oldLog;
181 wxString m_swf;
182 FlashState m_state;
183
184 wxTextCtrl *m_varname,
185 *m_varvalue,
186 *m_funcname,
187 *m_funcarg;
188
189 DECLARE_EVENT_TABLE()
190 wxDECLARE_NO_COPY_CLASS(FlashFrame);
191 };
192
193 // ----------------------------------------------------------------------------
194 // event tables and other macros for wxWidgets
195 // ----------------------------------------------------------------------------
196
197 BEGIN_EVENT_TABLE(FlashFrame, wxFrame)
198 EVT_MENU(wxID_OPEN, FlashFrame::OnOpen)
199 EVT_MENU(wxID_EXIT, FlashFrame::OnQuit)
200 EVT_MENU(wxID_ABOUT, FlashFrame::OnAbout)
201
202 EVT_BUTTON(Flash_Play, FlashFrame::OnPlay)
203 EVT_BUTTON(wxID_STOP, FlashFrame::OnStop)
204 EVT_BUTTON(wxID_BACKWARD, FlashFrame::OnBack)
205 EVT_BUTTON(wxID_FORWARD, FlashFrame::OnForward)
206
207 EVT_BUTTON(wxID_INFO, FlashFrame::OnInfo)
208 EVT_BUTTON(Flash_Get, FlashFrame::OnVarGet)
209 EVT_BUTTON(Flash_Set, FlashFrame::OnVarSet)
210 EVT_BUTTON(Flash_Call, FlashFrame::OnCall)
211 EVT_BUTTON(Flash_CallWithArg, FlashFrame::OnCallWithArg)
212
213 EVT_ACTIVEX(wxID_ANY, FlashFrame::OnActiveXEvent)
214 END_EVENT_TABLE()
215
216 IMPLEMENT_APP(FlashApp)
217
218 // ============================================================================
219 // implementation
220 // ============================================================================
221
222 // ----------------------------------------------------------------------------
223 // the application class
224 // ----------------------------------------------------------------------------
225
226 void FlashApp::OnInitCmdLine(wxCmdLineParser& parser)
227 {
228 wxApp::OnInitCmdLine(parser);
229
230 parser.AddParam("SWF file to play",
231 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
232 }
233
234 bool FlashApp::OnCmdLineParsed(wxCmdLineParser& parser)
235 {
236 if ( parser.GetParamCount() )
237 m_swf = parser.GetParam(0);
238
239 return wxApp::OnCmdLineParsed(parser);
240 }
241
242 bool FlashApp::OnInit()
243 {
244 if ( !wxApp::OnInit() )
245 return false;
246
247 IShockwaveFlash *flash = NULL;
248 HRESULT hr = ::CoCreateInstance
249 (
250 CLSID_ShockwaveFlash,
251 NULL,
252 CLSCTX_INPROC_SERVER,
253 IID_IShockwaveFlash,
254 (void **)&flash
255 );
256 if ( FAILED(hr) )
257 {
258 wxLogSysError(hr, "Failed to create Flash ActiveX control");
259 return false;
260 }
261
262 new FlashFrame(flash, m_swf);
263
264 return true;
265 }
266
267 bool FlashApp::OnExceptionInMainLoop()
268 {
269 try
270 {
271 throw;
272 }
273 catch ( _com_error& ce )
274 {
275 wxLogMessage("COM exception: %s", ce.ErrorMessage());
276
277 return true;
278 }
279 catch ( ... )
280 {
281 throw;
282 }
283 }
284
285 // ----------------------------------------------------------------------------
286 // main frame creation
287 // ----------------------------------------------------------------------------
288
289 // frame constructor
290 FlashFrame::FlashFrame(IShockwaveFlash *flash, const wxString& swf)
291 : wxFrame(NULL, wxID_ANY, "wxWidgets Flash sample"),
292 m_flash(flash, false /* take ownership */),
293 m_swf(swf),
294 m_state(FlashState_Unknown)
295 {
296 // set the frame icon
297 SetIcon(wxICON(sample));
298
299 // create the new log target before doing anything with the Flash that
300 // could result in log messages
301 wxTextCtrl * const log = new wxTextCtrl(this, wxID_ANY, "",
302 wxDefaultPosition, wxSize(-1, 100),
303 wxTE_MULTILINE);
304 m_oldLog = wxLog::SetActiveTarget(new wxLogTextCtrl(log));
305
306 #if wxUSE_MENUS
307 // create a menu bar
308 wxMenu *fileMenu = new wxMenu;
309 fileMenu->Append(wxID_OPEN);
310 fileMenu->AppendSeparator();
311 fileMenu->Append(wxID_EXIT);
312
313 wxMenu *helpMenu = new wxMenu;
314 helpMenu->Append(wxID_ABOUT);
315
316 wxMenuBar *menuBar = new wxMenuBar();
317 menuBar->Append(fileMenu, "&File");
318 menuBar->Append(helpMenu, "&Help");
319 SetMenuBar(menuBar);
320 #endif // wxUSE_MENUS
321
322 #if wxUSE_STATUSBAR
323 CreateStatusBar(2);
324 SetStatusText("Welcome to wxWidgets Flash embedding sample");
325 SetStatusText("No loaded file", 1);
326 #endif // wxUSE_STATUSBAR
327
328 wxPanel * const panel = new wxPanel(this);
329 wxSizer * const sizerPanel = new wxBoxSizer(wxVERTICAL);
330 wxWindow * const activeXParent = new wxWindow(panel, wxID_ANY,
331 wxDefaultPosition,
332 wxSize(300, 200));
333 new wxActiveXContainer(activeXParent, IID_IShockwaveFlash, flash);
334 if ( !swf.empty() )
335 SetMovie(swf);
336
337 sizerPanel->Add(activeXParent,
338 wxSizerFlags(1).Expand().Border());
339
340 const wxSizerFlags flagsHorz(wxSizerFlags().Centre().HorzBorder());
341
342 wxSizer * const sizerBtns = new wxBoxSizer(wxHORIZONTAL);
343 sizerBtns->Add(new wxButton(panel, wxID_BACKWARD), flagsHorz);
344 sizerBtns->Add(new wxButton(panel, Flash_Play, "&Play"), flagsHorz);
345 sizerBtns->Add(new wxButton(panel, wxID_STOP), flagsHorz);
346 sizerBtns->Add(new wxButton(panel, wxID_FORWARD), flagsHorz);
347 sizerBtns->AddSpacer(20);
348 sizerBtns->Add(new wxButton(panel, wxID_INFO), flagsHorz);
349 sizerPanel->Add(sizerBtns, wxSizerFlags().Center().Border());
350
351 wxSizer * const sizerVar = new wxBoxSizer(wxHORIZONTAL);
352 sizerVar->Add(new wxStaticText(panel, wxID_ANY, "Variable &name:"),
353 flagsHorz);
354 m_varname = new wxTextCtrl(panel, wxID_ANY);
355 sizerVar->Add(m_varname, flagsHorz);
356 sizerVar->Add(new wxStaticText(panel, wxID_ANY, "&value:"),
357 flagsHorz);
358 m_varvalue = new wxTextCtrl(panel, wxID_ANY);
359 sizerVar->Add(m_varvalue, flagsHorz);
360 sizerVar->AddSpacer(10);
361 sizerVar->Add(new wxButton(panel, Flash_Get, "&Get"), flagsHorz);
362 sizerVar->Add(new wxButton(panel, Flash_Set, "&Set"), flagsHorz);
363 sizerPanel->Add(sizerVar, wxSizerFlags().Center().Border());
364
365 wxSizer * const sizerCall = new wxBoxSizer(wxHORIZONTAL);
366 sizerCall->Add(new wxStaticText(panel, wxID_ANY, "&Function name:"),
367 flagsHorz);
368 m_funcname = new wxTextCtrl(panel, wxID_ANY);
369 sizerCall->Add(m_funcname, flagsHorz);
370 sizerCall->Add(new wxButton(panel, Flash_Call, "&Call"), flagsHorz);
371 sizerCall->Add(new wxStaticText(panel, wxID_ANY, "&argument:"),
372 flagsHorz);
373 m_funcarg = new wxTextCtrl(panel, wxID_ANY);
374 sizerCall->Add(m_funcarg, flagsHorz);
375 sizerCall->Add(new wxButton(panel, Flash_CallWithArg, "Call &with arg"),
376 flagsHorz);
377 sizerPanel->Add(sizerCall, wxSizerFlags().Center().Border());
378
379 panel->SetSizer(sizerPanel);
380
381 wxSizer * const sizerFrame = new wxBoxSizer(wxVERTICAL);
382 sizerFrame->Add(panel, wxSizerFlags(2).Expand());
383 sizerFrame->Add(log, wxSizerFlags(1).Expand());
384 SetSizerAndFit(sizerFrame);
385
386 Show();
387
388 m_flash->PutAllowScriptAccess(L"always");
389 wxLogMessage("Script access changed to \"%s\"",
390 bstr2wx(m_flash->GetAllowScriptAccess()));
391 }
392
393 FlashFrame::~FlashFrame()
394 {
395 delete wxLog::SetActiveTarget(m_oldLog);
396 }
397
398 // ----------------------------------------------------------------------------
399 // Flash API wrappers
400 // ----------------------------------------------------------------------------
401
402 void FlashFrame::CheckFlashCall(HRESULT hr, const char *func)
403 {
404 if ( FAILED(hr) )
405 {
406 wxLogSysError(hr, "Call to IShockwaveFlash::%s() failed", func);
407 }
408 }
409
410 void FlashFrame::CallFlashFunc(const wxString& argtype,
411 const wxString& func,
412 const wxString& arg)
413 {
414 wxString args;
415 if ( !argtype.empty() )
416 {
417 args = wxString::Format("<%s>%s</%s>", argtype, arg, argtype);
418 }
419
420 // take care with XML formatting: there should be no spaces in it or the
421 // call would fail!
422 wxString request = wxString::Format
423 (
424 "<invoke name=\"%s\" returntype=\"xml\">"
425 "<arguments>"
426 "%s"
427 "</arguments>"
428 "</invoke>",
429 func,
430 args
431 );
432
433 wxLogMessage("%s(%s) returned \"%s\"",
434 func, args,
435 bstr2wx(m_flash->CallFunction(wx2bstr(request))));
436 }
437
438 wxString FlashFrame::GetFlashStateString(int state)
439 {
440 static const char *knownStates[] =
441 {
442 "Loading", "Uninitialized", "Loaded", "Interactive", "Complete",
443 };
444
445 if ( state >= 0 && state < WXSIZEOF(knownStates) )
446 return knownStates[state];
447
448 return wxString::Format("unknown state (%d)", state);
449 }
450
451 void FlashFrame::SetMovie(const wxString& movie)
452 {
453 // Flash doesn't like relative file names
454 wxFileName fn(movie);
455 fn.MakeAbsolute();
456 const wxString swf = fn.GetFullPath();
457 if ( swf == m_swf )
458 m_flash->PutMovie(L"");
459 else
460 m_swf = swf;
461
462 m_flash->PutMovie(m_swf.wc_str());
463
464 SetStatusText("Loaded \"" + m_swf + '"', 1);
465 }
466
467 void FlashFrame::Play()
468 {
469 CheckFlashCall(m_flash->Play(), "Play");
470 }
471
472 void FlashFrame::Stop()
473 {
474 CheckFlashCall(m_flash->Stop(), "Stop");
475 }
476
477 // ----------------------------------------------------------------------------
478 // event handlers
479 // ----------------------------------------------------------------------------
480
481 void FlashFrame::OnOpen(wxCommandEvent& WXUNUSED(event))
482 {
483 wxString swf = wxLoadFileSelector("Flash movie", ".swf", m_swf, this);
484 if ( swf.empty() )
485 return;
486
487 SetMovie(swf);
488 }
489
490 void FlashFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
491 {
492 // true is to force the frame to close
493 Close(true);
494 }
495
496 void FlashFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
497 {
498 wxMessageBox("Flash ActiveX control embedding sample\n"
499 "\n"
500 "(c) 2009 Vadim Zeitlin",
501 "About " + GetTitle(),
502 wxOK | wxICON_INFORMATION,
503 this);
504 }
505
506 void FlashFrame::OnActiveXEvent(wxActiveXEvent& event)
507 {
508 switch ( event.GetDispatchId() )
509 {
510 case FLASH_DISPID_ONREADYSTATECHANGE:
511 {
512 const int state = event[0].GetInteger();
513 if ( state != m_state )
514 {
515 wxLogMessage("State changed to %s",
516 GetFlashStateString(state));
517
518 if ( state >= 0 && state < FlashState_Max )
519 m_state = static_cast<FlashState>(state);
520 else
521 m_state = FlashState_Unknown;
522 }
523 }
524 break;
525
526 case FLASH_DISPID_ONPROGRESS:
527 wxLogMessage("Progress: %d%%", event[0].GetInteger());
528 break;
529
530 case FLASH_DISPID_FSCOMMAND:
531 wxLogMessage("Flash command %s(%s)",
532 event[0].GetString(), event[1].GetString());
533 break;
534
535 case FLASH_DISPID_FLASHCALL:
536 wxLogMessage("Flash request \"%s\"", event[0].GetString());
537 break;
538
539 default:
540 wxLogMessage("Unknown event %ld", event.GetDispatchId());
541 }
542
543 event.Skip();
544 }
545
546 void FlashFrame::OnBack(wxCommandEvent& WXUNUSED(event))
547 {
548 CheckFlashCall(m_flash->Back(), "Back");
549 }
550
551 void FlashFrame::OnForward(wxCommandEvent& WXUNUSED(event))
552 {
553 CheckFlashCall(m_flash->Forward(), "Forward");
554 }
555
556 void FlashFrame::OnInfo(wxCommandEvent& WXUNUSED(event))
557 {
558 const int state = m_flash->GetReadyState();
559 wxString msg = "State: " + GetFlashStateString(state);
560
561 if ( state == FlashState_Complete )
562 {
563 msg += wxString::Format(", frame: %ld/%ld",
564 m_flash->GetFrameNum() + 1,
565 m_flash->GetTotalFrames());
566 }
567
568 if ( m_flash->IsPlaying() )
569 msg += ", playing";
570
571 wxLogMessage("%s", msg);
572 }
573
574 void FlashFrame::OnVarGet(wxCommandEvent& WXUNUSED(event))
575 {
576 m_varvalue->SetValue(bstr2wx(
577 m_flash->GetVariable(wx2bstr(m_varname->GetValue()))));
578 }
579
580 void FlashFrame::OnVarSet(wxCommandEvent& WXUNUSED(event))
581 {
582 m_flash->SetVariable(wx2bstr(m_varname->GetValue()),
583 wx2bstr(m_varvalue->GetValue()));
584 }
585
586 void FlashFrame::OnCall(wxCommandEvent& WXUNUSED(event))
587 {
588 CallFlashFunc("", m_funcname->GetValue());
589 }
590
591 void FlashFrame::OnCallWithArg(wxCommandEvent& WXUNUSED(event))
592 {
593 CallFlashFunc("string", m_funcname->GetValue(), m_funcarg->GetValue());
594 }
595
596
597