DEADSOFTWARE

Map: Add fast preview feature
[d2df-editor.git] / src / editor / f_main.pas
index 8fd1fa3372b6a4e87fe52580603a0f3a82def031..8158c03344f380660a7de069841ce8357aedb984 100644 (file)
@@ -5,10 +5,10 @@ unit f_main;
 interface
 
 uses
-  LCLIntf, LCLType, LMessages, SysUtils, Variants, Classes, Graphics,
-  Controls, Forms, Dialogs, ImgList, StdCtrls, Buttons,
-  ComCtrls, ValEdit, Types, ToolWin, Menus, ExtCtrls,
-  CheckLst, Grids, OpenGLContext, utils;
+  LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics,
+  Controls, Forms, Dialogs, StdCtrls, Buttons,
+  ComCtrls, ValEdit, Types, Menus, ExtCtrls,
+  CheckLst, Grids, OpenGLContext, utils, UTF8Process;
 
 type
 
@@ -207,12 +207,16 @@ type
     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 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);
     procedure RenderPanelPaint(Sender: TObject);
     procedure RenderPanelResize(Sender: TObject);
+    procedure Splitter1Moved(Sender: TObject);
     procedure vleObjectPropertyEditButtonClick(Sender: TObject);
+    procedure vleObjectPropertyApply(Sender: TObject);
     procedure vleObjectPropertyGetPickList(Sender: TObject; const KeyName: String; Values: TStrings);
     procedure vleObjectPropertyKeyDown(Sender: TObject; var Key: Word;
                                        Shift: TShiftState);
@@ -258,8 +262,6 @@ type
     procedure OnIdle(Sender: TObject; var Done: Boolean);
   public
     procedure RefreshRecentMenu();
-    { procedure lbTextureListDrawItem(Control: TWinControl; Index: Integer;
-                                    Rect: TRect; State: TOwnerDrawState); }
   end;
 
 const
@@ -309,7 +311,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;
@@ -323,10 +325,10 @@ procedure ChangeShownProperty(Name: String; NewValue: String);
 implementation
 
 uses
-  f_options, e_graphics, e_log, GL, GLExt, Math,
+  f_options, e_graphics, e_log, GL, Math,
   f_mapoptions, g_basic, f_about, f_mapoptimization,
   f_mapcheck, f_addresource_texture, g_textures,
-  f_activationtype, f_keys, MAPWRITER, MAPSTRUCT,
+  f_activationtype, f_keys,
   MAPREADER, f_selectmap, f_savemap, WADEDITOR, WADSTRUCT, MAPDEF,
   g_map, f_saveminimap, f_addresource, CONFIG, f_packmap,
   f_addresource_sound, f_maptest, f_choosetype,
@@ -951,7 +953,8 @@ begin
           case TriggerType of
             TRIGGER_EXIT:
               begin
-                with ItemProps[InsertRow(_lc[I_PROP_TR_NEXT_MAP], Data.MapName, True)] do
+                str := win2utf(Data.MapName);
+                with ItemProps[InsertRow(_lc[I_PROP_TR_NEXT_MAP], str, True)] do
                 begin
                   EditStyle := esEllipsis;
                   ReadOnly := True;
@@ -1105,7 +1108,8 @@ begin
 
             TRIGGER_SOUND:
               begin
-                with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_NAME], Data.SoundName, True)] do
+                str := win2utf(Data.SoundName);
+                with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_NAME], str, True)] do
                 begin
                   EditStyle := esEllipsis;
                   ReadOnly := True;
@@ -1268,7 +1272,8 @@ begin
 
            TRIGGER_MUSIC:
              begin
-               with ItemProps[InsertRow(_lc[I_PROP_TR_MUSIC_NAME], Data.MusicName, True)] do
+               str := win2utf(Data.MusicName);
+               with ItemProps[InsertRow(_lc[I_PROP_TR_MUSIC_NAME], str, True)] do
                begin
                  EditStyle := esEllipsis;
                  ReadOnly := True;
