DEADSOFTWARE

Main: Implement fast panel/trigger painting
[d2df-editor.git] / src / editor / f_main.pas
index 2b9ddee5a2a3f6a26c8311f6984b84d4655e3836..578d950a500a77af475424140529b87e717e5bba 100644 (file)
@@ -26,6 +26,7 @@ type
     miSaveMapAs: TMenuItem;
     miOpenWadMap: TMenuItem;
     miLine1: TMenuItem;
+    miReopenMap: TMenuItem;
     miSaveMiniMap: TMenuItem;
     miDeleteMap: TMenuItem;
     miPackMap: TMenuItem;
@@ -204,11 +205,13 @@ type
     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
+    procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
     procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
     procedure FormResize(Sender: TObject);
     procedure lbTextureListClick(Sender: TObject);
     procedure lbTextureListDrawItem(Control: TWinControl; Index: Integer;
       ARect: TRect; State: TOwnerDrawState);
+    procedure miReopenMapClick(Sender: TObject);
     procedure RenderPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
     procedure RenderPanelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
     procedure RenderPanelMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
@@ -262,6 +265,8 @@ type
     procedure OnIdle(Sender: TObject; var Done: Boolean);
   public
     procedure RefreshRecentMenu();
+    procedure OpenMapFile(FileName: String);
+    function RenderMousePos(): TPoint;
   end;
 
 const
@@ -293,6 +298,7 @@ var
   DrawPanelSize: Boolean;
   BackColor: TColor;
   PreviewColor: TColor;
+  UseCheckerboard: Boolean;
   Scale: Byte;
   RecentCount: Integer;
   RecentFiles: TStringList;
@@ -311,7 +317,7 @@ var
 
   LayerEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
     (True, True, True, True, True, True, True, True, True);
-  PreviewMode: Boolean = False;
+  PreviewMode: Byte = 0;
   gLanguage: String;
 
   FormCaption: String;
@@ -382,7 +388,7 @@ const
   SELECTFLAG_SHOTPANEL  = 7;
   SELECTFLAG_SELECTED   = 8;
 
-  RECENT_FILES_MENU_START = 11;
+  RECENT_FILES_MENU_START = 12;
 
   CLIPBOARD_SIG         = 'DF:ED';
 
@@ -435,6 +441,7 @@ var
   MouseRDown: Boolean;
   MouseLDownPos: Types.TPoint;
   MouseRDownPos: Types.TPoint;
+  WASDOffset: TPoint;
 
   SelectFlag: Byte = SELECTFLAG_NONE;
   MouseAction: Byte = MOUSEACTION_NONE;
@@ -1903,6 +1910,9 @@ begin
   else
     SectionName := aSection;
 
+  if aWAD = '' then
+    aWAD := _lc[I_WAD_SPECIAL_MAP];
+
   if aWAD = _lc[I_WAD_SPECIAL_MAP] then
     begin // Файл карты
       g_ProcessResourceStr(OpenedMap, @fn, nil, nil);
@@ -1961,14 +1971,21 @@ begin
       begin // Аним. текстура
         GetFrame(FullResourceName, Data, FrameLen, Width, Height);
 
-        if g_CreateTextureMemorySize(Data, FrameLen, ResourceName, 0, 0, Width, Height, 1) then
-          a := MainForm.lbTextureList.Items.Add(ResourceName);
+        if not g_CreateTextureMemorySize(Data, FrameLen, ResourceName, 0, 0, Width, Height, 1) then
+          ok := False;
+        a := MainForm.lbTextureList.Items.Add(ResourceName);
       end
     else // Обычная текстура
       begin
-        if g_CreateTextureWAD(ResourceName, FullResourceName) then
-          a := MainForm.lbTextureList.Items.Add(ResourceName);
+        if not g_CreateTextureWAD(ResourceName, FullResourceName) then
+          ok := False;
+        a := MainForm.lbTextureList.Items.Add(ResourceName);
       end;
+    if (not ok) and (slInvalidTextures.IndexOf(ResourceName) = -1) then
+    begin
+      slInvalidTextures.Add(ResourceName);
+      ok := True;
+    end;
     if (a > -1) and (not silent) then
       SelectTexture(a);
   end;
