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; }
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; }
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); } }
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); }
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); } } }
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(); }
// 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); } }
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; }
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); }