diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h index a4fefc2eee..0068c8c173 100644 --- a/include/wx/html/htmlcell.h +++ b/include/wx/html/htmlcell.h @@ -35,7 +35,8 @@ public: wxHtmlSelection() : m_fromPos(wxDefaultPosition), m_toPos(wxDefaultPosition), m_fromCharacterPos(-1), m_toCharacterPos(-1), - m_fromCell(NULL), m_toCell(NULL) {} + m_fromCell(NULL), m_toCell(NULL), + m_extBeforeSel(0), m_extBeforeSelEnd(0) {} // this version is used for the user selection defined with the mouse void Set(const wxPoint& fromPos, const wxHtmlCell *fromCell, @@ -58,6 +59,11 @@ public: wxCoord GetFromCharacterPos () const { return m_fromCharacterPos; } wxCoord GetToCharacterPos () const { return m_toCharacterPos; } + void SetExtentBeforeSelection(unsigned ext) { m_extBeforeSel = ext; } + void SetExtentBeforeSelectionEnd(unsigned ext) { m_extBeforeSelEnd = ext; } + unsigned GetExtentBeforeSelection() const { return m_extBeforeSel; } + unsigned GetExtentBeforeSelectionEnd() const { return m_extBeforeSelEnd; } + bool IsEmpty() const { return m_fromPos == wxDefaultPosition && m_toPos == wxDefaultPosition; } @@ -66,6 +72,12 @@ private: wxPoint m_fromPos, m_toPos; wxCoord m_fromCharacterPos, m_toCharacterPos; const wxHtmlCell *m_fromCell, *m_toCell; + + // Extent of the text before selection start. + unsigned m_extBeforeSel; + + // Extent of the text from the beginning to the selection end. + unsigned m_extBeforeSelEnd; }; @@ -115,8 +127,17 @@ public: class WXDLLIMPEXP_HTML wxDefaultHtmlRenderingStyle : public wxHtmlRenderingStyle { public: + explicit wxDefaultHtmlRenderingStyle(const wxWindowBase* wnd = NULL) + : m_wnd(wnd) + {} + virtual wxColour GetSelectedTextColour(const wxColour& clr) wxOVERRIDE; virtual wxColour GetSelectedTextBgColour(const wxColour& clr) wxOVERRIDE; + +private: + const wxWindowBase* const m_wnd; + + wxDECLARE_NO_COPY_CLASS(wxDefaultHtmlRenderingStyle); }; @@ -320,7 +341,12 @@ public: // Returns absolute position of the cell on HTML canvas. // If rootCell is provided, then it's considered to be the root of the // hierarchy and the returned value is relative to it. - wxPoint GetAbsPos(wxHtmlCell *rootCell = NULL) const; + wxPoint GetAbsPos(const wxHtmlCell *rootCell = NULL) const; + + // Returns minimum bounding rectangle of this cell in coordinates, relative + // to the rootCell, if it is provided, or relative to the result of + // GetRootCell() if the rootCell is NULL. + wxRect GetRect(const wxHtmlCell *rootCell = NULL) const; // Returns root cell of the hierarchy (i.e. grand-grand-...-parent that // doesn't have a parent itself) @@ -417,7 +443,8 @@ protected: void SetSelectionPrivPos(const wxDC& dc, wxHtmlSelection *s) const; void Split(const wxDC& dc, const wxPoint& selFrom, const wxPoint& selTo, - unsigned& pos1, unsigned& pos2) const; + unsigned& pos1, unsigned& pos2, + unsigned& ext1, unsigned& ext2) const; wxString m_Word; bool m_allowLinebreak; diff --git a/include/wx/html/htmlwin.h b/include/wx/html/htmlwin.h index 1dfc31b5cd..472fffbc81 100644 --- a/include/wx/html/htmlwin.h +++ b/include/wx/html/htmlwin.h @@ -408,6 +408,7 @@ protected: void OnMouseMove(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseUp(wxMouseEvent& event); + void OnFocusEvent(wxFocusEvent& event); #if wxUSE_CLIPBOARD void OnKeyUp(wxKeyEvent& event); void OnDoubleClick(wxMouseEvent& event); diff --git a/src/generic/htmllbox.cpp b/src/generic/htmllbox.cpp index 011f6cc557..e1c3720235 100644 --- a/src/generic/htmllbox.cpp +++ b/src/generic/htmllbox.cpp @@ -159,7 +159,9 @@ private: class wxHtmlListBoxStyle : public wxDefaultHtmlRenderingStyle { public: - wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) : m_hlbox(hlbox) { } + wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) + : wxDefaultHtmlRenderingStyle(&hlbox), m_hlbox(hlbox) + { } virtual wxColour GetSelectedTextColour(const wxColour& colFg) wxOVERRIDE { diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp index f5b115823d..a853cc0084 100644 --- a/src/html/htmlcell.cpp +++ b/src/html/htmlcell.cpp @@ -65,7 +65,12 @@ wxColour wxDefaultHtmlRenderingStyle:: GetSelectedTextBgColour(const wxColour& WXUNUSED(clr)) { - return wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); + // By default we use the fixed standard selection colour, but if we're + // associated with a window use the colour appropriate for the window + // state, i.e. grey out selection when it's not in focus. + + return wxSystemSettings::GetColour(!m_wnd || m_wnd->HasFocus() ? + wxSYS_COLOUR_HIGHLIGHT : wxSYS_COLOUR_BTNSHADOW); } @@ -216,10 +221,10 @@ wxHtmlCell *wxHtmlCell::FindCellByPos(wxCoord x, wxCoord y, } -wxPoint wxHtmlCell::GetAbsPos(wxHtmlCell *rootCell) const +wxPoint wxHtmlCell::GetAbsPos(const wxHtmlCell *rootCell) const { wxPoint p(m_PosX, m_PosY); - for (wxHtmlCell *parent = m_Parent; parent && parent != rootCell; + for (const wxHtmlCell *parent = m_Parent; parent && parent != rootCell; parent = parent->m_Parent) { p.x += parent->m_PosX; @@ -228,6 +233,11 @@ wxPoint wxHtmlCell::GetAbsPos(wxHtmlCell *rootCell) const return p; } +wxRect wxHtmlCell::GetRect(const wxHtmlCell* rootCell) const +{ + return wxRect(GetAbsPos(rootCell), wxSize(m_Width, m_Height)); +} + wxHtmlCell *wxHtmlCell::GetRootCell() const { wxHtmlCell *c = wxConstCast(this, wxHtmlCell); @@ -333,7 +343,8 @@ void wxHtmlWordCell::SetPreviousWord(wxHtmlWordCell *cell) // where s2 and s3 start: void wxHtmlWordCell::Split(const wxDC& dc, const wxPoint& selFrom, const wxPoint& selTo, - unsigned& pos1, unsigned& pos2) const + unsigned& pos1, unsigned& pos2, + unsigned& ext1, unsigned& ext2) const { wxPoint pt1 = (selFrom == wxDefaultPosition) ? wxDefaultPosition : selFrom - GetAbsPos(); @@ -390,21 +401,30 @@ void wxHtmlWordCell::Split(const wxDC& dc, pos2 = j; wxASSERT( pos2 >= pos1 ); + + ext1 = pos1 == 0 ? 0 : (pos1 < widths.size() ? widths[pos1-1] : widths.Last()); + ext2 = pos2 == 0 ? 0 : (pos2 < widths.size() ? widths[pos2-1] : widths.Last()); } void wxHtmlWordCell::SetSelectionPrivPos(const wxDC& dc, wxHtmlSelection *s) const { - unsigned p1, p2; + unsigned p1, p2, ext1, ext2; Split(dc, this == s->GetFromCell() ? s->GetFromPos() : wxDefaultPosition, this == s->GetToCell() ? s->GetToPos() : wxDefaultPosition, - p1, p2); + p1, p2, ext1, ext2); if ( this == s->GetFromCell() ) - s->SetFromCharacterPos (p1); // selection starts here + { + s->SetFromCharacterPos(p1); // selection starts here + s->SetExtentBeforeSelection(ext1); + } if ( this == s->GetToCell() ) - s->SetToCharacterPos (p2); // selection ends here + { + s->SetToCharacterPos(p2); // selection ends here + s->SetExtentBeforeSelectionEnd(ext2); + } } @@ -449,7 +469,6 @@ void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, // Selection changing, we must draw the word piecewise: wxHtmlSelection *s = info.GetSelection(); wxString txt; - int w, h; int ofs = 0; // NB: this is quite a hack: in order to compute selection boundaries @@ -469,8 +488,7 @@ void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, { txt = m_Word.Mid(0, part1); dc.DrawText(txt, x + m_PosX, y + m_PosY); - dc.GetTextExtent(txt, &w, &h); - ofs += w; + ofs += s->GetExtentBeforeSelection(); } SwitchSelState(dc, info, true); @@ -480,11 +498,9 @@ void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, if ( (size_t)part2 < m_Word.length() ) { - dc.GetTextExtent(txt, &w, &h); - ofs += w; SwitchSelState(dc, info, false); txt = m_Word.Mid(part2); - dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY); + dc.DrawText(txt, x + m_PosX + s->GetExtentBeforeSelectionEnd(), y + m_PosY); } else drawSelectionAfterCell = true; diff --git a/src/html/htmlwin.cpp b/src/html/htmlwin.cpp index d3373316a5..8fdbe56cf6 100644 --- a/src/html/htmlwin.cpp +++ b/src/html/htmlwin.cpp @@ -1138,7 +1138,7 @@ void wxHtmlWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) dc->SetLayoutDirection(GetLayoutDirection()); wxHtmlRenderingInfo rinfo; - wxDefaultHtmlRenderingStyle rstyle; + wxDefaultHtmlRenderingStyle rstyle(this); rinfo.SetSelection(m_selection); rinfo.SetStyle(&rstyle); m_Cell->Draw(*dc, 0, 0, @@ -1184,7 +1184,115 @@ void wxHtmlWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) } } +namespace +{ +// Returns true if leftCell is an ancestor of rightCell. +bool IsAncestor(const wxHtmlCell* leftCell, const wxHtmlCell* rightCell) +{ + for ( const wxHtmlCell* parent = rightCell->GetParent(); + parent; parent = parent->GetParent() ) + { + if ( leftCell == parent ) + return true; + } + return false; +} + +// Returns minimum bounding rectangle of all the cells between fromCell +// and toCell, inclusive. +wxRect GetBoundingRect(const wxHtmlCell* const fromCell, + const wxHtmlCell* const toCell) +{ + wxCHECK_MSG(fromCell || toCell, wxRect(), "At least one cell is required"); + + // Check if we have only one cell or the cells are equal. + if ( !fromCell ) + return toCell->GetRect(); + else if ( !toCell || fromCell == toCell ) + return fromCell->GetRect(); + + // Check if one of the cells is an ancestor of the other. + if ( IsAncestor(fromCell, toCell) ) + return fromCell->GetRect(); + else if ( IsAncestor(toCell, fromCell) ) + return toCell->GetRect(); + + // Combine MBRs, starting with the fromCell. + wxRect boundingRect = fromCell->GetRect(); + + // For each subtree toward the lowest common ancestor, + // combine MBRs until the (subtree of) toCell is reached. + for ( const wxHtmlCell *startCell = fromCell, + *parent = fromCell->GetParent(); + parent; + startCell = parent, parent = parent->GetParent() ) + { + if ( IsAncestor(parent, toCell) ) + { + // Combine all the cells up to the toCell or its subtree. + for ( const wxHtmlCell* nextCell = startCell->GetNext(); + nextCell; + nextCell = nextCell->GetNext() ) + { + if ( nextCell == toCell ) + return boundingRect.Union(toCell->GetRect()); + + if ( IsAncestor(nextCell, toCell) ) + { + return boundingRect.Union(GetBoundingRect( + nextCell->GetFirstTerminal(), toCell)); + } + + boundingRect.Union(nextCell->GetRect()); + } + + wxFAIL_MSG("Unexpected: toCell is not reachable from the fromCell"); + return GetBoundingRect(toCell, fromCell); + } + else + { + // Combine rest of current container. + for ( const wxHtmlCell* nextCell = startCell->GetNext(); + nextCell; + nextCell = nextCell->GetNext() ) + { + boundingRect.Union(nextCell->GetRect()); + } + } + } + + wxFAIL_MSG("The cells have no common ancestor"); + return wxRect(); +} + +} // namespace + +void wxHtmlWindow::OnFocusEvent(wxFocusEvent& event) +{ + event.Skip(); + + // Redraw selection, because its background colour depends on + // whether the window has keyboard focus or not. + + if ( !m_selection || m_selection->IsEmpty() ) + return; + + const wxHtmlCell* fromCell = m_selection->GetFromCell(); + const wxHtmlCell* toCell = m_selection->GetToCell(); + wxCHECK_RET(fromCell || toCell, + "Unexpected: selection is set but cells are not"); + + wxRect boundingRect = GetBoundingRect(fromCell, toCell); + + boundingRect = wxRect + ( + CalcScrolledPosition(boundingRect.GetTopLeft()), + CalcScrolledPosition(boundingRect.GetBottomRight()) + ); + + RefreshRect(boundingRect); +} void wxHtmlWindow::OnSize(wxSizeEvent& event) @@ -1502,14 +1610,12 @@ void wxHtmlWindow::OnMouseLeave(wxMouseEvent& event) void wxHtmlWindow::OnKeyUp(wxKeyEvent& event) { - if ( IsSelectionEnabled() && - (event.GetKeyCode() == 'C' && event.CmdDown()) ) + if ( IsSelectionEnabled() && event.GetModifiers() == wxMOD_CONTROL && + (event.GetKeyCode() == 'C' || event.GetKeyCode() == WXK_INSERT) ) { wxClipboardTextEvent evt(wxEVT_TEXT_COPY, GetId()); - evt.SetEventObject(this); - - GetEventHandler()->ProcessEvent(evt); + ProcessWindowEvent(evt); } else { @@ -1656,6 +1762,8 @@ wxBEGIN_EVENT_TABLE(wxHtmlWindow, wxScrolledWindow) EVT_MOTION(wxHtmlWindow::OnMouseMove) EVT_PAINT(wxHtmlWindow::OnPaint) EVT_ERASE_BACKGROUND(wxHtmlWindow::OnEraseBackground) + EVT_SET_FOCUS(wxHtmlWindow::OnFocusEvent) + EVT_KILL_FOCUS(wxHtmlWindow::OnFocusEvent) #if wxUSE_CLIPBOARD EVT_LEFT_DCLICK(wxHtmlWindow::OnDoubleClick) EVT_ENTER_WINDOW(wxHtmlWindow::OnMouseEnter)