@@ -1370,7 +1375,8 @@ begin
                   EditStyle := esPickList;
                   ReadOnly := True;
                 end;
-                with ItemProps[InsertRow(_lc[I_PROP_TR_MESSAGE_TEXT], Data.MessageText, True)] do
+                str := win2utf(Data.MessageText);
+                with ItemProps[InsertRow(_lc[I_PROP_TR_MESSAGE_TEXT], str, True)] do
                 begin
                   EditStyle := esSimple;
                   MaxLength := 100;
@@ -1795,6 +1801,7 @@ procedure FullClear();
 begin
   RemoveSelectFromObjects();
   ClearMap();
+  LoadSky(gMapInfo.SkyName);
   UndoBuffer := nil;
   slInvalidTextures.Clear();
   MapCheckForm.lbErrorList.Clear();
@@ -1880,13 +1887,17 @@ var
   ok: Boolean;
   FileName: String;
   ResourceName: String;
-  UResourceName: String;
   FullResourceName: String;
   SectionName: String;
   Data: Pointer;
   Width, Height: Word;
   fn: String;
 begin
+  Data := nil;
+  FrameLen := 0;
+  Width := 0;
+  Height := 0;
+
   if aSection = '..' then
     SectionName := ''
   else
@@ -1912,15 +1923,14 @@ begin
       end;
 
   ok := True;
-  UResourceName := win2utf(ResourceName);
 
 // Есть ли уже такая текстура:
   for a := 0 to MainForm.lbTextureList.Items.Count-1 do
-    if UResourceName = MainForm.lbTextureList.Items[a] then
+    if ResourceName = MainForm.lbTextureList.Items[a] then
     begin
       if not silent then
         ErrorMessageBox(Format(_lc[I_MSG_TEXTURE_ALREADY],
-                               [UResourceName]));
+                               [ResourceName]));
       ok := False;
     end;
 
@@ -1929,7 +1939,7 @@ begin
   begin
     if not silent then
       ErrorMessageBox(Format(_lc[I_MSG_RES_NAME_64],
-                             [UResourceName]));
+                             [ResourceName]));
     ok := False;
   end;
 
@@ -1938,7 +1948,7 @@ begin
     a := -1;
     if aWAD = _lc[I_WAD_SPECIAL_TEXS] then
     begin
-      a := MainForm.lbTextureList.Items.Add(UResourceName);
+      a := MainForm.lbTextureList.Items.Add(ResourceName);
       if not silent then
         SelectTexture(a);
       Result := True;
@@ -1952,12 +1962,12 @@ begin
         GetFrame(FullResourceName, Data, FrameLen, Width, Height);
 
         if g_CreateTextureMemorySize(Data, FrameLen, ResourceName, 0, 0, Width, Height, 1) then
-          a := MainForm.lbTextureList.Items.Add(UResourceName);
+          a := MainForm.lbTextureList.Items.Add(ResourceName);
       end
     else // Обычная текстура
       begin
         if g_CreateTextureWAD(ResourceName, FullResourceName) then
-          a := MainForm.lbTextureList.Items.Add(UResourceName);
+          a := MainForm.lbTextureList.Items.Add(ResourceName);
       end;
     if (a > -1) and (not silent) then
       SelectTexture(a);
@@ -1986,6 +1996,7 @@ var
   MapName: String;
   idx: Integer;
 begin
+  SelectMapForm.Caption := _lc[I_CAP_OPEN];
   SelectMapForm.GetMaps(FileName);
 
   if (FileName = OpenedWAD) and
@@ -2212,7 +2223,7 @@ end;
 function SelectedTexture(): String;
 begin
   if MainForm.lbTextureList.ItemIndex <> -1 then
-    Result := utf2win(MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex])
+    Result := MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]
   else
     Result := '';
 end;