@@ -1996,6 +2013,7 @@ var
   MapName: String;
   idx: Integer;
 begin
+  SelectMapForm.Caption := _lc[I_CAP_OPEN];
   SelectMapForm.GetMaps(FileName);
 
   if (FileName = OpenedWAD) and
@@ -2326,7 +2344,8 @@ begin
   Result := Res;
 end;
 
-procedure StringToCopyBuffer(Str: String; var CopyBuf: TCopyRecArray);
+procedure StringToCopyBuffer(Str: String; var CopyBuf: TCopyRecArray;
+  var pmin: TPoint);
 var
   i, j, t: Integer;
 
@@ -2408,6 +2427,8 @@ begin
             PanelType := StrToIntDef(GetNext(), PANEL_WALL);
             X := StrToIntDef(GetNext(), 0);
             Y := StrToIntDef(GetNext(), 0);
+            pmin.X := Min(X, pmin.X);
+            pmin.Y := Min(Y, pmin.Y);
             Width := StrToIntDef(GetNext(), 16);
             Height := StrToIntDef(GetNext(), 16);
             TextureName := GetNext();
@@ -2422,6 +2443,8 @@ begin
           ItemType := StrToIntDef(GetNext(), ITEM_MEDKIT_SMALL);
           X := StrToIntDef(GetNext(), 0);
           Y := StrToIntDef(GetNext(), 0);
+          pmin.X := Min(X, pmin.X);
+          pmin.Y := Min(Y, pmin.Y);
           OnlyDM := (GetNext() = '1');
           Fall := (GetNext() = '1');
         end;
@@ -2432,6 +2455,8 @@ begin
           MonsterType := StrToIntDef(GetNext(), MONSTER_DEMON);
           X := StrToIntDef(GetNext(), 0);
           Y := StrToIntDef(GetNext(), 0);
+          pmin.X := Min(X, pmin.X);
+          pmin.Y := Min(Y, pmin.Y);
 
           if GetNext() = '1' then
             Direction := D_LEFT
@@ -2445,6 +2470,8 @@ begin
           AreaType := StrToIntDef(GetNext(), AREA_PLAYERPOINT1);
           X := StrToIntDef(GetNext(), 0);
           Y := StrToIntDef(GetNext(), 0);
+          pmin.X := Min(X, pmin.X);
+          pmin.Y := Min(Y, pmin.Y);
           if GetNext() = '1' then
             Direction := D_LEFT
           else
@@ -2457,6 +2484,8 @@ begin
           TriggerType := StrToIntDef(GetNext(), TRIGGER_EXIT);
           X := StrToIntDef(GetNext(), 0);
           Y := StrToIntDef(GetNext(), 0);
+          pmin.X := Min(X, pmin.X);
+          pmin.Y := Min(Y, pmin.Y);
           Width := StrToIntDef(GetNext(), 16);
           Height := StrToIntDef(GetNext(), 16);
           ActivateType := StrToIntDef(GetNext(), 0);
@@ -2466,6 +2495,34 @@ begin
 
           for j := 0 to 127 do
             Data.Default[j] := StrToIntDef(GetNext(), 0);
+
+          case TriggerType of
+            TRIGGER_TELEPORT:
+              begin
+                pmin.X := Min(Data.TargetPoint.X, pmin.X);
+                pmin.Y := Min(Data.TargetPoint.Y, pmin.Y);
+              end;
+            TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
+              begin
+                pmin.X := Min(Data.tX, pmin.X);
+                pmin.Y := Min(Data.tY, pmin.Y);
+              end;
+            TRIGGER_SPAWNMONSTER:
+              begin
+                pmin.X := Min(Data.MonPos.X, pmin.X);
+                pmin.Y := Min(Data.MonPos.Y, pmin.Y);
+              end;
+            TRIGGER_SPAWNITEM:
+              begin
+                pmin.X := Min(Data.ItemPos.X, pmin.X);
+                pmin.Y := Min(Data.ItemPos.Y, pmin.Y);
+              end;
+            TRIGGER_SHOT:
+              begin
+                pmin.X := Min(Data.ShotPos.X, pmin.X);
+                pmin.Y := Min(Data.ShotPos.Y, pmin.Y);
+              end;
+          end;
         end;
     end;
   end;
