added support for undo/redo using Ctrl-z/Ctrl-Shift-z in level editor
[rocksndiamonds.git] / src / editor.c
index 5eed006605c50a65ef0d33629739fe6e5018f797..c8444b1158c22f1acf52c5c0e94814e46f956be2 100644 (file)
@@ -11996,27 +11996,28 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
     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);
+    char *format = "%s%03d";
+
+    for (y = 0; y < height; y++)
+      for (x = 0; x < width; x++)
+       if ((draw_with_brush ? brush_buffer[x][y] : Feld[x][y]) > 999)
+         format = "%s%04d";
 
     for (y = 0; y < height; y++)
     {
       for (x = 0; x < width; x++)
       {
        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;
-       else if (IS_GROUP_ELEMENT(element))
-         element_mapped = EL_GROUP_START;
-       else if (element >= NUM_FILE_ELEMENTS)
-         element_mapped = EL_UNKNOWN;
+       if (element >= NUM_FILE_ELEMENTS)
+         element = EL_UNKNOWN;
 
        // 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)
-       snprintf(part, MAX_CB_PART_SIZE + 1, "%s%03d", prefix, element_mapped);
+       // - large tiles: `xxx or `xxxx (0x60 ASCII)
+       // - small tiles: ¸xxx or ¸xxxx (0xb8 ISO-8859-1, 0xc2b8 UTF-8)
+       snprintf(part, MAX_CB_PART_SIZE + 1, format, prefix, element);
        strcat(text, part);
       }
 
@@ -12098,7 +12099,7 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
        }
       }
 
-      // check if prefix found and followed by three numbers
+      // check if prefix found and followed by (at least) three digits
       if (prefix_found &&
          strlen(ptr) >= 3 &&
          ptr[0] >= '0' && ptr[0] <= '9' &&
@@ -12111,6 +12112,13 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
 
        ptr += 3;
 
+       // level sketch element number might consist of four digits
+       if (ptr[0] >= '0' && ptr[0] <= '9')
+       {
+         element = element * 10 + (ptr[0] - '0');
+         ptr++;
+       }
+
        if (element >= NUM_FILE_ELEMENTS)
          element = EL_UNKNOWN;
 
@@ -12347,6 +12355,16 @@ void CopyBrushToClipboard_Small(void)
   CopyBrushExt(0, 0, 0, 0, 0, CB_BRUSH_TO_CLIPBOARD_SMALL);
 }
 
+void UndoLevelEditorOperation(void)
+{
+  ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], -1);
+}
+
+void RedoLevelEditorOperation(void)
+{
+  ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], 3);
+}
+
 static void FloodFill(int from_x, int from_y, int fill_element)
 {
   FloodFillLevel(from_x, from_y, fill_element, Feld, lev_fieldx, lev_fieldy);
@@ -13774,6 +13792,10 @@ static void HandleControlButtons(struct GadgetInfo *gi)
                     button == 2 ? ed_tilesize_default :
                     button == 3 ? ed_tilesize / 2 : ed_tilesize);
 
+      // when using touch device, cycle through all zoom tilesizes
+      if (runtime.uses_touch_device && ed_tilesize > TILESIZE)
+       ed_tilesize = MICRO_TILESIZE;
+
       // limit zoom level by upper and lower bound
       ed_tilesize = MIN(MAX(MICRO_TILESIZE, ed_tilesize), TILESIZE);
 
@@ -13808,7 +13830,9 @@ static void HandleControlButtons(struct GadgetInfo *gi)
       break;
 
     case GADGET_ID_UNDO:
-      if (button == 1 && GetKeyModState() & (KMOD_Shift|KMOD_Control))
+      if (button < 0)  // keep button value (even if modifier keys are pressed)
+       button = -button;
+      else if (button == 1 && GetKeyModState() & (KMOD_Shift | KMOD_Control))
        button = 3;
 
       if (button == 1 && undo_buffer_steps == 0)
@@ -14059,7 +14083,6 @@ static void HandleControlButtons(struct GadgetInfo *gi)
 void HandleLevelEditorKeyInput(Key key)
 {
   char letter = getCharFromKey(key);
-  int button = MB_LEFTBUTTON;
 
   if (drawing_function == GADGET_ID_TEXT &&
       DrawLevelText(0, 0, 0, TEXT_QUERY_TYPING) == TRUE)
@@ -14072,150 +14095,141 @@ void HandleLevelEditorKeyInput(Key key)
       DrawLevelText(0, 0, 0, TEXT_NEWLINE);
     else if (key == KSYM_Escape)
       DrawLevelText(0, 0, 0, TEXT_END);
+
+    return;
   }
