// Flags for wxHtmlCell::FindCellByPos
enum
{
- wxHTML_FIND_TERMINAL = 0x0001,
- wxHTML_FIND_NONTERMINAL = 0x0002
+ wxHTML_FIND_EXACT = 1,
+ wxHTML_FIND_NEAREST_BEFORE = 2,
+ wxHTML_FIND_NEAREST_AFTER = 4,
};
// nonterminal cells or both. In either case, returned cell is deepest
// cell in cells tree that contains [x,y].
virtual wxHtmlCell *FindCellByPos(wxCoord x, wxCoord y,
- unsigned flags = wxHTML_FIND_TERMINAL) const;
+ unsigned flags = wxHTML_FIND_EXACT) const;
+
+ // Returns absolute position of the cell on HTML canvas
+ wxPoint GetAbsPos() const;
+
+ // Returns first (last) terminal cell inside this cell. It may return NULL,
+ // but it is rare -- only if there are no terminals in the tree.
+ virtual wxHtmlCell *GetFirstTerminal() const
+ { return wxConstCast(this, wxHtmlCell); }
+ virtual wxHtmlCell *GetLastTerminal() const
+ { return wxConstCast(this, wxHtmlCell); }
protected:
wxHtmlCell *m_Next;
virtual bool IsTerminalCell() const { return FALSE; }
virtual wxHtmlCell *FindCellByPos(wxCoord x, wxCoord y,
- unsigned flags = wxHTML_FIND_TERMINAL) const;
+ unsigned flags = wxHTML_FIND_EXACT) const;
+
+ virtual wxHtmlCell *GetFirstTerminal() const;
+ virtual wxHtmlCell *GetLastTerminal() const;
protected:
void UpdateRenderingStatePre(wxHtmlRenderingState& state,
void OnDraw(wxDC& dc);
void OnSize(wxSizeEvent& event);
void OnMouseMove(wxMouseEvent& event);
- void OnMouseButton(wxMouseEvent& event);
+ void OnMouseDown(wxMouseEvent& event);
+ void OnMouseUp(wxMouseEvent& event);
void OnIdle(wxIdleEvent& event);
// Returns new filter (will be stored into m_DefaultFilter variable)
int m_Style;
+ // current text selection or NULL
+ wxHtmlSelection *m_selection;
+
+ // true if the user is dragging mouse to select text
+ bool m_makingSelection;
+
private:
+ // variables used when user is selecting text
+ wxPoint m_tmpSelFromPos;
+ wxHtmlCell *m_tmpSelFromCell;
+
// a flag indicated if mouse moved
// (if TRUE we will try to change cursor in last call to OnIdle)
bool m_tmpMouseMoved;
virtual void OnLinkClicked(const wxHtmlLinkInfo& link)
{
wxHtmlWindow::OnLinkClicked(link);
- m_Frame->NotifyPageChanged();
+ const wxMouseEvent *e = link.GetEvent();
+ if (e == NULL || e->LeftUp())
+ m_Frame->NotifyPageChanged();
}
private:
wxHtmlCell *wxHtmlCell::FindCellByPos(wxCoord x, wxCoord y,
unsigned flags) const
{
- if ( (flags & wxHTML_FIND_TERMINAL) &&
- x >= 0 && x < m_Width && y >= 0 && y < m_Height )
+ if ( x >= 0 && x < m_Width && y >= 0 && y < m_Height )
{
return wxConstCast(this, wxHtmlCell);
}
- return NULL;
+ else
+ {
+ if ((flags & wxHTML_FIND_NEAREST_AFTER) &&
+ (y < 0 || (y == 0 && x <= 0)))
+ return wxConstCast(this, wxHtmlCell);
+ else if ((flags & wxHTML_FIND_NEAREST_BEFORE) &&
+ (y > m_Height-1 || (y == m_Height-1 && x >= m_Width)))
+ return wxConstCast(this, wxHtmlCell);
+ else
+ return NULL;
+ }
+}
+
+
+wxPoint wxHtmlCell::GetAbsPos() const
+{
+ wxPoint p(m_PosX, m_PosY);
+ for (wxHtmlCell *parent = m_Parent; parent; parent = parent->m_Parent)
+ {
+ p.x += parent->m_PosX;
+ p.y += parent->m_PosY;
+ }
+ return p;
}
int WXUNUSED(view_y1), int WXUNUSED(view_y2),
wxHtmlRenderingState& state)
{
- if (state.GetSelectionState() == wxHTML_SEL_IN &&
+ if (state.GetSelectionState() != wxHTML_SEL_OUT &&
dc.GetBackgroundMode() != wxSOLID)
{
dc.SetBackgroundMode(wxSOLID);
{
wxHtmlSelection *s = state.GetSelection();
if (!s) return;
- if (s->GetFromCell() == cell)
- state.SetSelectionState(wxHTML_SEL_IN);
- else if (s->GetToCell() == cell)
+ if (s->GetToCell() == cell)
state.SetSelectionState(wxHTML_SEL_OUT);
+ else if (s->GetFromCell() == cell)
+ state.SetSelectionState(wxHTML_SEL_IN);
}
#define mMin(a, b) (((a) < (b)) ? (a) : (b))
wxHtmlCell *wxHtmlContainerCell::FindCellByPos(wxCoord x, wxCoord y,
unsigned flags) const
{
- for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
+ if (flags & wxHTML_FIND_EXACT)
{
- int cx = cell->GetPosX(),
- cy = cell->GetPosY();
+ for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
+ {
+ int cx = cell->GetPosX(),
+ cy = cell->GetPosY();
+
+ if ( (cx <= x) && (cx + cell->GetWidth() > x) &&
+ (cy <= y) && (cy + cell->GetHeight() > y) )
+ {
+ return cell->FindCellByPos(x - cx, y - cy, flags);
+ }
+ }
+ return NULL;
+ }
- if ( (cx <= x) && (cx + cell->GetWidth() > x) &&
- (cy <= y) && (cy + cell->GetHeight() > y) )
+ if ( flags & wxHTML_FIND_NEAREST_AFTER )
+ {
+ wxHtmlCell *c;
+ int y2;
+ for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
{
- wxHtmlCell *c = cell->FindCellByPos(x - cx, y - cy, flags);
- if (c == NULL && (flags & wxHTML_FIND_NONTERMINAL))
- return wxConstCast(this, wxHtmlContainerCell);
- else
- return c;
+ y2 = cell->GetPosY() + cell->GetHeight() - 1;
+ if (y2 < y || (y2 == y && cell->GetPosX()+cell->GetWidth()-1 < x))
+ continue;
+ c = cell->FindCellByPos(x - cell->GetPosX(), y - cell->GetPosY(),
+ flags);
+ if (c) return c;
}
+ return NULL;
}
- return (flags & wxHTML_FIND_NONTERMINAL) ?
- wxConstCast(this, wxHtmlContainerCell) : NULL;
+ if ( flags & wxHTML_FIND_NEAREST_BEFORE )
+ {
+ wxHtmlCell *c = NULL;
+ wxHtmlCell *cx;
+ for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
+ {
+ if (cell->GetPosY() > y ||
+ (cell->GetPosY() == y && cell->GetPosX() > x))
+ break;
+ cx = cell->FindCellByPos(x - cell->GetPosX(), y - cell->GetPosY(),
+ flags);
+ if (cx) c = cx;
+ }
+ return c;
+ }
}
*right = cright;
}
+
+wxHtmlCell *wxHtmlContainerCell::GetFirstTerminal() const
+{
+ if (m_Cells)
+ return m_Cells->GetFirstTerminal();
+ else
+ return NULL;
+}
+
+wxHtmlCell *wxHtmlContainerCell::GetLastTerminal() const
+{
+ if (m_Cells)
+ {
+ wxHtmlCell *c;
+ for (c = m_Cells; c->GetNext(); c = c->GetNext()) {}
+ return c;
+ }
+ else
+ return NULL;
+}
m_Processors = NULL;
m_Style = 0;
SetBorders(10);
+ m_selection = NULL;
+ m_makingSelection = false;
}
bool wxHtmlWindow::Create(wxWindow *parent, wxWindowID id,
{
wxString newsrc(source);
+ wxDELETE(m_selection);
+
// pass HTML through registered processors:
if (m_Processors || m_GlobalProcessors)
{
dc.SetBackgroundMode(wxTRANSPARENT);
GetViewStart(&x, &y);
- wxHtmlRenderingState rstate(NULL);
+ wxHtmlRenderingState rstate(m_selection);
m_Cell->Draw(dc, 0, 0,
y * wxHTML_SCROLL_STEP + rect.GetTop(),
y * wxHTML_SCROLL_STEP + rect.GetBottom(),
m_tmpMouseMoved = true;
}
-void wxHtmlWindow::OnMouseButton(wxMouseEvent& event)
+void wxHtmlWindow::OnMouseDown(wxMouseEvent& event)
{
- SetFocus();
- if ( m_Cell )
+ if ( event.LeftDown() && IsSelectionEnabled() )
{
- int sx, sy;
- GetViewStart(&sx, &sy);
- sx *= wxHTML_SCROLL_STEP;
- sy *= wxHTML_SCROLL_STEP;
+ m_makingSelection = true;
+
+ if ( m_selection )
+ {
+ wxDELETE(m_selection);
+ Refresh();
+ }
+ m_tmpSelFromPos = CalcUnscrolledPosition(event.GetPosition());
+ m_tmpSelFromCell = NULL;
- wxPoint pos = event.GetPosition();
- pos.x += sx;
- pos.y += sy;
+ CaptureMouse();
+ }
+}
+void wxHtmlWindow::OnMouseUp(wxMouseEvent& event)
+{
+ if ( m_makingSelection )
+ {
+ ReleaseMouse();
+ m_makingSelection = false;
+
+ // did the user move the mouse far enough from starting point?
+ if ( m_selection )
+ {
+ // we don't want mouse up event that ended selecting to be
+ // handled as mouse click and e.g. follow hyperlink:
+ return;
+ }
+ }
+
+ SetFocus();
+ if ( m_Cell )
+ {
+ wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
wxHtmlCell *cell = m_Cell->FindCellByPos(pos.x, pos.y);
// VZ: is it possible that we don't find anything at all?
if (m_tmpMouseMoved && (m_Cell != NULL))
{
- int sx, sy;
- GetViewStart(&sx, &sy);
- sx *= wxHTML_SCROLL_STEP;
- sy *= wxHTML_SCROLL_STEP;
-
- int x, y;
- wxGetMousePosition(&x, &y);
- ScreenToClient(&x, &y);
- x += sx;
- y += sy;
+ int xc, yc, x, y;
+ wxGetMousePosition(&xc, &yc);
+ ScreenToClient(&xc, &yc);
+ CalcUnscrolledPosition(xc, yc, &x, &y);
wxHtmlCell *cell = m_Cell->FindCellByPos(x, y);
+
+ // handle selection update:
+ if ( m_makingSelection )
+ {
+ bool goingDown = m_tmpSelFromPos.y < y ||
+ m_tmpSelFromPos.y == y && m_tmpSelFromPos.x < x;
+
+ if ( !m_tmpSelFromCell )
+ {
+ if (goingDown)
+ {
+ m_tmpSelFromCell = m_Cell->FindCellByPos(x, y,
+ wxHTML_FIND_NEAREST_BEFORE);
+ if (!m_tmpSelFromCell)
+ m_tmpSelFromCell = m_Cell->GetFirstTerminal();
+ }
+ else
+ {
+ m_tmpSelFromCell = m_Cell->FindCellByPos(x, y,
+ wxHTML_FIND_NEAREST_AFTER);
+ if (!m_tmpSelFromCell)
+ m_tmpSelFromCell = m_Cell->GetLastTerminal();
+ }
+ }
+
+ wxHtmlCell *selcell = cell;
+ if (!selcell)
+ {
+ if (goingDown)
+ {
+ selcell = m_Cell->FindCellByPos(x, y,
+ wxHTML_FIND_NEAREST_AFTER);
+ if (!selcell)
+ selcell = m_Cell->GetLastTerminal();
+ }
+ else
+ {
+ selcell = m_Cell->FindCellByPos(x, y,
+ wxHTML_FIND_NEAREST_BEFORE);
+ if (!selcell)
+ selcell = m_Cell->GetFirstTerminal();
+ }
+ }
+
+ // NB: it may *rarely* happen that the code above didn't find one
+ // of the cells, e.g. if wxHtmlWindow doesn't contain any
+ // visible cells.
+ if ( selcell && m_tmpSelFromCell )
+ {
+ if ( !m_selection )
+ {
+ // start selecting only if mouse movement was big enough
+ // (otherwise it was meant as mouse click, not selection):
+ const int PRECISION = 2;
+ wxPoint diff = m_tmpSelFromPos - wxPoint(x,y);
+ if (abs(diff.x) > PRECISION || abs(diff.y) > PRECISION)
+ {
+ m_selection = new wxHtmlSelection();
+ }
+ }
+ if ( m_selection )
+ {
+ if (goingDown)
+ {
+ m_selection->Set(m_tmpSelFromPos, m_tmpSelFromCell,
+ wxPoint(x,y), selcell);
+ }
+ else
+ {
+ m_selection->Set(wxPoint(x,y), selcell,
+ m_tmpSelFromPos, m_tmpSelFromCell);
+ }
+ Refresh(); // FIXME - optimize!
+ }
+ }
+ }
+
+ // handle cursor and status bar text changes:
if ( cell != m_tmpLastCell )
{
wxHtmlLinkInfo *lnk = cell ? cell->GetLink(x, y) : NULL;
BEGIN_EVENT_TABLE(wxHtmlWindow, wxScrolledWindow)
EVT_SIZE(wxHtmlWindow::OnSize)
- EVT_LEFT_UP(wxHtmlWindow::OnMouseButton)
- EVT_RIGHT_UP(wxHtmlWindow::OnMouseButton)
+ EVT_LEFT_DOWN(wxHtmlWindow::OnMouseDown)
+ EVT_LEFT_UP(wxHtmlWindow::OnMouseUp)
+ EVT_RIGHT_UP(wxHtmlWindow::OnMouseUp)
EVT_MOTION(wxHtmlWindow::OnMouseMove)
EVT_IDLE(wxHtmlWindow::OnIdle)
END_EVENT_TABLE()