@@ -2651,6 +2708,7 @@ begin
   DrawPanelSize := config.ReadBool('Editor', 'DrawPanelSize', True);
   BackColor := config.ReadInt('Editor', 'BackColor', $7F6040);
   PreviewColor := config.ReadInt('Editor', 'PreviewColor', $00FF00);
+  UseCheckerboard := config.ReadBool('Editor', 'UseCheckerboard', True);
   gColorEdge := config.ReadInt('Editor', 'EdgeColor', COLOR_EDGE);
   gAlphaEdge := config.ReadInt('Editor', 'EdgeAlpha', ALPHA_EDGE);
   if gAlphaEdge = 255 then
@@ -2772,7 +2830,7 @@ begin
   end;
 
 // Рисуем сетку:
-  if DotEnable and (not PreviewMode) then
+  if DotEnable and (PreviewMode = 0) then
   begin
     if DotSize = 2 then
       a := -1
@@ -2789,13 +2847,19 @@ begin
 
 // Превью текстуры:
   if (lbTextureList.ItemIndex <> -1) and (cbPreview.Checked) and
-     (not IsSpecialTextureSel()) and (not PreviewMode) then
+     (not IsSpecialTextureSel()) and (PreviewMode = 0) then
   begin
     if not g_GetTexture(SelectedTexture(), ID) then
       g_GetTexture('NOTEXTURE', ID);
     g_GetTextureSizeByID(ID, Width, Height);
-    if g_GetTexture('PREVIEW', PID) then
-       e_DrawFill(PID, RenderPanel.Width-Width, RenderPanel.Height-Height, Width div 16 + 1, Height div 16 + 1, 0, True, False);
+    if UseCheckerboard then
+    begin
+      if g_GetTexture('PREVIEW', PID) then
+        e_DrawFill(PID, RenderPanel.Width-Width, RenderPanel.Height-Height, Width div 16 + 1, Height div 16 + 1, 0, True, False);
+    end else
+      e_DrawFillQuad(RenderPanel.Width-Width-2, RenderPanel.Height-Height-2,
+                     RenderPanel.Width-1, RenderPanel.Height-1,
+                     GetRValue(PreviewColor), GetGValue(PreviewColor), GetBValue(PreviewColor), 0);
     e_Draw(ID, RenderPanel.Width-Width, RenderPanel.Height-Height, 0, True, False);
   end;
 
@@ -2884,8 +2948,9 @@ begin
       g_GetTexture('NOTEXTURE', ID);
     g_GetTextureSizeByID(ID, Width, Height);
     with DrawRect^ do
-      e_DrawFill(ID, Min(Left, Right), Min(Top, Bottom), Abs(Right-Left) div Width,
-                 Abs(Bottom-Top) div Height, 0, True, False);
+      if (Abs(Right-Left) >= Width) and (Abs(Bottom-Top) >= Height) then
+        e_DrawFill(ID, Min(Left, Right), Min(Top, Bottom), Abs(Right-Left) div Width,
+                   Abs(Bottom-Top) div Height, 64, True, False);
   end;
 
 // Прямоугольник выделения:
@@ -2894,7 +2959,8 @@ begin
       e_DrawQuad(Left, Top, Right-1, Bottom-1, 255, 255, 255);
 
 // Чертим мышью панель/триггер или меняем мышью их размер:
-  if (MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER, MOUSEACTION_RESIZE]) and
+  if (((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
+     not(ssCtrl in GetKeyShiftState())) or (MouseAction = MOUSEACTION_RESIZE)) and
      (DrawPanelSize) then
   begin
     e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 192, 192, 192, 127);
@@ -2904,7 +2970,7 @@ begin
       begin // Чертим новый
         PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH],
                           [Abs(MousePos.X-MouseLDownPos.X)]), gEditorFont);
