+// Splits m_Word into up to three parts according to selection, returns
+// substring before, in and after selection and the points (in relative coords)
+// where s2 and s3 start:
+void wxHtmlWordCell::Split(const wxDC& dc,
+                           const wxPoint& selFrom, const wxPoint& selTo,
+                           unsigned& pos1, unsigned& pos2) const
+{
+    wxPoint pt1 = (selFrom == wxDefaultPosition) ?
+                   wxDefaultPosition : selFrom - GetAbsPos();
+    wxPoint pt2 = (selTo == wxDefaultPosition) ?
+                   wxPoint(m_Width, wxDefaultCoord) : selTo - GetAbsPos();
+
+    // if the selection is entirely within this cell, make sure pt1 < pt2 in
+    // order to make the rest of this function simpler:
+    if ( selFrom != wxDefaultPosition && selTo != wxDefaultPosition &&
+         selFrom.x > selTo.x )
+    {
+        wxPoint tmp = pt1;
+        pt1 = pt2;
+        pt2 = tmp;
+    }
+
+    unsigned len = m_Word.length();
+    unsigned i = 0;
+    pos1 = 0;
+
+    // adjust for cases when the start/end position is completely
+    // outside the cell:
+    if ( pt1.y < 0 )
+        pt1.x = 0;
+    if ( pt2.y >= m_Height )
+        pt2.x = m_Width;
+
+    // before selection:
+    // (include character under caret only if in first half of width)
+#ifdef __WXMAC__
+    // implementation using PartialExtents to support fractional widths
+    wxArrayInt widths ;
+    dc.GetPartialTextExtents(m_Word,widths) ;
+    while( i < len && pt1.x >= widths[i] )
+        i++ ;
+    if ( i < len )
+    {
+        int charW = (i > 0) ? widths[i] - widths[i-1] : widths[i];
+        if ( widths[i] - pt1.x < charW/2 )
+            i++;
+    }
+#else // !__WXMAC__
+    wxCoord charW, charH;
+    while ( pt1.x > 0 && i < len )
+    {
+        dc.GetTextExtent(m_Word[i], &charW, &charH);
+        pt1.x -= charW;
+        if ( pt1.x >= -charW/2 )
+        {
+            pos1 += charW;
+            i++;
+        }
+    }
+#endif // __WXMAC__/!__WXMAC__
+
+    // in selection:
+    // (include character under caret only if in first half of width)
+    unsigned j = i;
+#ifdef __WXMAC__
+    while( j < len && pt2.x >= widths[j] )
+        j++ ;
+    if ( j < len )
+    {
+        int charW = (j > 0) ? widths[j] - widths[j-1] : widths[j];
+        if ( widths[j] - pt2.x < charW/2 )
+            j++;
+    }
+#else // !__WXMAC__
+    pos2 = pos1;
+    pt2.x -= pos2;
+    while ( pt2.x > 0 && j < len )
+    {
+        dc.GetTextExtent(m_Word[j], &charW, &charH);
+        pt2.x -= charW;
+        if ( pt2.x >= -charW/2 )
+        {
+            pos2 += charW;
+            j++;
+        }
+    }
+#endif // __WXMAC__/!__WXMAC__
+
+    pos1 = i;
+    pos2 = j;
+
+    wxASSERT( pos2 >= pos1 );
+}
+
+void wxHtmlWordCell::SetSelectionPrivPos(const wxDC& dc, wxHtmlSelection *s) const
+{
+    unsigned p1, p2;
+
+    Split(dc,
+          this == s->GetFromCell() ? s->GetFromPos() : wxDefaultPosition,
+          this == s->GetToCell() ? s->GetToPos() : wxDefaultPosition,
+          p1, p2);
+
+    wxPoint p(0, m_Word.length());
+
+    if ( this == s->GetFromCell() )
+        p.x = p1; // selection starts here
+    if ( this == s->GetToCell() )
+        p.y = p2; // selection ends here
+
+    if ( this == s->GetFromCell() )
+        s->SetFromPrivPos(p);
+    if ( this == s->GetToCell() )
+        s->SetToPrivPos(p);
+}
+
+
+static void SwitchSelState(wxDC& dc, wxHtmlRenderingInfo& info,
+                           bool toSelection)
+{
+    wxColour fg = info.GetState().GetFgColour();
+    wxColour bg = info.GetState().GetBgColour();
+
+    if ( toSelection )
+    {
+        dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
+        dc.SetTextForeground(info.GetStyle().GetSelectedTextColour(fg));
+        dc.SetTextBackground(info.GetStyle().GetSelectedTextBgColour(bg));
+        dc.SetBackground(wxBrush(info.GetStyle().GetSelectedTextBgColour(bg),
+                                 wxBRUSHSTYLE_SOLID));
+    }
+    else
+    {
+        dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
+        dc.SetTextForeground(fg);
+        dc.SetTextBackground(bg);
+        dc.SetBackground(wxBrush(bg, wxBRUSHSTYLE_SOLID));
+    }
+}
+
+
+void wxHtmlWordCell::Draw(wxDC& dc, int x, int y,
+                          int WXUNUSED(view_y1), int WXUNUSED(view_y2),
+                          wxHtmlRenderingInfo& info)
+{
+#if 0 // useful for debugging
+    dc.SetPen(*wxBLACK_PEN);
+    dc.DrawRectangle(x+m_PosX,y+m_PosY,m_Width /* VZ: +1? */ ,m_Height);
+#endif
+
+    bool drawSelectionAfterCell = false;
+
+    if ( info.GetState().GetSelectionState() == wxHTML_SEL_CHANGING )
+    {
+        // Selection changing, we must draw the word piecewise:
+        wxHtmlSelection *s = info.GetSelection();
+        wxString txt;
+        int w, h;
+        int ofs = 0;
+
+        wxPoint priv = (this == s->GetFromCell()) ?
+                           s->GetFromPrivPos() : s->GetToPrivPos();
+
+        // NB: this is quite a hack: in order to compute selection boundaries
+        //     (in word's characters) we must know current font, which is only
+        //     possible inside rendering code. Therefore we update the
+        //     information here and store it in wxHtmlSelection so that
+        //     ConvertToText can use it later:
+        if ( priv == wxDefaultPosition )
+        {
+            SetSelectionPrivPos(dc, s);
+            priv = (this == s->GetFromCell()) ?
+                    s->GetFromPrivPos() : s->GetToPrivPos();
+        }
+
+        int part1 = priv.x;
+        int part2 = priv.y;
+
+        if ( part1 > 0 )
+        {
+            txt = m_Word.Mid(0, part1);
+            dc.DrawText(txt, x + m_PosX, y + m_PosY);
+            dc.GetTextExtent(txt, &w, &h);
+            ofs += w;
+        }
+
+        SwitchSelState(dc, info, true);
+
+        txt = m_Word.Mid(part1, part2-part1);
+        dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY);
+
+        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);
+        }
+        else
+            drawSelectionAfterCell = true;
+    }
+    else
+    {
+        wxHtmlSelectionState selstate = info.GetState().GetSelectionState();
+        // Not changing selection state, draw the word in single mode:
+        if ( selstate != wxHTML_SEL_OUT &&
+             dc.GetBackgroundMode() != wxBRUSHSTYLE_SOLID )
+        {
+            SwitchSelState(dc, info, true);
+        }
+        else if ( selstate == wxHTML_SEL_OUT &&
+                  dc.GetBackgroundMode() == wxBRUSHSTYLE_SOLID )
+        {
+            SwitchSelState(dc, info, false);
+        }
+        dc.DrawText(m_Word, x + m_PosX, y + m_PosY);
+        drawSelectionAfterCell = (selstate != wxHTML_SEL_OUT);
+    }
+
+    // NB: If the text is justified then there is usually some free space
+    //     between adjacent cells and drawing the selection only onto cells
+    //     would result in ugly unselected spaces. The code below detects
+    //     this special case and renders the selection *outside* the sell,
+    //     too.
+    if ( m_Parent->GetAlignHor() == wxHTML_ALIGN_JUSTIFY &&
+         drawSelectionAfterCell )
+    {
+        wxHtmlCell *nextCell = m_Next;
+        while ( nextCell && nextCell->IsFormattingCell() )
+            nextCell = nextCell->GetNext();
+        if ( nextCell )
+        {
+            int nextX = nextCell->GetPosX();
+            if ( m_PosX + m_Width < nextX )
+            {
+                dc.SetBrush(dc.GetBackground());
+                dc.SetPen(*wxTRANSPARENT_PEN);
+                dc.DrawRectangle(x + m_PosX + m_Width, y + m_PosY,
+                                 nextX - m_PosX - m_Width, m_Height);
+            }
+        }
+    }
+}
+
+wxCursor wxHtmlWordCell::GetMouseCursor(wxHtmlWindowInterface *window) const
+{
+    if ( !GetLink() )
+    {
+        return window->GetHTMLCursor(wxHtmlWindowInterface::HTMLCursor_Text);
+    }
+    else
+    {
+        return wxHtmlCell::GetMouseCursor(window);
+    }
+}
+
+wxString wxHtmlWordCell::ConvertToText(wxHtmlSelection *s) const
+{
+    if ( s && (this == s->GetFromCell() || this == s->GetToCell()) )
+    {
+        wxPoint priv = this == s->GetFromCell() ? s->GetFromPrivPos()
+                                                : s->GetToPrivPos();
+
+        // VZ: we may be called before we had a chance to re-render ourselves
+        //     and in this case GetFrom/ToPrivPos() is not set yet -- assume
+        //     that this only happens in case of a double/triple click (which
+        //     seems to be the case now) and so it makes sense to select the
+        //     entire contents of the cell in this case
+        //
+        // TODO: but this really needs to be fixed in some better way later...
+        if ( priv != wxDefaultPosition )
+        {
+            const int part1 = priv.x;
+            const int part2 = priv.y;
+            if ( part1 == part2 )
+                return wxEmptyString;
+            return GetPartAsText(part1, part2);
+        }
+        //else: return the whole word below
+    }
+
+    return GetAllAsText();
+}