1 /*
2
3 Copyright (C) 2003 - 2013 Razvan Cojocaru <rzvncj@gmail.com>
4 Mac OS specific patches contributed by Chanler White
5 <cawhite@nwrails.com>
6 "Save link as" patch contributed by Joerg Wunsch
7 <joerg_wunsch@users.sourceforge.net>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 MA 02110-1301, USA.
23
24 */
25
26
27 #include <chmhtmlwindow.h>
28 #include <chmhtmlnotebook.h>
29 #include <hhcparser.h>
30 #include <chminputstream.h>
31 #include <chmframe.h>
32 #include <wx/wx.h>
33 #include <wx/log.h>
34 #include <wx/clipbrd.h>
35 #include <wx/filename.h>
36 #include <wx/uri.h>
37 #include <wx/wfstream.h>
38 #include <memory>
39
40
41
CHMHtmlWindow(wxWindow * parent,wxTreeCtrl * tc,CHMFrame * frame)42 CHMHtmlWindow::CHMHtmlWindow(wxWindow *parent, wxTreeCtrl *tc, CHMFrame *frame)
43 : wxHtmlWindow(parent, -1, wxDefaultPosition, wxSize(200,200)),
44 _tcl(tc), _syncTree(true), _found(false), _menu(NULL),
45 _frame(frame), _fdlg(NULL)
46 {
47 _menu = new wxMenu;
48 _menu->Append(ID_PopupForward, _("For&ward"));
49 _menu->Append(ID_PopupBack, _("&Back"));
50 _menu->Append(ID_CopyLink, _("Copy &link location"));
51 _menu->Append(ID_SaveLinkAs, _("&Save link as.."));
52 _menu->Append(ID_OpenInNewTab, _("&Open in a new tab"));
53
54 _menu->AppendSeparator();
55 _menu->Append(ID_CopySel, _("&Copy selection"));
56 _menu->AppendSeparator();
57 _menu->Append(ID_PopupFind, _("&Find in page.."));
58 _menu->AppendSeparator();
59 _menu->Append(ID_PopupFullScreen, _("&Toggle fullscreen mode"));
60 }
61
62
~CHMHtmlWindow()63 CHMHtmlWindow::~CHMHtmlWindow()
64 {
65 delete _menu;
66 delete _fdlg;
67 }
68
69
LoadPage(const wxString & location)70 bool CHMHtmlWindow::LoadPage(const wxString& location)
71 {
72 wxLogNull log;
73 wxString tmp = location;
74 if(!tmp.Left(19).CmpNoCase(wxT("javascript:fullsize")))
75 tmp = tmp.AfterFirst(wxT('\'')).BeforeLast(wxT('\''));
76
77 if(_syncTree &&
78 // We should be looking for a valid page, not / (home).
79 !location.AfterLast(wxT('/')).IsEmpty() &&
80 _tcl->GetCount() > 1) {
81
82 wxFileName fwfn(tmp.AfterLast(wxT(':')).BeforeFirst(wxT('#')),
83 wxPATH_UNIX);
84 wxString cwd = GetParser()->GetFS()->
85 GetPath().AfterLast(wxT(':'));
86 fwfn.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE, cwd,
87 wxPATH_UNIX);
88
89 // Sync will call SelectItem() on the tree item
90 // if it finds one, and that in turn will call
91 // LoadPage() with _syncTree set to false.
92 Sync(_tcl->GetRootItem(), fwfn.GetFullPath(wxPATH_UNIX));
93
94 if(_found)
95 _found = false;
96 }
97 return wxHtmlWindow::LoadPage(tmp);
98 }
99
100
Sync(wxTreeItemId root,const wxString & page)101 void CHMHtmlWindow::Sync(wxTreeItemId root, const wxString& page)
102 {
103 if(_found)
104 return;
105
106 URLTreeItem *data = reinterpret_cast<URLTreeItem *>(
107 _tcl->GetItemData(root));
108
109 wxString url;
110
111 if(data)
112 url = (data->_url).BeforeFirst(wxT('#'));
113
114 if(data && !url.CmpNoCase(page)) {
115 _found = true;
116 _tcl->SelectItem(root);
117 return;
118 }
119
120 wxTreeItemIdValue cookie;
121 wxTreeItemId child = _tcl->GetFirstChild(root, cookie);
122
123 for(size_t i = 0; i < _tcl->GetChildrenCount(root, FALSE); ++i) {
124 Sync(child, page);
125 child = _tcl->GetNextChild(root, cookie);
126 }
127 }
128
129
GetPrefix(const wxString & location) const130 wxString CHMHtmlWindow::GetPrefix(const wxString& location) const
131 {
132 return location.AfterLast(wxT(':')).AfterFirst(
133 wxT('/')).BeforeLast(wxT('/'));
134 }
135
136
OnLinkClicked(const wxHtmlLinkInfo & link)137 void CHMHtmlWindow::OnLinkClicked(const wxHtmlLinkInfo& link)
138 {
139 wxString url = link.GetHref();
140
141 LoadPage(url);
142
143 if(!url.Left(7).CmpNoCase(wxT("MS-ITS:")))
144 _frame->UpdateCHMInfo();
145 }
146
147
FindFirst(wxHtmlCell * parent,const wxString & word,bool wholeWords,bool caseSensitive)148 wxHtmlCell* CHMHtmlWindow::FindFirst(wxHtmlCell *parent, const wxString& word,
149 bool wholeWords, bool caseSensitive)
150 {
151 wxString tmp = word;
152
153 if(!parent)
154 return NULL;
155
156 if(!caseSensitive)
157 tmp.MakeLower();
158
159 // If this cell is not a container, the for body will never happen
160 // (GetFirstChild() will return NULL).
161 for(wxHtmlCell *cell = parent->GetFirstChild(); cell;
162 cell = cell->GetNext()) {
163
164 wxHtmlCell *result;
165 if((result = FindFirst(cell, word, wholeWords, caseSensitive)))
166 return result;
167 }
168
169 wxHtmlSelection ws;
170 ws.Set(parent, parent);
171 wxString text = parent->ConvertToText(&ws);
172
173 if(text.IsEmpty())
174 return NULL;
175
176 if(!caseSensitive)
177 text.MakeLower();
178
179 text.Trim(TRUE);
180 text.Trim(FALSE);
181
182 bool found = false;
183
184 if(wholeWords && text == tmp) {
185 found = true;
186 } else if(!wholeWords && text.Find(tmp.c_str()) != -1) {
187 found = true;
188 }
189
190 if(found) {
191 // What is all this wxWidgets protected member crap?
192 delete m_selection;
193 m_selection = new wxHtmlSelection();
194
195 // !! Must see if this works now. !!
196 m_selection->Set(parent, parent);
197
198 int y;
199 wxHtmlCell *cell = parent;
200
201 for (y = 0; cell != NULL; cell = cell->GetParent())
202 y += cell->GetPosY();
203 Scroll(-1, y / wxHTML_SCROLL_STEP);
204 Refresh();
205
206 return parent;
207 }
208
209 return NULL;
210 }
211
212
FindNext(wxHtmlCell * start,const wxString & word,bool wholeWords,bool caseSensitive)213 wxHtmlCell* CHMHtmlWindow::FindNext(wxHtmlCell *start, const wxString& word,
214 bool wholeWords, bool caseSensitive)
215 {
216 wxHtmlCell *cell;
217
218 if(!start)
219 return NULL;
220
221 for(cell = start; cell; cell = cell->GetNext()) {
222 wxHtmlCell *result;
223 if((result = FindFirst(cell, word, wholeWords, caseSensitive)))
224 return result;
225 }
226
227 cell = start->GetParent();
228
229 while(cell && !cell->GetNext())
230 cell = cell->GetParent();
231
232 if(!cell)
233 return NULL;
234
235 return FindNext(cell->GetNext(), word, wholeWords, caseSensitive);
236 }
237
238
ClearSelection()239 void CHMHtmlWindow::ClearSelection()
240 {
241 delete m_selection;
242 m_selection = NULL;
243 Refresh();
244 }
245
OnCopy(wxCommandEvent & WXUNUSED (event))246 void CHMHtmlWindow::OnCopy(wxCommandEvent& WXUNUSED(event))
247 {
248 CopySelection();
249 }
250
251
OnFind(wxCommandEvent & WXUNUSED (event))252 void CHMHtmlWindow::OnFind(wxCommandEvent& WXUNUSED(event))
253 {
254 if(!_fdlg) {
255 wxWindow* p = GetParent();
256 while(p->GetParent())
257 p = p->GetParent();
258
259 _fdlg = new CHMFindDialog(p, this);
260 }
261
262 _fdlg->CentreOnParent();
263 _fdlg->ShowModal();
264 _fdlg->SetFocusToTextBox();
265 _fdlg->Reset();
266 }
267
268
OnForward(wxCommandEvent & WXUNUSED (event))269 void CHMHtmlWindow::OnForward(wxCommandEvent& WXUNUSED(event))
270 {
271 HistoryForward();
272 }
273
274
OnBack(wxCommandEvent & WXUNUSED (event))275 void CHMHtmlWindow::OnBack(wxCommandEvent& WXUNUSED(event))
276 {
277 HistoryBack();
278 }
279
280
OnSize(wxSizeEvent & event)281 void CHMHtmlWindow::OnSize(wxSizeEvent& event)
282 {
283 int x, y;
284 GetViewStart(&x, &y);
285
286 wxHtmlWindow::OnSize(event);
287
288 Scroll(x, y);
289 event.Skip(false);
290 }
291
292
OnCopyLink(wxCommandEvent & WXUNUSED (event))293 void CHMHtmlWindow::OnCopyLink(wxCommandEvent& WXUNUSED(event))
294 {
295 if(wxTheClipboard->Open()) {
296 wxTheClipboard->SetData(
297 new wxTextDataObject(_link));
298 wxTheClipboard->Close();
299 }
300 }
301
OnSaveLinkAs(wxCommandEvent & WXUNUSED (event))302 void CHMHtmlWindow::OnSaveLinkAs(wxCommandEvent& WXUNUSED(event))
303 {
304 std::auto_ptr<wxFSFile> f(m_FS->OpenFile(_link));
305
306 if (f.get() == NULL) {
307 ::wxMessageBox(_("OpenFile(") + _link + _(") failed"),
308 _("Error"), wxOK, this);
309 return;
310 }
311
312 wxFileName wfn(_link);
313 wxString suggestedName = wfn.GetFullName();
314
315 if(suggestedName.IsEmpty())
316 suggestedName = wxT("index.html");
317
318 wxString filename = ::wxFileSelector(_("Save as"),
319 wxT(""),
320 suggestedName,
321 wxT(""),
322 wxT("*.*"),
323 wxFD_SAVE | wxFD_OVERWRITE_PROMPT,
324 this);
325 if (!filename.empty()) {
326 wxInputStream *s = f->GetStream();
327 wxFileOutputStream out(filename);
328 if (!out.IsOk()) {
329 ::wxMessageBox(_("Error creating file ") + filename,
330 _("Error"), wxOK, this);
331 } else {
332 char buffer[4096];
333 while (!s->Eof()) {
334 s->Read(buffer, sizeof(buffer));
335 size_t nbytes = s->LastRead();
336 out.Write((void *)buffer, nbytes);
337 }
338 ::wxMessageBox(_("Saved file ") + filename,
339 _("Success"), wxOK, this);
340 }
341 }
342 }
343
344
OnRightClick(wxMouseEvent & event)345 void CHMHtmlWindow::OnRightClick(wxMouseEvent& event)
346 {
347 if(IsSelectionEnabled())
348 _menu->Enable(ID_CopySel, m_selection != NULL);
349
350 _menu->Enable(ID_PopupForward, HistoryCanForward());
351 _menu->Enable(ID_PopupBack, HistoryCanBack());
352 _menu->Enable(ID_CopyLink, false);
353 _menu->Enable(ID_SaveLinkAs, false);
354 _menu->Enable(ID_OpenInNewTab, false);
355
356 int x, y;
357 CalcUnscrolledPosition(event.m_x, event.m_y, &x, &y);
358
359 wxHtmlCell *cell = GetInternalRepresentation()->
360 FindCellByPos(x, y);
361
362 wxHtmlLinkInfo* linfo = NULL;
363
364 if(cell)
365 linfo = cell->GetLink();
366
367 if(linfo) {
368 _link = linfo->GetHref();
369 _menu->Enable(ID_CopyLink, true);
370 _menu->Enable(ID_SaveLinkAs, true);
371 _menu->Enable(ID_OpenInNewTab, true);
372 }
373
374 PopupMenu(_menu, event.GetPosition());
375 }
376
377
OnOpenInNewTab(wxCommandEvent & WXUNUSED (event))378 void CHMHtmlWindow::OnOpenInNewTab(wxCommandEvent& WXUNUSED(event))
379 {
380 wxString link = _link;
381
382 if(link.StartsWith(wxT("#"))) // anchor
383 link = GetOpenedPage() + _link;
384
385 _frame->AddHtmlView(GetParser()->GetFS()->GetPath(), link);
386 }
387
388
OnToggleFullScreen(wxCommandEvent & WXUNUSED (event))389 void CHMHtmlWindow::OnToggleFullScreen(wxCommandEvent& WXUNUSED(event))
390 {
391 _frame->ToggleFullScreen();
392 }
393
394
OnChar(wxKeyEvent & event)395 void CHMHtmlWindow::OnChar(wxKeyEvent& event)
396 {
397 int x = 0, y = 0;
398 int xUnit = 0, yUnit = 0;
399
400 switch(event.GetKeyCode()) {
401 case WXK_SPACE:
402 event.m_keyCode = WXK_PAGEDOWN;
403 break;
404 case WXK_BACK:
405 event.m_keyCode = WXK_PAGEUP;
406 break;
407 case WXK_ESCAPE:
408 _frame->ToggleFullScreen(true);
409 break;
410 case 'g':
411 case WXK_HOME:
412 Scroll(0, 0);
413 break;
414 case WXK_END:
415 case 'G':
416 GetVirtualSize(&x, &y);
417 GetScrollPixelsPerUnit(&xUnit, &yUnit);
418 Scroll(0, y / yUnit);
419 break;
420 case 'j':
421 event.m_keyCode = WXK_DOWN;
422 break;
423 case 'k':
424 event.m_keyCode = WXK_UP;
425 break;
426 case 'h':
427 event.m_keyCode = WXK_LEFT;
428 break;
429 case 'l':
430 event.m_keyCode = WXK_RIGHT;
431 break;
432 default:
433 break;
434 }
435
436 event.Skip();
437 }
438
439
OnSetTitle(const wxString & title)440 void CHMHtmlWindow::OnSetTitle(const wxString& title)
441 {
442 // Direct access to the notebook
443 // TODO: design a new event type
444 CHMHtmlNotebook* parent = dynamic_cast<CHMHtmlNotebook*>(GetParent());
445
446 if(parent)
447 parent->OnChildrenTitleChanged(title);
448
449 wxHtmlWindow::OnSetTitle(title);
450 }
451
452
453 BEGIN_EVENT_TABLE(CHMHtmlWindow, wxHtmlWindow)
454 EVT_MENU(ID_CopySel, CHMHtmlWindow::OnCopy)
455 EVT_MENU(ID_PopupFind, CHMHtmlWindow::OnFind)
456 EVT_MENU(ID_PopupForward, CHMHtmlWindow::OnForward)
457 EVT_MENU(ID_PopupBack, CHMHtmlWindow::OnBack)
458 EVT_MENU(ID_CopyLink, CHMHtmlWindow::OnCopyLink)
459 EVT_MENU(ID_SaveLinkAs, CHMHtmlWindow::OnSaveLinkAs)
460 EVT_MENU(ID_OpenInNewTab, CHMHtmlWindow::OnOpenInNewTab)
461 EVT_MENU(ID_PopupFullScreen, CHMHtmlWindow::OnToggleFullScreen)
462 EVT_CHAR(CHMHtmlWindow::OnChar)
463 EVT_RIGHT_DOWN(CHMHtmlWindow::OnRightClick)
464 EVT_SIZE(CHMHtmlWindow::OnSize)
465 END_EVENT_TABLE()
466
467
468 /*
469 Local Variables:
470 mode: c++
471 c-basic-offset: 8
472 tab-width: 8
473 c-indent-comments-syntactically-p: t
474 c-tab-always-indent: t
475 indent-tabs-mode: t
476 End:
477 */
478
479 // vim:shiftwidth=8:autoindent:tabstop=8:noexpandtab:softtabstop=8
480
481
482