-        PrintBlack(MousePos.X+2, MousePos.Y+14, Format(_glc[I_HINT_HEIGHT],
+        PrintBlack(MousePos.X+2, MousePos.Y+16, Format(_glc[I_HINT_HEIGHT],
                           [Abs(MousePos.Y-MouseLDownPos.Y)]), gEditorFont);
       end
     else // Растягиваем существующий
@@ -2923,7 +2989,7 @@ begin
 
         PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH], [Width]),
                           gEditorFont);
-        PrintBlack(MousePos.X+2, MousePos.Y+14, Format(_glc[I_HINT_HEIGHT], [Height]),
+        PrintBlack(MousePos.X+2, MousePos.Y+16, Format(_glc[I_HINT_HEIGHT], [Height]),
                           gEditorFont);
       end;
   end;
@@ -3547,6 +3613,8 @@ var
   IDArray: DWArray;
   rRect: TRectWH;
   rSelectRect: Boolean;
+  wWidth, wHeight: Word;
+  TextureID: DWORD;
 begin
   if Button = mbLeft then
     MouseLDown := False;
@@ -3555,14 +3623,18 @@ begin
 
   DrawRect := nil;
   ResizeType := RESIZETYPE_NONE;
+  TextureID := 0;
 
   if Button = mbLeft then // Left Mouse Button
     begin
       if MouseAction <> MOUSEACTION_NONE then
       begin // Было действие мышью
-      // Мышь сдвинулась во время удержания клавиши:
-        if (MousePos.X <> MouseLDownPos.X) and
-           (MousePos.Y <> MouseLDownPos.Y) then
+      // Мышь сдвинулась во время удержания клавиши,
+      // либо активирован режим быстрого рисования:
+        if ((MousePos.X <> MouseLDownPos.X) and
+           (MousePos.Y <> MouseLDownPos.Y)) or
+           ((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
+           (ssCtrl in Shift)) then
           case MouseAction of
           // Рисовали панель:
             MOUSEACTION_DRAWPANEL:
@@ -3592,8 +3664,25 @@ begin
 
                     Panel.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
                     Panel.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
-                    Panel.Width := Abs(MousePos.X-MouseLDownPos.X);
-                    Panel.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                    if ssCtrl in Shift then
+                      begin
+                        wWidth := DotStep;
+                        wHeight := DotStep;
+                        if (lbTextureList.ItemIndex <> -1) and
+                           (not IsSpecialTextureSel()) then
+                        begin
+                          if not g_GetTexture(SelectedTexture(), TextureID) then
+                            g_GetTexture('NOTEXTURE', TextureID);
+                          g_GetTextureSizeByID(TextureID, wWidth, wHeight);
+                        end;
+                        Panel.Width := wWidth;
+                        Panel.Height := wHeight;
+                      end
+                    else
+                      begin
+                        Panel.Width := Abs(MousePos.X-MouseLDownPos.X);
+                        Panel.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                      end;
 
                   // Лифты, блокМон или отсутствие текстуры - пустая текстура:
                     if (lbPanelType.ItemIndex in [9, 10, 11, 12, 13]) or
@@ -3635,8 +3724,18 @@ begin
               begin
                 trigger.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
                 trigger.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
-                trigger.Width := Abs(MousePos.X-MouseLDownPos.X);
-                trigger.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                if ssCtrl in Shift then
+                  begin
+                    wWidth := DotStep;
+                    wHeight := DotStep;
+                    trigger.Width := wWidth;
+                    trigger.Height := wHeight;
+                  end
+                else
+                  begin
+                    trigger.Width := Abs(MousePos.X-MouseLDownPos.X);
+                    trigger.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                  end;
 
                 trigger.Enabled := True;
                 trigger.TriggerType := lbTriggersList.ItemIndex+1;
@@ -3899,14 +3998,22 @@ begin
   Draw();
 end;
 
+function TMainForm.RenderMousePos(): Types.TPoint;
+begin
+  Result := RenderPanel.ScreenToClient(Mouse.CursorPos);
+end;
+
 procedure TMainForm.RenderPanelMouseMove(Sender: TObject;
   Shift: TShiftState; X, Y: Integer);
 var
   sX, sY: Integer;
   dWidth, dHeight: Integer;
   _id: Integer;
+  TextureID: DWORD;
+  wWidth, wHeight: Word;
 begin
   _id := GetFirstSelected();
+  TextureID := 0;
 
 // Рисуем панель с текстурой, сетка - размеры текстуры:
   if (MouseAction = MOUSEACTION_DRAWPANEL) and
@@ -3981,8 +4088,10 @@ begin
       if MouseAction = MOUSEACTION_MOVEOBJ then
         begin
           MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift,
-                              MousePos.X-LastMovePoint.X,
-                              MousePos.Y-LastMovePoint.Y);
+                              MousePos.X-LastMovePoint.X+WASDOffset.X,
+                              MousePos.Y-LastMovePoint.Y+WASDOffset.Y);
+          WASDOffset.X := 0;
+          WASDOffset.Y := 0;
         end
       else
       // Меняем размер выделенного объекта:
