Implement Edit Positon multi-dropping
authorH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 28 Feb 2018 14:44:41 +0000 (15:44 +0100)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 28 Feb 2018 16:15:37 +0000 (17:15 +0100)
Right-clicking a piece in Edit Position mode now 'lifts it off the board',
and makes it available for dropping multiple times, by left-clicking
empty squares while no piece is selected (highlighted) for moving.
Left-clicks on occupied squares select the piece for moving as usual,
but this does not erase the memory of the 'lifted' piece types. This way
click-click moves remain possible in between drop events, e.g. to get a
piece out of the way.
 The type of the piece that will be dropped is indicated in the message
field. Right-clicking an empty square (which is an invalid action, as empty
squares cannot be lifted for dropping) pops up a note with an overview of
what can be done in Edit Position mode.
 This is an alternative for sweep selection as a method of dropping pieces
on the board. For now it replaces the piece menu. Multiple drop clicks to
the same square will increment the piece type, so piece types not in the
original position can still be created. It doesn't seem possible to do
anything with a board that is completely empty, though!

backend.c

index 0e04a16..2927c42 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -7182,7 +7182,7 @@ UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
            DrawPosition(FALSE, boards[currentMove]);
            return;
        } else if (toX >= 0 && toY >= 0) {
-           if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
+           if(toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
                ChessSquare p = boards[0][rf][ff];
                if(PieceToChar(p) == '+') gatingPiece = CHUDEMOTED(p); else
                if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); else
@@ -7610,6 +7610,7 @@ void ReportClick(char *action, int x, int y)
 Boolean right; // instructs front-end to use button-1 events as if they were button 3
 Boolean deferChoice;
 int createX = -1, createY = -1; // square where we last created a piece in EditPosition mode
+ChessSquare selectedType = EmptySquare;
 
 void
 LeftClick (ClickType clickType, int xPix, int yPix)
@@ -7633,6 +7634,19 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        x = BOARD_WIDTH - 1 - x;
     }
 
+    if(gameMode == EditPosition && fromX < 0 && selectedType != EmptySquare &&
+       (boards[currentMove][y][x] == EmptySquare || x == createX && y == createY)) { // placement click
+       if(clickType == Press) {
+           ChessSquare newType = boards[currentMove][y][x];
+           if(newType != EmptySquare) {
+               do { if(++newType == EmptySquare) newType = WhitePawn; } while(PieceToChar(newType) == '.');
+           } else newType = selectedType;
+           EditPositionMenuEvent(newType, x, y);
+           createX = x; createY = y; // remember where we last dropped
+       }
+       return;
+    }
+
     // map clicks in offsetted holdings back to true coords (or switch the offset)
     if(x == BOARD_RGHT+1) {
        if(handOffsets & 1) {
@@ -8042,6 +8056,13 @@ LeftClick (ClickType clickType, int xPix, int yPix)
     }
 }
 
+void
+Deselect ()
+{
+    fromX = -1;
+    ClearHighlights();
+}
+
 int
 RightClick (ClickType action, int x, int y, int *fromX, int *fromY)
 {   // front-end-free part taken out of PieceMenuPopup
@@ -8086,8 +8107,32 @@ RightClick (ClickType action, int x, int y, int *fromX, int *fromY)
       case EditPosition:
        if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;
        if (xSqr < 0 || ySqr < 0) return -1;
-       if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
+//     if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
        if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
+       if(appData.pieceMenu) { // select click
+           ChessSquare newType = boards[currentMove][ySqr][xSqr];
+           boards[currentMove][ySqr][xSqr] = EmptySquare;
+           if(newType == EmptySquare && selectedType == EmptySquare) { // click on empty summons context menu if not deselect
+               DisplayNote(  _("To edit the position you can:\n"
+                               "* Move pieces around with left button\n"
+                               "* Copy pieces by moving with Ctrl key pressed\n"
+                               "   OR by starting the move with a double-click\n"
+                               "* Click a K, R or P a second time to toggle its rights\n"
+                               "* 'Lift' piece with right-click for multi-dropping\n"
+                               "* Drop a piece of the lifted type by left-click on empty\n"
+                               "* Adjust the type of a dropped piece by clicking it again\n"
+                               "* Click a clock to set the side to move"));
+               return -2;
+           }
+           selectedType = newType; Deselect();
+           if(selectedType == EmptySquare) DisplayMessage("", ""); else {
+               char buf[MSG_SIZ];
+               snprintf(buf, MSG_SIZ, "left-click places %s %c", newType < BlackPawn ? _("white") : _("black"), ToUpper(PieceToChar(newType)));
+               DisplayMessage("", buf);
+           }
+            DrawPosition(FALSE, boards[currentMove]);
+           return -2;
+       }
        if(xSqr == createX && ySqr == createY && xSqr != BOARD_LEFT-2 && xSqr != BOARD_RGHT+1) {
            ChessSquare p = boards[currentMove][ySqr][xSqr];
            do { if(++p == EmptySquare) p = WhitePawn; } while(PieceToChar(p) == '.');
@@ -15578,6 +15623,7 @@ EditPositionDone (Boolean fakeRights)
 {
     int king = gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing;
 
+    selectedType = EmptySquare;
     startedFromSetupPosition = TRUE;
     InitChessProgram(&first, FALSE);
     if(fakeRights) { // [HGM] suppress this if we just pasted a FEN.