@@ -2220,7 +2231,7 @@ end;
 function IsSpecialTextureSel(): Boolean;
 begin
   Result := (MainForm.lbTextureList.ItemIndex <> -1) and
-            IsSpecialTexture(utf2win(MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]));
+            IsSpecialTexture(MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]);
 end;
 
 function CopyBufferToString(var CopyBuf: TCopyRecArray): String;
@@ -2500,6 +2511,7 @@ procedure TMainForm.aRecentFileExecute(Sender: TObject);
 var
   n, pw: Integer;
   s, fn: String;
+  b: Boolean;
 begin
   s := LowerCase((Sender as TMenuItem).Caption);
   Delete(s, Pos('&', s), 1);
@@ -2511,17 +2523,31 @@ begin
 
   s := RecentFiles[n];
   pw := Pos('.wad:\', LowerCase(s));
+  b := False;
   
   if pw > 0 then
     begin // Map name included
       fn := Copy(s, 1, pw + 3);
       Delete(s, 1, pw + 5);
       if (FileExists(fn)) then
+      begin
         OpenMap(fn, s);
+        b := True;
+      end;
     end
   else // Only wad name
     if (FileExists(s)) then
+    begin
       OpenMap(s, '');
+      b := True;
+    end;
+
+  if (not b) and (MessageBox(0, PChar(_lc[I_MSG_DEL_RECENT_PROMT]),
+    PChar(_lc[I_MSG_DEL_RECENT]), MB_ICONQUESTION or MB_YESNO) = idYes) then
+  begin
+    RecentFiles.Delete(n);
+    RefreshRecentMenu();
+  end;
 end;
 
 procedure TMainForm.aEditorOptionsExecute(Sender: TObject);
@@ -2539,7 +2565,9 @@ var
   cfglen: Integer;
   config: TConfig;
 begin
+  cfgdata := nil;
   cfglen := 0;
+  ID := 0;
 
   wad := TWADEditor_1.Create;
   if wad.ReadFile(EditorDir+'data/Game.wad') then
@@ -2696,6 +2724,11 @@ var
   ObjCount: Word;
   aX, aY, aX2, aY2, XX, ScaleSz: Integer;
 begin
+  ID := 0;
+  PID := 0;
+  Width := 0;
+  Height := 0;
+
   e_BeginRender();
 
   e_Clear(GL_COLOR_BUFFER_BIT,
@@ -2740,7 +2773,7 @@ begin
   end;
 
 // Рисуем сетку:
-  if DotEnable and (not PreviewMode) then
+  if DotEnable and (PreviewMode = 0) then
   begin
     if DotSize = 2 then
       a := -1
@@ -2757,7 +2790,7 @@ 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);
@@ -4032,10 +4065,20 @@ var
 begin
   config := TConfig.CreateFile(EditorDir+'Editor.cfg');
 
-  config.WriteInt('Editor', 'XPos', Left);
-  config.WriteInt('Editor', 'YPos', Top);
-  config.WriteInt('Editor', 'Width', Width);
-  config.WriteInt('Editor', 'Height', Height);
+  if WindowState <> wsMaximized then
+  begin
+    config.WriteInt('Editor', 'XPos', Left);
+    config.WriteInt('Editor', 'YPos', Top);
+    config.WriteInt('Editor', 'Width', Width);
+    config.WriteInt('Editor', 'Height', Height);
+  end
+  else
+  begin
+    config.WriteInt('Editor', 'XPos', RestoredLeft);
+    config.WriteInt('Editor', 'YPos', RestoredTop);
+    config.WriteInt('Editor', 'Width', RestoredWidth);
+    config.WriteInt('Editor', 'Height', RestoredHeight);
+  end;
   config.WriteBool('Editor', 'Maximize', WindowState = wsMaximized);
   config.WriteBool('Editor', 'Minimap', ShowMap);
   config.WriteInt('Editor', 'PanelProps', PanelProps.ClientWidth);
@@ -4069,6 +4112,11 @@ begin
     MainForm.Resize();
 end;
 
+procedure TMainForm.Splitter1Moved(Sender: TObject);
+begin
+  FormResize(Sender);
+end;
+
 procedure TMainForm.aMapOptionsExecute(Sender: TObject);
 var
   ResName: String;
@@ -4155,6 +4203,13 @@ 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
@@ -4244,6 +4299,7 @@ begin
         DrawPressRect := False;
         Exit;
       end;
+      i := -1;
 
     // Выбор области воздействия, в зависимости от типа триггера
       vleObjectProperty.FindRow(_lc[I_PROP_TR_EX_AREA], i);
@@ -4284,6 +4340,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
@@ -4300,11 +4357,11 @@ begin
       begin
         AddSoundForm.OKFunction := nil;
         AddSoundForm.lbResourcesList.MultiSelect := False;
-        AddSoundForm.SetResource := utf2win(vleObjectProperty.Cells[1, i]);
+        AddSoundForm.SetResource := vleObjectProperty.Cells[1, i];
 
         if (AddSoundForm.ShowModal() = mrOk) then
         begin
-          vleObjectProperty.Cells[1, i] := win2utf(AddSoundForm.ResourceName);
+          vleObjectProperty.Cells[1, i] := AddSoundForm.ResourceName;
           bApplyProperty.Click();
         end;
         Exit;
@@ -4344,6 +4401,9 @@ var
   TextureID: DWORD;
   TextureWidth, TextureHeight: Word;
 begin
+  TextureID := 0;
+  TextureWidth := 0;
+  TextureHeight := 0;
   if (lbTextureList.ItemIndex <> -1) and
      (not IsSpecialTextureSel()) then
     begin
@@ -4366,6 +4426,27 @@ begin
     end;
 end;
 
+procedure TMainForm.lbTextureListDrawItem(Control: TWinControl; Index: Integer;
+  ARect: TRect; State: TOwnerDrawState);
+begin
+  with Control as TListBox do
+  begin
+    if LCLType.odSelected in State then
+    begin
+      Canvas.Brush.Color := clHighlight;
+      Canvas.Font.Color := clHighlightText;
+    end else
+      if (Items <> nil) and (Index >= 0) then
+        if slInvalidTextures.IndexOf(Items[Index]) > -1 then
+        begin
+          Canvas.Brush.Color := clRed;
+          Canvas.Font.Color := clWhite;
+        end;
+    Canvas.FillRect(ARect);
+    Canvas.TextRect(ARect, ARect.Left, ARect.Top, Items[Index]);
+  end;
+end;
+
 procedure TMainForm.vleObjectPropertyGetPickList(Sender: TObject;
   const KeyName: String; Values: TStrings);
 begin
@@ -4476,6 +4557,10 @@ var
   NoTextureID: DWORD;
   NW, NH: Word;
 begin
+  NoTextureID := 0;
+  NW := 0;
+  NH := 0;
+
   if SelectedObjectCount() <> 1 then
     Exit;
   if not SelectedObjects[GetFirstSelected()].Live then
@@ -4673,10 +4758,10 @@ begin
           case TriggerType of
             TRIGGER_EXIT:
               begin
-                s := vleObjectProperty.Values[_lc[I_PROP_TR_NEXT_MAP]];
+                s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_NEXT_MAP]]);
                 FillByte(Data.MapName[0], 16, 0);
                 if s <> '' then