@@ -3991,8 +4100,10 @@ begin
           if (SelectedObjectCount = 1) and
              (SelectedObjects[GetFirstSelected].Live) then
           begin
-            dWidth := MousePos.X-LastMovePoint.X;
-            dHeight := MousePos.Y-LastMovePoint.Y;
+            dWidth := MousePos.X-LastMovePoint.X+WASDOffset.X;
+            dHeight := MousePos.Y-LastMovePoint.Y+WASDOffset.Y;
+            WASDOffset.X := 0;
+            WASDOffset.Y := 0;
 
             case ResizeType of
               RESIZETYPE_VERTICAL: dWidth := 0;
@@ -4023,10 +4134,29 @@ begin
       begin
         if DrawRect = nil then
           New(DrawRect);
-        DrawRect.Top := MouseLDownPos.y;
-        DrawRect.Left := MouseLDownPos.x;
-        DrawRect.Bottom := MousePos.y;
-        DrawRect.Right := MousePos.x;
+        if ssCtrl in Shift then
+          begin
+            wWidth := DotStep;
+            wHeight := DotStep;
+            if (lbTextureList.ItemIndex <> -1) and (not IsSpecialTextureSel()) and
+               (MouseAction = MOUSEACTION_DRAWPANEL) then
+            begin
+              if not g_GetTexture(SelectedTexture(), TextureID) then
+                g_GetTexture('NOTEXTURE', TextureID);
+              g_GetTextureSizeByID(TextureID, wWidth, wHeight);
+            end;
+            DrawRect.Top := MouseLDownPos.y;
+            DrawRect.Left := MouseLDownPos.x;
+            DrawRect.Bottom := DrawRect.Top + wHeight;
+            DrawRect.Right := DrawRect.Left + wWidth;
+          end
+        else
+          begin
+            DrawRect.Top := MouseLDownPos.y;
+            DrawRect.Left := MouseLDownPos.x;
+            DrawRect.Bottom := MousePos.y;
+            DrawRect.Right := MousePos.x;
+          end;
       end
     else // Двигаем карту:
       if MouseAction = MOUSEACTION_MOVEMAP then
@@ -4105,6 +4235,15 @@ begin
   slInvalidTextures.Free;
 end;
 
+procedure TMainForm.FormDropFiles(Sender: TObject;
+  const FileNames: array of String);
+begin
+  if Length(FileNames) <> 1 then
+    Exit;
+
+  OpenMapFile(FileNames[0]);
+end;
+
 procedure TMainForm.RenderPanelResize(Sender: TObject);
 begin
   if MainForm.Visible then
@@ -4202,17 +4341,34 @@ begin
 
     if not (ssCtrl in Shift) then
     begin
+    // Быстрое превью карты:
+      if Key = Ord('E') then
+      begin
+        if PreviewMode = 0 then
+          PreviewMode := 2;
+      end;
+
     // Вертикальный скролл карты:
       with sbVertical do
       begin
         if Key = Ord('W') then
         begin
+          if (MouseLDown or MouseRDown) and (Position >= DotStep) then
+          begin
+            Dec(WASDOffset.Y, DotStep);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
+          end;
           Position := IfThen(Position > DotStep, Position-DotStep, 0);
           MapOffset.Y := -Round(Position/16) * 16;
         end;
 
         if Key = Ord('S') then
         begin