-  else if (button_status == MB_RELEASED)
-  {
-    int id = GADGET_ID_NONE;
-    int new_element_shift = element_shift;
-    int step = ED_ELEMENTLIST_BUTTONS_VERT - 1;
-    int i;
 
-    switch (key)
-    {
-      case KSYM_Left:
-       id = GADGET_ID_SCROLL_LEFT;
-       break;
-      case KSYM_Right:
-       id = GADGET_ID_SCROLL_RIGHT;
-       break;
-      case KSYM_Up:
-       id = GADGET_ID_SCROLL_UP;
-       break;
-      case KSYM_Down:
-       id = GADGET_ID_SCROLL_DOWN;
-       break;
+  int id = GADGET_ID_NONE;
+  int new_element_shift = element_shift;
+  int step = ED_ELEMENTLIST_BUTTONS_VERT - 1;
+  int button = MB_LEFTBUTTON;
+  int i;
 
-      case KSYM_Page_Up:
-      case KSYM_Page_Down:
-       step *= (key == KSYM_Page_Up ? -1 : +1);
-        element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
+  switch (key)
+  {
+    case KSYM_Left:
+      id = GADGET_ID_SCROLL_LEFT;
+      break;
+    case KSYM_Right:
+      id = GADGET_ID_SCROLL_RIGHT;
+      break;
+    case KSYM_Up:
+      id = GADGET_ID_SCROLL_UP;
+      break;
+    case KSYM_Down:
+      id = GADGET_ID_SCROLL_DOWN;
+      break;
 
-        if (element_shift < 0)
-          element_shift = 0;
-        if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
-          element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
+    case KSYM_Page_Up:
+    case KSYM_Page_Down:
+      step *= (key == KSYM_Page_Up ? -1 : +1);
+      element_shift += step * ED_ELEMENTLIST_BUTTONS_HORIZ;
 
-        ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
-                     GDI_SCROLLBAR_ITEM_POSITION,
-                     element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
+      if (element_shift < 0)
+       element_shift = 0;
+      if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
+       element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
 
-       ModifyEditorElementList();
+      ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
+                  GDI_SCROLLBAR_ITEM_POSITION,
+                  element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
 
-       break;
+      ModifyEditorElementList();
 
-      case KSYM_Home:
-      case KSYM_End:
-       element_shift = (key == KSYM_Home ? 0 :
-                        num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS);
+      break;
 
-       ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
-                    GDI_SCROLLBAR_ITEM_POSITION,
-                    element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
+    case KSYM_Home:
+    case KSYM_End:
+      element_shift = (key == KSYM_Home ? 0 :
+                      num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS);
 
-       ModifyEditorElementList();
+      ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
+                  GDI_SCROLLBAR_ITEM_POSITION,
+                  element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
 
-       break;
+      ModifyEditorElementList();
 
-      case KSYM_Insert:
-      case KSYM_Delete:
+      break;
 
-       // this is needed to prevent interference with running "True X-Mouse"
-       if (GetKeyModStateFromEvents() & KMOD_Control)
-         break;
+    case KSYM_Insert:
+    case KSYM_Delete:
 
-       // check for last or next editor cascade block in element list
-       for (i = 0; i < num_editor_elements; i++)
-       {
-         if ((key == KSYM_Insert && i == element_shift) ||
-             (key == KSYM_Delete && new_element_shift > element_shift))
-           break;
+      // this is needed to prevent interference with running "True X-Mouse"
+      if (GetKeyModStateFromEvents() & KMOD_Control)
+       break;
 
-         // jump to next cascade block (or to start of element list)
-         if (i == 0 || IS_EDITOR_CASCADE(editor_elements[i]))
-           new_element_shift = i;
-       }
+      // check for last or next editor cascade block in element list
+      for (i = 0; i < num_editor_elements; i++)
+      {
+       if ((key == KSYM_Insert && i == element_shift) ||
+           (key == KSYM_Delete && new_element_shift > element_shift))
+         break;
 
-       if (i < num_editor_elements)
-         element_shift = new_element_shift;
+       // jump to next cascade block (or to start of element list)
+       if (i == 0 || IS_EDITOR_CASCADE(editor_elements[i]))
+         new_element_shift = i;
+      }
 
-       if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
-         element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
+      if (i < num_editor_elements)
+       element_shift = new_element_shift;
 
-       ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
-                    GDI_SCROLLBAR_ITEM_POSITION,
-                    element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
+      if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
+       element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
 
-       ModifyEditorElementList();
+      ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL],
+                  GDI_SCROLLBAR_ITEM_POSITION,
+                  element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ, GDI_END);
 
-       break;
+      ModifyEditorElementList();
 
-      case KSYM_Escape:
-        if (edit_mode == ED_MODE_DRAWING)
-       {
-         RequestExitLevelEditor(setup.ask_on_escape_editor, TRUE);
-       }
-        else if (edit_mode == ED_MODE_INFO)
-       {
-         HandleControlButtons(level_editor_gadget[GADGET_ID_INFO]);
-       }
-        else if (edit_mode == ED_MODE_PROPERTIES)
-       {
-         HandleControlButtons(level_editor_gadget[GADGET_ID_PROPERTIES]);
-       }
-        else if (edit_mode == ED_MODE_PALETTE)
-       {
-         HandleControlButtons(level_editor_gadget[GADGET_ID_PALETTE]);
-       }
-       else            // should never happen
-       {
-         ChangeEditModeWindow(ED_MODE_DRAWING);
-       }
+      break;
 
-        break;
+    case KSYM_Escape:
+      if (edit_mode == ED_MODE_DRAWING)
+       RequestExitLevelEditor(setup.ask_on_escape_editor, TRUE);
+      else if (edit_mode == ED_MODE_INFO)
+       HandleControlButtons(level_editor_gadget[GADGET_ID_INFO]);
+      else if (edit_mode == ED_MODE_PROPERTIES)
+       HandleControlButtons(level_editor_gadget[GADGET_ID_PROPERTIES]);
+      else if (edit_mode == ED_MODE_PALETTE)
+       HandleControlButtons(level_editor_gadget[GADGET_ID_PALETTE]);
+      else             // should never happen
+       ChangeEditModeWindow(ED_MODE_DRAWING);
 
-      default:
-       break;
-    }
+      break;
 