-                  Move(Data.MapName[0], s[1], Min(Length(s), 16));
+                  Move(s[1], Data.MapName[0], Min(Length(s), 16));
               end;
 
             TRIGGER_TEXTURE:
@@ -4712,10 +4797,10 @@ begin
 
             TRIGGER_SOUND:
               begin
-                s := vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_NAME]];
+                s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_NAME]]);
                 FillByte(Data.SoundName[0], 64, 0);
                 if s <> '' then
-                  Move(Data.SoundName[0], s[1], Min(Length(s), 64));
+                  Move(s[1], Data.SoundName[0], Min(Length(s), 64));
 
                 Data.Volume := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_VOLUME]], 0), 255);
                 Data.Pan := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_PAN]], 0), 255);
@@ -4766,10 +4851,10 @@ begin
 
             TRIGGER_MUSIC:
               begin
-                s := vleObjectProperty.Values[_lc[I_PROP_TR_MUSIC_NAME]];
+                s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_MUSIC_NAME]]);
                 FillByte(Data.MusicName[0], 64, 0);
                 if s <> '' then
-                  Move(Data.MusicName[0], s[1], Min(Length(s), 64));
+                  Move(s[1], Data.MusicName[0], Min(Length(s), 64));
 
                 if vleObjectProperty.Values[_lc[I_PROP_TR_MUSIC_ACT]] = _lc[I_PROP_TR_MUSIC_ON] then
                   Data.MusicAction := 1