+          if (MouseLDown or MouseRDown) and (Position+DotStep <= Max) then
+          begin
+            Inc(WASDOffset.Y, DotStep);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
+          end;
           Position := IfThen(Position+DotStep < Max, Position+DotStep, Max);
           MapOffset.Y := -Round(Position/16) * 16;
         end;
@@ -4223,16 +4379,36 @@ begin
       begin
         if Key = Ord('A') then
         begin
+          if (MouseLDown or MouseRDown) and (Position >= DotStep) then
+          begin
+            Dec(WASDOffset.X, DotStep);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
+          end;
           Position := IfThen(Position > DotStep, Position-DotStep, 0);
           MapOffset.X := -Round(Position/16) * 16;
         end;
 
         if Key = Ord('D') then
         begin
+          if (MouseLDown or MouseRDown) and (Position+DotStep <= Max) then
+          begin
+            Inc(WASDOffset.X, DotStep);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
+          end;
           Position := IfThen(Position+DotStep < Max, Position+DotStep, Max);
           MapOffset.X := -Round(Position/16) * 16;
         end;
       end;
+    end
+    else // ssCtrl in Shift
+    begin
+      if ssShift in Shift then
+      begin
+      // Вставка по абсолютному смещению:
+        if Key = Ord('V') then
+          aPasteObjectExecute(Sender);
+      end;
+      RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
     end;
   end;
 
@@ -4332,6 +4508,7 @@ begin
       if i > 0 then
       begin
         g_ProcessResourceStr(OpenedMap, @FileName, nil, nil);
+        SelectMapForm.Caption := _lc[I_CAP_SELECT];
         SelectMapForm.GetMaps(FileName);
 
         if SelectMapForm.ShowModal() = mrOK then
@@ -4438,6 +4615,21 @@ begin
   end;
 end;
 
+procedure TMainForm.miReopenMapClick(Sender: TObject);
+var
+  FileName, Resource: String;
+begin
+  if OpenedMap = '' then
+    Exit;
+
+  if MessageBox(0, PChar(_lc[I_MSG_REOPEN_MAP_PROMT]),
+  PChar(_lc[I_MENU_FILE_REOPEN]), MB_ICONQUESTION or MB_YESNO) <> idYes then
+    Exit;
+
+  g_ProcessResourceStr(OpenedMap, @FileName, nil, @Resource);
+  OpenMap(FileName, Resource);
+end;
+
 procedure TMainForm.vleObjectPropertyGetPickList(Sender: TObject;
   const KeyName: String; Values: TStrings);
 begin
@@ -5312,12 +5504,18 @@ procedure TMainForm.aPasteObjectExecute(Sender: TObject);
 var
   a, h: Integer;
   CopyBuffer: TCopyRecArray;
-  res: Boolean;
+  res, rel: Boolean;
   swad, ssec, sres: String;
+  NoTextureID: DWORD;
+  pmin: TPoint;
 begin
   CopyBuffer := nil;
+  NoTextureID := 0;
+  pmin.X := High(pmin.X);
+  pmin.Y := High(pmin.Y);
 
-  StringToCopyBuffer(ClipBoard.AsText, CopyBuffer);
+  StringToCopyBuffer(ClipBoard.AsText, CopyBuffer, pmin);
+  rel := not(ssShift in GetKeyShiftState());
 
   if CopyBuffer = nil then
     Exit;
@@ -5332,8 +5530,11 @@ begin
         OBJECT_PANEL:
           if Panel <> nil then
           begin
-            Panel^.X := Panel^.X + 16;
-            Panel^.Y := Panel^.Y + 16;
+            if rel then
+            begin
+              Panel^.X := Panel^.X - pmin.X - MapOffset.X + 32;
+              Panel^.Y := Panel^.Y - pmin.Y - MapOffset.Y + 32;
+            end;
 
             Panel^.TextureID := TEXTURE_SPECIAL_NONE;
             Panel^.TextureWidth := 1;