-    if (id != GADGET_ID_NONE)
-      ClickOnGadget(level_editor_gadget[id], button);
-    else if (letter == '1' || letter == '?')
-      ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_LEFT], button);
-    else if (letter == '2')
-      ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_MIDDLE], button);
-    else if (letter == '3')
-      ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_RIGHT], button);
-    else if (letter == '.')
-      ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
-    else if (letter == 'U')
-      ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], 3);
-    else if (letter == '-' || key == KSYM_KP_Subtract)
-      ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 3);
-    else if (letter == '0' || key == KSYM_KP_0)
-      ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 2);
-    else if (letter == '+' || key == KSYM_KP_Add ||
-            letter == '=')     // ("Shift-=" is "+" on US keyboards)
-      ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 1);
-    else if (key == KSYM_Return ||
-            key == KSYM_space ||
-            key == setup.shortcut.toggle_pause)
-      ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button);
-    else
-      for (i = 0; i < ED_NUM_CTRL_BUTTONS; i++)
-       if (letter && letter == controlbutton_info[i].shortcut)
-         if (!anyTextGadgetActive())
-           ClickOnGadget(level_editor_gadget[i], button);
+    default:
+      break;
   }
+
+  if (id != GADGET_ID_NONE)
+    ClickOnGadget(level_editor_gadget[id], button);
+  else if (letter == '1' || letter == '?')
+    ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_LEFT], button);
+  else if (letter == '2')
+    ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_MIDDLE], button);
+  else if (letter == '3')
+    ClickOnGadget(level_editor_gadget[GADGET_ID_ELEMENT_RIGHT], button);
+  else if (letter == '.')
+    ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
+  else if (letter == 'U')
+    ClickOnGadget(level_editor_gadget[GADGET_ID_UNDO], 3);
+  else if (letter == '-' || key == KSYM_KP_Subtract)
+    ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 3);
+  else if (letter == '0' || key == KSYM_KP_0)
+    ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 2);
+  else if (letter == '+' || key == KSYM_KP_Add ||
+          letter == '=')       // ("Shift-=" is "+" on US keyboards)
+    ClickOnGadget(level_editor_gadget[GADGET_ID_ZOOM], 1);
+  else if (key == KSYM_Return ||
+          key == KSYM_space ||
+          key == setup.shortcut.toggle_pause)
+    ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button);
+  else
+    for (i = 0; i < ED_NUM_CTRL_BUTTONS; i++)
+      if (letter && letter == controlbutton_info[i].shortcut)
+       if (!anyTextGadgetActive())
+         ClickOnGadget(level_editor_gadget[i], button);
 }
 
 void HandleLevelEditorIdle(void)