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)
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;
/*
/* Restart a new selection */
GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION;
- UpdateSelection(GuiData, &GuiData->dwSelectionCursor);
+ UpdateSelection(GuiData, &SelectionAnchor, &GuiData->dwSelectionCursor);
}
static LRESULT
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
+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 coord)
+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);
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;
/* Clear the old selection */
if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
{
- InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
+ InvalidateRgn(GuiData->hWindow, oldRgn, FALSE);
}
/*
/* 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;
/* Restore the console title */
SetWindowText(GuiData->hWindow, Console->Title.Buffer);
}
+
+ DeleteObject(oldRgn);
}
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);
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;
}
{
/* Keyboard selection mode */
BOOL Interpreted = FALSE;
- BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000);
+ BOOL MajPressed = !!(GetKeyState(VK_SHIFT) & 0x8000);
switch (VirtualKeyCode)
{
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))
{
if (!IsSystemKey(VirtualKeyCode))
{
/* Clear the selection and send the key into the input buffer */
- UpdateSelection(GuiData, NULL);
+ UpdateSelection(GuiData, NULL, NULL);
}
else
{
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;
* 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;
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;
}
}
/* Clear the selection */
- UpdateSelection(GuiData, NULL);
+ UpdateSelection(GuiData, NULL, NULL);
}
VOID
#include "guiterm.h"
+/* GLOBALS ********************************************************************/
+
+#define IS_WHITESPACE(c) ((c) == L'\0' || (c) == L' ' || (c) == L'\t')
+
/* FUNCTIONS ******************************************************************/
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;
*/
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);
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)
* 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;
}
}
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)