@@ -5365,7 +5566,11 @@ begin
                       g_GetTextureSizeByName(Panel^.TextureName,
                         Panel^.TextureWidth, Panel^.TextureHeight)
                     else
-                      Panel^.TextureName := '';
+                      if g_GetTexture('NOTEXTURE', NoTextureID) then
+                      begin
+                        Panel^.TextureID := TEXTURE_SPECIAL_NOTEXTURE;
+                        g_GetTextureSizeByID(NoTextureID, Panel^.TextureWidth, Panel^.TextureHeight);
+                      end;
                   end
                 else // Спец.текстура:
                   begin
@@ -5384,8 +5589,11 @@ begin
 
         OBJECT_ITEM:
           begin
-            Item.X := Item.X + 16;
-            Item.Y := Item.Y + 16;
+            if rel then
+            begin
+              Item.X := Item.X - pmin.X - MapOffset.X + 32;
+              Item.Y := Item.Y - pmin.Y - MapOffset.Y + 32;
+            end;
 
             ID := AddItem(Item);
             Undo_Add(OBJECT_ITEM, ID, a > 0);
@@ -5394,8 +5602,11 @@ begin
 
         OBJECT_MONSTER:
           begin
-            Monster.X := Monster.X + 16;
-            Monster.Y := Monster.Y + 16;
+            if rel then
+            begin
+              Monster.X := Monster.X - pmin.X - MapOffset.X + 32;
+              Monster.Y := Monster.Y - pmin.Y - MapOffset.Y + 32;
+            end;
 
             ID := AddMonster(Monster);
             Undo_Add(OBJECT_MONSTER, ID, a > 0);
@@ -5404,8 +5615,11 @@ begin
 
         OBJECT_AREA:
           begin
-            Area.X := Area.X + 16;
-            Area.Y := Area.Y + 16;
+            if rel then
+            begin
+              Area.X := Area.X - pmin.X - MapOffset.X + 32;
+              Area.Y := Area.Y - pmin.Y - MapOffset.Y + 32;
+            end;
 
             ID := AddArea(Area);
             Undo_Add(OBJECT_AREA, ID, a > 0);
@@ -5414,8 +5628,48 @@ begin
 
         OBJECT_TRIGGER:
           begin
-            Trigger.X := Trigger.X + 16;
-            Trigger.Y := Trigger.Y + 16;
+            if rel then
+              with Trigger do
+              begin
+                X := X - pmin.X - MapOffset.X + 32;
+                Y := Y - pmin.Y - MapOffset.Y + 32;
+
+                case TriggerType of
+                  TRIGGER_TELEPORT:
+                    begin
+                      Data.TargetPoint.X :=
+                      Data.TargetPoint.X - pmin.X - MapOffset.X + 32;
+                      Data.TargetPoint.Y :=
+                      Data.TargetPoint.Y - pmin.Y - MapOffset.Y + 32;
+                    end;
+                  TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
+                    begin
+                      Data.tX := Data.tX - pmin.X - MapOffset.X + 32;
+                      Data.tY := Data.tY - pmin.Y - MapOffset.Y + 32;
+                    end;
+                  TRIGGER_SPAWNMONSTER:
+                    begin
+                      Data.MonPos.X :=
+                      Data.MonPos.X - pmin.X - MapOffset.X + 32;
+                      Data.MonPos.Y :=
+                      Data.MonPos.Y - pmin.Y - MapOffset.Y + 32;
+                    end;
+                  TRIGGER_SPAWNITEM:
+                    begin
+                      Data.ItemPos.X :=
+                      Data.ItemPos.X - pmin.X - MapOffset.X + 32;
+                      Data.ItemPos.Y :=
+                      Data.ItemPos.Y - pmin.Y - MapOffset.Y + 32;
+                    end;
+                  TRIGGER_SHOT:
+                    begin
+                      Data.ShotPos.X :=
+                      Data.ShotPos.X - pmin.X - MapOffset.X + 32;
+                      Data.ShotPos.Y :=
+                      Data.ShotPos.Y - pmin.Y - MapOffset.Y + 32;
+                    end;
+                end;
+              end;
 
             ID := AddTrigger(Trigger);
             Undo_Add(OBJECT_TRIGGER, ID, a > 0);
