From: Holger Schemel Date: Mon, 12 Nov 2018 23:31:34 +0000 (+0100) Subject: added support for level sketch copy/paste using Ctrl-c/v in level editor X-Git-Tag: 4.1.2.0~102 X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=commitdiff_plain;h=85ab2d263ae6041c7263a4330071512c436382f9 added support for level sketch copy/paste using Ctrl-c/v in level editor This enhances the existing support for dumping brushes from the level editor as level sketches (to STDOUT and/or "stdout.txt", which can then be added to posts in the R'n'D web forum), as follows: - Ctrl-c copies the current level (or brush) into the clipboard - Ctrl-x copies the current level, but as small sized level sketch - Ctrl-v pastes a level sketch from the clipboard into the current level (or brush, if active) This makes it easy to copy level sketches from the level editor to the R'n'D web forum, and also from the forum to the level editor (just temporarily go to the "reply" form for a post containing a level sketch, then copy the "source code" text of the level sketch to the clipboard using Ctrl-c, then paste it into the level editor using Ctrl-v). This will also make it easy to copy a whole playfield from one level file to another in the level editor (playfield size will automatically be adjusted to the size of the pasted playfield). --- diff --git a/src/editor.c b/src/editor.c index 57c79132..c993b6ea 100644 --- a/src/editor.c +++ b/src/editor.c @@ -3796,6 +3796,8 @@ static int use_permanent_palette = TRUE; #define PYSIZE (use_permanent_palette ? DYSIZE : SYSIZE) // forward declaration for internal use +static void CopyBrushToCursor(int, int); +static void DeleteBrushFromCursor(void); static void ModifyEditorCounterValue(int, int); static void ModifyEditorCounterLimits(int, int, int); static void ModifyEditorSelectboxValue(int, int); @@ -11895,12 +11897,23 @@ static void SelectArea(int from_x, int from_y, int to_x, int to_y, } // values for CopyBrushExt() -#define CB_AREA_TO_BRUSH 0 -#define CB_BRUSH_TO_CURSOR 1 -#define CB_BRUSH_TO_LEVEL 2 -#define CB_DELETE_OLD_CURSOR 3 -#define CB_DUMP_BRUSH 4 -#define CB_DUMP_BRUSH_SMALL 5 +#define CB_AREA_TO_BRUSH 0 +#define CB_BRUSH_TO_CURSOR 1 +#define CB_BRUSH_TO_LEVEL 2 +#define CB_DELETE_OLD_CURSOR 3 +#define CB_DUMP_BRUSH 4 +#define CB_DUMP_BRUSH_SMALL 5 +#define CB_CLIPBOARD_TO_BRUSH 6 +#define CB_BRUSH_TO_CLIPBOARD 7 +#define CB_BRUSH_TO_CLIPBOARD_SMALL 8 +#define CB_UPDATE_BRUSH_POSITION 9 + +#define MAX_CB_PART_SIZE 10 +#define MAX_CB_LINE_SIZE (MAX_LEV_FIELDX + 1) // text plus newline +#define MAX_CB_NUM_LINES (MAX_LEV_FIELDY) +#define MAX_CB_TEXT_SIZE (MAX_CB_LINE_SIZE * \ + MAX_CB_NUM_LINES * \ + MAX_CB_PART_SIZE) static void DrawBrushElement(int sx, int sy, int element, boolean change_level) { @@ -11913,26 +11926,31 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, static short brush_buffer[MAX_LEV_FIELDX][MAX_LEV_FIELDY]; static int brush_width, brush_height; static int last_cursor_x = -1, last_cursor_y = -1; - static boolean delete_old_brush; + static boolean delete_old_brush = FALSE; int new_element = BUTTON_ELEMENT(button); int x, y; if (mode == CB_DUMP_BRUSH || - mode == CB_DUMP_BRUSH_SMALL) + mode == CB_DUMP_BRUSH_SMALL || + mode == CB_BRUSH_TO_CLIPBOARD || + mode == CB_BRUSH_TO_CLIPBOARD_SMALL) { - if (!draw_with_brush) - { - Error(ERR_WARN, "no brush selected"); - + if (edit_mode != ED_MODE_DRAWING) return; - } - for (y = 0; y < brush_height; y++) + char part[MAX_CB_PART_SIZE + 1] = ""; + char text[MAX_CB_TEXT_SIZE + 1] = ""; + int width = (draw_with_brush ? brush_width : lev_fieldx); + int height = (draw_with_brush ? brush_height : lev_fieldy); + + for (y = 0; y < height; y++) { - for (x = 0; x < brush_width; x++) + for (x = 0; x < width; x++) { - int element = brush_buffer[x][y]; + int element = (draw_with_brush ? brush_buffer[x][y] : Feld[x][y]); int element_mapped = element; + char *prefix = (mode == CB_DUMP_BRUSH || + mode == CB_BRUSH_TO_CLIPBOARD ? "`" : "¸"); if (IS_CUSTOM_ELEMENT(element)) element_mapped = EL_CUSTOM_START; @@ -11941,13 +11959,176 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, else if (element >= NUM_FILE_ELEMENTS) element_mapped = EL_UNKNOWN; - // dump brush as level sketch text for the R'n'D forum: + // copy brush to level sketch text buffer for the R'n'D forum: // - large tiles: `xxx (0x60 ASCII) // - small tiles: ¸xxx (0xb8 ISO-8859-1, 0xc2b8 UTF-8) - printf("%s%03d", (mode == CB_DUMP_BRUSH ? "`" : "¸"), element_mapped); + snprintf(part, MAX_CB_PART_SIZE + 1, "%s%03d", prefix, element_mapped); + strcat(text, part); + } + + strcat(text, "\n"); + } + + if (mode == CB_BRUSH_TO_CLIPBOARD || + mode == CB_BRUSH_TO_CLIPBOARD_SMALL) + SDL_SetClipboardText(text); + else + printf("%s", text); + + return; + } + + if (mode == CB_CLIPBOARD_TO_BRUSH) + { + if (edit_mode != ED_MODE_DRAWING) + return; + + if (!SDL_HasClipboardText()) + { + Request("Clipboard is empty!", REQ_CONFIRM); + + return; + } + + boolean copy_to_brush = (draw_with_brush || + drawing_function == GADGET_ID_GRAB_BRUSH); + + // this will delete the old brush, if already drawing with a brush + if (copy_to_brush) + ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], MB_LEFTBUTTON); + + // initialization is required for "odd" (incomplete) clipboard content + for (x = 0; x < MAX_LEV_FIELDX; x++) + for (y = 0; y < MAX_LEV_FIELDY; y++) + brush_buffer[x][y] = EL_EMPTY; + + brush_width = 0; + brush_height = 0; + x = 0; + y = 0; + + char *clipboard_text = SDL_GetClipboardText(); + char *ptr = clipboard_text; + boolean stop = FALSE; + + while (*ptr && !stop) + { + boolean prefix_found = FALSE; + + // level sketch element number prefixes (may be multi-byte characters) + char *prefix_list[] = { "`", "¸" }; + int i; + + for (i = 0; i < ARRAY_SIZE(prefix_list); i++) + { + char *prefix = prefix_list[i]; + + // check if string is large enough for prefix + if (strlen(ptr) < strlen(prefix)) + { + stop = TRUE; + + break; + } + + // check if string starts with prefix + if (strPrefix(ptr, prefix)) + { + ptr += strlen(prefix); + + prefix_found = TRUE; + + break; + } + } + + // continue with next character if prefix not found + if (!prefix_found) + { + ptr++; // !!! FIX THIS for real UTF-8 handling !!! + + continue; + } + + // continue with next character if prefix not found + if (strlen(ptr) < 3) + break; + + if (ptr[0] >= '0' && ptr[0] <= '9' && + ptr[1] >= '0' && ptr[1] <= '9' && + ptr[2] >= '0' && ptr[2] <= '9') + { + int element = ((ptr[0] - '0') * 100 + + (ptr[1] - '0') * 10 + + (ptr[2] - '0')); + + ptr += 3; + + if (element >= NUM_FILE_ELEMENTS) + element = EL_UNKNOWN; + + brush_buffer[x][y] = element; + + brush_width = MAX(x + 1, brush_width); + brush_height = MAX(y + 1, brush_height); + + x++; + + if (x >= MAX_LEV_FIELDX || *ptr == '\n') + { + x = 0; + y++; + + if (y >= MAX_LEV_FIELDY) + stop = TRUE; + } } + } + + SDL_free(clipboard_text); + + if (brush_width == 0 || brush_height == 0) + { + Request("No level sketch found in clipboard!", REQ_CONFIRM); + + return; + } + + if (copy_to_brush) + { + struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_DRAWING_LEVEL]; + int mx, my; + + SDL_GetMouseState(&mx, &my); + + // if inside drawing area, activate and draw brush at last mouse position + if (mx >= gi->x && mx < gi->x + gi->width && + my >= gi->y && my < gi->y + gi->height) + CopyBrushToCursor(last_cursor_x, last_cursor_y); + + draw_with_brush = TRUE; + } + else + { + char request[100]; + + sprintf(request, "Replace level with %dx%d level sketch from clipboard?", + brush_width, brush_height); + + if (!Request(request, REQ_ASK)) + return; + + for (x = 0; x < MAX_LEV_FIELDX; x++) + for (y = 0; y < MAX_LEV_FIELDY; y++) + Feld[x][y] = brush_buffer[x][y]; + + lev_fieldx = level.fieldx = brush_width; + lev_fieldy = level.fieldy = brush_height; + + SetBorderElement(); - printf("\n"); + DrawEditModeWindow(); + CopyLevelToUndoBuffer(UNDO_IMMEDIATE); } return; @@ -12005,6 +12186,7 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, !IN_LEV_FIELD(cursor_x + level_xpos, cursor_y + level_ypos)) { delete_old_brush = FALSE; + return; } @@ -12042,8 +12224,15 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y, last_cursor_x = cursor_x; last_cursor_y = cursor_y; + delete_old_brush = TRUE; } + + if (mode == CB_UPDATE_BRUSH_POSITION) + { + last_cursor_x = from_x; + last_cursor_y = from_y; + } } static void CopyAreaToBrush(int from_x, int from_y, int to_x, int to_y, @@ -12062,6 +12251,11 @@ static void CopyBrushToCursor(int x, int y) CopyBrushExt(x, y, 0, 0, 0, CB_BRUSH_TO_CURSOR); } +static void UpdateBrushPosition(int x, int y) +{ + CopyBrushExt(x, y, 0, 0, 0, CB_UPDATE_BRUSH_POSITION); +} + static void DeleteBrushFromCursor(void) { CopyBrushExt(0, 0, 0, 0, 0, CB_DELETE_OLD_CURSOR); @@ -12077,6 +12271,21 @@ void DumpBrush_Small(void) CopyBrushExt(0, 0, 0, 0, 0, CB_DUMP_BRUSH_SMALL); } +void CopyClipboardToBrush(void) +{ + CopyBrushExt(0, 0, 0, 0, 0, CB_CLIPBOARD_TO_BRUSH); +} + +void CopyBrushToClipboard(void) +{ + CopyBrushExt(0, 0, 0, 0, 0, CB_BRUSH_TO_CLIPBOARD); +} + +void CopyBrushToClipboard_Small(void) +{ + CopyBrushExt(0, 0, 0, 0, 0, CB_BRUSH_TO_CLIPBOARD_SMALL); +} + static void FloodFill(int from_x, int from_y, int fill_element) { FloodFillLevel(from_x, from_y, fill_element, Feld, lev_fieldx, lev_fieldy); @@ -14131,6 +14340,9 @@ static void HandleDrawingAreaInfo(struct GadgetInfo *gi) else DeleteBrushFromCursor(); } + + if (!draw_with_brush) + UpdateBrushPosition(sx, sy); } else if (actual_drawing_function == GADGET_ID_PICK_ELEMENT) { diff --git a/src/editor.h b/src/editor.h index 662d2093..09c2ccac 100644 --- a/src/editor.h +++ b/src/editor.h @@ -27,4 +27,8 @@ void PrintEditorElementList(void); void DumpBrush(void); void DumpBrush_Small(void); +void CopyClipboardToBrush(void); +void CopyBrushToClipboard(void); +void CopyBrushToClipboard_Small(void); + #endif diff --git a/src/events.c b/src/events.c index ef78d9c9..5a3d0ca8 100644 --- a/src/events.c +++ b/src/events.c @@ -1778,6 +1778,22 @@ static void HandleKeysSpecial(Key key) { DumpBrush_Small(); } + + if (GetKeyModState() & (KMOD_Control | KMOD_Meta)) + { + if (letter == 'x') // copy brush to clipboard (small size) + { + CopyBrushToClipboard_Small(); + } + else if (letter == 'c') // copy brush to clipboard (normal size) + { + CopyBrushToClipboard(); + } + else if (letter == 'v') // paste brush from Clipboard + { + CopyClipboardToBrush(); + } + } } // special key shortcuts for all game modes