@@ -4826,10 +4911,10 @@ begin
                 else if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TO]] = _lc[I_PROP_TR_MESSAGE_TO_5] then
                   Data.MessageSendTo := 5;
 
-                s := vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TEXT]];
+                s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TEXT]]);
                 FillByte(Data.MessageText[0], 100, 0);
                 if s <> '' then
-                  Move(Data.MessageText[0], s[1], Min(Length(s), 100));
+                  Move(s[1], Data.MessageText[0], Min(Length(s), 100));
 
                 Data.MessageTime := Min(Max(
                   StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TIME]], 0), 0), 65535);
@@ -5295,8 +5380,8 @@ begin
                   begin
                     Panel^.TextureID := SpecialTextureID(Panel^.TextureName);
                     with MainForm.lbTextureList.Items do
-                      if IndexOf(win2utf(Panel^.TextureName)) = -1 then
-                        Add(win2utf(Panel^.TextureName));
+                      if IndexOf(Panel^.TextureName) = -1 then
+                        Add(Panel^.TextureName);
                   end;
               end;
 
@@ -5414,7 +5499,7 @@ begin
         begin
           b := lbTypeSelect.ItemIndex;
           Values[Key] := PANELNAMES[b];
-          bApplyProperty.Click();
+          vleObjectPropertyApply(Sender);
         end;
       end
     end
@@ -5444,12 +5529,13 @@ 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
       begin
         vleObjectProperty.Values[Key] := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if (Key = _lc[I_PROP_TR_SOUND_NAME]) or
@@ -5457,12 +5543,12 @@ begin
     begin // Выбор файла звука/музыки:
       AddSoundForm.OKFunction := nil;
       AddSoundForm.lbResourcesList.MultiSelect := False;
-      AddSoundForm.SetResource := utf2win(vleObjectProperty.Values[Key]);
+      AddSoundForm.SetResource := vleObjectProperty.Values[Key];
 
       if (AddSoundForm.ShowModal() = mrOk) then
       begin
-        vleObjectProperty.Values[Key] := utf2win(AddSoundForm.ResourceName);
-        bApplyProperty.Click();
+        vleObjectProperty.Values[Key] := AddSoundForm.ResourceName;
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_ACTIVATION] then
@@ -5492,7 +5578,7 @@ begin
           b := b or ACTIVATE_NOMONSTER;
 
         Values[Key] := ActivateToStr(b);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_KEYS] then
@@ -5519,7 +5605,7 @@ begin
           b := b or KEY_BLUETEAM;
 
         Values[Key] := KeyToStr(b);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_FX_TYPE] then
@@ -5537,7 +5623,7 @@ begin
       begin
         b := lbTypeSelect.ItemIndex;
         Values[Key] := EffectToStr(b);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_MONSTER_TYPE] then
@@ -5555,7 +5641,7 @@ begin
       begin
         b := lbTypeSelect.ItemIndex + MONSTER_DEMON;
         Values[Key] := MonsterToStr(b);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_ITEM_TYPE] then
