Add missing header for wxMotif.
[wxWidgets.git] / src / html / htmlwin.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: htmlwin.cpp
3 // Purpose: wxHtmlWindow class for parsing & displaying HTML (implementation)
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 #ifdef __GNUG__
12 #pragma implementation "htmlwin.h"
13 #pragma implementation "htmlproc.h"
14 #endif
15
16 #include "wx/wxprec.h"
17
18 #include "wx/defs.h"
19 #if wxUSE_HTML && wxUSE_STREAMS
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WXPRECOMP
26 #include "wx/log.h"
27 #include "wx/intl.h"
28 #include "wx/dcclient.h"
29 #include "wx/frame.h"
30 #endif
31
32 #include "wx/html/htmlwin.h"
33 #include "wx/html/htmlproc.h"
34 #include "wx/list.h"
35 #include "wx/clipbrd.h"
36 #include "wx/dataobj.h"
37 #include "wx/timer.h"
38 #include "wx/dcmemory.h"
39
40 #include "wx/arrimpl.cpp"
41 #include "wx/listimpl.cpp"
42
43
44
45 #if wxUSE_CLIPBOARD
46 // ----------------------------------------------------------------------------
47 // wxHtmlWinAutoScrollTimer: the timer used to generate a stream of scroll
48 // events when a captured mouse is held outside the window
49 // ----------------------------------------------------------------------------
50
51 class wxHtmlWinAutoScrollTimer : public wxTimer
52 {
53 public:
54 wxHtmlWinAutoScrollTimer(wxScrolledWindow *win,
55 wxEventType eventTypeToSend,
56 int pos, int orient)
57 {
58 m_win = win;
59 m_eventType = eventTypeToSend;
60 m_pos = pos;
61 m_orient = orient;
62 }
63
64 virtual void Notify();
65
66 private:
67 wxScrolledWindow *m_win;
68 wxEventType m_eventType;
69 int m_pos,
70 m_orient;
71
72 DECLARE_NO_COPY_CLASS(wxHtmlWinAutoScrollTimer)
73 };
74
75 void wxHtmlWinAutoScrollTimer::Notify()
76 {
77 // only do all this as long as the window is capturing the mouse
78 if ( wxWindow::GetCapture() != m_win )
79 {
80 Stop();
81 }
82 else // we still capture the mouse, continue generating events
83 {
84 // first scroll the window if we are allowed to do it
85 wxScrollWinEvent event1(m_eventType, m_pos, m_orient);
86 event1.SetEventObject(m_win);
87 if ( m_win->GetEventHandler()->ProcessEvent(event1) )
88 {
89 // and then send a pseudo mouse-move event to refresh the selection
90 wxMouseEvent event2(wxEVT_MOTION);
91 wxGetMousePosition(&event2.m_x, &event2.m_y);
92
93 // the mouse event coordinates should be client, not screen as
94 // returned by wxGetMousePosition
95 wxWindow *parentTop = m_win;
96 while ( parentTop->GetParent() )
97 parentTop = parentTop->GetParent();
98 wxPoint ptOrig = parentTop->GetPosition();
99 event2.m_x -= ptOrig.x;
100 event2.m_y -= ptOrig.y;
101
102 event2.SetEventObject(m_win);
103
104 // FIXME: we don't fill in the other members - ok?
105 m_win->GetEventHandler()->ProcessEvent(event2);
106 }
107 else // can't scroll further, stop
108 {
109 Stop();
110 }
111 }
112 }
113 #endif
114
115
116
117 //-----------------------------------------------------------------------------
118 // wxHtmlHistoryItem
119 //-----------------------------------------------------------------------------
120
121 // item of history list
122 class WXDLLEXPORT wxHtmlHistoryItem
123 {
124 public:
125 wxHtmlHistoryItem(const wxString& p, const wxString& a) {m_Page = p, m_Anchor = a, m_Pos = 0;}
126 int GetPos() const {return m_Pos;}
127 void SetPos(int p) {m_Pos = p;}
128 const wxString& GetPage() const {return m_Page;}
129 const wxString& GetAnchor() const {return m_Anchor;}
130
131 private:
132 wxString m_Page;
133 wxString m_Anchor;
134 int m_Pos;
135 };
136
137
138 //-----------------------------------------------------------------------------
139 // our private arrays:
140 //-----------------------------------------------------------------------------
141
142 WX_DECLARE_OBJARRAY(wxHtmlHistoryItem, wxHtmlHistoryArray);
143 WX_DEFINE_OBJARRAY(wxHtmlHistoryArray);
144
145 WX_DECLARE_LIST(wxHtmlProcessor, wxHtmlProcessorList);
146 WX_DEFINE_LIST(wxHtmlProcessorList);
147
148 //-----------------------------------------------------------------------------
149 // wxHtmlWindow
150 //-----------------------------------------------------------------------------
151
152
153 void wxHtmlWindow::Init()
154 {
155 m_tmpMouseMoved = FALSE;
156 m_tmpLastLink = NULL;
157 m_tmpLastCell = NULL;
158 m_tmpCanDrawLocks = 0;
159 m_FS = new wxFileSystem();
160 m_RelatedStatusBar = -1;
161 m_RelatedFrame = NULL;
162 m_TitleFormat = wxT("%s");
163 m_OpenedPage = m_OpenedAnchor = m_OpenedPageTitle = wxEmptyString;
164 m_Cell = NULL;
165 m_Parser = new wxHtmlWinParser(this);
166 m_Parser->SetFS(m_FS);
167 m_HistoryPos = -1;
168 m_HistoryOn = TRUE;
169 m_History = new wxHtmlHistoryArray;
170 m_Processors = NULL;
171 m_Style = 0;
172 SetBorders(10);
173 m_selection = NULL;
174 m_makingSelection = false;
175 #if wxUSE_CLIPBOARD
176 m_timerAutoScroll = NULL;
177 #endif
178 m_backBuffer = NULL;
179 }
180
181 bool wxHtmlWindow::Create(wxWindow *parent, wxWindowID id,
182 const wxPoint& pos, const wxSize& size,
183 long style, const wxString& name)
184 {
185 if (!wxScrolledWindow::Create(parent, id, pos, size,
186 style | wxVSCROLL | wxHSCROLL, name))
187 return FALSE;
188
189 m_Style = style;
190 SetPage(wxT("<html><body></body></html>"));
191 return TRUE;
192 }
193
194
195 wxHtmlWindow::~wxHtmlWindow()
196 {
197 #if wxUSE_CLIPBOARD
198 StopAutoScrolling();
199 #endif
200 HistoryClear();
201
202 if (m_Cell) delete m_Cell;
203
204 delete m_Parser;
205 delete m_FS;
206 delete m_History;
207 delete m_Processors;
208 delete m_backBuffer;
209 }
210
211
212
213 void wxHtmlWindow::SetRelatedFrame(wxFrame* frame, const wxString& format)
214 {
215 m_RelatedFrame = frame;
216 m_TitleFormat = format;
217 }
218
219
220
221 void wxHtmlWindow::SetRelatedStatusBar(int bar)
222 {
223 m_RelatedStatusBar = bar;
224 }
225
226
227
228 void wxHtmlWindow::SetFonts(wxString normal_face, wxString fixed_face, const int *sizes)
229 {
230 wxString op = m_OpenedPage;
231
232 m_Parser->SetFonts(normal_face, fixed_face, sizes);
233 // fonts changed => contents invalid, so reload the page:
234 SetPage(wxT("<html><body></body></html>"));
235 if (!op.IsEmpty()) LoadPage(op);
236 }
237
238
239
240 bool wxHtmlWindow::SetPage(const wxString& source)
241 {
242 wxString newsrc(source);
243
244 wxDELETE(m_selection);
245
246 // pass HTML through registered processors:
247 if (m_Processors || m_GlobalProcessors)
248 {
249 wxHtmlProcessorList::Node *nodeL, *nodeG;
250 int prL, prG;
251
252 nodeL = (m_Processors) ? m_Processors->GetFirst() : NULL;
253 nodeG = (m_GlobalProcessors) ? m_GlobalProcessors->GetFirst() : NULL;
254
255 // VS: there are two lists, global and local, both of them sorted by
256 // priority. Since we have to go through _both_ lists with
257 // decreasing priority, we "merge-sort" the lists on-line by
258 // processing that one of the two heads that has higher priority
259 // in every iteration
260 while (nodeL || nodeG)
261 {
262 prL = (nodeL) ? nodeL->GetData()->GetPriority() : -1;
263 prG = (nodeG) ? nodeG->GetData()->GetPriority() : -1;
264 if (prL > prG)
265 {
266 if (nodeL->GetData()->IsEnabled())
267 newsrc = nodeL->GetData()->Process(newsrc);
268 nodeL = nodeL->GetNext();
269 }
270 else // prL <= prG
271 {
272 if (nodeG->GetData()->IsEnabled())
273 newsrc = nodeG->GetData()->Process(newsrc);
274 nodeG = nodeG->GetNext();
275 }
276 }
277 }
278
279 // ...and run the parser on it:
280 wxClientDC *dc = new wxClientDC(this);
281 dc->SetMapMode(wxMM_TEXT);
282 SetBackgroundColour(wxColour(0xFF, 0xFF, 0xFF));
283 m_OpenedPage = m_OpenedAnchor = m_OpenedPageTitle = wxEmptyString;
284 m_Parser->SetDC(dc);
285 if (m_Cell)
286 {
287 delete m_Cell;
288 m_Cell = NULL;
289 }
290 m_Cell = (wxHtmlContainerCell*) m_Parser->Parse(newsrc);
291 delete dc;
292 m_Cell->SetIndent(m_Borders, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
293 m_Cell->SetAlignHor(wxHTML_ALIGN_CENTER);
294 CreateLayout();
295 if (m_tmpCanDrawLocks == 0)
296 Refresh();
297 return TRUE;
298 }
299
300 bool wxHtmlWindow::AppendToPage(const wxString& source)
301 {
302 return SetPage(*(GetParser()->GetSource()) + source);
303 }
304
305 bool wxHtmlWindow::LoadPage(const wxString& location)
306 {
307 wxBusyCursor busyCursor;
308
309 wxFSFile *f;
310 bool rt_val;
311 bool needs_refresh = FALSE;
312
313 m_tmpCanDrawLocks++;
314 if (m_HistoryOn && (m_HistoryPos != -1))
315 {
316 // store scroll position into history item:
317 int x, y;
318 GetViewStart(&x, &y);
319 (*m_History)[m_HistoryPos].SetPos(y);
320 }
321
322 if (location[0] == wxT('#'))
323 {
324 // local anchor:
325 wxString anch = location.Mid(1) /*1 to end*/;
326 m_tmpCanDrawLocks--;
327 rt_val = ScrollToAnchor(anch);
328 m_tmpCanDrawLocks++;
329 }
330 else if (location.Find(wxT('#')) != wxNOT_FOUND && location.BeforeFirst(wxT('#')) == m_OpenedPage)
331 {
332 wxString anch = location.AfterFirst(wxT('#'));
333 m_tmpCanDrawLocks--;
334 rt_val = ScrollToAnchor(anch);
335 m_tmpCanDrawLocks++;
336 }
337 else if (location.Find(wxT('#')) != wxNOT_FOUND &&
338 (m_FS->GetPath() + location.BeforeFirst(wxT('#'))) == m_OpenedPage)
339 {
340 wxString anch = location.AfterFirst(wxT('#'));
341 m_tmpCanDrawLocks--;
342 rt_val = ScrollToAnchor(anch);
343 m_tmpCanDrawLocks++;
344 }
345
346 else
347 {
348 needs_refresh = TRUE;
349 // load&display it:
350 if (m_RelatedStatusBar != -1)
351 {
352 m_RelatedFrame->SetStatusText(_("Connecting..."), m_RelatedStatusBar);
353 Refresh(FALSE);
354 }
355
356 f = m_Parser->OpenURL(wxHTML_URL_PAGE, location);
357
358 // try to interpret 'location' as filename instead of URL:
359 if (f == NULL)
360 {
361 wxFileName fn(location);
362 wxString location2 = wxFileSystem::FileNameToURL(fn);
363 f = m_Parser->OpenURL(wxHTML_URL_PAGE, location2);
364 }
365
366 if (f == NULL)
367 {
368 wxLogError(_("Unable to open requested HTML document: %s"), location.c_str());
369 m_tmpCanDrawLocks--;
370 return FALSE;
371 }
372
373 else
374 {
375 wxNode *node;
376 wxString src = wxEmptyString;
377
378 if (m_RelatedStatusBar != -1)
379 {
380 wxString msg = _("Loading : ") + location;
381 m_RelatedFrame->SetStatusText(msg, m_RelatedStatusBar);
382 Refresh(FALSE);
383 }
384
385 node = m_Filters.GetFirst();
386 while (node)
387 {
388 wxHtmlFilter *h = (wxHtmlFilter*) node->GetData();
389 if (h->CanRead(*f))
390 {
391 src = h->ReadFile(*f);
392 break;
393 }
394 node = node->GetNext();
395 }
396 if (src == wxEmptyString)
397 {
398 if (m_DefaultFilter == NULL) m_DefaultFilter = GetDefaultFilter();
399 src = m_DefaultFilter->ReadFile(*f);
400 }
401
402 m_FS->ChangePathTo(f->GetLocation());
403 rt_val = SetPage(src);
404 m_OpenedPage = f->GetLocation();
405 if (f->GetAnchor() != wxEmptyString)
406 {
407 ScrollToAnchor(f->GetAnchor());
408 }
409
410 delete f;
411
412 if (m_RelatedStatusBar != -1) m_RelatedFrame->SetStatusText(_("Done"), m_RelatedStatusBar);
413 }
414 }
415
416 if (m_HistoryOn) // add this page to history there:
417 {
418 int c = m_History->GetCount() - (m_HistoryPos + 1);
419
420 if (m_HistoryPos < 0 ||
421 (*m_History)[m_HistoryPos].GetPage() != m_OpenedPage ||
422 (*m_History)[m_HistoryPos].GetAnchor() != m_OpenedAnchor)
423 {
424 m_HistoryPos++;
425 for (int i = 0; i < c; i++)
426 m_History->RemoveAt(m_HistoryPos);
427 m_History->Add(new wxHtmlHistoryItem(m_OpenedPage, m_OpenedAnchor));
428 }
429 }
430
431 if (m_OpenedPageTitle == wxEmptyString)
432 OnSetTitle(wxFileNameFromPath(m_OpenedPage));
433
434 if (needs_refresh)
435 {
436 m_tmpCanDrawLocks--;
437 Refresh();
438 }
439 else
440 m_tmpCanDrawLocks--;
441
442 return rt_val;
443 }
444
445
446 bool wxHtmlWindow::LoadFile(const wxFileName& filename)
447 {
448 wxString url = wxFileSystem::FileNameToURL(filename);
449 return LoadPage(url);
450 }
451
452
453 bool wxHtmlWindow::ScrollToAnchor(const wxString& anchor)
454 {
455 const wxHtmlCell *c = m_Cell->Find(wxHTML_COND_ISANCHOR, &anchor);
456 if (!c)
457 {
458 wxLogWarning(_("HTML anchor %s does not exist."), anchor.c_str());
459 return FALSE;
460 }
461 else
462 {
463 int y;
464
465 for (y = 0; c != NULL; c = c->GetParent()) y += c->GetPosY();
466 Scroll(-1, y / wxHTML_SCROLL_STEP);
467 m_OpenedAnchor = anchor;
468 return TRUE;
469 }
470 }
471
472
473 void wxHtmlWindow::OnSetTitle(const wxString& title)
474 {
475 if (m_RelatedFrame)
476 {
477 wxString tit;
478 tit.Printf(m_TitleFormat, title.c_str());
479 m_RelatedFrame->SetTitle(tit);
480 }
481 m_OpenedPageTitle = title;
482 }
483
484
485
486
487
488 void wxHtmlWindow::CreateLayout()
489 {
490 int ClientWidth, ClientHeight;
491
492 if (!m_Cell) return;
493
494 if (m_Style & wxHW_SCROLLBAR_NEVER)
495 {
496 SetScrollbars(wxHTML_SCROLL_STEP, 1, m_Cell->GetWidth() / wxHTML_SCROLL_STEP, 0); // always off
497 GetClientSize(&ClientWidth, &ClientHeight);
498 m_Cell->Layout(ClientWidth);
499 }
500
501 else {
502 GetClientSize(&ClientWidth, &ClientHeight);
503 m_Cell->Layout(ClientWidth);
504 if (ClientHeight < m_Cell->GetHeight() + GetCharHeight())
505 {
506 SetScrollbars(
507 wxHTML_SCROLL_STEP, wxHTML_SCROLL_STEP,
508 m_Cell->GetWidth() / wxHTML_SCROLL_STEP,
509 (m_Cell->GetHeight() + GetCharHeight()) / wxHTML_SCROLL_STEP
510 /*cheat: top-level frag is always container*/);
511 }
512 else /* we fit into window, no need for scrollbars */
513 {
514 SetScrollbars(wxHTML_SCROLL_STEP, 1, m_Cell->GetWidth() / wxHTML_SCROLL_STEP, 0); // disable...
515 GetClientSize(&ClientWidth, &ClientHeight);
516 m_Cell->Layout(ClientWidth); // ...and relayout
517 }
518 }
519 }
520
521
522
523 void wxHtmlWindow::ReadCustomization(wxConfigBase *cfg, wxString path)
524 {
525 wxString oldpath;
526 wxString tmp;
527 int p_fontsizes[7];
528 wxString p_fff, p_ffn;
529
530 if (path != wxEmptyString)
531 {
532 oldpath = cfg->GetPath();
533 cfg->SetPath(path);
534 }
535
536 m_Borders = cfg->Read(wxT("wxHtmlWindow/Borders"), m_Borders);
537 p_fff = cfg->Read(wxT("wxHtmlWindow/FontFaceFixed"), m_Parser->m_FontFaceFixed);
538 p_ffn = cfg->Read(wxT("wxHtmlWindow/FontFaceNormal"), m_Parser->m_FontFaceNormal);
539 for (int i = 0; i < 7; i++)
540 {
541 tmp.Printf(wxT("wxHtmlWindow/FontsSize%i"), i);
542 p_fontsizes[i] = cfg->Read(tmp, m_Parser->m_FontsSizes[i]);
543 }
544 SetFonts(p_ffn, p_fff, p_fontsizes);
545
546 if (path != wxEmptyString)
547 cfg->SetPath(oldpath);
548 }
549
550
551
552 void wxHtmlWindow::WriteCustomization(wxConfigBase *cfg, wxString path)
553 {
554 wxString oldpath;
555 wxString tmp;
556
557 if (path != wxEmptyString)
558 {
559 oldpath = cfg->GetPath();
560 cfg->SetPath(path);
561 }
562
563 cfg->Write(wxT("wxHtmlWindow/Borders"), (long) m_Borders);
564 cfg->Write(wxT("wxHtmlWindow/FontFaceFixed"), m_Parser->m_FontFaceFixed);
565 cfg->Write(wxT("wxHtmlWindow/FontFaceNormal"), m_Parser->m_FontFaceNormal);
566 for (int i = 0; i < 7; i++)
567 {
568 tmp.Printf(wxT("wxHtmlWindow/FontsSize%i"), i);
569 cfg->Write(tmp, (long) m_Parser->m_FontsSizes[i]);
570 }
571
572 if (path != wxEmptyString)
573 cfg->SetPath(oldpath);
574 }
575
576
577
578 bool wxHtmlWindow::HistoryBack()
579 {
580 wxString a, l;
581
582 if (m_HistoryPos < 1) return FALSE;
583
584 // store scroll position into history item:
585 int x, y;
586 GetViewStart(&x, &y);
587 (*m_History)[m_HistoryPos].SetPos(y);
588
589 // go to previous position:
590 m_HistoryPos--;
591
592 l = (*m_History)[m_HistoryPos].GetPage();
593 a = (*m_History)[m_HistoryPos].GetAnchor();
594 m_HistoryOn = FALSE;
595 m_tmpCanDrawLocks++;
596 if (a == wxEmptyString) LoadPage(l);
597 else LoadPage(l + wxT("#") + a);
598 m_HistoryOn = TRUE;
599 m_tmpCanDrawLocks--;
600 Scroll(0, (*m_History)[m_HistoryPos].GetPos());
601 Refresh();
602 return TRUE;
603 }
604
605 bool wxHtmlWindow::HistoryCanBack()
606 {
607 if (m_HistoryPos < 1) return FALSE;
608 return TRUE ;
609 }
610
611
612 bool wxHtmlWindow::HistoryForward()
613 {
614 wxString a, l;
615
616 if (m_HistoryPos == -1) return FALSE;
617 if (m_HistoryPos >= (int)m_History->GetCount() - 1)return FALSE;
618
619 m_OpenedPage = wxEmptyString; // this will disable adding new entry into history in LoadPage()
620
621 m_HistoryPos++;
622 l = (*m_History)[m_HistoryPos].GetPage();
623 a = (*m_History)[m_HistoryPos].GetAnchor();
624 m_HistoryOn = FALSE;
625 m_tmpCanDrawLocks++;
626 if (a == wxEmptyString) LoadPage(l);
627 else LoadPage(l + wxT("#") + a);
628 m_HistoryOn = TRUE;
629 m_tmpCanDrawLocks--;
630 Scroll(0, (*m_History)[m_HistoryPos].GetPos());
631 Refresh();
632 return TRUE;
633 }
634
635 bool wxHtmlWindow::HistoryCanForward()
636 {
637 if (m_HistoryPos == -1) return FALSE;
638 if (m_HistoryPos >= (int)m_History->GetCount() - 1)return FALSE;
639 return TRUE ;
640 }
641
642
643 void wxHtmlWindow::HistoryClear()
644 {
645 m_History->Empty();
646 m_HistoryPos = -1;
647 }
648
649 void wxHtmlWindow::AddProcessor(wxHtmlProcessor *processor)
650 {
651 if (!m_Processors)
652 {
653 m_Processors = new wxHtmlProcessorList;
654 m_Processors->DeleteContents(TRUE);
655 }
656 wxHtmlProcessorList::Node *node;
657
658 for (node = m_Processors->GetFirst(); node; node = node->GetNext())
659 {
660 if (processor->GetPriority() > node->GetData()->GetPriority())
661 {
662 m_Processors->Insert(node, processor);
663 return;
664 }
665 }
666 m_Processors->Append(processor);
667 }
668
669 /*static */ void wxHtmlWindow::AddGlobalProcessor(wxHtmlProcessor *processor)
670 {
671 if (!m_GlobalProcessors)
672 {
673 m_GlobalProcessors = new wxHtmlProcessorList;
674 m_GlobalProcessors->DeleteContents(TRUE);
675 }
676 wxHtmlProcessorList::Node *node;
677
678 for (node = m_GlobalProcessors->GetFirst(); node; node = node->GetNext())
679 {
680 if (processor->GetPriority() > node->GetData()->GetPriority())
681 {
682 m_GlobalProcessors->Insert(node, processor);
683 return;
684 }
685 }
686 m_GlobalProcessors->Append(processor);
687 }
688
689
690
691 wxList wxHtmlWindow::m_Filters;
692 wxHtmlFilter *wxHtmlWindow::m_DefaultFilter = NULL;
693 wxCursor *wxHtmlWindow::s_cur_hand = NULL;
694 wxCursor *wxHtmlWindow::s_cur_arrow = NULL;
695 wxHtmlProcessorList *wxHtmlWindow::m_GlobalProcessors = NULL;
696
697 void wxHtmlWindow::CleanUpStatics()
698 {
699 wxDELETE(m_DefaultFilter);
700 m_Filters.DeleteContents(TRUE);
701 m_Filters.Clear();
702 wxDELETE(m_GlobalProcessors);
703 wxDELETE(s_cur_hand);
704 wxDELETE(s_cur_arrow);
705 }
706
707
708
709 void wxHtmlWindow::AddFilter(wxHtmlFilter *filter)
710 {
711 m_Filters.Append(filter);
712 }
713
714
715 bool wxHtmlWindow::IsSelectionEnabled() const
716 {
717 #if wxUSE_CLIPBOARD
718 return !(m_Style & wxHW_NO_SELECTION);
719 #else
720 return false;
721 #endif
722 }
723
724
725 #if wxUSE_CLIPBOARD
726 wxString wxHtmlWindow::SelectionToText()
727 {
728 if ( !m_selection )
729 return wxEmptyString;
730
731 wxClientDC dc(this);
732
733 const wxHtmlCell *end = m_selection->GetToCell();
734 wxString text;
735 wxHtmlTerminalCellsInterator i(m_selection->GetFromCell(), end);
736 if ( i )
737 {
738 text << i->ConvertToText(m_selection);
739 ++i;
740 }
741 const wxHtmlCell *prev = *i;
742 while ( i )
743 {
744 if ( prev->GetParent() != i->GetParent() )
745 text << _T('\n');
746 text << i->ConvertToText(*i == end ? m_selection : NULL);
747 prev = *i;
748 ++i;
749 }
750 return text;
751 }
752
753 void wxHtmlWindow::CopySelection(ClipboardType t)
754 {
755 if ( m_selection )
756 {
757 wxTheClipboard->UsePrimarySelection(t == Primary);
758 wxString txt(SelectionToText());
759 if ( wxTheClipboard->Open() )
760 {
761 wxTheClipboard->SetData(new wxTextDataObject(txt));
762 wxTheClipboard->Close();
763 wxLogTrace(_T("wxhtmlselection"),
764 _("Copied to clipboard:\"%s\""), txt.c_str());
765 }
766 }
767 }
768 #endif
769
770
771 void wxHtmlWindow::OnLinkClicked(const wxHtmlLinkInfo& link)
772 {
773 const wxMouseEvent *e = link.GetEvent();
774 if (e == NULL || e->LeftUp())
775 LoadPage(link.GetHref());
776 }
777
778 void wxHtmlWindow::OnCellClicked(wxHtmlCell *cell,
779 wxCoord x, wxCoord y,
780 const wxMouseEvent& event)
781 {
782 wxCHECK_RET( cell, _T("can't be called with NULL cell") );
783
784 cell->OnMouseClick(this, x, y, event);
785 }
786
787 void wxHtmlWindow::OnCellMouseHover(wxHtmlCell * WXUNUSED(cell),
788 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
789 {
790 // do nothing here
791 }
792
793 void wxHtmlWindow::OnEraseBackground(wxEraseEvent& event)
794 {
795 }
796
797 void wxHtmlWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
798 {
799 wxPaintDC dc(this);
800
801 if (m_tmpCanDrawLocks > 0 || m_Cell == NULL) return;
802
803 int x, y;
804 GetViewStart(&x, &y);
805 wxRect rect = GetUpdateRegion().GetBox();
806 wxSize sz = GetSize();
807
808 wxMemoryDC dcm;
809 if ( !m_backBuffer )
810 m_backBuffer = new wxBitmap(sz.x, sz.y);
811 dcm.SelectObject(*m_backBuffer);
812 dcm.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
813 dcm.Clear();
814 PrepareDC(dcm);
815 dcm.SetMapMode(wxMM_TEXT);
816 dcm.SetBackgroundMode(wxTRANSPARENT);
817
818 wxHtmlRenderingInfo rinfo;
819 wxDefaultHtmlRenderingStyle rstyle;
820 rinfo.SetSelection(m_selection);
821 rinfo.SetStyle(&rstyle);
822 m_Cell->Draw(dcm, 0, 0,
823 y * wxHTML_SCROLL_STEP + rect.GetTop(),
824 y * wxHTML_SCROLL_STEP + rect.GetBottom(),
825 rinfo);
826
827 dcm.SetDeviceOrigin(0,0);
828 dc.Blit(0, rect.GetTop(),
829 sz.x, rect.GetBottom() - rect.GetTop() + 1,
830 &dcm,
831 0, rect.GetTop());
832 }
833
834
835
836
837 void wxHtmlWindow::OnSize(wxSizeEvent& event)
838 {
839 wxDELETE(m_backBuffer);
840
841 wxScrolledWindow::OnSize(event);
842 CreateLayout();
843
844 // Recompute selection if necessary:
845 if ( m_selection )
846 {
847 m_selection->Set(m_selection->GetFromCell(),
848 m_selection->GetToCell());
849 m_selection->ClearPrivPos();
850 }
851
852 Refresh();
853 }
854
855
856 void wxHtmlWindow::OnMouseMove(wxMouseEvent& event)
857 {
858 m_tmpMouseMoved = true;
859 }
860
861 void wxHtmlWindow::OnMouseDown(wxMouseEvent& event)
862 {
863 if ( event.LeftDown() && IsSelectionEnabled() )
864 {
865 m_makingSelection = true;
866
867 if ( m_selection )
868 {
869 wxDELETE(m_selection);
870 Refresh();
871 }
872 m_tmpSelFromPos = CalcUnscrolledPosition(event.GetPosition());
873 m_tmpSelFromCell = NULL;
874
875 CaptureMouse();
876 }
877 }
878
879 void wxHtmlWindow::OnMouseUp(wxMouseEvent& event)
880 {
881 #if wxUSE_CLIPBOARD
882 if ( m_makingSelection )
883 {
884 ReleaseMouse();
885 m_makingSelection = false;
886
887 // did the user move the mouse far enough from starting point?
888 if ( m_selection )
889 {
890 #ifdef __UNIX__
891 CopySelection(Primary);
892 #endif
893 // we don't want mouse up event that ended selecting to be
894 // handled as mouse click and e.g. follow hyperlink:
895 return;
896 }
897 }
898 #endif
899
900 SetFocus();
901 if ( m_Cell )
902 {
903 wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
904 wxHtmlCell *cell = m_Cell->FindCellByPos(pos.x, pos.y);
905
906 // VZ: is it possible that we don't find anything at all?
907 // VS: yes. FindCellByPos returns terminal cell and
908 // containers may have empty borders
909 if ( cell )
910 OnCellClicked(cell, pos.x, pos.y, event);
911 }
912 }
913
914
915
916 void wxHtmlWindow::OnIdle(wxIdleEvent& WXUNUSED(event))
917 {
918 if (s_cur_hand == NULL)
919 {
920 s_cur_hand = new wxCursor(wxCURSOR_HAND);
921 s_cur_arrow = new wxCursor(wxCURSOR_ARROW);
922 }
923
924 if (m_tmpMouseMoved && (m_Cell != NULL))
925 {
926 int xc, yc, x, y;
927 wxGetMousePosition(&xc, &yc);
928 ScreenToClient(&xc, &yc);
929 CalcUnscrolledPosition(xc, yc, &x, &y);
930
931 wxHtmlCell *cell = m_Cell->FindCellByPos(x, y);
932
933 // handle selection update:
934 if ( m_makingSelection )
935 {
936 bool goingDown = m_tmpSelFromPos.y < y ||
937 m_tmpSelFromPos.y == y && m_tmpSelFromPos.x < x;
938
939 if ( !m_tmpSelFromCell )
940 {
941 if (goingDown)
942 {
943 m_tmpSelFromCell = m_Cell->FindCellByPos(
944 m_tmpSelFromPos.x,m_tmpSelFromPos.y,
945 wxHTML_FIND_NEAREST_AFTER);
946 if (!m_tmpSelFromCell)
947 m_tmpSelFromCell = m_Cell->GetFirstTerminal();
948 }
949 else
950 {
951 m_tmpSelFromCell = m_Cell->FindCellByPos(
952 m_tmpSelFromPos.x,m_tmpSelFromPos.y,
953 wxHTML_FIND_NEAREST_BEFORE);
954 if (!m_tmpSelFromCell)
955 m_tmpSelFromCell = m_Cell->GetLastTerminal();
956 }
957 }
958
959 wxHtmlCell *selcell = cell;
960 if (!selcell)
961 {
962 if (goingDown)
963 {
964 selcell = m_Cell->FindCellByPos(x, y,
965 wxHTML_FIND_NEAREST_AFTER);
966 if (!selcell)
967 selcell = m_Cell->GetLastTerminal();
968 }
969 else
970 {
971 selcell = m_Cell->FindCellByPos(x, y,
972 wxHTML_FIND_NEAREST_BEFORE);
973 if (!selcell)
974 selcell = m_Cell->GetFirstTerminal();
975 }
976 }
977
978 // NB: it may *rarely* happen that the code above didn't find one
979 // of the cells, e.g. if wxHtmlWindow doesn't contain any
980 // visible cells.
981 if ( selcell && m_tmpSelFromCell )
982 {
983 if ( !m_selection )
984 {
985 // start selecting only if mouse movement was big enough
986 // (otherwise it was meant as mouse click, not selection):
987 const int PRECISION = 2;
988 wxPoint diff = m_tmpSelFromPos - wxPoint(x,y);
989 if (abs(diff.x) > PRECISION || abs(diff.y) > PRECISION)
990 {
991 m_selection = new wxHtmlSelection();
992 }
993 }
994 if ( m_selection )
995 {
996 if ( m_tmpSelFromCell->IsBefore(selcell) )
997 {
998 m_selection->Set(m_tmpSelFromPos, m_tmpSelFromCell,
999 wxPoint(x,y), selcell); }
1000 else
1001 {
1002 m_selection->Set(wxPoint(x,y), selcell,
1003 m_tmpSelFromPos, m_tmpSelFromCell);
1004 }
1005 m_selection->ClearPrivPos();
1006 Refresh();
1007 }
1008 }
1009 }
1010
1011 // handle cursor and status bar text changes:
1012 if ( cell != m_tmpLastCell )
1013 {
1014 wxHtmlLinkInfo *lnk = cell ? cell->GetLink(x, y) : NULL;
1015
1016 if (lnk != m_tmpLastLink)
1017 {
1018 if (lnk == NULL)
1019 {
1020 SetCursor(*s_cur_arrow);
1021 if (m_RelatedStatusBar != -1)
1022 m_RelatedFrame->SetStatusText(wxEmptyString,
1023 m_RelatedStatusBar);
1024 }
1025 else
1026 {
1027 SetCursor(*s_cur_hand);
1028 if (m_RelatedStatusBar != -1)
1029 m_RelatedFrame->SetStatusText(lnk->GetHref(),
1030 m_RelatedStatusBar);
1031 }
1032 m_tmpLastLink = lnk;
1033 }
1034
1035 m_tmpLastCell = cell;
1036 }
1037 else // mouse moved but stayed in the same cell
1038 {
1039 if ( cell )
1040 OnCellMouseHover(cell, x, y);
1041 }
1042
1043 m_tmpMouseMoved = FALSE;
1044 }
1045 }
1046
1047 #if wxUSE_CLIPBOARD
1048 void wxHtmlWindow::StopAutoScrolling()
1049 {
1050 if ( m_timerAutoScroll )
1051 {
1052 wxDELETE(m_timerAutoScroll);
1053 }
1054 }
1055
1056 void wxHtmlWindow::OnMouseEnter(wxMouseEvent& event)
1057 {
1058 StopAutoScrolling();
1059 event.Skip();
1060 }
1061
1062 void wxHtmlWindow::OnMouseLeave(wxMouseEvent& event)
1063 {
1064 // don't prevent the usual processing of the event from taking place
1065 event.Skip();
1066
1067 // when a captured mouse leave a scrolled window we start generate
1068 // scrolling events to allow, for example, extending selection beyond the
1069 // visible area in some controls
1070 if ( wxWindow::GetCapture() == this )
1071 {
1072 // where is the mouse leaving?
1073 int pos, orient;
1074 wxPoint pt = event.GetPosition();
1075 if ( pt.x < 0 )
1076 {
1077 orient = wxHORIZONTAL;
1078 pos = 0;
1079 }
1080 else if ( pt.y < 0 )
1081 {
1082 orient = wxVERTICAL;
1083 pos = 0;
1084 }
1085 else // we're lower or to the right of the window
1086 {
1087 wxSize size = GetClientSize();
1088 if ( pt.x > size.x )
1089 {
1090 orient = wxHORIZONTAL;
1091 pos = GetVirtualSize().x / wxHTML_SCROLL_STEP;
1092 }
1093 else if ( pt.y > size.y )
1094 {
1095 orient = wxVERTICAL;
1096 pos = GetVirtualSize().y / wxHTML_SCROLL_STEP;
1097 }
1098 else // this should be impossible
1099 {
1100 // but seems to happen sometimes under wxMSW - maybe it's a bug
1101 // there but for now just ignore it
1102
1103 //wxFAIL_MSG( _T("can't understand where has mouse gone") );
1104
1105 return;
1106 }
1107 }
1108
1109 // only start the auto scroll timer if the window can be scrolled in
1110 // this direction
1111 if ( !HasScrollbar(orient) )
1112 return;
1113
1114 delete m_timerAutoScroll;
1115 m_timerAutoScroll = new wxHtmlWinAutoScrollTimer
1116 (
1117 this,
1118 pos == 0 ? wxEVT_SCROLLWIN_LINEUP
1119 : wxEVT_SCROLLWIN_LINEDOWN,
1120 pos,
1121 orient
1122 );
1123 m_timerAutoScroll->Start(50); // FIXME: make configurable
1124 }
1125 }
1126
1127 void wxHtmlWindow::OnKeyUp(wxKeyEvent& event)
1128 {
1129 if ( IsSelectionEnabled() &&
1130 event.GetKeyCode() == 'C' && event.ControlDown() )
1131 {
1132 if ( m_selection )
1133 CopySelection();
1134 }
1135 }
1136
1137 void wxHtmlWindow::OnCopy(wxCommandEvent& event)
1138 {
1139 if ( m_selection )
1140 CopySelection();
1141 }
1142
1143 void wxHtmlWindow::OnDoubleClick(wxMouseEvent& event)
1144 {
1145 // select word under cursor:
1146 if ( IsSelectionEnabled() )
1147 {
1148 wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
1149 wxHtmlCell *cell = m_Cell->FindCellByPos(pos.x, pos.y);
1150 if ( cell )
1151 {
1152 delete m_selection;
1153 m_selection = new wxHtmlSelection();
1154 m_selection->Set(cell, cell);
1155 RefreshRect(wxRect(CalcScrolledPosition(cell->GetAbsPos()),
1156 wxSize(cell->GetWidth(), cell->GetHeight())));
1157 }
1158 }
1159 else
1160 event.Skip();
1161 }
1162 #endif
1163
1164
1165
1166 IMPLEMENT_ABSTRACT_CLASS(wxHtmlProcessor,wxObject)
1167
1168 IMPLEMENT_DYNAMIC_CLASS(wxHtmlWindow,wxScrolledWindow)
1169
1170 BEGIN_EVENT_TABLE(wxHtmlWindow, wxScrolledWindow)
1171 EVT_SIZE(wxHtmlWindow::OnSize)
1172 EVT_LEFT_DOWN(wxHtmlWindow::OnMouseDown)
1173 EVT_LEFT_UP(wxHtmlWindow::OnMouseUp)
1174 EVT_RIGHT_UP(wxHtmlWindow::OnMouseUp)
1175 EVT_MOTION(wxHtmlWindow::OnMouseMove)
1176 EVT_IDLE(wxHtmlWindow::OnIdle)
1177 EVT_ERASE_BACKGROUND(wxHtmlWindow::OnEraseBackground)
1178 EVT_PAINT(wxHtmlWindow::OnPaint)
1179 #if wxUSE_CLIPBOARD
1180 EVT_LEFT_DCLICK(wxHtmlWindow::OnDoubleClick)
1181 EVT_ENTER_WINDOW(wxHtmlWindow::OnMouseEnter)
1182 EVT_LEAVE_WINDOW(wxHtmlWindow::OnMouseLeave)
1183 EVT_KEY_UP(wxHtmlWindow::OnKeyUp)
1184 EVT_MENU(wxID_COPY, wxHtmlWindow::OnCopy)
1185 #endif
1186 END_EVENT_TABLE()
1187
1188
1189
1190
1191
1192 // A module to allow initialization/cleanup
1193 // without calling these functions from app.cpp or from
1194 // the user's application.
1195
1196 class wxHtmlWinModule: public wxModule
1197 {
1198 DECLARE_DYNAMIC_CLASS(wxHtmlWinModule)
1199 public:
1200 wxHtmlWinModule() : wxModule() {}
1201 bool OnInit() { return TRUE; }
1202 void OnExit() { wxHtmlWindow::CleanUpStatics(); }
1203 };
1204
1205 IMPLEMENT_DYNAMIC_CLASS(wxHtmlWinModule, wxModule)
1206
1207
1208 // This hack forces the linker to always link in m_* files
1209 // (wxHTML doesn't work without handlers from these files)
1210 #include "wx/html/forcelnk.h"
1211 FORCE_WXHTML_MODULES()
1212
1213 #endif