Esempio n. 1
0
POINT Editor::paramToPoint(uint32 param)
{
  POINT pt;
  pt.x = (GET_X_LPARAM(param) + chSize.cx / 2 - LeftMargin()) / chSize.cx + scrollPos.x;
  pt.y = GET_Y_LPARAM(param) / chSize.cy + scrollPos.y;
  return pt;
}
Esempio n. 2
0
int Editor::toolHitTest(POINT pt, ToolInfo* ti)
{
  if (!running || running->getDebugLevel() < 0)
    return -1;
  pt.x = (pt.x + chSize.cx / 2 - LeftMargin()) / chSize.cx + scrollPos.x;
  pt.y = pt.y / chSize.cy + scrollPos.y;
  fixPoint(pt);
  int offset = fromPoint(pt);
  int sela = (selStart < caret ? selStart : caret);
  int selb = (selStart > caret ? selStart : caret);
  if (offset < sela || offset >= selb)
  {
    int ptStart = wordEnd(lines[pt.y].text.c_str(), pt.x, -1);
    int ptEnd = wordEnd(lines[pt.y].text.c_str(), pt.x, 1);
    int offset = fromPoint(pt);
    sela = offset - (pt.x - ptStart);
    selb = offset + (ptEnd - pt.x);
  }
  int index = -1;
  if (offset >= sela && offset < selb)
  {
    String text(substring(sela, selb));
    lua_State* L = running->getEngine()->lock();
    int res = luae::getvalue(L, running->getDebugLevel(), text);
    if (res != luae::eFail && !lua_isnil(L, -1))
    {
      ti->text = luae::towstring(L, -1, true);
      lua_pop(L, 1);
      POINT pta = toPoint(sela);
      POINT ptb = toPoint(selb);
      int x0 = (pta.y == pt.y ? pta.x : 0);
      int x1 = (ptb.y == pt.y ? ptb.x : lines[pt.y].text.length());
      ti->rc.top = (pt.y - scrollPos.y) * chSize.cy;
      ti->rc.bottom = (pt.y + 1 - scrollPos.y) * chSize.cy;
      ti->rc.left = (x0 - scrollPos.x) * chSize.cx + LeftMargin();
      ti->rc.right = (x1 - scrollPos.x) * chSize.cx + LeftMargin();
      index = sela;
    }
    else if (res != luae::eFail)
      lua_pop(L, 1);
    running->getEngine()->unlock();
  }

  return index;
}
Esempio n. 3
0
void Editor::placeCaret()
{
  if (GetFocus() == hWnd)
  {
    POINT pt = toPoint(caret);
    if (insertMode || caret != selStart)
      CreateCaret(hWnd, NULL, 1, chSize.cy);
    else
      CreateCaret(hWnd, NULL, chSize.cx, chSize.cy);
    SetCaretPos((pt.x - scrollPos.x) * chSize.cx + LeftMargin(), (pt.y - scrollPos.y) * chSize.cy);
    ShowCaret(hWnd);
  }
}
Esempio n. 4
0
void Editor::updateExtent()
{
  extent.x = 0;
  extent.y = lines.length();
  for (int i = 0; i < lines.length(); i++)
    if (lines[i].text.length() > extent.x)
      extent.x = lines[i].text.length();

  RECT rc;
  GetClientRect(hWnd, &rc);
  SCROLLINFO si;
  memset(&si, 0, sizeof si);
  si.cbSize = sizeof si;
  si.fMask = SIF_PAGE | SIF_RANGE;
  si.nMin = 0;
  si.nPage = (rc.right - LeftMargin()) / chSize.cx;
  si.nMax = extent.x + 10;
  SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
  si.nPage = rc.bottom / chSize.cy;
  si.nMax = extent.y - 1;
  SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
Esempio n. 5
0
void Editor::doScroll(int x, int y)
{
  SCROLLINFO si;
  memset(&si, 0, sizeof si);
  si.cbSize = sizeof si;
  si.fMask = SIF_RANGE | SIF_PAGE;
  GetScrollInfo(hWnd, SB_HORZ, &si);
  if (x < si.nMin) x = si.nMin;
  if (x > si.nMax - si.nPage + 1) x = si.nMax - si.nPage + 1;
  GetScrollInfo(hWnd, SB_VERT, &si);
  if (y < si.nMin) y = si.nMin;
  if (y > si.nMax - si.nPage + 1) y = si.nMax - si.nPage + 1;
  si.fMask = SIF_POS;
  if (x != scrollPos.x)
  {
    si.nPos = x;
    SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
  }
  if (y != scrollPos.y)
  {
    si.nPos = y;
    SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
  }
  if (x != scrollPos.x || y != scrollPos.y)
  {
    int deltaX = scrollPos.x - x;
    int deltaY = scrollPos.y - y;
    scrollPos.x = x;
    scrollPos.y = y;
    ScrollWindowEx(hWnd, chSize.cx * deltaX, chSize.cy * deltaY, NULL, NULL, NULL, NULL, SW_INVALIDATE);
    if (GetFocus() == hWnd)
    {
      POINT pt = toPoint(caret);
      SetCaretPos((pt.x - scrollPos.x) * chSize.cx + LeftMargin(), (pt.y - scrollPos.y) * chSize.cy);
    }
  }
}
Esempio n. 6
0
void CTextStyle::FromOldStyle(const TextStyle& style)
{
	// Set some defaults.
	SetDefault();

	// Copy the style information over.
	Font(style.get_face());
	Size(MakeFixed(style.get_size(), style.get_size_fraction()));
	BaseSize(MakeFixed(style.get_base_size(), style.get_base_size_fraction()));
	Expansion(DivFixed(MakeFixed(style.get_base_size(), style.get_base_size_fraction()),
							 MakeFixed(FONT_EXPANSION_UNIT)));
	Fill(style.get_pattern(), style.get_color());
	Outline(style.get_outline(), style.get_color());
	Shadow(style.get_shadow(), style.get_color());
	m_Character.m_nEffectsVersion = 1;
	XFlipped(style.get_xflipped());
	YFlipped(style.get_yflipped());
//	Color(style.get_color());

	Alignment(style.get_line_alignment());
	VerticalAlignment(style.get_vertical_alignment());
	// Left and right margin should be zero (default) unless set by user.
	// This fixes a problem converting old warp text boxes - they should
	// always have zero margins!
	LeftMargin(0);
	RightMargin(0);
//	LeftMargin(PageToInches(style.get_left_margin()));
//	RightMargin(PageToInches(style.get_right_margin()));
	LeadingType(LEADING_lines);
	Leading(MakeFixed(0.875));

	Underline(style.UnderlineStyle());

	// Update our metrics.
	UpdateFontMetrics();
}
Esempio n. 7
0
// Compute the distance from the left and right ends of each row to the
// left and right edges of the block's polyblock.  Illustration:
//  ____________________________   _______________________
//  |  Howdy neighbor!         |  |rectangular blocks look|
//  |  This text is  written to|  |more like stacked pizza|
//  |illustrate how useful poly-  |boxes.                 |
//  |blobs  are   in -----------  ------   The    polyblob|
//  |dealing    with|     _________     |for a BLOCK  rec-|
//  |harder   layout|   /===========\   |ords the possibly|
//  |issues.        |    |  _    _  |   |skewed    pseudo-|
//  |  You  see this|    | |_| \|_| |   |rectangular      |
//  |text is  flowed|    |      }   |   |boundary     that|
//  |around  a  mid-|     \   ____  |   |forms the  ideal-|
//  |cloumn portrait._____ \       /  __|ized  text margin|
//  |  Polyblobs     exist| \    /   |from which we should|
//  |to account for insets|  |   |   |measure    paragraph|
//  |which make  otherwise|  -----   |indentation.        |
//  -----------------------          ----------------------
//
// If we identify a drop-cap, we measure the left margin for the lines
// below the first line relative to one space past the drop cap.  The
// first line's margin and those past the drop cap area are measured
// relative to the enclosing polyblock.
//
// TODO(rays): Before this will work well, we'll need to adjust the
//             polyblob tighter around the text near images, as in:
//             UNLV_AUTO:mag.3G0  page 2
//             UNLV_AUTO:mag.3G4  page 16
void BLOCK::compute_row_margins() {
  if (row_list()->empty() || row_list()->singleton()) {
    return;
  }

  // If Layout analysis was not called, default to this.
  POLY_BLOCK rect_block(bounding_box(), PT_FLOWING_TEXT);
  POLY_BLOCK *pblock = &rect_block;
  if (poly_block() != NULL) {
    pblock = poly_block();
  }

  // Step One: Determine if there is a drop-cap.
  //           TODO(eger): Fix up drop cap code for RTL languages.
  ROW_IT r_it(row_list());
  ROW *first_row = r_it.data();
  ROW *second_row = r_it.data_relative(1);

  // initialize the bottom of a fictitious drop cap far above the first line.
  int drop_cap_bottom = first_row->bounding_box().top() +
                        first_row->bounding_box().height();
  int drop_cap_right = first_row->bounding_box().left();
  int mid_second_line = second_row->bounding_box().top() -
                        second_row->bounding_box().height() / 2;
  WERD_IT werd_it(r_it.data()->word_list());  // words of line one
  if (!werd_it.empty()) {
    C_BLOB_IT cblob_it(werd_it.data()->cblob_list());
    for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list();
         cblob_it.forward()) {
      TBOX bbox = cblob_it.data()->bounding_box();
      if (bbox.bottom() <= mid_second_line) {
        // we found a real drop cap
        first_row->set_has_drop_cap(true);
        if (drop_cap_bottom >  bbox.bottom())
          drop_cap_bottom = bbox.bottom();
        if (drop_cap_right < bbox.right())
          drop_cap_right = bbox.right();
      }
    }
  }

  // Step Two: Calculate the margin from the text of each row to the block
  //           (or drop-cap) boundaries.
  PB_LINE_IT lines(pblock);
  r_it.set_to_list(row_list());
  for (r_it.mark_cycle_pt(); !r_it.cycled_list(); r_it.forward()) {
    ROW *row = r_it.data();
    TBOX row_box = row->bounding_box();
    int left_y = row->base_line(row_box.left()) + row->x_height();
    int left_margin;
    ICOORDELT_LIST *segments = lines.get_line(left_y);
    LeftMargin(segments, row_box.left(), &left_margin);
    delete segments;

    if (row_box.top() >= drop_cap_bottom) {
      int drop_cap_distance = row_box.left() - row->space() - drop_cap_right;
      if (drop_cap_distance < 0)
        drop_cap_distance = 0;
      if (drop_cap_distance < left_margin)
        left_margin = drop_cap_distance;
    }

    int right_y = row->base_line(row_box.right()) + row->x_height();
    int right_margin;
    segments = lines.get_line(right_y);
    RightMargin(segments, row_box.right(), &right_margin);
    delete segments;
    row->set_lmargin(left_margin);
    row->set_rmargin(right_margin);
  }
}
Esempio n. 8
0
uint32 Editor::onMessage(uint32 message, uint32 wParam, uint32 lParam)
{
  switch (message)
  {
  case WM_DESTROY:
    delete target;
    target = NULL;
    break;
  case WM_SETCURSOR:
    if (LOWORD(lParam) == HTCLIENT)
    {
      POINT pt;
      GetCursorPos(&pt);
      ScreenToClient(hWnd, &pt);
      if (pt.x < LeftMargin())
        SetCursor(cursors[cArrow]);
      else if (selStart != caret)
      {
        pt.x = (pt.x - LeftMargin()) / chSize.cx + scrollPos.x;
        pt.y = pt.y / chSize.cy + scrollPos.y;
        if (pt.y < 0 || pt.y >= lines.length() || pt.x < 0 || pt.x >= lines[pt.y].text.length())
          SetCursor(cursors[cBeam]);
        else
        {
          int offset = fromPoint(pt);
          int sela = (selStart < caret ? selStart : caret);
          int selb = (selStart < caret ? caret : selStart);
          if (offset >= sela && offset < selb)
            SetCursor(cursors[cArrow]);
          else
            SetCursor(cursors[cBeam]);
        }
      }
      else
        SetCursor(cursors[cBeam]);
    }
    else
      SetCursor(cursors[cArrow]);
    return TRUE;
  case WM_ERASEBKGND:
    return TRUE;
  case WM_PAINT:
    paint();
    return 0;
  case WM_SIZE:
    if (hBitmap)
    {
      int wd = LOWORD(lParam);
      int ht = HIWORD(lParam);
      if (wd < 10) wd = 10;
      if (ht < 10) ht = 10;
      hBitmap = CreateCompatibleBitmap(hDC, wd, ht);
      SelectObject(hDC, hBitmap);
    }
    updateExtent();
    return 0;
  case WM_SETFOCUS:
    placeCaret();
    invalidate();
    getParent()->notify(EN_FOCUSED, (uint32) this, 0);
    return 0;
  case WM_KILLFOCUS:
    DestroyCaret();
    updateCaret();
    invalidate();
    return 0;
  case WM_LBUTTONDBLCLK:
    {
      POINT pt = paramToPoint(lParam);
      fixPoint(pt);
      int ptStart = wordEnd(lines[pt.y].text.c_str(), pt.x, -1);
      int ptEnd = wordEnd(lines[pt.y].text.c_str(), pt.x, 1);
      int offset = fromPoint(pt);
      selStart = offset - (pt.x - ptStart);
      caret = offset + (ptEnd - pt.x);
      updateCaret();
    }
    return 0;
  case WM_LBUTTONDOWN:
    if (int(GET_X_LPARAM(lParam)) < int(settings->bpOffset - scrollPos.x * chSize.cx))
    {
      POINT pt = paramToPoint(lParam);
      toggleBreakpoint(pt.y);
    }
    else
    {
      POINT pt = paramToPoint(lParam);
      int offset = fromPoint(pt);
      int sela = (selStart < caret ? selStart : caret);
      int selb = (selStart > caret ? selStart : caret);
      if (offset >= sela && offset < selb)
      {
        dragop = 1;
        uint32 fx = DoDragDropEx(CF_UNICODETEXT, CreateGlobalText(getSelection()),
            DROPEFFECT_MOVE | DROPEFFECT_COPY, hWnd);
        if (fx == DROPEFFECT_NONE)
          dragop = 0;
        //else if (fx != DROPEFFECT_COPY && dragop != 2)
        //  replace(sela, selb, "");
      }
      else
        SetCapture(hWnd);
      if (dragop == 0)
      {
        caret = offset;
        if (!(wParam & MK_SHIFT))
          selStart = caret;
      }
      dragop = 0;
      if (GetFocus() != hWnd)
        SetFocus(hWnd);
      updateCaret();
    }
    return 0;
  case WM_RBUTTONDOWN:
    if (int(GET_X_LPARAM(lParam)) >= int(settings->bpOffset - scrollPos.x * chSize.cx))
    {
      POINT pt = paramToPoint(lParam);
      int offset = fromPoint(pt);
      int sela = (selStart < caret ? selStart : caret);
      int selb = (selStart > caret ? selStart : caret);
      if (offset < sela || offset >= selb)
        caret = selStart = offset;
      if (GetFocus() != hWnd)
        SetFocus(hWnd);
      updateCaret();
    }
    return 0;
  case WM_MOUSEMOVE:
    if (GetCapture() == hWnd && (wParam & MK_LBUTTON))
    {
      POINT pt = paramToPoint(lParam);
      caret = fromPoint(pt);
      updateCaret();
    }
    return 0;
  case WM_LBUTTONUP:
    ReleaseCapture();
    return 0;
  case WM_CHAR:
    if (iswprint(wParam) && (GetAsyncKeyState(VK_CONTROL) & 0x8000) == 0)
    {
      if (caret == selStart && !insertMode && caret < getTextLength())
        replace(caret, caret + 1, WideString((wchar_t) wParam));
      else
        replace(selStart, caret, WideString((wchar_t) wParam));
    }
    return 0;
  case WM_VSCROLL:
    {
      SCROLLINFO si;
      memset(&si, 0, sizeof si);
      si.cbSize = sizeof si;
      si.fMask = SIF_ALL;
      GetScrollInfo(hWnd, SB_VERT, &si);
      switch (LOWORD(wParam))
      {
      case SB_TOP:
        si.nPos = si.nMin;
        break;
      case SB_BOTTOM:
        si.nPos = si.nMax;
        break;
      case SB_LINEUP:
        si.nPos--;
        break;
      case SB_LINEDOWN:
        si.nPos++;
        break;
      case SB_PAGEUP:
        si.nPos -= si.nPage;
        break;
      case SB_PAGEDOWN:
        si.nPos += si.nPage;
        break;
      case SB_THUMBTRACK:
        si.nPos = si.nTrackPos;
        break;
      }
      doScroll(scrollPos.x, si.nPos);
    }
    return 0;
  case WM_HSCROLL:
    {
      SCROLLINFO si;
      memset(&si, 0, sizeof si);
      si.cbSize = sizeof si;
      si.fMask = SIF_ALL;
      GetScrollInfo(hWnd, SB_HORZ, &si);
      switch (LOWORD(wParam))
      {
      case SB_LEFT:
        si.nPos = si.nMin;
        break;
      case SB_RIGHT:
        si.nPos = si.nMax;
        break;
      case SB_LINELEFT:
        si.nPos--;
        break;
      case SB_LINERIGHT:
        si.nPos++;
        break;
      case SB_PAGELEFT:
        si.nPos -= si.nPage;
        break;
      case SB_PAGERIGHT:
        si.nPos += si.nPage;
        break;
      case SB_THUMBTRACK:
        si.nPos = si.nTrackPos;
        break;
      }
      doScroll(si.nPos, scrollPos.y);
    }
    return 0;
  case WM_MOUSEWHEEL:
    {
      int step;
      SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &step, 0);
      if (step < 0)
        step = 3;
      scrollAccum.y += GET_WHEEL_DELTA_WPARAM(wParam) * step;
      doScroll(scrollPos.x, scrollPos.y - scrollAccum.y / WHEEL_DELTA);
      scrollAccum.y %= WHEEL_DELTA;
    }
    return 0;
  case WM_MOUSEHWHEEL:
    {
      scrollAccum.x += GET_WHEEL_DELTA_WPARAM(wParam) * 4;
      doScroll(scrollPos.x + scrollAccum.x / WHEEL_DELTA, scrollPos.y);
      scrollAccum.x %= WHEEL_DELTA;
    }
    return 0;
  case WM_DRAGOVER:
    {
      if (running || settings->mode)
        return TRUE;
      POINT pt = paramToPoint(lParam);
      dropPos = fromPoint(pt);

      RECT rc;
      GetClientRect(hWnd, &rc);
      int xto = scrollPos.x;
      if (pt.x < 10)
        xto--;
      else if (pt.x > rc.right - 10)
        xto++;
      int yto = scrollPos.y;
      if (pt.y < 10)
        yto--;
      else if (pt.y > rc.bottom - 10)
        yto++;
      doScroll(xto, yto);

      int sela = (selStart < caret ? selStart : caret);
      int selb = (selStart > caret ? selStart : caret);
      if (dropPos > sela && dropPos < selb)
        return TRUE;
      else
      {
        fixPoint(pt);
        CreateCaret(hWnd, NULL, 2, chSize.cy);
        SetCaretPos((pt.x - scrollPos.x) * chSize.cx + LeftMargin(), (pt.y - scrollPos.y) * chSize.cy);
        ShowCaret(hWnd);
      }
    }
    return 0;
  case WM_DRAGLEAVE:
    dropPos = 0;
    updateCaret();
    return 0;
  case WM_DRAGDROP:
    if (running || settings->mode)
      return DROPEFFECT_NONE;
    if (dragop)
    {
      dragop = 2;
      int sela = (selStart < caret ? selStart : caret);
      int selb = (selStart > caret ? selStart : caret);
      if (dropPos < sela || dropPos > selb)
      {
        WideString text = getSelection();
        if (lParam != DROPEFFECT_COPY)
        {
          replace(sela, selb, L"");
          if (dropPos > selb)
            dropPos -= (selb - sela);
          caret = replace(dropPos, dropPos, text, NULL, true);
        }
        else
          caret = replace(dropPos, dropPos, text);
        selStart = dropPos;
      }
    }
    else
    {
      caret = replace(dropPos, dropPos, GetGlobalTextWide((HGLOBAL) wParam));
      selStart = dropPos;
      return DROPEFFECT_COPY;
    }
    return lParam;
  case WM_SYSKEYDOWN:
  case WM_KEYDOWN:
    onKey(wParam);
    return 0;
  case WM_COMMAND:
    switch (LOWORD(wParam))
    {
    case ID_EDIT_UNDO:
      {
        bool glue = true;
        bool first = true;
        while (glue && historyPos > 0)
        {
          HistoryItem& h = history[--historyPos];
          replace(h.begin, h.end, h.text, &h);
          glue = h.glue;
          h.glue = !first;
          first = false;
        }
      }
      break;
    case ID_EDIT_REDO:
      {
        bool glue = true;
        bool first = true;
        while (glue && historyPos < history.length())
        {
          HistoryItem& h = history[historyPos++];
          replace(h.begin, h.end, h.text, &h);
          glue = h.glue;
          h.glue = !first;
          first = false;
        }
      }
      break;
    case ID_EDIT_SELECTALL:
      selStart = 0;
      caret = getTextLength();
      updateCaret();
      break;
    case ID_EDIT_COPY:
      if (caret != selStart)
        SetClipboard(CF_UNICODETEXT, CreateGlobalText(getSelection()));
      else
      {
        POINT pt = toPoint(caret);
        pt.x = 0;
        int start = fromPoint(pt);
        if (pt.y < lines.length() - 1)
          pt.y++;
        else
          pt.x = lines[pt.y].text.length();
        int end = fromPoint(pt);
        if (pCopyLine)
          pCopyLine->Release();
        pCopyLine = SetClipboard(CF_UNICODETEXT, CreateGlobalText(substring(start, end)));
        if (pCopyLine)
          pCopyLine->AddRef();
      }
      break;
    case ID_EDIT_CUT:
      if (caret != selStart)
      {
        SetClipboard(CF_UNICODETEXT, CreateGlobalText(getSelection()));
        replace(selStart, caret, L"");
      }
      else
      {
        POINT pt = toPoint(caret);
        POINT save = pt;
        pt.x = 0;
        int start = fromPoint(pt);
        if (pt.y < lines.length() - 1)
          pt.y++;
        else
          pt.x = lines[pt.y].text.length();
        int end = fromPoint(pt);
        if (pCopyLine)
          pCopyLine->Release();
        pCopyLine = SetClipboard(CF_UNICODETEXT, CreateGlobalText(substring(start, end)));
        if (pCopyLine)
          pCopyLine->AddRef();
        replace(start, end, L"");
        caret = selStart = fromPoint(save);
        updateCaret();
      }
      break;
    case ID_EDIT_PASTE:
      {
        ClipboardReader reader(CF_UNICODETEXT);
        if (reader.getData())
        {
          if (OleIsCurrentClipboard(pCopyLine) == S_OK)
          {
            POINT pt = toPoint(caret);
            pt.x = 0;
            caret = selStart = fromPoint(pt);
          }
          selStart = caret = replace(selStart, caret, GetGlobalTextWide(reader.getData()));
          updateCaret();
        }
      }
      break;
    case ID_DEBUG_BREAKPOINT:
      {
        POINT pt = toPoint(caret);
        toggleBreakpoint(pt.y);
      }
      break;
    default:
      return M_UNHANDLED;
    }
    return 0;
  }
  return M_UNHANDLED;
}
Esempio n. 9
0
void Editor::paint()
{
  PAINTSTRUCT ps;
  HDC hPaintDC = BeginPaint(hWnd, &ps);

  if (hDC == NULL)
    hDC = CreateCompatibleDC(hPaintDC);
  if (hBitmap == NULL)
  {
    RECT rc;
    GetClientRect(hWnd, &rc);
    if (rc.right < 10) rc.right = 10;
    if (rc.bottom < 10) rc.bottom = 10;
    hBitmap = CreateCompatibleBitmap(hPaintDC, rc.right, rc.bottom);
    SelectObject(hDC, hBitmap);
  }

  Painter p(this, hDC);

  SetBkMode(hDC, OPAQUE);

  bool activeWnd = (GetFocus() == hWnd);

  if (settings->bpOffset)
  {
    SetBkColor(hDC, 0xF0F0F0);
    p.rc.top = ps.rcPaint.top;
    p.rc.bottom = ps.rcPaint.bottom;
    p.rc.left = -scrollPos.x * chSize.cx;
    p.rc.right = p.rc.left + settings->bpOffset;
    ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &p.rc, NULL, 0, NULL);
  }

  p.rc.top = ps.rcPaint.top;
  p.rc.bottom = p.rc.top;
  int absOffset = 0;
  for (int y = 0; y < lines.length(); y++)
  {
    p.init(y, absOffset);
    if (p.rc.top > ps.rcPaint.bottom)
      break;

    if (p.rc.bottom >= ps.rcPaint.top)
    {
      SelectObject(hDC, hFont);

      SetBkColor(hDC, settings->bgcolor);
      if (settings->lineNumbers.margin + settings->lineNumbers.fields)
      {
        p.rc.left = settings->bpOffset - scrollPos.x * chSize.cx;
        p.rc.right = p.rc.left + LeftMargin();
        ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &p.rc, NULL, 0, NULL);
      }
      if (settings->lineNumbers.fields)
      {
        p.rc.left = settings->bpOffset - scrollPos.x * chSize.cx;
        p.rc.right = settings->bpOffset + (settings->lineNumbers.fields - scrollPos.x) * chSize.cx;
        WideString lnum;
        if (settings->mode == 0)
          lnum = WideString(y + 1);
        else if (lines[y].timestamp)
          lnum = WideString(format_systime(lines[y].timestamp, "%H:%M:%S"));
        SetTextColor(hDC, settings->colors.line);
        DrawText(hDC, lnum.c_str(), lnum.length(), &p.rc, DT_RIGHT);
      }

      if (settings->mode == 0)
      {
        int prev = 0;
        do
        {
          p.next();
          SelectObject(hDC, hFont);
          if (p.lexemStart > prev)
            p.drawchunk(prev, p.lexemStart, 0);
          if (p.lexemStart < p.lexemEnd)
          {
            //if (p.lexemType == Painter::tFocusBracket || p.lexemType == Painter::tFocusKeyword)
            //  SelectObject(hDC, hFontBf);
            p.drawchunk(p.lexemStart, p.lexemEnd, p.lexemType);
          }
          prev = p.lexemEnd;
        } while (p.lexemType);
      }
      else
      {
        if (lines[y].ctx == Painter::tKeyword)
          SelectObject(hDC, hFontIt);
        p.drawchunk(0, lines[y].text.length(), lines[y].ctx);
      }

      if (p.rc.right < ps.rcPaint.right)
      {
        SetBkColor(hDC, settings->bgcolor);
        p.rc.left = p.rc.right;
        p.rc.right = ps.rcPaint.right;
        ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &p.rc, NULL, 0, NULL);
      }

      if (settings->mode == 0)
      {
        if (lines[y].breakpoint > 0)
          DrawIconEx(hDC, -scrollPos.x * chSize.cx, p.rc.top, icons[iBreakpoint],
            16, 16, 0, NULL, DI_IMAGE | DI_MASK);
        if (y == currentLine)
          DrawIconEx(hDC, -scrollPos.x * chSize.cx, p.rc.top, icons[iCurline],
            16, 16, 0, NULL, DI_IMAGE | DI_MASK);
      }
    }

    absOffset += lines[y].text.length() + 1;
  }

  if (p.rc.bottom < ps.rcPaint.bottom)
  {
    SetBkColor(hDC, settings->bgcolor);
    p.rc.top = p.rc.bottom;
    p.rc.bottom = ps.rcPaint.bottom;
    p.rc.left = settings->bpOffset - scrollPos.x * chSize.cx;
    p.rc.right = ps.rcPaint.right;
    ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &p.rc, NULL, 0, NULL);
  }

  BitBlt(hPaintDC, ps.rcPaint.left, ps.rcPaint.top,
    ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
    hDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);

  EndPaint(hWnd, &ps);
}