@@ -5584,7 +5670,7 @@ begin
         if b >= ITEM_WEAPON_KASTET then
           b := b + 2;
         Values[Key] := ItemToStr(b);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_SHOT_TYPE] then
@@ -5602,7 +5688,7 @@ begin
       begin
         b := lbTypeSelect.ItemIndex;
         Values[Key] := ShotToStr(b);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_EFFECT_TYPE] then
@@ -5625,7 +5711,7 @@ begin
           Values[Key] := _lc[I_PROP_TR_EFFECT_PARTICLE]
         else
           Values[Key] := _lc[I_PROP_TR_EFFECT_ANIMATION];
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_EFFECT_SUBTYPE] then
@@ -5681,7 +5767,7 @@ begin
             Values[Key] := _lc[I_PROP_TR_EFFECT_BUBBLE];
         end;
 
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_TR_EFFECT_COLOR] then
@@ -5691,16 +5777,24 @@ begin
       if ColorDialog.Execute then
       begin
         Values[Key] := IntToStr(ColorDialog.Color);
-        bApplyProperty.Click();
+        vleObjectPropertyApply(Sender);
       end;
     end
   else if Key = _lc[I_PROP_PANEL_TEX] then
     begin // Смена текстуры:
       vleObjectProperty.Values[Key] := SelectedTexture();
-      bApplyProperty.Click();
+      vleObjectPropertyApply(Sender);
     end;
 end;
 
+procedure TMainForm.vleObjectPropertyApply(Sender: TObject);
+begin
+  // hack to prevent empty ID in list
+  RenderPanel.SetFocus();
+  bApplyProperty.Click();
+  vleObjectProperty.SetFocus();
+end;
+
 procedure TMainForm.aSaveMapExecute(Sender: TObject);
 var
   FileName, Section, Res: String;
@@ -5801,17 +5895,18 @@ begin
 
   MapList := WAD.GetResourcesList('');
 
+  SelectMapForm.Caption := _lc[I_CAP_REMOVE];
   SelectMapForm.lbMapList.Items.Clear();
 
   if MapList <> nil then
     for a := 0 to High(MapList) do
-      SelectMapForm.lbMapList.Items.Add(MapList[a]);
+      SelectMapForm.lbMapList.Items.Add(win2utf(MapList[a]));
 
   if (SelectMapForm.ShowModal() = mrOK) then
   begin
     str := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
     MapName := '';
-    Move(MapName[0], str[1], Min(16, Length(str)));
+    Move(str[1], MapName[0], Min(16, Length(str)));
 
     if MessageBox(0, PChar(Format(_lc[I_MSG_DELETE_MAP_PROMT],
                            [MapName, OpenDialog.FileName])),
@@ -5820,7 +5915,7 @@ begin
                   MB_DEFBUTTON2) <> mrYes then
       Exit;
 
