1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/html/helpctrl.cpp
3 // Purpose:     wxHtmlHelpController
4 // Notes:       Based on htmlhelp.cpp, implementing a monolithic
5 //              HTML Help controller class,  by Vaclav Slavik
6 // Author:      Harm van der Heijden and Vaclav Slavik
7 // Copyright:   (c) Harm van der Heijden and Vaclav Slavik
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17 
18 #if wxUSE_WXHTML_HELP
19 
20 #ifndef WX_PRECOMP
21     #include "wx/app.h"
22     #include "wx/intl.h"
23 #endif // WX_PRECOMP
24 
25 #include "wx/busyinfo.h"
26 #include "wx/html/helpctrl.h"
27 #include "wx/html/helpwnd.h"
28 #include "wx/html/helpfrm.h"
29 #include "wx/html/helpdlg.h"
30 
31 #if wxUSE_HELP
32     #include "wx/tipwin.h"
33 #endif
34 
35 #if wxUSE_LIBMSPACK
36 #include "wx/html/forcelnk.h"
37 FORCE_LINK(wxhtml_chm_support)
38 #endif
39 
IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpController,wxHelpControllerBase)40 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpController, wxHelpControllerBase)
41 
42 wxHtmlHelpController::wxHtmlHelpController(int style, wxWindow* parentWindow):
43     wxHelpControllerBase(parentWindow)
44 {
45     Init(style);
46 }
47 
wxHtmlHelpController(wxWindow * parentWindow,int style)48 wxHtmlHelpController::wxHtmlHelpController(wxWindow* parentWindow, int style):
49     wxHelpControllerBase(parentWindow)
50 {
51     Init(style);
52 }
53 
Init(int style)54 void wxHtmlHelpController::Init(int style)
55 {
56     m_helpWindow = NULL;
57     m_helpFrame = NULL;
58     m_helpDialog = NULL;
59 #if wxUSE_CONFIG
60     m_Config = NULL;
61     m_ConfigRoot = wxEmptyString;
62 #endif // wxUSE_CONFIG
63     m_titleFormat = _("Help: %s");
64     m_FrameStyle = style;
65     m_shouldPreventAppExit = false;
66 }
67 
68 
~wxHtmlHelpController()69 wxHtmlHelpController::~wxHtmlHelpController()
70 {
71 #if wxUSE_CONFIG
72     if (m_Config)
73         WriteCustomization(m_Config, m_ConfigRoot);
74 #endif // wxUSE_CONFIG
75     if (m_helpWindow)
76         DestroyHelpWindow();
77 }
78 
79 
DestroyHelpWindow()80 void wxHtmlHelpController::DestroyHelpWindow()
81 {
82     if (m_FrameStyle & wxHF_EMBEDDED)
83         return;
84 
85     // Find top-most parent window
86     // If a modal dialog
87     wxWindow* parent = FindTopLevelWindow();
88     if (parent)
89     {
90         wxDialog* dialog = wxDynamicCast(parent, wxDialog);
91         if (dialog && dialog->IsModal())
92         {
93             dialog->EndModal(wxID_OK);
94         }
95         parent->Destroy();
96         m_helpWindow = NULL;
97     }
98     m_helpDialog = NULL;
99     m_helpFrame = NULL;
100 }
101 
OnCloseFrame(wxCloseEvent & evt)102 void wxHtmlHelpController::OnCloseFrame(wxCloseEvent& evt)
103 {
104 #if wxUSE_CONFIG
105     if (m_Config)
106         WriteCustomization(m_Config, m_ConfigRoot);
107 #endif // wxUSE_CONFIG
108 
109     evt.Skip();
110 
111     OnQuit();
112 
113     if ( m_helpWindow )
114         m_helpWindow->SetController(NULL);
115     m_helpWindow = NULL;
116     m_helpDialog = NULL;
117     m_helpFrame = NULL;
118 }
119 
SetShouldPreventAppExit(bool enable)120 void wxHtmlHelpController::SetShouldPreventAppExit(bool enable)
121 {
122     m_shouldPreventAppExit = enable;
123     if ( m_helpFrame )
124         m_helpFrame->SetShouldPreventAppExit(enable);
125 }
126 
SetTitleFormat(const wxString & title)127 void wxHtmlHelpController::SetTitleFormat(const wxString& title)
128 {
129     m_titleFormat = title;
130     wxHtmlHelpFrame* frame = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpFrame);
131     wxHtmlHelpDialog* dialog = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpDialog);
132     if (frame)
133     {
134         frame->SetTitleFormat(title);
135     }
136     else if (dialog)
137         dialog->SetTitleFormat(title);
138 }
139 
140 // Find the top-most parent window
FindTopLevelWindow()141 wxWindow* wxHtmlHelpController::FindTopLevelWindow()
142 {
143     return wxGetTopLevelParent(m_helpWindow);
144 }
145 
AddBook(const wxFileName & book_file,bool show_wait_msg)146 bool wxHtmlHelpController::AddBook(const wxFileName& book_file, bool show_wait_msg)
147 {
148     return AddBook(wxFileSystem::FileNameToURL(book_file), show_wait_msg);
149 }
150 
AddBook(const wxString & book,bool show_wait_msg)151 bool wxHtmlHelpController::AddBook(const wxString& book, bool show_wait_msg)
152 {
153     wxBusyCursor cur;
154 #if wxUSE_BUSYINFO
155     wxBusyInfo* busy = NULL;
156     wxString info;
157     if (show_wait_msg)
158     {
159         info.Printf(_("Adding book %s"), book.c_str());
160         busy = new wxBusyInfo(info);
161     }
162 #endif
163     bool retval = m_helpData.AddBook(book);
164 #if wxUSE_BUSYINFO
165     if (show_wait_msg)
166         delete busy;
167 #else
168     wxUnusedVar(show_wait_msg);
169 #endif
170     if (m_helpWindow)
171         m_helpWindow->RefreshLists();
172     return retval;
173 }
174 
CreateHelpFrame(wxHtmlHelpData * data)175 wxHtmlHelpFrame* wxHtmlHelpController::CreateHelpFrame(wxHtmlHelpData *data)
176 {
177     wxHtmlHelpFrame* frame = new wxHtmlHelpFrame(data);
178     frame->SetController(this);
179     frame->Create(m_parentWindow, -1, wxEmptyString, m_FrameStyle
180 #if wxUSE_CONFIG
181         , m_Config, m_ConfigRoot
182 #endif // wxUSE_CONFIG
183         );
184     frame->SetTitleFormat(m_titleFormat);
185     frame->SetShouldPreventAppExit(m_shouldPreventAppExit);
186     m_helpFrame = frame;
187     return frame;
188 }
189 
CreateHelpDialog(wxHtmlHelpData * data)190 wxHtmlHelpDialog* wxHtmlHelpController::CreateHelpDialog(wxHtmlHelpData *data)
191 {
192     wxHtmlHelpDialog* dialog = new wxHtmlHelpDialog(data);
193     dialog->SetController(this);
194     dialog->SetTitleFormat(m_titleFormat);
195     dialog->Create(m_parentWindow, -1, wxEmptyString, m_FrameStyle);
196     m_helpDialog = dialog;
197     return dialog;
198 }
199 
CreateHelpWindow()200 wxWindow* wxHtmlHelpController::CreateHelpWindow()
201 {
202     if (m_helpWindow)
203     {
204         if (m_FrameStyle & wxHF_EMBEDDED)
205             return m_helpWindow;
206 
207         wxWindow* topLevelWindow = FindTopLevelWindow();
208         if (topLevelWindow)
209             topLevelWindow->Raise();
210         return m_helpWindow;
211     }
212 
213 #if wxUSE_CONFIG
214     if (m_Config == NULL)
215     {
216         m_Config = wxConfigBase::Get(false);
217         if (m_Config != NULL)
218             m_ConfigRoot = wxT("wxWindows/wxHtmlHelpController");
219     }
220 #endif // wxUSE_CONFIG
221 
222     if (m_FrameStyle & wxHF_DIALOG)
223     {
224         wxHtmlHelpDialog* dialog = CreateHelpDialog(&m_helpData);
225         m_helpWindow = dialog->GetHelpWindow();
226     }
227     else if ((m_FrameStyle & wxHF_EMBEDDED) && m_parentWindow)
228     {
229         m_helpWindow = new wxHtmlHelpWindow(m_parentWindow, -1, wxDefaultPosition, wxDefaultSize,
230             wxTAB_TRAVERSAL|wxNO_BORDER, m_FrameStyle, &m_helpData);
231     }
232     else // wxHF_FRAME
233     {
234         wxHtmlHelpFrame* frame = CreateHelpFrame(&m_helpData);
235         m_helpWindow = frame->GetHelpWindow();
236         frame->Show(true);
237     }
238 
239     return m_helpWindow;
240 }
241 
242 #if wxUSE_CONFIG
ReadCustomization(wxConfigBase * cfg,const wxString & path)243 void wxHtmlHelpController::ReadCustomization(wxConfigBase* cfg, const wxString& path)
244 {
245     /* should not be called by the user; call UseConfig, and the controller
246      * will do the rest */
247     if (m_helpWindow && cfg)
248         m_helpWindow->ReadCustomization(cfg, path);
249 }
250 
WriteCustomization(wxConfigBase * cfg,const wxString & path)251 void wxHtmlHelpController::WriteCustomization(wxConfigBase* cfg, const wxString& path)
252 {
253     /* typically called by the controllers OnCloseFrame handler */
254     if (m_helpWindow && cfg)
255         m_helpWindow->WriteCustomization(cfg, path);
256 }
257 
UseConfig(wxConfigBase * config,const wxString & rootpath)258 void wxHtmlHelpController::UseConfig(wxConfigBase *config, const wxString& rootpath)
259 {
260     m_Config = config;
261     m_ConfigRoot = rootpath;
262     if (m_helpWindow) m_helpWindow->UseConfig(config, rootpath);
263     ReadCustomization(config, rootpath);
264 }
265 #endif // wxUSE_CONFIG
266 
267 //// Backward compatibility with wxHelpController API
268 
Initialize(const wxString & file)269 bool wxHtmlHelpController::Initialize(const wxString& file)
270 {
271     wxString dir, filename, ext;
272     wxFileName::SplitPath(file, & dir, & filename, & ext);
273 
274     if (!dir.empty())
275         dir = dir + wxFILE_SEP_PATH;
276 
277     // Try to find a suitable file
278     wxString actualFilename = dir + filename + wxString(wxT(".zip"));
279     if (!wxFileExists(actualFilename))
280     {
281         actualFilename = dir + filename + wxString(wxT(".htb"));
282         if (!wxFileExists(actualFilename))
283         {
284             actualFilename = dir + filename + wxString(wxT(".hhp"));
285             if (!wxFileExists(actualFilename))
286             {
287 #if wxUSE_LIBMSPACK
288                 actualFilename = dir + filename + wxString(wxT(".chm"));
289                 if (!wxFileExists(actualFilename))
290 #endif
291                     return false;
292             }
293         }
294     }
295     return AddBook(wxFileName(actualFilename));
296 }
297 
LoadFile(const wxString & WXUNUSED (file))298 bool wxHtmlHelpController::LoadFile(const wxString& WXUNUSED(file))
299 {
300     // Don't reload the file or we'll have it appear again, presumably.
301     return true;
302 }
303 
DisplaySection(int sectionNo)304 bool wxHtmlHelpController::DisplaySection(int sectionNo)
305 {
306     return Display(sectionNo);
307 }
308 
DisplayTextPopup(const wxString & text,const wxPoint & WXUNUSED (pos))309 bool wxHtmlHelpController::DisplayTextPopup(const wxString& text, const wxPoint& WXUNUSED(pos))
310 {
311 #if wxUSE_TIPWINDOW
312     static wxTipWindow* s_tipWindow = NULL;
313 
314     if (s_tipWindow)
315     {
316         // Prevent s_tipWindow being nulled in OnIdle,
317         // thereby removing the chance for the window to be closed by ShowHelp
318         s_tipWindow->SetTipWindowPtr(NULL);
319         s_tipWindow->Close();
320     }
321     s_tipWindow = NULL;
322 
323     if ( !text.empty() )
324     {
325         s_tipWindow = new wxTipWindow(wxTheApp->GetTopWindow(), text, 100, & s_tipWindow);
326 
327         return true;
328     }
329 #else
330     wxUnusedVar(text);
331 #endif // wxUSE_TIPWINDOW
332 
333     return false;
334 }
335 
SetHelpWindow(wxHtmlHelpWindow * helpWindow)336 void wxHtmlHelpController::SetHelpWindow(wxHtmlHelpWindow* helpWindow)
337 {
338     m_helpWindow = helpWindow;
339     if (helpWindow)
340         helpWindow->SetController(this);
341 }
342 
SetFrameParameters(const wxString & titleFormat,const wxSize & size,const wxPoint & pos,bool WXUNUSED (newFrameEachTime))343 void wxHtmlHelpController::SetFrameParameters(const wxString& titleFormat,
344                                    const wxSize& size,
345                                    const wxPoint& pos,
346                                    bool WXUNUSED(newFrameEachTime))
347 {
348     SetTitleFormat(titleFormat);
349     wxHtmlHelpFrame* frame = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpFrame);
350     wxHtmlHelpDialog* dialog = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpDialog);
351     if (frame)
352         frame->SetSize(pos.x, pos.y, size.x, size.y);
353     else if (dialog)
354         dialog->SetSize(pos.x, pos.y, size.x, size.y);
355 }
356 
GetFrameParameters(wxSize * size,wxPoint * pos,bool * newFrameEachTime)357 wxFrame* wxHtmlHelpController::GetFrameParameters(wxSize *size,
358                                    wxPoint *pos,
359                                    bool *newFrameEachTime)
360 {
361     if (newFrameEachTime)
362         (* newFrameEachTime) = false;
363 
364     wxHtmlHelpFrame* frame = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpFrame);
365     wxHtmlHelpDialog* dialog = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpDialog);
366     if (frame)
367     {
368         if (size)
369             (* size) = frame->GetSize();
370         if (pos)
371             (* pos) = frame->GetPosition();
372         return frame;
373     }
374     else if (dialog)
375     {
376         if (size)
377             (* size) = dialog->GetSize();
378         if (pos)
379             (* pos) = dialog->GetPosition();
380         return NULL;
381     }
382     return NULL;
383 }
384 
Quit()385 bool wxHtmlHelpController::Quit()
386 {
387     DestroyHelpWindow();
388     return true;
389 }
390 
391 // Make the help controller's frame 'modal' if
392 // needed
MakeModalIfNeeded()393 void wxHtmlHelpController::MakeModalIfNeeded()
394 {
395     if ((m_FrameStyle & wxHF_EMBEDDED) == 0)
396     {
397         wxHtmlHelpFrame* frame = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpFrame);
398         wxHtmlHelpDialog* dialog = wxDynamicCast(FindTopLevelWindow(), wxHtmlHelpDialog);
399         if (frame)
400             frame->AddGrabIfNeeded();
401         else if (dialog && (m_FrameStyle & wxHF_MODAL))
402         {
403             dialog->ShowModal();
404         }
405     }
406 }
407 
Display(const wxString & x)408 bool wxHtmlHelpController::Display(const wxString& x)
409 {
410     CreateHelpWindow();
411     bool success = m_helpWindow->Display(x);
412     MakeModalIfNeeded();
413     return success;
414 }
415 
Display(int id)416 bool wxHtmlHelpController::Display(int id)
417 {
418     CreateHelpWindow();
419     bool success = m_helpWindow->Display(id);
420     MakeModalIfNeeded();
421     return success;
422 }
423 
DisplayContents()424 bool wxHtmlHelpController::DisplayContents()
425 {
426     CreateHelpWindow();
427     bool success = m_helpWindow->DisplayContents();
428     MakeModalIfNeeded();
429     return success;
430 }
431 
DisplayIndex()432 bool wxHtmlHelpController::DisplayIndex()
433 {
434     CreateHelpWindow();
435     bool success = m_helpWindow->DisplayIndex();
436     MakeModalIfNeeded();
437     return success;
438 }
439 
KeywordSearch(const wxString & keyword,wxHelpSearchMode mode)440 bool wxHtmlHelpController::KeywordSearch(const wxString& keyword,
441                                          wxHelpSearchMode mode)
442 {
443     CreateHelpWindow();
444     bool success = m_helpWindow->KeywordSearch(keyword, mode);
445     MakeModalIfNeeded();
446     return success;
447 }
448 
449 /*
450  * wxHtmlModalHelp
451  * A convenience class, to use like this:
452  *
453  * wxHtmlModalHelp help(parent, helpFile, topic);
454  */
455 
wxHtmlModalHelp(wxWindow * parent,const wxString & helpFile,const wxString & topic,int style)456 wxHtmlModalHelp::wxHtmlModalHelp(wxWindow* parent, const wxString& helpFile, const wxString& topic, int style)
457 {
458     // Force some mandatory styles
459     style |= wxHF_DIALOG | wxHF_MODAL;
460 
461     wxHtmlHelpController controller(style, parent);
462     controller.Initialize(helpFile);
463 
464     if (topic.IsEmpty())
465         controller.DisplayContents();
466     else
467         controller.DisplaySection(topic);
468 }
469 
470 #endif // wxUSE_WXHTML_HELP
471 
472