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