-    WAD.RemoveResource('', MapName);
+    WAD.RemoveResource('', utf2win(MapName));
     
     MessageBox(0, PChar(Format(_lc[I_MSG_MAP_DELETED_PROMT],
                                [MapName])),
@@ -5846,7 +5941,7 @@ procedure TMainForm.vleObjectPropertyKeyDown(Sender: TObject;
             var Key: Word; Shift: TShiftState);
 begin
   if Key = VK_RETURN then
-    bApplyProperty.Click();
+    vleObjectPropertyApply(Sender);
 end;
 
 procedure MovePanel(var ID: DWORD; MoveType: Byte);
@@ -6082,7 +6177,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;
@@ -6105,8 +6203,10 @@ 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;
 
 procedure TMainForm.miLayer1Click(Sender: TObject);
@@ -6239,21 +6339,33 @@ end;
 
 procedure TMainForm.miTestMapClick(Sender: TObject);
 var
-  cmd, mapWAD, mapToRun: String;
+  cmd, mapWAD, mapToRun, tempWAD: String;
   opt: LongWord;
   time: Integer;
-  lpMsgBuf: PChar;
+  proc: TProcessUTF8;
+  res: Boolean;
 begin
+  mapToRun := '';
+  if OpenedMap <> '' then
+  begin
+    // Указываем текущую карту для теста:
+    g_ProcessResourceStr(OpenedMap, @mapWAD, nil, @mapToRun);
+    mapToRun := mapWAD + ':\' + mapToRun;
+    mapToRun := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', mapToRun);
+  end;
   // Сохраняем временную карту:
   time := 0;
   repeat
     mapWAD := ExtractFilePath(TestD2dExe) + Format('maps/temp%.4d.wad', [time]);
     Inc(time);
   until not FileExists(mapWAD);
-  mapToRun := mapWAD + ':\' + TEST_MAP_NAME;
-  SaveMap(mapToRun);
+  tempWAD := mapWAD + ':\' + TEST_MAP_NAME;
+  SaveMap(tempWAD);
 
-  mapToRun := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', mapToRun);
+  tempWAD := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', tempWAD);
+// Если карта не была открыта, указываем временную в качестве текущей:
+  if mapToRun = '' then
+    mapToRun := tempWAD;
 
 // Опции игры:
   opt := 32 + 64;
@@ -6269,7 +6381,8 @@ begin
     opt := opt + 16;
 
 // Составляем командную строку:
-  cmd := ' -map "' + mapToRun + '"';
+  cmd := '-map "' + mapToRun + '"';
+  cmd := cmd + ' -testmap "' + tempWAD + '"';
   cmd := cmd + ' -gm ' + TestGameMode;
   cmd := cmd + ' -limt ' + TestLimTime;
   cmd := cmd + ' -lims ' + TestLimScore;
@@ -6279,16 +6392,29 @@ begin
     cmd := cmd + ' --close';
 
   cmd := cmd + ' --debug';
-  cmd := cmd + ' --tempdelete';
 
 // Запускаем:
-  Application.Minimize();
-  if ExecuteProcess(TestD2dExe, cmd) < 0 then
+  proc := TProcessUTF8.Create(nil);
+  proc.Executable := TestD2dExe;
+  proc.Parameters.Add(cmd);
+  res := True;
+  try
+    proc.Execute();
+  except
+    res := False;
+  end;
+  if res then
+  begin
+    Application.Minimize();
+    proc.WaitOnExit();
+  end;
+  if (not res) or (proc.ExitCode < 0) then
   begin
     MessageBox(0, 'FIXME',
                PChar(_lc[I_MSG_EXEC_ERROR]),
                MB_OK or MB_ICONERROR);
   end;
+  proc.Free();
 
   SysUtils.DeleteFile(mapWAD);
   Application.Restore();
@@ -6381,28 +6507,12 @@ begin
        (Key = Ord('V')) then
       FillProperty();
   end;
-end;
-
-{
-procedure TMainForm.lbTextureListDrawItem(Control: TWinControl; Index: Integer;
-      Rect: TRect; State: LCLType.TOwnerDrawState);
-begin
-  with Control as TListBox do
+// Быстрое превью карты:
+  if Key = Ord('E') then
   begin
-    if LCLType.odSelected in State then
-    begin
-      Canvas.Brush.Color := clHighlight;
-      Canvas.Font.Color := clHighlightText;
-    end else
-      if (Items <> nil) and (Index >= 0) then
-        if slInvalidTextures.IndexOf(Items[Index]) > -1 then
-        begin
-          Canvas.Brush.Color := clRed;
-          Canvas.Font.Color := clWhite;
-        end;
-    Canvas.FillRect(Rect);
-    Canvas.TextRect(Rect, Rect.Left, Rect.Top, Items[Index]);
+    if PreviewMode = 2 then
+      PreviewMode := 0;
   end;
 end;
-}
+
 end.