@@ -5520,6 +5774,7 @@ begin
   else if Key = _lc[I_PROP_TR_NEXT_MAP] then
     begin // Выбор следующей карты:
       g_ProcessResourceStr(OpenedMap, @FileName, nil, nil);
+      SelectMapForm.Caption := _lc[I_CAP_SELECT];
       SelectMapForm.GetMaps(FileName);
 
       if SelectMapForm.ShowModal() = mrOK then
@@ -5806,31 +6061,35 @@ begin
 
   if OpenDialog.Execute() then
   begin
-    if (Pos('.ini', LowerCase(ExtractFileName(OpenDialog.FileName))) > 0) then
-      begin // INI карты:
-        FullClear();
+    OpenMapFile(OpenDialog.FileName);
+    OpenDialog.InitialDir := ExtractFileDir(OpenDialog.FileName);
+  end;
+end;
 
-        pLoadProgress.Left := (RenderPanel.Width div 2)-(pLoadProgress.Width div 2);
-        pLoadProgress.Top := (RenderPanel.Height div 2)-(pLoadProgress.Height div 2);
-        pLoadProgress.Show();
+procedure TMainForm.OpenMapFile(FileName: String);
+begin
+  if (Pos('.ini', LowerCase(ExtractFileName(FileName))) > 0) then
+    begin // INI карты:
+      FullClear();
 
-        OpenedMap := '';
-        OpenedWAD := '';
+      pLoadProgress.Left := (RenderPanel.Width div 2)-(pLoadProgress.Width div 2);
+      pLoadProgress.Top := (RenderPanel.Height div 2)-(pLoadProgress.Height div 2);
+      pLoadProgress.Show();
 
-        LoadMapOld(OpenDialog.FileName);
+      OpenedMap := '';
+      OpenedWAD := '';
 
-        MainForm.Caption := Format('%s - %s', [FormCaption, ExtractFileName(OpenDialog.FileName)]);
+      LoadMapOld(FileName);
 
-        pLoadProgress.Hide();
-        MainForm.FormResize(Self);
-      end
-    else // Карты из WAD:
-      begin
-        OpenMap(OpenDialog.FileName, '');
-      end;
+      MainForm.Caption := Format('%s - %s', [FormCaption, ExtractFileName(FileName)]);
 
-    OpenDialog.InitialDir := ExtractFileDir(OpenDialog.FileName);
-  end;
+      pLoadProgress.Hide();
+      MainForm.FormResize(Self);
+    end
+  else // Карты из WAD:
+    begin
+      OpenMap(FileName, '');
+    end;
 end;
 
 procedure TMainForm.FormActivate(Sender: TObject);
@@ -5885,6 +6144,7 @@ begin
 
   MapList := WAD.GetResourcesList('');
 
+  SelectMapForm.Caption := _lc[I_CAP_REMOVE];
   SelectMapForm.lbMapList.Items.Clear();
 
   if MapList <> nil then
@@ -6166,7 +6426,10 @@ end;
 
 procedure TMainForm.miMapPreviewClick(Sender: TObject);
 begin
-  if not PreviewMode then
+  if PreviewMode = 2 then
+    Exit;
+
+  if PreviewMode = 0 then
     begin
       Splitter2.Visible := False;
       Splitter1.Visible := False;
@@ -6189,8 +6452,8 @@ begin
       sbVertical.Visible := True;
     end;
 
-  PreviewMode := not PreviewMode;
-  (Sender as TMenuItem).Checked := PreviewMode;
+  PreviewMode := PreviewMode xor 1;
+  (Sender as TMenuItem).Checked := PreviewMode > 0;
 
   FormResize(Self);
 end;
@@ -6493,6 +6756,13 @@ begin
        (Key = Ord('V')) then
       FillProperty();
   end;
+// Быстрое превью карты:
+  if Key = Ord('E') then
+  begin
+    if PreviewMode = 2 then
+      PreviewMode := 0;
+  end;
+  RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
 end;
 
 end.