diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c b/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c index adbbb08c313..9c1a248757f 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c +++ b/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.c @@ -301,7 +301,9 @@ Copy(PGUI_CONSOLE_DATA GuiData); static VOID Paste(PGUI_CONSOLE_DATA GuiData); static VOID -UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord); +UpdateSelection(PGUI_CONSOLE_DATA GuiData, + PCOORD SelectionAnchor OPTIONAL, + PCOORD coord); static VOID Mark(PGUI_CONSOLE_DATA GuiData) @@ -309,30 +311,28 @@ Mark(PGUI_CONSOLE_DATA GuiData) PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; /* Clear the old selection */ - // UpdateSelection(GuiData, NULL); GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; /* Restart a new selection */ - GuiData->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X; - GuiData->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y; - GuiData->Selection.dwSelectionAnchor = GuiData->dwSelectionCursor; - UpdateSelection(GuiData, &GuiData->Selection.dwSelectionAnchor); + GuiData->dwSelectionCursor = ActiveBuffer->ViewOrigin; + UpdateSelection(GuiData, + &GuiData->dwSelectionCursor, + &GuiData->dwSelectionCursor); } static VOID SelectAll(PGUI_CONSOLE_DATA GuiData) { PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + COORD SelectionAnchor; /* Clear the old selection */ - // UpdateSelection(GuiData, NULL); GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; /* * The selection area extends to the whole screen buffer's width. */ - GuiData->Selection.dwSelectionAnchor.X = 0; - GuiData->Selection.dwSelectionAnchor.Y = 0; + SelectionAnchor.X = SelectionAnchor.Y = 0; GuiData->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1; /* @@ -358,7 +358,7 @@ SelectAll(PGUI_CONSOLE_DATA GuiData) /* Restart a new selection */ GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION; - UpdateSelection(GuiData, &GuiData->dwSelectionCursor); + UpdateSelection(GuiData, &SelectionAnchor, &GuiData->dwSelectionCursor); } static LRESULT @@ -678,18 +678,171 @@ SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect) Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit; } +VOID +GetSelectionBeginEnd(PCOORD Begin, PCOORD End, + PCOORD SelectionAnchor, + PSMALL_RECT SmallRect) +{ + if (Begin == NULL || End == NULL) return; + + *Begin = *SelectionAnchor; + End->X = (SelectionAnchor->X == SmallRect->Left) ? SmallRect->Right + /* Case X != Left, must be == Right */ : SmallRect->Left; + End->Y = (SelectionAnchor->Y == SmallRect->Top ) ? SmallRect->Bottom + /* Case Y != Top, must be == Bottom */ : SmallRect->Top; + + /* Exchange Begin / End if Begin > End lexicographically */ + if (Begin->Y > End->Y || (Begin->Y == End->Y && Begin->X > End->X)) + { + SHORT tmp; + + // End->X = InterlockedExchange16(&Begin->X, End->X); + tmp = Begin->X; + Begin->X = End->X; + End->X = tmp; + + // End->Y = InterlockedExchange16(&Begin->Y, End->Y); + tmp = Begin->Y; + Begin->Y = End->Y; + End->Y = tmp; + } +} + +static HRGN +CreateSelectionRgn(PGUI_CONSOLE_DATA GuiData, + BOOL LineSelection, + PCOORD SelectionAnchor, + PSMALL_RECT SmallRect) +{ + if (!LineSelection) + { + RECT rect; + SmallRectToRect(GuiData, &rect, SmallRect); + return CreateRectRgnIndirect(&rect); + } + else + { + HRGN SelRgn; + COORD Begin, End; + + GetSelectionBeginEnd(&Begin, &End, SelectionAnchor, SmallRect); + + if (Begin.Y == End.Y) + { + SMALL_RECT sr; + RECT r ; + + sr.Left = Begin.X; + sr.Top = Begin.Y; + sr.Right = End.X; + sr.Bottom = End.Y; + + // Debug thingie to see whether I can put this corner case + // together with the previous one. + if (SmallRect->Left != sr.Left || + SmallRect->Top != sr.Top || + SmallRect->Right != sr.Right || + SmallRect->Bottom != sr.Bottom) + { + DPRINT1("\n" + "SmallRect = (%d, %d, %d, %d)\n" + "sr = (%d, %d, %d, %d)\n" + "\n", + SmallRect->Left, SmallRect->Top, SmallRect->Right, SmallRect->Bottom, + sr.Left, sr.Top, sr.Right, sr.Bottom); + } + + SmallRectToRect(GuiData, &r, &sr); + SelRgn = CreateRectRgnIndirect(&r); + } + else + { + PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer; + + HRGN rg1, rg2, rg3; + SMALL_RECT sr1, sr2, sr3; + RECT r1 , r2 , r3 ; + + sr1.Left = Begin.X; + sr1.Top = Begin.Y; + sr1.Right = ActiveBuffer->ScreenBufferSize.X - 1; + sr1.Bottom = Begin.Y; + + sr2.Left = 0; + sr2.Top = Begin.Y + 1; + sr2.Right = ActiveBuffer->ScreenBufferSize.X - 1; + sr2.Bottom = End.Y - 1; + + sr3.Left = 0; + sr3.Top = End.Y; + sr3.Right = End.X; + sr3.Bottom = End.Y; + + SmallRectToRect(GuiData, &r1, &sr1); + SmallRectToRect(GuiData, &r2, &sr2); + SmallRectToRect(GuiData, &r3, &sr3); + + rg1 = CreateRectRgnIndirect(&r1); + rg2 = CreateRectRgnIndirect(&r2); + rg3 = CreateRectRgnIndirect(&r3); + + CombineRgn(rg1, rg1, rg2, RGN_XOR); + CombineRgn(rg1, rg1, rg3, RGN_XOR); + DeleteObject(rg3); + DeleteObject(rg2); + + SelRgn = rg1; + } + + return SelRgn; + } +} + static VOID -UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord) +PaintSelectionRect(PGUI_CONSOLE_DATA GuiData, PPAINTSTRUCT pps) +{ + HRGN rgnPaint = CreateRectRgnIndirect(&pps->rcPaint); + HRGN rgnSel = CreateSelectionRgn(GuiData, GuiData->LineSelection, + &GuiData->Selection.dwSelectionAnchor, + &GuiData->Selection.srSelection); + + /* Invert the selection */ + + int ErrorCode = CombineRgn(rgnPaint, rgnPaint, rgnSel, RGN_AND); + if (ErrorCode != ERROR && ErrorCode != NULLREGION) + { + InvertRgn(pps->hdc, rgnPaint); + } + + DeleteObject(rgnSel); + DeleteObject(rgnPaint); +} + +static VOID +UpdateSelection(PGUI_CONSOLE_DATA GuiData, + PCOORD SelectionAnchor OPTIONAL, + PCOORD coord) { PCONSOLE Console = GuiData->Console; - RECT oldRect; + HRGN oldRgn = CreateSelectionRgn(GuiData, GuiData->LineSelection, + &GuiData->Selection.dwSelectionAnchor, + &GuiData->Selection.srSelection); - SmallRectToRect(GuiData, &oldRect, &GuiData->Selection.srSelection); + /* Update the anchor if needed (use the old one if NULL) */ + if (SelectionAnchor) + GuiData->Selection.dwSelectionAnchor = *SelectionAnchor; if (coord != NULL) { - RECT newRect; SMALL_RECT rc; + HRGN newRgn; + + /* + * Pressing the Control key while selecting text, allows us to enter + * into line-selection mode, the selection mode of *nix terminals. + */ + BOOL OldLineSel = GuiData->LineSelection; + GuiData->LineSelection = !!(GetKeyState(VK_CONTROL) & 0x8000); /* Exchange left/top with right/bottom if required */ rc.Left = min(GuiData->Selection.dwSelectionAnchor.X, coord->X); @@ -697,34 +850,29 @@ UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord) rc.Right = max(GuiData->Selection.dwSelectionAnchor.X, coord->X); rc.Bottom = max(GuiData->Selection.dwSelectionAnchor.Y, coord->Y); - SmallRectToRect(GuiData, &newRect, &rc); + newRgn = CreateSelectionRgn(GuiData, GuiData->LineSelection, + &GuiData->Selection.dwSelectionAnchor, + &rc); if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) { - if (memcmp(&rc, &GuiData->Selection.srSelection, sizeof(SMALL_RECT)) != 0) + if (OldLineSel != GuiData->LineSelection || + memcmp(&rc, &GuiData->Selection.srSelection, sizeof(SMALL_RECT)) != 0) { - HRGN rgn1, rgn2; - /* Calculate the region that needs to be updated */ - if ((rgn1 = CreateRectRgnIndirect(&oldRect))) + if (oldRgn && newRgn && CombineRgn(newRgn, newRgn, oldRgn, RGN_XOR) != ERROR) { - if ((rgn2 = CreateRectRgnIndirect(&newRect))) - { - if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR) - { - InvalidateRgn(GuiData->hWindow, rgn1, FALSE); - } - DeleteObject(rgn2); - } - DeleteObject(rgn1); + InvalidateRgn(GuiData->hWindow, newRgn, FALSE); } } } else { - InvalidateRect(GuiData->hWindow, &newRect, FALSE); + InvalidateRgn(GuiData->hWindow, newRgn, FALSE); } + DeleteObject(newRgn); + GuiData->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY; GuiData->Selection.srSelection = rc; GuiData->dwSelectionCursor = *coord; @@ -737,7 +885,7 @@ UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord) /* Clear the old selection */ if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) { - InvalidateRect(GuiData->hWindow, &oldRect, FALSE); + InvalidateRgn(GuiData->hWindow, oldRgn, FALSE); } /* @@ -778,7 +926,7 @@ UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord) /* Clear the selection */ if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) { - InvalidateRect(GuiData->hWindow, &oldRect, FALSE); + InvalidateRgn(GuiData->hWindow, oldRgn, FALSE); } GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; @@ -787,6 +935,8 @@ UpdateSelection(PGUI_CONSOLE_DATA GuiData, PCOORD coord) /* Restore the console title */ SetWindowText(GuiData->hWindow, Console->Title.Buffer); } + + DeleteObject(oldRgn); } @@ -840,15 +990,10 @@ OnPaint(PGUI_CONSOLE_DATA GuiData) rcPaint.top, SRCCOPY); + /* Draw the selection region if needed */ if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) { - SmallRectToRect(GuiData, &rcPaint, &GuiData->Selection.srSelection); - - /* Invert the selection */ - if (IntersectRect(&rcPaint, &ps.rcPaint, &rcPaint)) - { - InvertRect(ps.hdc, &rcPaint); - } + PaintSelectionRect(GuiData, &ps); } LeaveCriticalSection(&GuiData->Lock); @@ -925,10 +1070,10 @@ OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) goto Quit; } else if ( VirtualKeyCode == VK_ESCAPE || - (VirtualKeyCode == 'C' && GetKeyState(VK_CONTROL) & 0x8000) ) + (VirtualKeyCode == 'C' && (GetKeyState(VK_CONTROL) & 0x8000)) ) { /* Cancel selection if ESC or Ctrl-C are pressed */ - UpdateSelection(GuiData, NULL); + UpdateSelection(GuiData, NULL, NULL); goto Quit; } @@ -936,7 +1081,7 @@ OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) { /* Keyboard selection mode */ BOOL Interpreted = FALSE; - BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000); + BOOL MajPressed = !!(GetKeyState(VK_SHIFT) & 0x8000); switch (VirtualKeyCode) { @@ -1017,10 +1162,9 @@ OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) if (Interpreted) { - if (!MajPressed) - GuiData->Selection.dwSelectionAnchor = GuiData->dwSelectionCursor; - - UpdateSelection(GuiData, &GuiData->dwSelectionCursor); + UpdateSelection(GuiData, + !MajPressed ? &GuiData->dwSelectionCursor : NULL, + &GuiData->dwSelectionCursor); } else if (!IsSystemKey(VirtualKeyCode)) { @@ -1037,7 +1181,7 @@ OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) if (!IsSystemKey(VirtualKeyCode)) { /* Clear the selection and send the key into the input buffer */ - UpdateSelection(GuiData, NULL); + UpdateSelection(GuiData, NULL, NULL); } else { @@ -1287,27 +1431,26 @@ OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) case WM_LBUTTONDOWN: { /* Clear the old selection */ - // UpdateSelection(GuiData, NULL); GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION; /* Restart a new selection */ - GuiData->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam); + GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam); SetCapture(GuiData->hWindow); GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; - UpdateSelection(GuiData, &GuiData->Selection.dwSelectionAnchor); + UpdateSelection(GuiData, + &GuiData->dwSelectionCursor, + &GuiData->dwSelectionCursor); break; } case WM_LBUTTONUP: { - // COORD c; - if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; - // c = PointToCoord(GuiData, lParam); + // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam); GuiData->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN; - // UpdateSelection(GuiData, &c); + // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor); ReleaseCapture(); break; @@ -1349,11 +1492,8 @@ OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) * Update the selection started with the single * left-click that preceded this double-click. */ - GuiData->Selection.dwSelectionAnchor = cL; - GuiData->dwSelectionCursor = cR; - GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; - UpdateSelection(GuiData, &GuiData->dwSelectionCursor); + UpdateSelection(GuiData, &cL, &cR); /* Ignore the next mouse move signal */ GuiData->IgnoreNextMouseSignal = TRUE; @@ -1381,13 +1521,12 @@ OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam) case WM_MOUSEMOVE: { - COORD c; - if (!(wParam & MK_LBUTTON)) break; if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break; - c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */ - UpdateSelection(GuiData, &c); + // TODO: Scroll buffer to bring SelectionCursor into view + GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam); + UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor); break; } @@ -1557,7 +1696,7 @@ Copy(PGUI_CONSOLE_DATA GuiData) } /* Clear the selection */ - UpdateSelection(GuiData, NULL); + UpdateSelection(GuiData, NULL, NULL); } VOID diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h b/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h index a6be0cf1146..f6caa6d57f5 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h +++ b/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h @@ -65,6 +65,7 @@ typedef struct _GUI_CONSOLE_DATA PCONSOLE_SCREEN_BUFFER ActiveBuffer; /* Pointer to the active screen buffer (then maybe the previous Console member is redundant?? Or not...) */ CONSOLE_SELECTION_INFO Selection; /* Contains information about the selection */ COORD dwSelectionCursor; /* Selection cursor position, most of the time different from Selection.dwSelectionAnchor */ + BOOL LineSelection; /* TRUE if line-oriented selection (a la *nix terminals), FALSE if block-oriented selection (default on Windows) */ GUI_CONSOLE_INFO GuiInfo; /* GUI terminal settings */ } GUI_CONSOLE_DATA, *PGUI_CONSOLE_DATA; diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/gui/text.c b/reactos/win32ss/user/winsrv/consrv/frontends/gui/text.c index 2de3b11ffd8..feb128bd19e 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/gui/text.c +++ b/reactos/win32ss/user/winsrv/consrv/frontends/gui/text.c @@ -18,6 +18,10 @@ #include "guiterm.h" +/* GLOBALS ********************************************************************/ + +#define IS_WHITESPACE(c) ((c) == L'\0' || (c) == L' ' || (c) == L'\t') + /* FUNCTIONS ******************************************************************/ COLORREF PaletteRGBFromAttrib(PCONSOLE Console, WORD Attribute) @@ -31,35 +35,33 @@ COLORREF PaletteRGBFromAttrib(PCONSOLE Console, WORD Attribute) return PALETTERGB(pe.peRed, pe.peGreen, pe.peBlue); } -VOID -GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, - PGUI_CONSOLE_DATA GuiData) +static VOID +CopyBlock(PTEXTMODE_SCREEN_BUFFER Buffer, + PSMALL_RECT Selection) { - /* - * This function supposes that the system clipboard was opened. - */ - /* * Pressing the Shift key while copying text, allows us to copy * text without newline characters (inline-text copy mode). */ - BOOL InlineCopyMode = (GetKeyState(VK_SHIFT) & 0x8000); + BOOL InlineCopyMode = !!(GetKeyState(VK_SHIFT) & 0x8000); HANDLE hData; PCHAR_INFO ptr; LPWSTR data, dstPos; ULONG selWidth, selHeight; - ULONG xPos, yPos, size; + ULONG xPos, yPos; + ULONG size; - selWidth = GuiData->Selection.srSelection.Right - GuiData->Selection.srSelection.Left + 1; - selHeight = GuiData->Selection.srSelection.Bottom - GuiData->Selection.srSelection.Top + 1; - DPRINT("Selection is (%d|%d) to (%d|%d)\n", - GuiData->Selection.srSelection.Left, - GuiData->Selection.srSelection.Top, - GuiData->Selection.srSelection.Right, - GuiData->Selection.srSelection.Bottom); + DPRINT("CopyBlock(%u, %u, %u, %u)\n", + Selection->Left, Selection->Top, Selection->Right, Selection->Bottom); -#define IS_WHITESPACE(c) ((c) == L'\0' || (c) == L' ' || (c) == L'\t') + /* Prevent against empty blocks */ + if (Selection == NULL) return; + if (Selection->Left > Selection->Right || Selection->Top > Selection->Bottom) + return; + + selWidth = Selection->Right - Selection->Left + 1; + selHeight = Selection->Bottom - Selection->Top + 1; /* Basic size for one line... */ size = selWidth; @@ -72,6 +74,11 @@ GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, */ size += (selWidth + (!InlineCopyMode ? 2 : 0)) * (selHeight - 1); } + else + { + DPRINT1("This case must never happen, because selHeight is at least == 1\n"); + } + size += 1; /* Null-termination */ size *= sizeof(WCHAR); @@ -94,8 +101,8 @@ GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, ULONG length = selWidth; ptr = ConioCoordToPointer(Buffer, - GuiData->Selection.srSelection.Left, - GuiData->Selection.srSelection.Top + yPos); + Selection->Left, + Selection->Top + yPos); /* Trim whitespace from the right */ while (length > 0) @@ -113,16 +120,15 @@ GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, * Sometimes, applications can put NULL chars into the screen-buffer * (this behaviour is allowed). Detect this and replace by a space. */ - dstPos[xPos] = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' '); + *dstPos++ = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' '); } - dstPos += length; /* Add newline characters if we are not in inline-text copy mode */ if (!InlineCopyMode) { if (yPos != (selHeight - 1)) { - wcscat(data, L"\r\n"); + wcscat(dstPos, L"\r\n"); dstPos += 2; } } @@ -135,6 +141,128 @@ GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, SetClipboardData(CF_UNICODETEXT, hData); } +static VOID +CopyLines(PTEXTMODE_SCREEN_BUFFER Buffer, + PCOORD Begin, + PCOORD End) +{ + HANDLE hData; + PCHAR_INFO ptr; + LPWSTR data, dstPos; + ULONG NumChars, size; + ULONG xPos, yPos, xBeg, xEnd; + + DPRINT("CopyLines((%u, %u) ; (%u, %u))\n", + Begin->X, Begin->Y, End->X, End->Y); + + /* Prevent against empty blocks... */ + if (Begin == NULL || End == NULL) return; + /* ... or malformed blocks */ + if (Begin->Y > End->Y || (Begin->Y == End->Y && Begin->X > End->X)) return; + + /* Compute the number of characters to copy */ + if (End->Y == Begin->Y) // top == bottom + { + NumChars = End->X - Begin->X + 1; + } + else // if (End->Y > Begin->Y) + { + NumChars = (Buffer->ScreenBufferSize.X - 1) - (Begin->X) + 1; + + if (Begin->Y + 1 <= End->Y - 1) + { + NumChars += ( (Buffer->ScreenBufferSize.X - 1) + 1 ) * + ( (End->Y - 1) - (Begin->Y + 1) + 1); + } + + NumChars += End->X + 1; + } + + size = (NumChars + 1) * sizeof(WCHAR); /* Null-terminated */ + + /* Allocate some memory area to be given to the clipboard, so it will not be freed here */ + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size); + if (hData == NULL) return; + + data = GlobalLock(hData); + if (data == NULL) + { + GlobalFree(hData); + return; + } + + DPRINT("Copying %d characters\n", NumChars); + dstPos = data; + + /* + * We need to walk per-lines, and not just looping in the big screen-buffer + * array, because of the way things are stored inside it. The downside is + * that it makes the code more complicated. + */ + for (yPos = Begin->Y; (yPos <= End->Y) && (NumChars > 0); yPos++) + { + xBeg = (yPos == Begin->Y ? Begin->X : 0); + xEnd = (yPos == End->Y ? End->X : Buffer->ScreenBufferSize.X - 1); + + ptr = ConioCoordToPointer(Buffer, xBeg, yPos); + + /* Copy only the characters, leave attributes alone */ + for (xPos = xBeg; (xPos <= xEnd) && (NumChars-- > 0); xPos++) + { + /* + * Sometimes, applications can put NULL chars into the screen-buffer + * (this behaviour is allowed). Detect this and replace by a space. + */ + *dstPos++ = (ptr[xPos].Char.UnicodeChar ? ptr[xPos].Char.UnicodeChar : L' '); + } + } + + DPRINT("Setting data <%S> to clipboard\n", data); + GlobalUnlock(hData); + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, hData); +} + + +VOID +GetSelectionBeginEnd(PCOORD Begin, PCOORD End, + PCOORD SelectionAnchor, + PSMALL_RECT SmallRect); + +VOID +GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, + PGUI_CONSOLE_DATA GuiData) +{ + /* + * This function supposes that the system clipboard was opened. + */ + + BOOL LineSelection = GuiData->LineSelection; + + DPRINT("Selection is (%d|%d) to (%d|%d) in %s mode\n", + GuiData->Selection.srSelection.Left, + GuiData->Selection.srSelection.Top, + GuiData->Selection.srSelection.Right, + GuiData->Selection.srSelection.Bottom, + (LineSelection ? "line" : "block")); + + if (!LineSelection) + { + CopyBlock(Buffer, &GuiData->Selection.srSelection); + } + else + { + COORD Begin, End; + + GetSelectionBeginEnd(&Begin, &End, + &GuiData->Selection.dwSelectionAnchor, + &GuiData->Selection.srSelection); + + CopyLines(Buffer, &Begin, &End); + } +} + VOID GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer, PGUI_CONSOLE_DATA GuiData)