Martin Prikryl 20 years ago
parent
commit
1323e96b40
100 changed files with 4245 additions and 1497 deletions
  1. 5 5
      Console.bpr
  2. BIN
      Console.res
  3. 2 3
      DScpComp.bpk
  4. 3 3
      DragExt.bpr
  5. BIN
      DragExt.res
  6. 4 1
      Putty.bpf
  7. 1 2
      RScpComp.bpr
  8. 2 0
      ScpForms.bpf
  9. 19 16
      ScpForms.bpr
  10. 5 5
      WinSCP3.bpr
  11. 11 11
      WinSCP3.drc
  12. BIN
      WinSCP3.res
  13. 12 9
      components/UnixDirView.cpp
  14. 55 40
      console/Main.cpp
  15. 69 21
      core/Bookmarks.cpp
  16. 7 0
      core/Bookmarks.h
  17. 80 13
      core/Common.cpp
  18. 39 1
      core/Common.h
  19. 50 0
      core/Configuration.cpp
  20. 3 0
      core/Configuration.h
  21. 161 61
      core/CopyParam.cpp
  22. 17 5
      core/CopyParam.h
  23. 58 16
      core/FileMasks.cpp
  24. 8 5
      core/FileMasks.h
  25. 3 3
      core/FileOperationProgress.cpp
  26. 1 0
      core/FileOperationProgress.h
  27. 2 1
      core/FileSystems.h
  28. 14 1
      core/HierarchicalStorage.cpp
  29. 8 0
      core/Net.cpp
  30. 6 0
      core/PuttyIntf.c
  31. 6 0
      core/PuttyIntf.h
  32. 119 7
      core/Queue.cpp
  33. 9 5
      core/Queue.h
  34. 57 25
      core/RemoteFiles.cpp
  35. 5 0
      core/RemoteFiles.h
  36. 63 47
      core/ScpFileSystem.cpp
  37. 8 4
      core/ScpFileSystem.h
  38. 14 5
      core/ScpMain.cpp
  39. 4 0
      core/ScpMain.h
  40. 104 60
      core/Script.cpp
  41. 11 8
      core/Script.h
  42. 41 3
      core/SecureShell.cpp
  43. 10 0
      core/SecureShell.h
  44. 33 9
      core/SessionData.cpp
  45. 3 0
      core/SessionData.h
  46. 224 48
      core/SftpFileSystem.cpp
  47. 3 1
      core/SftpFileSystem.h
  48. 230 61
      core/Terminal.cpp
  49. 41 40
      core/Terminal.h
  50. 38 20
      forms/About.cpp
  51. 183 253
      forms/About.dfm
  52. 10 12
      forms/About.h
  53. 54 0
      forms/Banner.cpp
  54. 64 0
      forms/Banner.dfm
  55. 28 0
      forms/Banner.h
  56. 9 33
      forms/Console.cpp
  57. 3 3
      forms/Console.dfm
  58. 1 3
      forms/Console.h
  59. 122 48
      forms/Copy.cpp
  60. 2 0
      forms/Copy.dfm
  61. 3 0
      forms/Copy.h
  62. 4 1
      forms/CopyParamCustom.dfm
  63. 10 0
      forms/CopyParamPreset.cpp
  64. 19 0
      forms/CopyParamPreset.dfm
  65. 2 0
      forms/CopyParamPreset.h
  66. 22 8
      forms/CopyParams.cpp
  67. 13 1
      forms/CopyParams.dfm
  68. 2 0
      forms/CopyParams.h
  69. 8 2
      forms/CustomCommand.cpp
  70. 42 35
      forms/CustomCommand.dfm
  71. 2 1
      forms/CustomCommand.h
  72. 427 159
      forms/CustomScpExplorer.cpp
  73. 11 1
      forms/CustomScpExplorer.dfm
  74. 41 21
      forms/CustomScpExplorer.h
  75. 155 67
      forms/Editor.cpp
  76. 2 15
      forms/Editor.dfm
  77. 5 3
      forms/Editor.h
  78. 182 0
      forms/EditorPreferences.cpp
  79. 166 0
      forms/EditorPreferences.dfm
  80. 50 0
      forms/EditorPreferences.h
  81. 35 15
      forms/FullSynchronize.cpp
  82. 4 4
      forms/FullSynchronize.dfm
  83. 2 1
      forms/FullSynchronize.h
  84. 2 2
      forms/GeneralSettings.dfm
  85. 1 1
      forms/GeneralSettings.h
  86. 368 104
      forms/Glyphs.dfm
  87. 3 2
      forms/InputDlg.cpp
  88. 98 26
      forms/LocationProfiles.cpp
  89. 3 0
      forms/LocationProfiles.dfm
  90. 2 0
      forms/LocationProfiles.h
  91. 9 9
      forms/LogSettings.dfm
  92. 80 13
      forms/Login.cpp
  93. 6 2
      forms/Login.dfm
  94. 4 0
      forms/Login.h
  95. 1 1
      forms/MessageDlg.cpp
  96. 142 27
      forms/NonVisual.cpp
  97. 121 24
      forms/NonVisual.dfm
  98. 21 3
      forms/NonVisual.h
  99. 29 30
      forms/Password.cpp
  100. 4 2
      forms/Password.dfm

+ 5 - 5
Console.bpr

@@ -54,9 +54,9 @@
 IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=1
-MinorVer=2
-Release=1
-Build=57
+MinorVer=3
+Release=0
+Build=61
 Debug=0
 PreRelease=0
 Special=0
@@ -68,13 +68,13 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Console interface for WinSCP
-FileVersion=1.2.1.57
+FileVersion=1.3.0.61
 InternalName=console
 LegalCopyright=(c) 2004-2005 Martin Prikryl
 LegalTrademarks=
 OriginalFilename=winscp3.com
 ProductName=WinSCP
-ProductVersion=3.7.5.0
+ProductVersion=3.7.6.0
 Comments=
 
 [Compiler]

BIN
Console.res


+ 2 - 3
DScpComp.bpk

@@ -32,8 +32,7 @@
     <OTHERFILES value=""/>
   </MACROS>
   <OPTIONS>
-    <CFLAG1 value="-O2 -H=c:\PROGRA~1\borland\CBUILD~1\lib\vcl60.csm -Hc -Vx -Ve -X- -a8 -b- 
-      -k- -vi -c -tWM"/>
+    <CFLAG1 value="-O2 -H=$(BCB)\lib\vcl60.csm -Hc -Vx -Ve -X- -a8 -b- -k- -vi -c -tWM"/>
     <PFLAGS value="  -$Y- -$L- -$D- -$C- -$A8 -v -JPHNE -M"/>
     <RFLAGS value=""/>
     <AFLAGS value="/mx /w2 /zn"/>
@@ -92,7 +91,7 @@ ProductVersion=1.0.0.0
 Comments=
 
 [Compiler]
-ShowInfoMsgs=0
+ShowInfoMsgs=1
 LinkDebugVcl=0
 
 [Linker]

+ 3 - 3
DragExt.bpr

@@ -57,7 +57,7 @@ AutoIncBuild=1
 MajorVer=1
 MinorVer=1
 Release=4
-Build=60
+Build=63
 Debug=0
 PreRelease=0
 Special=0
@@ -69,13 +69,13 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Drag&amp;Drop shell extension for WinSCP
-FileVersion=1.1.4.60
+FileVersion=1.1.4.63
 InternalName=dragext
 LegalCopyright=(c) 2004-2005 Martin Prikryl
 LegalTrademarks=
 OriginalFilename=dragext.dll
 ProductName=WinSCP
-ProductVersion=3.7.5.0
+ProductVersion=3.7.6.0
 Comments=
 WWW=http://winscp.net/
 

BIN
DragExt.res


+ 4 - 1
Putty.bpf

@@ -6,4 +6,7 @@
 
 // To add a file to the library use the Project menu 'Add to Project'.
 
- 
+ 
+
+
+

+ 1 - 2
RScpComp.bpr

@@ -29,8 +29,7 @@
     <OTHERFILES value=""/>
   </MACROS>
   <OPTIONS>
-    <CFLAG1 value="-O2 -H=c:\PROGRA~1\borland\CBUILD~1\lib\vcl60.csm -Hc -w -Vx -Ve -X- -a8 
-      -b- -k- -vi -c -tW -tWM"/>
+    <CFLAG1 value="-O2 -H=$(BCB)\lib\vcl60.csm -Hc -w -Vx -Ve -X- -a8 -b- -k- -vi -c -tW -tWM"/>
     <PFLAGS value="  -$Y- -$L- -$D- -$C- -$A8 -v -JPHNE -M"/>
     <AFLAGS value="/mx /w2 /zi"/>
     <LFLAGS value="/P64"/>

+ 2 - 0
ScpForms.bpf

@@ -3,6 +3,7 @@
 #include <vcl.h>
 #pragma hdrstop
 USEFORM("forms\About.cpp", AboutDialog);
+USEFORM("forms\Banner.cpp", BannerDialog);
 USEFORM("forms\Cleanup.cpp", CleanupDialog);
 USEFORM("forms\ComboInput.cpp", ComboInputDialog);
 USEFORM("forms\Console.cpp", ConsoleDialog);
@@ -12,6 +13,7 @@ USEFORM("forms\CopyParamCustom.cpp", CopyParamCustomDialog);
 USEFORM("forms\CopyParamPreset.cpp", CopyParamPresetDialog);
 USEFORM("forms\CustomCommand.cpp", CustomCommandDialog);
 USEFORM("forms\Editor.cpp", EditorForm);
+USEFORM("forms\EditorPreferences.cpp", EditorPreferencesDialog);
 USEFORM("forms\FileSystemInfo.cpp", FileSystemInfoDialog);
 USEFORM("forms\FullSynchronize.cpp", FullSynchronizeDialog);
 USEFORM("forms\GeneralSettings.cpp", GeneralSettingsFrame); /* TFrame: File Type */

+ 19 - 16
ScpForms.bpr

@@ -5,10 +5,11 @@
     <VERSION value="BCB.06.00"/>
     <PROJECT value="lib\ScpForms.lib"/>
     <OBJFILES value="forms\InputDlg.obj forms\MessageDlg.obj windows\VCLCommon.obj 
-      forms\About.obj forms\Cleanup.obj forms\ComboInput.obj 
-      forms\Console.obj forms\Copy.obj forms\CopyParams.obj 
-      forms\CopyParamCustom.obj forms\CopyParamPreset.obj 
-      forms\CustomCommand.obj forms\Editor.obj forms\FileSystemInfo.obj 
+      forms\About.obj forms\Banner.obj forms\Cleanup.obj 
+      forms\ComboInput.obj forms\Console.obj forms\Copy.obj 
+      forms\CopyParams.obj forms\CopyParamCustom.obj 
+      forms\CopyParamPreset.obj forms\CustomCommand.obj forms\Editor.obj 
+      forms\EditorPreferences.obj forms\FileSystemInfo.obj 
       forms\FullSynchronize.obj forms\GeneralSettings.obj forms\Glyphs.obj 
       forms\ImportSessions.obj forms\Licence.obj 
       forms\LocationProfiles.obj forms\Log.obj forms\Login.obj 
@@ -19,17 +20,18 @@
       forms\Synchronize.obj forms\SynchronizeProgress.obj"/>
     <RESFILES value=""/>
     <DEFFILE value=""/>
-    <RESDEPEN value="$(RESFILES) forms\About.dfm forms\Cleanup.dfm forms\ComboInput.dfm 
-      forms\Console.dfm forms\Copy.dfm forms\CopyParams.dfm 
+    <RESDEPEN value="$(RESFILES) forms\About.dfm forms\Banner.dfm forms\Cleanup.dfm 
+      forms\ComboInput.dfm forms\Console.dfm forms\Copy.dfm forms\CopyParams.dfm 
       forms\CopyParamCustom.dfm forms\CopyParamPreset.dfm 
-      forms\CustomCommand.dfm forms\Editor.dfm forms\FileSystemInfo.dfm 
-      forms\FullSynchronize.dfm forms\GeneralSettings.dfm forms\Glyphs.dfm 
-      forms\ImportSessions.dfm forms\Licence.dfm forms\LocationProfiles.dfm 
-      forms\Log.dfm forms\Login.dfm forms\LogSettings.dfm 
-      forms\OpenDirectory.dfm forms\OperationStatus.dfm forms\Password.dfm 
-      forms\Preferences.dfm forms\Progress.dfm forms\Properties.dfm 
-      forms\Rights.dfm forms\RightsExt.dfm forms\SelectMask.dfm 
-      forms\Symlink.dfm forms\Synchronize.dfm forms\SynchronizeProgress.dfm"/>
+      forms\CustomCommand.dfm forms\Editor.dfm forms\EditorPreferences.dfm 
+      forms\FileSystemInfo.dfm forms\FullSynchronize.dfm 
+      forms\GeneralSettings.dfm forms\Glyphs.dfm forms\ImportSessions.dfm 
+      forms\Licence.dfm forms\LocationProfiles.dfm forms\Log.dfm forms\Login.dfm 
+      forms\LogSettings.dfm forms\OpenDirectory.dfm forms\OperationStatus.dfm 
+      forms\Password.dfm forms\Preferences.dfm forms\Progress.dfm 
+      forms\Properties.dfm forms\Rights.dfm forms\RightsExt.dfm 
+      forms\SelectMask.dfm forms\Symlink.dfm forms\Synchronize.dfm 
+      forms\SynchronizeProgress.dfm"/>
     <LIBFILES value=""/>
     <LIBRARIES value=""/>
     <SPARELIBS value=""/>
@@ -52,8 +54,7 @@
     <OTHERFILES value=""/>
   </MACROS>
   <OPTIONS>
-    <CFLAG1 value="-O2 -H=c:\PROGRA~1\borland\CBUILD~1\lib\vcl60.csm -Hc -w -Vx -Ve -X- -a8 
-      -b- -k- -vi -c -tW -tWM"/>
+    <CFLAG1 value="-O2 -H=$(BCB)\lib\vcl60.csm -Hc -w -Vx -Ve -X- -a8 -b- -k- -vi -c -tW -tWM"/>
     <PFLAGS value="  -$Y- -$L- -$D- -$C- -$A8 -v -JPHNE -M"/>
     <AFLAGS value="/mx /w2 /zi"/>
     <LFLAGS value="/P512"/>
@@ -70,6 +71,7 @@
       <FILE FILENAME="forms\MessageDlg.cpp" FORMNAME="" UNITNAME="MessageDlg" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="windows\VCLCommon.cpp" FORMNAME="" UNITNAME="VCLCommon" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\About.cpp" FORMNAME="AboutDialog" UNITNAME="About" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="forms\Banner.cpp" FORMNAME="BannerDialog" UNITNAME="Banner" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\Cleanup.cpp" FORMNAME="CleanupDialog" UNITNAME="Cleanup" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\ComboInput.cpp" FORMNAME="ComboInputDialog" UNITNAME="ComboInput" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\Console.cpp" FORMNAME="ConsoleDialog" UNITNAME="Console" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
@@ -79,6 +81,7 @@
       <FILE FILENAME="forms\CopyParamPreset.cpp" FORMNAME="CopyParamPresetDialog" UNITNAME="CopyParamPreset" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\CustomCommand.cpp" FORMNAME="CustomCommandDialog" UNITNAME="CustomCommand" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\Editor.cpp" FORMNAME="EditorForm" UNITNAME="Editor" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="forms\EditorPreferences.cpp" FORMNAME="EditorPreferencesDialog" UNITNAME="EditorPreferences" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\FileSystemInfo.cpp" FORMNAME="FileSystemInfoDialog" UNITNAME="FileSystemInfo" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\FullSynchronize.cpp" FORMNAME="FullSynchronizeDialog" UNITNAME="FullSynchronize" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\GeneralSettings.cpp" FORMNAME="GeneralSettingsFrame" UNITNAME="GeneralSettings" CONTAINERID="CCompiler" DESIGNCLASS="TFrame" LOCALCOMMAND=""/>

+ 5 - 5
WinSCP3.bpr

@@ -94,8 +94,8 @@ IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=3
 MinorVer=7
-Release=5
-Build=293
+Release=6
+Build=305
 Debug=0
 PreRelease=0
 Special=0
@@ -107,13 +107,13 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Windows SFTP/SCP client
-FileVersion=3.7.5.293
+FileVersion=3.7.6.305
 InternalName=winscp3
 LegalCopyright=(c) 2000-2005 Martin Prikryl
 LegalTrademarks=
-OriginalFilename=winscp375.exe
+OriginalFilename=winscp376.exe
 ProductName=WinSCP
-ProductVersion=3.7.5.0
+ProductVersion=3.7.6.0
 WWW=http://winscp.net/
 
 [Compiler]

+ 11 - 11
WinSCP3.drc

@@ -271,13 +271,13 @@
 #define Rtlconsts_SInvalidProperty 65469
 #define Rtlconsts_SInvalidPropertyPath 65470
 #define Rtlconsts_SInvalidPropertyValue 65471
-#define Customdriveview_SDragDropError 65472
-#define Morebutton_SDefaultExpandedCaption 65473
-#define Morebutton_SDefaultCollapsedCaption 65474
-#define Comboedit_SBrowse 65475
-#define Comboedit_SDefaultFilter 65476
-#define Comboedit_SInvalidFileName 65477
-#define Discmon_STooManyWatchDirectories 65478
+#define Iepathcombobox_SSpecialFolderDesktop 65472
+#define Customdriveview_SDragDropError 65473
+#define Morebutton_SDefaultExpandedCaption 65474
+#define Morebutton_SDefaultCollapsedCaption 65475
+#define Comboedit_SBrowse 65476
+#define Comboedit_SDefaultFilter 65477
+#define Comboedit_SInvalidFileName 65478
 #define Tcpip_SSocketError 65479
 #define Tcpip_SUnknownSockError 65480
 #define HelpIntfs_16398 65481
@@ -301,8 +301,8 @@
 #define Unixdirviewcolproperties_SUnixDirViewOwnerCol 65499
 #define Unixdirviewcolproperties_SUnixDirViewGroupCol 65500
 #define Unixdirviewcolproperties_SUnixDirViewLinkTargetCol 65501
-#define Iepathcombobox_SSpecialFolderMyDocuments 65502
-#define Iepathcombobox_SSpecialFolderDesktop 65503
+#define Unixdirviewcolproperties_SUnixDirViewTypeCol 65502
+#define Iepathcombobox_SSpecialFolderMyDocuments 65503
 #define Driveview_coInvalidDosChars 65504
 #define Driveview_Space 65505
 #define DriveView_16407 65506
@@ -601,13 +601,13 @@ BEGIN
 	Rtlconsts_SInvalidProperty,	"Invalid property value"
 	Rtlconsts_SInvalidPropertyPath,	"Invalid property path"
 	Rtlconsts_SInvalidPropertyValue,	"Invalid property value"
+	Iepathcombobox_SSpecialFolderDesktop,	"Desktop"
 	Customdriveview_SDragDropError,	"Drag&drop error: %d"
 	Morebutton_SDefaultExpandedCaption,	"<< &Less"
 	Morebutton_SDefaultCollapsedCaption,	"&More >>"
 	Comboedit_SBrowse,	"Browse"
 	Comboedit_SDefaultFilter,	"All files (*.*)|*.*"
 	Comboedit_SInvalidFileName,	"Invalid file name - %s"
-	Discmon_STooManyWatchDirectories,	"Cannot watch for changes in more then %d directories and subdirectories."
 	Tcpip_SSocketError,	"Error creating socket (%s)"
 	Tcpip_SUnknownSockError,	"Unknown error"
 	HelpIntfs_16398,	"Unable to find a Table of Contents"
@@ -631,8 +631,8 @@ BEGIN
 	Unixdirviewcolproperties_SUnixDirViewOwnerCol,	"Owner"
 	Unixdirviewcolproperties_SUnixDirViewGroupCol,	"Group"
 	Unixdirviewcolproperties_SUnixDirViewLinkTargetCol,	"Link target"
+	Unixdirviewcolproperties_SUnixDirViewTypeCol,	"Type"
 	Iepathcombobox_SSpecialFolderMyDocuments,	"My documents"
-	Iepathcombobox_SSpecialFolderDesktop,	"Desktop"
 	Driveview_coInvalidDosChars,	"\\/:*?\"<>|"
 	Driveview_Space,	" "
 	DriveView_16407,	"New name contains invalid characters %s"

BIN
WinSCP3.res


+ 12 - 9
components/UnixDirView.cpp

@@ -72,6 +72,7 @@ DEFINE_COMPARE_FUNC(Owner, AnsiCompareText);
 DEFINE_COMPARE_FUNC(Group, AnsiCompareText);
 DEFINE_COMPARE_FUNC(Extension, AnsiCompareText);
 DEFINE_COMPARE_FUNC(LinkTo, AnsiCompareText);
+DEFINE_COMPARE_FUNC(TypeName, AnsiCompareText);
 //---------------------------------------------------------------------------
 #undef DEFINE_COMPARE_FUNC
 #undef COMPARE_NUMBER
@@ -122,9 +123,9 @@ void __fastcall TUnixDirView::ExecuteFile(TListItem * Item)
 #ifndef DESIGN_ONLY
   ASSERT_VALID_ITEM;
   if (ITEMFILE->IsDirectory ||
-      !Terminal->SessionData->ResolveSymlinks || !Terminal->IsCapable[fcResolveSymlink])
+      !Terminal->ResolvingSymlinks)
   {
-    FLastPath = PathName;
+    PathChanging(true);
     ChangeDirectory(ITEMFILE->FileName);
   }
   else
@@ -137,8 +138,7 @@ void __fastcall TUnixDirView::ExecuteFile(TListItem * Item)
 //---------------------------------------------------------------------------
 void __fastcall TUnixDirView::ExecuteParentDirectory()
 {
-  // We need to remember this to select directory being leaved in parent directory
-  FLastPath = PathName;
+  PathChanging(true);
 #ifndef DESIGN_ONLY
   ChangeDirectory(PARENTDIRECTORY);
 #endif
@@ -148,7 +148,7 @@ void __fastcall TUnixDirView::ExecuteHomeDirectory()
 {
 #ifndef DESIGN_ONLY
   // don't select any directory
-  FLastPath = "";
+  PathChanging(false);
   AnsiString APath = Terminal->SessionData->RemoteDirectory;
   if (WinConfiguration->DefaultDirIsHome && !APath.IsEmpty() &&
       !Terminal->SessionData->UpdateDirectories)
@@ -194,7 +194,7 @@ void __fastcall TUnixDirView::ExecuteRootDirectory()
   // after entering root directory
   // DISABLED: see PathChanged(): back moves to top directory, not to current
 
-  FLastPath = PathName;
+  PathChanging(false);
   ChangeDirectory(ROOTDIRECTORY);
 #endif
 }
@@ -288,7 +288,7 @@ bool __fastcall TUnixDirView::ItemMatchesFilter(TListItem * Item,
     ((!(int)Filter.ModificationFrom) || (File->Modification >= Filter.ModificationFrom)) &&
     ((!(int)Filter.ModificationTo) || (File->Modification <= Filter.ModificationTo)) &&
     ((Filter.Masks.IsEmpty()) ||
-     FileNameMatchesMasks(File->FileName, Filter.Masks));
+     FileNameMatchesMasks(File->FileName, File->IsDirectory, Filter.Masks));
 #else
   return false;
 #endif
@@ -382,6 +382,7 @@ void __fastcall TUnixDirView::GetDisplayInfo(TListItem * Item, tagLVITEMA &DispI
         case uvGroup: Value = File->Group; break;
         case uvExt: Value = File->Extension; break;
         case uvLinkTarget: Value = File->LinkTo; break;
+        case uvType: Value = File->TypeName; break;
         default: assert(false);
       }
       StrPLCopy(DispInfo.pszText, Value, DispInfo.cchTextMax);
@@ -495,7 +496,7 @@ void __fastcall TUnixDirView::SetTerminal(TTerminal *value)
       }
     }
     FTerminal = value;
-    FLastPath = "";
+    PathChanging(false);
     if (FDriveView != NULL)
     {
       FDriveView->Terminal = FTerminal;
@@ -598,7 +599,7 @@ void __fastcall TUnixDirView::SetPath(AnsiString Value)
 
   if (Active && (Terminal->CurrentDirectory != Value))
   {
-    FLastPath = PathName;
+    PathChanging(true);
     Terminal->CurrentDirectory = Value;
   }
 #endif
@@ -620,6 +621,7 @@ void __fastcall TUnixDirView::SortItems()
       case uvGroup: SortProc = (PFNLVCOMPARE)CompareGroup; break;
       case uvExt: SortProc = (PFNLVCOMPARE)CompareExtension; break;
       case uvLinkTarget: SortProc = (PFNLVCOMPARE)CompareLinkTo; break;
+      case uvType: SortProc = (PFNLVCOMPARE)CompareTypeName; break;
       default: assert(false);
     }
     CustomSortItems(SortProc);
@@ -742,6 +744,7 @@ void __fastcall TUnixDirView::InternalEdit(const tagLVITEMA & HItem)
   TListItem *Item = GetItemFromHItem(HItem);
   ASSERT_VALID_ITEM;
   FSelectFile = HItem.pszText;
+  LoadEnabled = true;
   Terminal->RenameFile(ITEMFILE, HItem.pszText, true);
 #endif
 }

+ 55 - 40
console/Main.cpp

@@ -12,6 +12,9 @@ HANDLE ConsoleInput = NULL;
 HANDLE Child = NULL;
 HANDLE CancelEvent = NULL;
 unsigned int OutputType = FILE_TYPE_UNKNOWN;
+enum { RESULT_GLOBAL_ERROR = 1, RESULT_INIT_ERROR = 2, RESULT_PROCESSING_ERROR = 3,
+  RESULT_UNKNOWN_ERROR = 4 };
+const char * CONSOLE_CHILD_PARAM = "consolechild";
 //---------------------------------------------------------------------------
 inline TConsoleCommStruct* GetCommStruct(HANDLE FileMapping)
 {
@@ -97,54 +100,66 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
 //---------------------------------------------------------------------------
 void InitializeChild(int argc, char* argv[], int InstanceNumber, HANDLE& Child)
 {
-  char ChildPath[MAX_PATH];
-  const char* AppPath = argv[0];
-  const char* LastDelimiter = strrchr(AppPath, '\\');
-  const char* AppFileName;
-  if (LastDelimiter != NULL)
-  {
-    strncpy(ChildPath, AppPath, LastDelimiter - AppPath + 1);
-    ChildPath[LastDelimiter - AppPath + 1] = '\0';
-    #ifndef TEST_CHILD
-    AppFileName = LastDelimiter + 1;
-    #endif
-  }
-  else
-  {
-    ChildPath[0] = '\0';
-    #ifndef TEST_CHILD
-    AppFileName = AppPath;
-    #endif
-  }
+  int SkipParam = 0;
+  char ChildPath[MAX_PATH] = "";
 
-  #ifdef TEST_CHILD
-  AppFileName = "WinSCP3.com";
-  #endif
-
-  const char* ExtensionStart = strrchr(AppFileName, '.');
-  if (ExtensionStart != NULL)
+  for (int i = 1; i < argc; i++)
   {
-    char* End = ChildPath + strlen(ChildPath);
-    strncpy(End, AppFileName, ExtensionStart - AppFileName);
-    *(End + (ExtensionStart - AppFileName)) = '\0';
+    if ((strchr("-/", argv[i][0]) != NULL) &&
+        (strncmpi(argv[i] + 1, CONSOLE_CHILD_PARAM, strlen(CONSOLE_CHILD_PARAM)) == 0) &&
+        (argv[i][strlen(CONSOLE_CHILD_PARAM) + 1] == '='))
+    {
+      SkipParam = i;
+      strcpy(ChildPath, argv[i] + 1 + strlen(CONSOLE_CHILD_PARAM) + 1);
+      break;
+    }
   }
-  else
+
+  if (strlen(ChildPath) == 0)
   {
-    strcat(ChildPath, AppFileName);
+    const char* AppPath = argv[0];
+    const char* LastDelimiter = strrchr(AppPath, '\\');
+    const char* AppFileName;
+    if (LastDelimiter != NULL)
+    {
+      strncpy(ChildPath, AppPath, LastDelimiter - AppPath + 1);
+      ChildPath[LastDelimiter - AppPath + 1] = '\0';
+      AppFileName = LastDelimiter + 1;
+    }
+    else
+    {
+      ChildPath[0] = '\0';
+      AppFileName = AppPath;
+    }
+
+    const char* ExtensionStart = strrchr(AppFileName, '.');
+    if (ExtensionStart != NULL)
+    {
+      char* End = ChildPath + strlen(ChildPath);
+      strncpy(End, AppFileName, ExtensionStart - AppFileName);
+      *(End + (ExtensionStart - AppFileName)) = '\0';
+    }
+    else
+    {
+      strcat(ChildPath, AppFileName);
+    }
+    strcat(ChildPath, ".exe");
   }
-  strcat(ChildPath, ".exe");
 
   char Parameters[10240];
   sprintf(Parameters, "\"%s\" /console /consoleinstance=%d ", ChildPath, InstanceNumber);
   for (int i = 1; i < argc; i++)
   {
-    if (strlen(Parameters) + strlen(argv[i]) + 4 > sizeof(Parameters))
+    if (i != SkipParam)
     {
-      throw length_error("Too many parameters");
+      if (strlen(Parameters) + strlen(argv[i]) + 4 > sizeof(Parameters))
+      {
+        throw length_error("Too many parameters");
+      }
+      strcat(Parameters, "\"");
+      strcat(Parameters, argv[i]);
+      strcat(Parameters, "\" ");
     }
-    strcat(Parameters, "\"");
-    strcat(Parameters, argv[i]);
-    strcat(Parameters, "\" ");
   }
 
   STARTUPINFO StartupInfo = { sizeof(STARTUPINFO) };
@@ -385,7 +400,7 @@ BOOL WINAPI HandlerRoutine(DWORD CtrlType)
 #pragma argsused
 int main(int argc, char* argv[])
 {
-  unsigned long Result = 4;
+  unsigned long Result = RESULT_UNKNOWN_ERROR;
 
   try
   {
@@ -453,7 +468,7 @@ int main(int argc, char* argv[])
       catch(const exception& e)
       {
         puts(e.what());
-        Result = 3;
+        Result = RESULT_PROCESSING_ERROR;
       }
 
       #ifndef CONSOLE_TEST
@@ -465,7 +480,7 @@ int main(int argc, char* argv[])
     catch(const exception& e)
     {
       puts(e.what());
-      Result = 2;
+      Result = RESULT_INIT_ERROR;
     }
 
     FinalizeConsole(InstanceNumber, RequestEvent, ResponseEvent,
@@ -474,7 +489,7 @@ int main(int argc, char* argv[])
   catch(const exception& e)
   {
     puts(e.what());
-    Result = 1;
+    Result = RESULT_GLOBAL_ERROR;
   }
 
   return Result;

+ 69 - 21
core/Bookmarks.cpp

@@ -33,13 +33,13 @@ void __fastcall TBookmarks::Clear()
   FBookmarkLists->Clear();
 }
 //---------------------------------------------------------------------------
+AnsiString TBookmarks::Keys[] = { "Local", "Remote", "Options" };
+//---------------------------------------------------------------------------
 void __fastcall TBookmarks::Load(THierarchicalStorage * Storage)
 {
-  bool Local = false;
-  do
+  for (int i = 0; i <= 2; i++)
   {
-    Local = !Local;
-    if (Storage->OpenSubKey(Local ? "Local" : "Remote", false))
+    if (Storage->OpenSubKey(Keys[i], false))
     {
       TStrings * BookmarkKeys = new TStringList();
       try
@@ -56,7 +56,14 @@ void __fastcall TBookmarks::Load(THierarchicalStorage * Storage)
               BookmarkList = new TBookmarkList();
               FBookmarkLists->AddObject(Key, BookmarkList);
             }
-            LoadLevel(Storage, "", Local, BookmarkList);
+            if (i < 2)
+            {
+              LoadLevel(Storage, "", (i == 0), BookmarkList);
+            }
+            else
+            {
+              BookmarkList->LoadOptions(Storage);
+            }
             Storage->CloseSubKey();
           }
         } 
@@ -68,7 +75,6 @@ void __fastcall TBookmarks::Load(THierarchicalStorage * Storage)
       Storage->CloseSubKey();
     }
   }
-  while (Local);
 
   ModifyAll(false);
 }
@@ -135,11 +141,9 @@ void __fastcall TBookmarks::LoadLevel(THierarchicalStorage * Storage, const Ansi
 //---------------------------------------------------------------------------
 void __fastcall TBookmarks::Save(THierarchicalStorage * Storage)
 {
-  bool Local = false;
-  do
+  for (int i = 0; i <= 2; i++)
   {
-    Local = !Local;
-    if (Storage->OpenSubKey(Local ? "Local" : "Remote", true))
+    if (Storage->OpenSubKey(Keys[i], true))
     {
       for (int Index = 0; Index < FBookmarkLists->Count; Index++)
       {
@@ -151,22 +155,29 @@ void __fastcall TBookmarks::Save(THierarchicalStorage * Storage)
           Storage->RecursiveDeleteSubKey(Key);
           if (Storage->OpenSubKey(Key, true))
           {
-            for (int IndexB = 0; IndexB < BookmarkList->Count; IndexB++)
+            if (i < 2)
             {
-              TBookmark * Bookmark = BookmarkList->Bookmarks[IndexB];
-              AnsiString Directory = Local ? Bookmark->Local : Bookmark->Remote;
-              if (!Bookmark->Node.IsEmpty())
+              for (int IndexB = 0; IndexB < BookmarkList->Count; IndexB++)
               {
-                if (Storage->OpenSubKey(Bookmark->Node, true))
+                TBookmark * Bookmark = BookmarkList->Bookmarks[IndexB];
+                AnsiString Directory = (i == 0) ? Bookmark->Local : Bookmark->Remote;
+                if (!Bookmark->Node.IsEmpty())
+                {
+                  if (Storage->OpenSubKey(Bookmark->Node, true))
+                  {
+                    Storage->WriteString(Bookmark->Name, Directory);
+                    Storage->CloseSubKey();
+                  }
+                }
+                else
                 {
                   Storage->WriteString(Bookmark->Name, Directory);
-                  Storage->CloseSubKey();
                 }
               }
-              else
-              {
-                Storage->WriteString(Bookmark->Name, Directory);
-              }
+            }
+            else
+            {
+              BookmarkList->SaveOptions(Storage);
             }
             Storage->CloseSubKey();
           }
@@ -175,7 +186,6 @@ void __fastcall TBookmarks::Save(THierarchicalStorage * Storage)
       Storage->CloseSubKey();
     }
   }
-  while (Local);
 
   ModifyAll(false);
 }
@@ -229,12 +239,16 @@ __fastcall TBookmarkList::TBookmarkList(): TPersistent()
   FModified = false;
   FBookmarks = new TStringList();
   FBookmarks->CaseSensitive = false;
+  FOpenedNodes = new TStringList();
+  FOpenedNodes->CaseSensitive = false;
+  FOpenedNodes->Sorted = true;
 }
 //---------------------------------------------------------------------------
 __fastcall TBookmarkList::~TBookmarkList()
 {
   Clear();
   SAFE_DESTROY(FBookmarks);
+  SAFE_DESTROY(FOpenedNodes);
 }
 //---------------------------------------------------------------------------
 void __fastcall TBookmarkList::Clear()
@@ -244,6 +258,7 @@ void __fastcall TBookmarkList::Clear()
     delete FBookmarks->Objects[i];
   }
   FBookmarks->Clear();
+  FOpenedNodes->Clear();
 }
 //---------------------------------------------------------------------------
 void __fastcall TBookmarkList::Assign(TPersistent * Source)
@@ -259,6 +274,7 @@ void __fastcall TBookmarkList::Assign(TPersistent * Source)
       Bookmark->Assign(dynamic_cast<TBookmark *>(SourceList->FBookmarks->Objects[i]));
       Add(Bookmark);
     }
+    FOpenedNodes->Assign(SourceList->FOpenedNodes);
     Modified = SourceList->Modified;
   }
   else
@@ -267,6 +283,16 @@ void __fastcall TBookmarkList::Assign(TPersistent * Source)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TBookmarkList::LoadOptions(THierarchicalStorage * Storage)
+{
+  FOpenedNodes->CommaText = Storage->ReadString("OpenedNodes", "");
+}
+//---------------------------------------------------------------------------
+void __fastcall TBookmarkList::SaveOptions(THierarchicalStorage * Storage)
+{
+  Storage->WriteString("OpenedNodes", FOpenedNodes->CommaText);
+}
+//---------------------------------------------------------------------------
 void __fastcall TBookmarkList::Add(TBookmark * Bookmark)
 {
   Insert(Count, Bookmark);
@@ -363,6 +389,28 @@ TBookmark * __fastcall TBookmarkList::GetBookmarks(int Index)
   return Bookmark;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TBookmarkList::GetNodeOpened(AnsiString Index)
+{
+  return (FOpenedNodes->IndexOf(Index) >= 0);
+}
+//---------------------------------------------------------------------------
+void __fastcall TBookmarkList::SetNodeOpened(AnsiString Index, bool value)
+{
+  int I = FOpenedNodes->IndexOf(Index);
+  if ((I >= 0) != value)
+  {
+    if (value)
+    {
+      FOpenedNodes->Add(Index);
+    }
+    else
+    {
+      FOpenedNodes->Delete(I);
+    }
+    FModified = true;
+  }
+}
+//---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 __fastcall TBookmark::TBookmark()
 {

+ 7 - 0
core/Bookmarks.h

@@ -20,6 +20,7 @@ public:
 
 private:
   TStringList * FBookmarkLists;
+  static AnsiString Keys[];
 
   TBookmarkList * __fastcall GetBookmarks(AnsiString Index);
   void __fastcall SetBookmarks(AnsiString Index, TBookmarkList * value);
@@ -43,9 +44,12 @@ public:
   void __fastcall Delete(TBookmark * Bookmark);
   TBookmark * __fastcall FindByName(const AnsiString Node, const AnsiString Name);
   virtual void __fastcall Assign(TPersistent * Source);
+  void __fastcall LoadOptions(THierarchicalStorage * Storage);
+  void __fastcall SaveOptions(THierarchicalStorage * Storage);
 
   __property int Count = { read = GetCount };
   __property TBookmark * Bookmarks[int Index] = { read = GetBookmarks };
+  __property bool NodeOpened[AnsiString Index] = { read = GetNodeOpened, write = SetNodeOpened };
 
 protected:
   int __fastcall IndexOf(TBookmark * Bookmark);
@@ -55,10 +59,13 @@ protected:
 
 private:
   TStringList * FBookmarks;
+  TStringList * FOpenedNodes;
   bool FModified;
 
   int __fastcall GetCount();
   TBookmark * __fastcall GetBookmarks(int Index);
+  bool __fastcall GetNodeOpened(AnsiString Index);
+  void __fastcall SetNodeOpened(AnsiString Index, bool value);
 };
 //---------------------------------------------------------------------------
 class TBookmark : public TPersistent

+ 80 - 13
core/Common.cpp

@@ -17,46 +17,51 @@ void __fastcall Trace(const AnsiString SourceFile, const AnsiString Func,
   int Line, const AnsiString Message)
 {
   const char * FileName = getenv(TRACEENV);
-  //!!!
   if (FileName == NULL)
   {
     FileName = "C:\\winscptrace.log";
   }
-  //!!!
-  if (FileName != NULL)
+  FILE * File = fopen(FileName, "a");
+  if (File != NULL)
   {
-    FILE * File = fopen(FileName, "a");
-    if (File != NULL)
-    {
-      fprintf(File, "[%s] %s:%d:%s\n  %s\n",
-        Now().TimeString().c_str(),
-        ExtractFileName(SourceFile).c_str(), Line, Func.c_str(), Message.c_str());
+    fprintf(File, "[%s] %s:%d:%s\n  %s\n",
+      Now().TimeString().c_str(),
+      ExtractFileName(SourceFile).c_str(), Line, Func.c_str(), Message.c_str());
 
-      fclose(File);
-    }
+    fclose(File);
   }
 }
+//---------------------------------------------------------------------------
+void __fastcall DoAssert(char * Message, char * Filename, int LineNumber)
+{
+  Trace(Filename, "assert", LineNumber, Message);
+  _assert(Message, Filename, LineNumber);
+}
 #endif // ifdef _DEBUG
 //---------------------------------------------------------------------------
 // TCriticalSection
 //---------------------------------------------------------------------------
 __fastcall TCriticalSection::TCriticalSection()
 {
+  FAcquired = 0;
   InitializeCriticalSection(&FSection);
 }
 //---------------------------------------------------------------------------
 __fastcall TCriticalSection::~TCriticalSection()
 {
+  assert(FAcquired == 0);
   DeleteCriticalSection(&FSection);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCriticalSection::Enter()
 {
   EnterCriticalSection(&FSection);
+  FAcquired++;
 }
 //---------------------------------------------------------------------------
 void __fastcall TCriticalSection::Leave()
 {
+  FAcquired--;
   LeaveCriticalSection(&FSection);
 }
 //---------------------------------------------------------------------------
@@ -175,6 +180,59 @@ AnsiString CutToChar(AnsiString &Str, Char Ch, bool Trim)
   return Result;
 }
 //---------------------------------------------------------------------------
+AnsiString CutToChars(AnsiString & Str, AnsiString Chs, bool Trim)
+{
+  int P;
+  for (P = 1; P <= Str.Length(); P++)
+  {
+    if (IsDelimiter(Chs, Str, P))
+    {
+      break;
+    }
+  }
+
+  AnsiString Result;
+  if (P <= Str.Length())
+  {
+    Result = Str.SubString(1, P-1);
+    Str.Delete(1, P);
+  }
+  else
+  {
+    Result = Str;
+    Str = "";
+  }
+  if (Trim)
+  {
+    Result = Result.TrimRight();
+    Str = Str.TrimLeft();
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+AnsiString DelimitStr(AnsiString Str, AnsiString Chars)
+{
+  for (int i = 1; i <= Str.Length(); i++)
+  {
+    if (Str.IsDelimiter(Chars, i))
+    {
+      Str.Insert("\\", i);
+      i++;
+    }
+  }
+  return Str;
+}
+//---------------------------------------------------------------------------
+AnsiString ShellDelimitStr(AnsiString Str, char Quote)
+{
+  AnsiString Chars = "$\\";
+  if (Quote == '"')
+  {
+    Chars += "`\"";
+  }
+  return DelimitStr(Str, Chars);
+}
+//---------------------------------------------------------------------------
 AnsiString ExceptionLogString(Exception *E)
 {
   assert(E);
@@ -313,12 +371,17 @@ bool __fastcall IsDisplayableStr(const AnsiString Str)
   return Displayable;
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall CharToHex(char Ch)
+{
+  return IntToHex((unsigned char)Ch, 2);
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall StrToHex(const AnsiString Str)
 {
   AnsiString Result;
   for (int i = 1; i <= Str.Length(); i++)
   {
-    Result += IntToHex(Str[i], 2);
+    Result += CharToHex(Str[i]);
   }
   return Result;
 }
@@ -348,7 +411,7 @@ AnsiString __fastcall HexToStr(const AnsiString Hex)
   return Result;
 }
 //---------------------------------------------------------------------------
-unsigned int __fastcall HexToInt(const AnsiString Hex)
+unsigned int __fastcall HexToInt(const AnsiString Hex, int MinChars)
 {
   static AnsiString Digits = "0123456789ABCDEF";
   int Result = 0;
@@ -358,6 +421,10 @@ unsigned int __fastcall HexToInt(const AnsiString Hex)
     int A = Digits.Pos(Hex[I]);
     if (A <= 0)
     {
+      if ((MinChars < 0) || (I <= MinChars))
+      {
+        Result = 0;
+      }
       break;
     }
 

+ 39 - 1
core/Common.h

@@ -30,6 +30,9 @@ AnsiString RootKeyToStr(HKEY RootKey);
 AnsiString BooleanToStr(bool B);
 AnsiString BooleanToEngStr(bool B);
 AnsiString CutToChar(AnsiString &Str, Char Ch, bool Trim);
+AnsiString CutToChars(AnsiString &Str, AnsiString Chs, bool Trim);
+AnsiString DelimitStr(AnsiString Str, AnsiString Chars);
+AnsiString ShellDelimitStr(AnsiString Str, char Quote);
 void __fastcall OemToAnsi(AnsiString & Str);
 void __fastcall AnsiToOem(AnsiString & Str);
 AnsiString ExceptionLogString(Exception *E);
@@ -42,9 +45,10 @@ void __fastcall SplitCommand(AnsiString Command, AnsiString &Program,
 AnsiString __fastcall ExtractProgram(AnsiString Command);
 AnsiString __fastcall FormatCommand(AnsiString Program, AnsiString Params);
 bool __fastcall IsDisplayableStr(const AnsiString Str);
+AnsiString __fastcall CharToHex(char Ch);
 AnsiString __fastcall StrToHex(const AnsiString Str);
 AnsiString __fastcall HexToStr(const AnsiString Hex);
-unsigned int __fastcall HexToInt(const AnsiString Hex);
+unsigned int __fastcall HexToInt(const AnsiString Hex, int MinChars = 0);
 AnsiString __fastcall DecodeUrlChars(AnsiString S);
 bool __fastcall RecursiveDeleteFile(const AnsiString FileName, bool ToRecycleBin);
 int __fastcall CancelAnswer(int Answers);
@@ -78,8 +82,11 @@ public:
   void __fastcall Enter();
   void __fastcall Leave();
 
+  __property int Acquired = { read = FAcquired };
+
 private:
   TRTLCriticalSection FSection;
+  int FAcquired;
 };
 //---------------------------------------------------------------------------
 class TGuard
@@ -118,10 +125,34 @@ void __fastcall Trace(const AnsiString SourceFile, const AnsiString Func,
   int Line, const AnsiString Message);
 #define TRACE(MESSAGE) Trace(__FILE__, __FUNC__, __LINE__, MESSAGE)
 #define TRACEFMT(MESSAGE, PARAMS) Trace(__FILE__, __FUNC__, __LINE__, FORMAT(MESSAGE, PARAMS))
+class Callstack
+{
+public:
+  inline Callstack(const char * File, const char * Func, unsigned int Line, AnsiString Message) :
+    FFile(File), FFunc(Func), FLine(Line), FMessage(Message)
+  {
+    Trace(FFile, FFunc, FLine, AnsiString("Entry: ") + FMessage);
+  }
+
+  inline ~Callstack()
+  {
+    Trace(FFile, FFunc, FLine, AnsiString("Exit ") + FMessage);
+  }
+
+private:
+  const char * FFile;
+  const char * FFunc;
+  unsigned int FLine;
+  AnsiString FMessage;
+};
+#define CALLSTACKIMPL(X) Callstack X(__FILE__, __FUNC__, __LINE__, "")
 #else // ifdef _DEBUG
 #define TRACE(PARAMS)
 #define TRACEFMT(MESSAGE, PARAMS)
+#define CALLSTACKIMPL(X)
 #endif // ifdef _DEBUG
+#define CALLSTACK CALLSTACKIMPL(__callstack)
+#define CALLSTACK1 CALLSTACKIMPL(__callstack1)
 //---------------------------------------------------------------------------
 #endif
 //---------------------------------------------------------------------------
@@ -131,6 +162,13 @@ void __fastcall Trace(const AnsiString SourceFile, const AnsiString Func,
 #define assert(p)   ((void)0)
 #define CHECK(p) p
 #else
+#ifndef C_ONLY
+#ifndef DESIGN_ONLY
+#undef assert
+void __fastcall DoAssert(char * Message, char * Filename, int LineNumber);
+#define assert(p) ((p) ? (void)0 : DoAssert(#p, __FILE__, __LINE__))
+#endif // ifndef DESIGN_ONLY
+#endif // ifndef C_ONLY
 #define CHECK(p) { bool __CHECK_RESULT__ = (p); assert(__CHECK_RESULT__); }
 #endif
 #define USEDPARAM(p) ((p) == (p))

+ 50 - 0
core/Configuration.cpp

@@ -191,6 +191,56 @@ void __fastcall TConfiguration::SaveDirectoryChangesCache(const AnsiString Sessi
   }
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::BannerHash(const AnsiString & Banner)
+{
+  AnsiString Result;
+  Result.SetLength(16);
+  md5checksum(Banner.c_str(), Banner.Length(), (unsigned char*)Result.c_str());
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TConfiguration::ShowBanner(const AnsiString SessionKey,
+  const AnsiString & Banner)
+{
+  bool Result;
+  THierarchicalStorage * Storage = CreateScpStorage(false);
+  try
+  {
+    Storage->AccessMode = smRead;
+    Result =
+      !Storage->OpenSubKey(ConfigurationSubKey, false) ||
+      !Storage->OpenSubKey("Banners", false) ||
+      !Storage->ValueExists(SessionKey) ||
+      (Storage->ReadString(SessionKey, "") != StrToHex(BannerHash(Banner)));
+  }
+  __finally
+  {
+    delete Storage;
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TConfiguration::NeverShowBanner(const AnsiString SessionKey,
+  const AnsiString & Banner)
+{
+  THierarchicalStorage * Storage = CreateScpStorage(false);
+  try
+  {
+    Storage->AccessMode = smReadWrite;
+
+    if (Storage->OpenSubKey(ConfigurationSubKey, true) &&
+        Storage->OpenSubKey("Banners", true))
+    {
+      Storage->WriteString(SessionKey, StrToHex(BannerHash(Banner)));
+    }
+  }
+  __finally
+  {
+    delete Storage;
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TConfiguration::Changed()
 {
   if (FUpdating == 0)

+ 3 - 0
core/Configuration.h

@@ -83,6 +83,7 @@ protected:
   virtual AnsiString __fastcall GetDefaultKeyFile();
   virtual void __fastcall ModifyAll();
   void __fastcall CleanupRegistry(AnsiString CleanupSubKey);
+  AnsiString __fastcall BannerHash(const AnsiString & Banner);
 
   virtual bool __fastcall GetConfirmOverwriting();
   virtual void __fastcall SetConfirmOverwriting(bool value);
@@ -115,6 +116,8 @@ public:
     TRemoteDirectoryChangesCache * DirectoryChangesCache);   
   void __fastcall SaveDirectoryChangesCache(const AnsiString SessionKey,
     TRemoteDirectoryChangesCache * DirectoryChangesCache);
+  bool __fastcall ShowBanner(const AnsiString SessionKey, const AnsiString & Banner);
+  void __fastcall NeverShowBanner(const AnsiString SessionKey, const AnsiString & Banner);
   virtual THierarchicalStorage * CreateScpStorage(bool SessionList);
 
   __property TVSFixedFileInfo *FixedApplicationInfo  = { read=GetFixedApplicationInfo };

+ 161 - 61
core/CopyParam.cpp

@@ -35,7 +35,7 @@ void __fastcall TCopyParamType::Default()
   AddXToDirectories = true;
   ResumeSupport = rsSmart;
   ResumeThreshold = 100 * 1024; // (100 kB)
-  ReplaceInvalidChars = true;
+  InvalidCharsReplacement = TokenReplacement;
   LocalInvalidChars = "/\\:*?\"<>|";
   CalculateSize = true;
   FileMask = "*.*";
@@ -44,65 +44,90 @@ void __fastcall TCopyParamType::Default()
   ClearArchive = false;
 }
 //---------------------------------------------------------------------------
-AnsiString __fastcall TCopyParamType::GetInfoStr(AnsiString Separator) const
+AnsiString __fastcall TCopyParamType::GetInfoStr(AnsiString Separator, int Options) const
 {
   TCopyParamType Defaults;
   AnsiString Result;
 
   #define ADD(STR) Result += (Result.IsEmpty() ? AnsiString() : Separator) + (STR)
-  if ((TransferMode != Defaults.TransferMode) ||
-      ((TransferMode == tmAutomatic) && !(AsciiFileMask == Defaults.AsciiFileMask)))
+  if (FLAGCLEAR(Options, cpiExcludeMaskOnly))
   {
-    AnsiString S = FORMAT(LoadStrPart(COPY_INFO_TRANSFER_TYPE, 1),
-      (LoadStrPart(COPY_INFO_TRANSFER_TYPE, TransferMode + 2)));
-    if (TransferMode == tmAutomatic)
+    if ((TransferMode != Defaults.TransferMode) ||
+        ((TransferMode == tmAutomatic) && !(AsciiFileMask == Defaults.AsciiFileMask)))
     {
-      S = FORMAT(S, (AsciiFileMask.Masks));
+      AnsiString S = FORMAT(LoadStrPart(COPY_INFO_TRANSFER_TYPE, 1),
+        (LoadStrPart(COPY_INFO_TRANSFER_TYPE, TransferMode + 2)));
+      if (TransferMode == tmAutomatic)
+      {
+        S = FORMAT(S, (AsciiFileMask.Masks));
+      }
+      ADD(S);
     }
-    ADD(S);
-  }
 
-  if (FileNameCase != Defaults.FileNameCase)
-  {
-    ADD(FORMAT(LoadStrPart(COPY_INFO_FILENAME, 1),
-      (LoadStrPart(COPY_INFO_FILENAME, FileNameCase + 2))));
-  }
+    if (FileNameCase != Defaults.FileNameCase)
+    {
+      ADD(FORMAT(LoadStrPart(COPY_INFO_FILENAME, 1),
+        (LoadStrPart(COPY_INFO_FILENAME, FileNameCase + 2))));
+    }
 
-  if (ReplaceInvalidChars != Defaults.ReplaceInvalidChars)
-  {
-    assert(!ReplaceInvalidChars);
-    ADD(LoadStr(COPY_INFO_DONT_REPLACE_INV_CHARS));
-  }
+    if ((InvalidCharsReplacement == NoReplacement) !=
+          (Defaults.InvalidCharsReplacement == NoReplacement))
+    {
+      assert(InvalidCharsReplacement == NoReplacement);
+      if (InvalidCharsReplacement == NoReplacement)
+      {
+        ADD(LoadStr(COPY_INFO_DONT_REPLACE_INV_CHARS));
+      }
+    }
 
-  if ((PreserveRights != Defaults.PreserveRights) ||
-      (PreserveRights && 
-       ((Rights != Defaults.Rights) || (AddXToDirectories != Defaults.AddXToDirectories))))
-  {
-    assert(PreserveRights);
+    if ((PreserveRights != Defaults.PreserveRights) ||
+        (PreserveRights && 
+         ((Rights != Defaults.Rights) || (AddXToDirectories != Defaults.AddXToDirectories))))
+    {
+      assert(PreserveRights);
     
-    AnsiString RightsStr = Rights.Text;
-    if (AddXToDirectories)
+      if (PreserveRights)
+      {
+        AnsiString RightsStr = Rights.Text;
+        if (AddXToDirectories)
+        {
+          RightsStr += ", " + LoadStr(COPY_INFO_ADD_X_TO_DIRS);
+        }
+        ADD(FORMAT(LoadStr(COPY_INFO_PERMISSIONS), (RightsStr)));
+      }
+    }
+
+    if (PreserveReadOnly != Defaults.PreserveReadOnly)
     {
-      RightsStr += ", " + LoadStr(COPY_INFO_ADD_X_TO_DIRS);
+      assert(!PreserveReadOnly);
+      if (!PreserveReadOnly)
+      {
+        ADD(LoadStr(COPY_INFO_DONT_PRESERVE_READONLY));
+      }
     }
-    ADD(FORMAT(LoadStr(COPY_INFO_PERMISSIONS), (RightsStr)));
-  }
 
-  if (PreserveReadOnly != Defaults.PreserveReadOnly)
-  {
-    assert(!PreserveReadOnly);
-    ADD(LoadStr(COPY_INFO_DONT_PRESERVE_READONLY));
-  }
+    if (PreserveTime != Defaults.PreserveTime)
+    {
+      ADD(LoadStr(PreserveTime ? COPY_INFO_TIMESTAMP : COPY_INFO_DONT_PRESERVE_TIME));
+    }
 
-  if (PreserveTime != Defaults.PreserveTime)
-  {
-    ADD(LoadStr(PreserveTime ? COPY_INFO_TIMESTAMP : COPY_INFO_DONT_PRESERVE_TIME));
-  }
+    if (CalculateSize != Defaults.CalculateSize)
+    {
+      assert(!CalculateSize);
+      if (!CalculateSize)
+      {
+        ADD(LoadStr(COPY_INFO_DONT_CALCULATE_SIZE));
+      }
+    }
 
-  if (CalculateSize != Defaults.CalculateSize)
-  {
-    assert(!CalculateSize);
-    ADD(LoadStr(COPY_INFO_DONT_CALCULATE_SIZE));
+    if (ClearArchive != Defaults.ClearArchive)
+    {
+      assert(ClearArchive);
+      if (ClearArchive)
+      {
+        ADD(LoadStr(COPY_INFO_CLEAR_ARCHIVE));
+      }
+    }
   }
 
   if ((NegativeExclude != Defaults.NegativeExclude) ||
@@ -111,12 +136,6 @@ AnsiString __fastcall TCopyParamType::GetInfoStr(AnsiString Separator) const
     ADD(FORMAT(LoadStr(NegativeExclude ? COPY_INFO_INCLUDE_MASK : COPY_INFO_EXCLUDE_MASK),
       (ExcludeFileMask.Masks)));
   }
-
-  if (ClearArchive != Defaults.ClearArchive)
-  {
-    assert(ClearArchive);
-    ADD(LoadStr(COPY_INFO_CLEAR_ARCHIVE));
-  }
   #undef ADD
 
   if (Result.IsEmpty())
@@ -141,7 +160,7 @@ void __fastcall TCopyParamType::Assign(const TCopyParamType * Source)
   COPY(PreserveRights);
   COPY(ResumeSupport);
   COPY(ResumeThreshold);
-  COPY(ReplaceInvalidChars);
+  COPY(InvalidCharsReplacement);
   COPY(LocalInvalidChars);
   COPY(CalculateSize);
   COPY(FileMask);
@@ -157,16 +176,93 @@ TCopyParamType & __fastcall TCopyParamType::operator =(const TCopyParamType & rh
   return *this;
 }
 //---------------------------------------------------------------------------
+void __fastcall TCopyParamType::SetLocalInvalidChars(AnsiString value)
+{
+  if (value != LocalInvalidChars)
+  {
+    FLocalInvalidChars = value;
+    FTokenizibleChars = FLocalInvalidChars + TokenPrefix;
+  }
+}
+//---------------------------------------------------------------------------
+bool __fastcall TCopyParamType::GetReplaceInvalidChars() const
+{
+  return (InvalidCharsReplacement != NoReplacement);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyParamType::SetReplaceInvalidChars(bool value)
+{
+  if (ReplaceInvalidChars != value)
+  {
+    InvalidCharsReplacement = (value ? TokenReplacement : NoReplacement);
+  }
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall TCopyParamType::ValidLocalFileName(AnsiString FileName) const
 {
-  char * InvalidChar;
-  while ((InvalidChar = strpbrk(FileName.c_str(), LocalInvalidChars.c_str())) != NULL)
+  if (InvalidCharsReplacement != NoReplacement)
   {
-    FileName[InvalidChar - FileName.c_str() + 1] = '_';
+    bool ATokenReplacement = (InvalidCharsReplacement == TokenReplacement);
+    const char * Chars =
+      (ATokenReplacement ? FTokenizibleChars : LocalInvalidChars).c_str();
+    char * InvalidChar = FileName.c_str();
+    while ((InvalidChar = strpbrk(InvalidChar, Chars)) != NULL)
+    {
+      int Index = InvalidChar - FileName.c_str() + 1;
+      if (FileName.ByteType(Index) == mbSingleByte)
+      {
+        if (ATokenReplacement)
+        {
+          FileName.Insert(CharToHex(FileName[Index]), Index + 1);
+          FileName[Index] = TokenPrefix;
+          InvalidChar = FileName.c_str() + Index + 2;
+        }
+        else
+        {
+          FileName[Index] = InvalidCharsReplacement;
+          InvalidChar++;
+        }
+      }
+      else
+      {
+        InvalidChar++;
+      }
+    }
   }
   return FileName;
 }
 //---------------------------------------------------------------------------
+// not used yet
+AnsiString __fastcall TCopyParamType::Untokenize(AnsiString FileName)
+{
+  char * Token;
+  AnsiString Result = FileName;
+  while ((Token = AnsiStrScan(Result.c_str(), TokenPrefix)) != NULL)
+  {
+    int Index = Token - Result.c_str() + 1;
+    if (Index > Result.Length() - 2)
+    {
+      Result = FileName;
+      break;
+    }
+    else
+    {
+      char Ch = (char)HexToInt(Result.SubString(Index + 1, 2), -1);
+      if (Ch == '\0')
+      {
+        Result = FileName;
+        break;
+      }
+      else
+      {
+        Result[Index] = Ch;
+        Result.Delete(Index + 1, 2);
+      }
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall TCopyParamType::ChangeFileName(AnsiString FileName,
   TOperationSide Side, bool FirstLevel) const
 {
@@ -191,7 +287,7 @@ AnsiString __fastcall TCopyParamType::ChangeFileName(AnsiString FileName,
       /*nothing*/
       break;
   }
-  if (ReplaceInvalidChars && (Side == osRemote))
+  if (Side == osRemote)
   {
     FileName = ValidLocalFileName(FileName);
   }
@@ -204,7 +300,7 @@ bool __fastcall TCopyParamType::UseAsciiTransfer(AnsiString FileName,
   switch (TransferMode) {
     case tmBinary: return false;
     case tmAscii: return true;
-    case tmAutomatic: return AsciiFileMask.Matches(FileName, (Side == osLocal));
+    case tmAutomatic: return AsciiFileMask.Matches(FileName, (Side == osLocal), false);
     default: assert(false); return false;
   }
 }
@@ -234,7 +330,7 @@ AnsiString __fastcall TCopyParamType::GetLogStr() const
      Rights.Text,
      BooleanToEngStr(PreserveRights),
      CaseC[FileNameCase],
-     BooleanToEngStr(ReplaceInvalidChars),
+     CharToHex(InvalidCharsReplacement),
      ResumeC[ResumeSupport],
      (int)ResumeThreshold,
      BooleanToEngStr(CalculateSize),
@@ -267,12 +363,13 @@ bool __fastcall TCopyParamType::AllowResume(__int64 Size) const
 }
 //---------------------------------------------------------------------------
 bool __fastcall TCopyParamType::AllowTransfer(AnsiString FileName,
-  TOperationSide Side) const
+  TOperationSide Side, bool Directory) const
 {
   bool Result = true;
   if (!ExcludeFileMask.Masks.IsEmpty())
   {
-    Result = (ExcludeFileMask.Matches(FileName, (Side == osLocal)) == NegativeExclude);
+    Result = (ExcludeFileMask.Matches(FileName, (Side == osLocal), Directory) ==
+      NegativeExclude);
   }
   return Result;
 }
@@ -289,10 +386,12 @@ void __fastcall TCopyParamType::Load(THierarchicalStorage * Storage)
   TransferMode = (TTransferMode)Storage->ReadInteger("TransferMode", TransferMode);
   ResumeSupport = (TResumeSupport)Storage->ReadInteger("ResumeSupport", ResumeSupport);
   ResumeThreshold = Storage->ReadInt64("ResumeThreshold", ResumeThreshold);
-  ReplaceInvalidChars = Storage->ReadBool("ReplaceInvalidChars", ReplaceInvalidChars);
+  InvalidCharsReplacement = (char)Storage->ReadInteger("ReplaceInvalidChars",
+    (unsigned char)InvalidCharsReplacement);
   LocalInvalidChars = Storage->ReadString("LocalInvalidChars", LocalInvalidChars);
   CalculateSize = Storage->ReadBool("CalculateSize", CalculateSize);
   ExcludeFileMask.Masks = Storage->ReadString("ExcludeFileMask", ExcludeFileMask.Masks);
+  NegativeExclude = Storage->ReadBool("NegativeExclude", NegativeExclude);
   ClearArchive = Storage->ReadBool("ClearArchive", ClearArchive);
 }
 //---------------------------------------------------------------------------
@@ -308,10 +407,11 @@ void __fastcall TCopyParamType::Save(THierarchicalStorage * Storage) const
   Storage->WriteInteger("TransferMode", TransferMode);
   Storage->WriteInteger("ResumeSupport", ResumeSupport);
   Storage->WriteInt64("ResumeThreshold", ResumeThreshold);
-  Storage->WriteBool("ReplaceInvalidChars", ReplaceInvalidChars);
+  Storage->WriteInteger("ReplaceInvalidChars", (unsigned char)InvalidCharsReplacement);
   Storage->WriteString("LocalInvalidChars", LocalInvalidChars);
   Storage->WriteBool("CalculateSize", CalculateSize);
   Storage->WriteString("ExcludeFileMask", ExcludeFileMask.Masks);
+  Storage->WriteBool("NegativeExclude", NegativeExclude);
   Storage->WriteBool("ClearArchive", ClearArchive);
 }
 //---------------------------------------------------------------------------
@@ -329,7 +429,7 @@ bool __fastcall TCopyParamType::operator==(const TCopyParamType & rhp) const
     C(TransferMode) &&
     C(ResumeSupport) &&
     C(ResumeThreshold) &&
-    C(ReplaceInvalidChars) &&
+    C(InvalidCharsReplacement) &&
     C(LocalInvalidChars) &&
     C(CalculateSize) &&
     C(ExcludeFileMask) &&

+ 17 - 5
core/CopyParam.h

@@ -26,13 +26,22 @@ private:
   TResumeSupport FResumeSupport;
   __int64 FResumeThreshold;
   AnsiString __fastcall GetLogStr() const;
-  bool FReplaceInvalidChars;
+  char FInvalidCharsReplacement;
   AnsiString FLocalInvalidChars;
+  AnsiString FTokenizibleChars;
   bool FCalculateSize;
   AnsiString FFileMask;
   TFileMasks FExcludeFileMask;
   bool FNegativeExclude;
   bool FClearArchive;
+  static const char TokenPrefix = '%';
+  static const char NoReplacement = char(false);
+  static const char TokenReplacement = char(true);
+
+  static AnsiString __fastcall Untokenize(AnsiString FileName);
+  void __fastcall SetLocalInvalidChars(AnsiString value);
+  bool __fastcall GetReplaceInvalidChars() const;
+  void __fastcall SetReplaceInvalidChars(bool value);
 
 public:
   __fastcall TCopyParamType();
@@ -48,11 +57,13 @@ public:
   bool __fastcall UseAsciiTransfer(AnsiString FileName, TOperationSide Side) const;
   bool __fastcall AllowResume(__int64 Size) const;
   AnsiString __fastcall ValidLocalFileName(AnsiString FileName) const;
-  bool __fastcall AllowTransfer(AnsiString FileName, TOperationSide Side) const;
+  bool __fastcall AllowTransfer(AnsiString FileName, TOperationSide Side,
+    bool Directory) const;
 
   void __fastcall Load(THierarchicalStorage * Storage);
   void __fastcall Save(THierarchicalStorage * Storage) const;
-  AnsiString __fastcall GetInfoStr(AnsiString Separator) const;
+  static const int cpiExcludeMaskOnly = 0x01;
+  AnsiString __fastcall GetInfoStr(AnsiString Separator, int Options = 0) const;
 
   bool __fastcall operator==(const TCopyParamType & rhp) const;
 
@@ -67,8 +78,9 @@ public:
   __property bool PreserveRights = { read = FPreserveRights, write = FPreserveRights };
   __property TResumeSupport ResumeSupport = { read = FResumeSupport, write = FResumeSupport };
   __property __int64 ResumeThreshold = { read = FResumeThreshold, write = FResumeThreshold };
-  __property bool ReplaceInvalidChars = { read = FReplaceInvalidChars, write = FReplaceInvalidChars };
-  __property AnsiString LocalInvalidChars = { read = FLocalInvalidChars, write = FLocalInvalidChars };
+  __property char InvalidCharsReplacement = { read = FInvalidCharsReplacement, write = FInvalidCharsReplacement };
+  __property bool ReplaceInvalidChars = { read = GetReplaceInvalidChars, write = SetReplaceInvalidChars };
+  __property AnsiString LocalInvalidChars = { read = FLocalInvalidChars, write = SetLocalInvalidChars };
   __property bool CalculateSize = { read = FCalculateSize, write = FCalculateSize };
   __property AnsiString FileMask = { read = FFileMask, write = FFileMask };
   __property TFileMasks ExcludeFileMask = { read = FExcludeFileMask, write = FExcludeFileMask };

+ 58 - 16
core/FileMasks.cpp

@@ -140,29 +140,43 @@ __fastcall TFileMasks::TFileMasks(const AnsiString AMasks)
   FMasks = AMasks;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TFileMasks::Matches(AnsiString FileName, AnsiString Path) const
+bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Directory,
+  AnsiString Path) const
 {
   AnsiString S = Masks;
   while (!S.IsEmpty())
   {
     AnsiString M;
-    M = CutToChar(S, ';', True);
+    M = CutToChars(S, ",;", true);
     int D = M.LastDelimiter("\\/");
-    bool PathMatch = true;
-    if (D > 0)
+    bool DirectoryMask = (D == M.Length());
+
+    // directory masks match only directories,
+    // non-directory masks match anything
+    if (!DirectoryMask || Directory)
     {
-      AnsiString MP = ToUnixPath(M.SubString(1, D - 1));
-      // 'Path' must already have unix slashes
-      PathMatch = MatchesMask(Path, MP);
-      M = M.SubString(D + 1, M.Length() - D);
-    }
+      bool PathMatch = true;
+      if (DirectoryMask)
+      {
+        M.SetLength(M.Length() - 1);
+        D = M.LastDelimiter("\\/");
+      }
+      if (D > 0)
+      {
+        AnsiString MP = ToUnixPath(M.SubString(1, D - 1));
+        // 'Path' must already have unix slashes
+        PathMatch = MatchesMask(Path, MP);
+        M = M.SubString(D + 1, M.Length() - D);
+      }
 
-    if (PathMatch && MatchesMask(FileName, M)) return true;
+      if (PathMatch && MatchesMask(FileName, M)) return true;
+    }
   }
   return false;
 }
 //---------------------------------------------------------------------------
-bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Local) const
+bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Local,
+  bool Directory) const
 {
   AnsiString Path;
   if (Local)
@@ -179,7 +193,7 @@ bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Local) const
     Path = UnixExcludeTrailingBackslash(UnixExtractFilePath(FileName));
     FileName = UnixExtractFileName(FileName);
   }
-  return Matches(FileName, Path);
+  return Matches(FileName, Directory, Path);
 }
 //---------------------------------------------------------------------------
 bool __fastcall TFileMasks::IsValid()
@@ -254,6 +268,9 @@ TFileMasks & __fastcall TFileMasks::operator =(const char * rhs)
 //---------------------------------------------------------------------------
 #define TEXT_TOKEN '\255'
 //---------------------------------------------------------------------------
+const char TCustomCommand::NoQuote = '\0';
+const AnsiString TCustomCommand::Quotes = "\"'";
+//---------------------------------------------------------------------------
 TCustomCommand::TCustomCommand()
 {
 }
@@ -341,15 +358,28 @@ AnsiString __fastcall TCustomCommand::Complete(const AnsiString & Command,
     }
     else
     {
+      char Quote = NoQuote;
+      if ((Index > 1) && (Index + Len - 1 < Command.Length()) &&
+          Command.IsDelimiter(Quotes, Index - 1) &&
+          Command.IsDelimiter(Quotes, Index + Len) &&
+          (Command[Index - 1] == Command[Index + Len]))
+      {
+        Quote = Command[Index - 1];
+      }
       AnsiString Pattern = Command.SubString(Index, Len);
       AnsiString Replacement;
-      if (PatternReplacement(Index, Pattern, Replacement))
+      bool Delimit = true;
+      if (PatternReplacement(Index, Pattern, Replacement, Delimit))
       {
         if (!LastPass)
         {
           Replacement = StringReplace(Replacement, "!", "!!",
             TReplaceFlags() << rfReplaceAll);
         }
+        if (Delimit)
+        {
+          DelimitReplacement(Replacement, Quote);
+        }
         Result += Replacement;
       }
       else
@@ -364,6 +394,11 @@ AnsiString __fastcall TCustomCommand::Complete(const AnsiString & Command,
   return Result;
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomCommand::DelimitReplacement(AnsiString & Replacement, char Quote)
+{
+  Replacement = ShellDelimitStr(Replacement, Quote);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomCommand::Validate(const AnsiString & Command)
 {
   CustomValidate(Command, NULL);
@@ -443,7 +478,7 @@ int __fastcall TInteractiveCustomCommand::PatternLen(int Index, char PatternCmd)
 }
 //---------------------------------------------------------------------------
 bool __fastcall TInteractiveCustomCommand::PatternReplacement(int Index, const AnsiString & Pattern,
-  AnsiString & Replacement)
+  AnsiString & Replacement, bool & Delimit)
 {
   bool Result;
   if ((Pattern.Length() >= 3) && (Pattern[2] == '?'))
@@ -452,8 +487,13 @@ bool __fastcall TInteractiveCustomCommand::PatternReplacement(int Index, const A
     int Pos = Pattern.SubString(3, Pattern.Length() - 2).Pos("?");
     if (Pos > 0)
     {
-      PromptStr = Pattern.SubString(3, Pos - 1);
       Replacement = Pattern.SubString(3 + Pos, Pattern.Length() - 3 - Pos);
+      if ((Pos > 1) && (Pattern[3 + Pos - 2] == '\\'))
+      {
+        Delimit = false;
+        Pos--;
+      }
+      PromptStr = Pattern.SubString(3, Pos - 1);
     }
     else
     {
@@ -502,11 +542,13 @@ int __fastcall TFileCustomCommand::PatternLen(int /*Index*/, char PatternCmd)
 }
 //---------------------------------------------------------------------------
 bool __fastcall TFileCustomCommand::PatternReplacement(int /*Index*/,
-  const AnsiString & Pattern, AnsiString & Replacement)
+  const AnsiString & Pattern, AnsiString & Replacement, bool & Delimit)
 {
   if (Pattern == "!&")
   {
     Replacement = FFileList;
+    // already delimited
+    Delimit = false;
   }
   else
   {

+ 8 - 5
core/FileMasks.h

@@ -16,8 +16,8 @@ public:
   TFileMasks & __fastcall operator =(const AnsiString rhs);
   bool __fastcall operator ==(const TFileMasks & rhm) const;
   bool __fastcall operator ==(const AnsiString rhs) const;
-  bool __fastcall Matches(AnsiString FileName, AnsiString Path = "") const;
-  bool __fastcall Matches(AnsiString FileName, bool Local) const;
+  bool __fastcall Matches(AnsiString FileName, bool Directory, AnsiString Path = "") const;
+  bool __fastcall Matches(AnsiString FileName, bool Local, bool Directory) const;
   bool __fastcall IsValid();
   bool __fastcall IsValid(int & Start, int & Length);
 
@@ -46,6 +46,8 @@ public:
   virtual void __fastcall Validate(const AnsiString & Command);
 
 protected:
+  static const char NoQuote;
+  static const AnsiString Quotes;
   void __fastcall GetToken(const AnsiString & Command,
     int Index, int & Len, char & PatternCmd);
   void __fastcall CustomValidate(const AnsiString & Command, void * Arg);
@@ -56,7 +58,8 @@ protected:
 
   virtual int __fastcall PatternLen(int Index, char PatternCmd) = 0;
   virtual bool __fastcall PatternReplacement(int Index, const AnsiString & Pattern,
-    AnsiString & Replacement) = 0;
+    AnsiString & Replacement, bool & Delimit) = 0;
+  virtual void __fastcall DelimitReplacement(AnsiString & Replacement, char Quote);
 };
 //---------------------------------------------------------------------------
 class TInteractiveCustomCommand : public TCustomCommand
@@ -69,7 +72,7 @@ protected:
     AnsiString & Value);
   virtual int __fastcall PatternLen(int Index, char PatternCmd);
   virtual bool __fastcall PatternReplacement(int Index, const AnsiString & Pattern,
-    AnsiString & Replacement);
+    AnsiString & Replacement, bool & Delimit);
 
 private:
   TCustomCommand * FChildCustomCommand;
@@ -91,7 +94,7 @@ public:
 protected:
   virtual int __fastcall PatternLen(int Index, char PatternCmd);
   virtual bool __fastcall PatternReplacement(int Index, const AnsiString & Pattern,
-    AnsiString & Replacement);
+    AnsiString & Replacement, bool & Delimit);
 
 private:
   AnsiString FFileName;

+ 3 - 3
core/FileOperationProgress.cpp

@@ -19,12 +19,13 @@ __fastcall TFileOperationProgressType::TFileOperationProgressType(
 {
   FOnProgress = AOnProgress;
   FOnFinished = AOnFinished;
+  FReset = false;
   Clear();
 }
 //---------------------------------------------------------------------------
 __fastcall TFileOperationProgressType::~TFileOperationProgressType()
 {
-  assert(!InProgress && !Suspended);
+  assert((!InProgress && !Suspended) || FReset);
 }
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::Clear()
@@ -87,8 +88,7 @@ void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::Reset()
 {
-  InProgress = false;
-  Suspended = false;
+  FReset = true;
 }
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::Stop()

+ 1 - 0
core/FileOperationProgress.h

@@ -30,6 +30,7 @@ private:
   int FFilesFinished;
   TFileOperationProgressEvent FOnProgress;
   TFileOperationFinished FOnFinished;
+  bool FReset;
 
 protected:
   void __fastcall ClearTransfer();

+ 2 - 1
core/FileSystems.h

@@ -29,7 +29,8 @@ class TCustomFileSystem : public TObject
 public:
   virtual AnsiString __fastcall AbsolutePath(AnsiString Path) = 0;
   virtual void __fastcall KeepAlive() = 0;
-  virtual void __fastcall AnyCommand(const AnsiString Command) = 0;
+  virtual void __fastcall AnyCommand(const AnsiString Command,
+    TLogAddLineEvent OutputEvent) = 0;
   virtual void __fastcall ChangeDirectory(const AnsiString Directory) = 0;
   virtual void __fastcall CachedChangeDirectory(const AnsiString Directory) = 0;
   virtual void __fastcall ChangeFileProperties(const AnsiString FileName,

+ 14 - 1
core/HierarchicalStorage.cpp

@@ -627,7 +627,20 @@ double __fastcall TIniFileStorage::ReadFloat(const AnsiString Name, double Defau
 //---------------------------------------------------------------------------
 AnsiString __fastcall TIniFileStorage::ReadStringRaw(const AnsiString Name, AnsiString Default)
 {
-  return FIniFile->ReadString(CurrentSection, Name, Default);
+  AnsiString Section = CurrentSection;
+  AnsiString Result;
+  Result = FIniFile->ReadString(Section, Name, Default);
+  // TIniFile::ReadString has limit of 2 kB.
+  // We could straithly use our routine, but call to legacy code is preserved
+  // until ours it proved to work and also to save memory overhead
+  if (Result.Length() == 2047)
+  {
+    char Buffer[10240];
+    GetPrivateProfileString(Section.c_str(), Name.c_str(), Default.c_str(),
+      Buffer, sizeof(Buffer), FIniFile->FileName.c_str());
+    Result = Buffer;
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 int __fastcall TIniFileStorage::ReadBinaryData(const AnsiString Name,

+ 8 - 0
core/Net.cpp

@@ -144,3 +144,11 @@ void SSHOldKeyfileWarning(void)
 {
   // no reference to TSecureShell instace available
 }
+//---------------------------------------------------------------------------
+void SSHDisplayBanner(void * frontend, const char * banner, int size)
+{
+  assert(frontend);
+  AnsiString Banner(banner, size);
+  ((TSecureShell *)frontend)->DisplayBanner(Banner);
+}
+

+ 6 - 0
core/PuttyIntf.c

@@ -28,6 +28,7 @@ void SSHConnectionFatal(void * frontend, char * string);
 void SSHVerifyHostKey(void * frontend, char *host, int port, char * keytype,
   char * keystr, char * fingerprint);
 void SSHOldKeyfileWarning(void);
+void SSHDisplayBanner(void * frontend, const char * banner, int size);
 void SSHAskAlg(void * frontend, const char * AlgType, const char * AlgName);
 long RegOpenWinSCPKey(HKEY hKey, const char * lpSubKey, HKEY * phkResult);
 long RegCreateWinSCPKey(HKEY hKey, const char * lpSubKey, HKEY * phkResult);
@@ -195,4 +196,9 @@ void set_busy_status(void * frontend, int status)
   // nothing
 }
 //---------------------------------------------------------------------------
+void display_banner(void * frontend, const char * banner, int size)
+{
+  SSHDisplayBanner(frontend, banner, size);
+}
+//---------------------------------------------------------------------------
 #pragma option pop // -w-par

+ 6 - 0
core/PuttyIntf.h

@@ -7,6 +7,7 @@
 #endif
 //---------------------------------------------------------------------------
 struct charset_spec;
+typedef struct Filename Filename;
 #include "charset\Charset.h"
 //---------------------------------------------------------------------------
 typedef UINT_PTR SOCKET;
@@ -21,6 +22,8 @@ extern "C"
   void random_save_seed(void);
   int verify_host_key(char * hostname, int port, char * keytype, char * key);
   void store_host_key(char * hostname, int port, char * keytype, char * key);
+  void display_banner(void * frontend, const char * banner, int size);
+  void md5checksum(const char * buffer, int len, unsigned char output[16]);
   void * saferealloc(void * ptr, size_t n, size_t size);
   void * safemalloc(size_t n, size_t size);
   void safefree(void * ptr);
@@ -37,6 +40,8 @@ extern "C"
     charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx);
   void write_utf8(charset_spec const *charset, long int input_chr,
     charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx);
+  int key_type(const Filename * filename);
+  char * key_type_to_str(int type);
   // -------------
 
   void ssh_close(void * handle);
@@ -82,6 +87,7 @@ extern "C"
   void SSHFatalError(char * string);
   void SSHAskAlg(void * frontend, const char * AlgType, const char * AlgName);
   void SSHOldKeyfileWarning(void);
+  void SSHDisplayBanner(void * frontend, const char * banner, int size);
   long RegOpenWinSCPKey(HKEY hKey, const char * lpSubKey, HKEY * phkResult);
   long RegCreateWinSCPKey(HKEY hKey, const char * lpSubKey, HKEY * phkResult);
   long RegQueryWinSCPValueEx(HKEY Key, const char * ValueName, unsigned long * Reserved,

+ 119 - 7
core/Queue.cpp

@@ -20,6 +20,8 @@ public:
   bool __fastcall ProcessUserAction(void * Arg);
   void __fastcall Cancel();
   void __fastcall Idle();
+  bool __fastcall Pause();
+  bool __fastcall Resume();
 
 protected:
   struct TQueryUserRec
@@ -54,6 +56,7 @@ protected:
   TCriticalSection * FCriticalSection;
   void * FUserActionParams;
   bool FCancel;
+  bool FPause;
 
   virtual void __fastcall ProcessEvent();
   virtual void __fastcall Finished();
@@ -510,12 +513,13 @@ bool __fastcall TTerminalQueue::ItemExecuteNow(TQueueItem * Item)
       TGuard Guard(FItemsSection);
 
       int Index = FItems->IndexOf(Item);
-      Result = (Index >= 0) && (Item->GetStatus() == TQueueItem::qsPending);
+      Result = (Index >= 0) && (Item->GetStatus() == TQueueItem::qsPending) &&
+        // prevent double-initiation when "execute" is clicked twice too fast
+        (Index >= FItemsInProcess);
       if (Result)
       {
-        if (Index != FItemsInProcess)
+        if (Index > FItemsInProcess)
         {
-          assert(Index > FItemsInProcess);
           FItems->Move(Index, FItemsInProcess);
         }
 
@@ -573,6 +577,42 @@ bool __fastcall TTerminalQueue::ItemDelete(TQueueItem * Item)
   return Result;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TTerminalQueue::ItemPause(TQueueItem * Item, bool Pause)
+{
+  // to prevent deadlocks when closing queue from other thread
+  bool Result = !FFinished;
+  if (Result)
+  {
+    TTerminalItem * TerminalItem;
+
+    {
+      TGuard Guard(FItemsSection);
+
+      Result = (FItems->IndexOf(Item) >= 0) &&
+        ((Pause && (Item->Status == TQueueItem::qsProcessing)) ||
+         (!Pause && (Item->Status == TQueueItem::qsPaused)));
+      if (Result)
+      {
+        TerminalItem = Item->FTerminalItem;
+      }
+    }
+
+    if (Result)
+    {
+      if (Pause)
+      {
+        Result = TerminalItem->Pause();
+      }
+      else
+      {
+        Result = TerminalItem->Resume();
+      }
+    }
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TTerminalQueue::Idle()
 {
   if (Now() - FLastIdle > FIdleInterval)
@@ -776,6 +816,7 @@ void __fastcall TTerminalItem::ProcessEvent()
   bool Retry = true;
 
   FCancel = false;
+  FPause = false;
   FItem->FTerminalItem = this;
 
   try
@@ -852,6 +893,32 @@ void __fastcall TTerminalItem::Idle()
 void __fastcall TTerminalItem::Cancel()
 {
   FCancel = true;
+  if (FItem->GetStatus() == TQueueItem::qsPaused)
+  {
+    TriggerEvent();
+  }
+}
+//---------------------------------------------------------------------------
+bool __fastcall TTerminalItem::Pause()
+{
+  assert(FItem != NULL);
+  bool Result = (FItem->GetStatus() == TQueueItem::qsProcessing) && !FPause;
+  if (Result)
+  {
+    FPause = true;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TTerminalItem::Resume()
+{
+  assert(FItem != NULL);
+  bool Result = (FItem->GetStatus() == TQueueItem::qsPaused);
+  if (Result)
+  {
+    TriggerEvent();
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 bool __fastcall TTerminalItem::ProcessUserAction(void * Arg)
@@ -1011,6 +1078,28 @@ void __fastcall TTerminalItem::OperationFinished(TFileOperation /*Operation*/,
 void __fastcall TTerminalItem::OperationProgress(
   TFileOperationProgressType & ProgressData, TCancelStatus & Cancel)
 {
+  if (FPause && !FTerminated && !FCancel)
+  {
+    TQueueItem::TStatus PrevStatus = FItem->GetStatus();
+    assert(PrevStatus == TQueueItem::qsProcessing);
+    // must be set before TFileOperationProgressType::Suspend(), because
+    // it invokes this method back
+    FPause = false;
+    ProgressData.Suspend();
+
+    try
+    {
+      FItem->SetStatus(TQueueItem::qsPaused);
+
+      WaitForEvent();
+    }
+    __finally
+    {
+      FItem->SetStatus(PrevStatus);
+      ProgressData.Resume();
+    }
+  }
+
   if (FTerminated || FCancel)
   {
     if (ProgressData.TransferingFile)
@@ -1203,6 +1292,16 @@ bool __fastcall TQueueItemProxy::Delete()
   return FQueue->ItemDelete(FQueueItem);
 }
 //---------------------------------------------------------------------------
+bool __fastcall TQueueItemProxy::Pause()
+{
+  return FQueue->ItemPause(FQueueItem, true);
+}
+//---------------------------------------------------------------------------
+bool __fastcall TQueueItemProxy::Resume()
+{
+  return FQueue->ItemPause(FQueueItem, false);
+}
+//---------------------------------------------------------------------------
 bool __fastcall TQueueItemProxy::ProcessUserAction(void * Arg)
 {
   assert(FQueueItem != NULL);
@@ -1372,12 +1471,15 @@ __fastcall TUploadQueueItem::TUploadQueueItem(TTerminal * Terminal,
     if (FLAGSET(Params, cpTemporary))
     {
       FInfo->Source = "";
+      FInfo->ModifiedLocal = "";
     }
     else
     {
       ExtractCommonPath(FilesToCopy, FInfo->Source);
       // this way the trailing backslash is preserved for root directories like D:\\
       FInfo->Source = ExtractFileDir(IncludeTrailingBackslash(FInfo->Source));
+      FInfo->ModifiedLocal = FLAGCLEAR(Params, cpDelete) ? AnsiString() :
+        IncludeTrailingBackslash(FInfo->Source);
     }
   }
   else
@@ -1385,18 +1487,20 @@ __fastcall TUploadQueueItem::TUploadQueueItem(TTerminal * Terminal,
     if (FLAGSET(Params, cpTemporary))
     {
       FInfo->Source = ExtractFileName(FilesToCopy->Strings[0]);
+      FInfo->ModifiedLocal = "";
     }
     else
     {
       assert(FilesToCopy->Count > 0);
       FInfo->Source = FilesToCopy->Strings[0];
+      FInfo->ModifiedLocal = FLAGCLEAR(Params, cpDelete) ? AnsiString() :
+        IncludeTrailingBackslash(ExtractFilePath(FInfo->Source));
     }
   }
 
   FInfo->Destination =
     UnixIncludeTrailingBackslash(TargetDir) + CopyParam->FileMask;
-  FInfo->ModifiesLocal = ((Params & cpDelete) != 0);
-  FInfo->ModifiesRemote = true;
+  FInfo->ModifiedRemote = UnixIncludeTrailingBackslash(TargetDir);
 }
 //---------------------------------------------------------------------------
 void __fastcall TUploadQueueItem::DoExecute(TTerminal * Terminal)
@@ -1421,6 +1525,8 @@ __fastcall TDownloadQueueItem::TDownloadQueueItem(TTerminal * Terminal,
       FInfo->Source = Terminal->CurrentDirectory;
     }
     FInfo->Source = UnixExcludeTrailingBackslash(FInfo->Source);
+    FInfo->ModifiedRemote = FLAGCLEAR(Params, cpDelete) ? AnsiString() :
+      UnixIncludeTrailingBackslash(FInfo->Source);
   }
   else
   {
@@ -1430,6 +1536,13 @@ __fastcall TDownloadQueueItem::TDownloadQueueItem(TTerminal * Terminal,
     {
       FInfo->Source = UnixIncludeTrailingBackslash(Terminal->CurrentDirectory) +
         FInfo->Source;
+      FInfo->ModifiedRemote = FLAGCLEAR(Params, cpDelete) ? AnsiString() :
+        UnixIncludeTrailingBackslash(Terminal->CurrentDirectory);
+    }
+    else
+    {
+      FInfo->ModifiedRemote = FLAGCLEAR(Params, cpDelete) ? AnsiString() :
+        UnixExtractFilePath(FInfo->Source);
     }
   }
 
@@ -1442,8 +1555,7 @@ __fastcall TDownloadQueueItem::TDownloadQueueItem(TTerminal * Terminal,
     FInfo->Destination =
       IncludeTrailingBackslash(TargetDir) + CopyParam->FileMask;
   }
-  FInfo->ModifiesLocal = true;
-  FInfo->ModifiesRemote = ((Params & cpDelete) != 0);
+  FInfo->ModifiedLocal = IncludeTrailingBackslash(TargetDir);
 }
 //---------------------------------------------------------------------------
 void __fastcall TDownloadQueueItem::DoExecute(TTerminal * Terminal)

+ 9 - 5
core/Queue.h

@@ -104,6 +104,7 @@ protected:
   bool __fastcall ItemMove(TQueueItem * Item, TQueueItem * BeforeItem);
   bool __fastcall ItemExecuteNow(TQueueItem * Item);
   bool __fastcall ItemDelete(TQueueItem * Item);
+  bool __fastcall ItemPause(TQueueItem * Item, bool Pause);
 
   void __fastcall RetryItem(TQueueItem * Item);
   void __fastcall DeleteItem(TQueueItem * Item);
@@ -133,15 +134,16 @@ friend class TTerminalItem;
 
 public:
   enum TStatus {
-    qsPending, qsConnecting, qsProcessing, qsPrompt, qsQuery, qsError, qsDone };
+    qsPending, qsConnecting, qsProcessing, qsPrompt, qsQuery, qsError,
+    qsPaused, qsDone };
   struct TInfo
   {
     TFileOperation Operation;
     TOperationSide Side;
     AnsiString Source;
     AnsiString Destination;
-    bool ModifiesLocal;
-    bool ModifiesRemote;
+    AnsiString ModifiedLocal;
+    AnsiString ModifiedRemote;
   };
 
   static bool __fastcall IsUserActionStatus(TStatus Status);
@@ -167,7 +169,7 @@ protected:
   virtual void __fastcall DoExecute(TTerminal * Terminal) = 0;
   void __fastcall SetProgress(const TFileOperationProgressType & ProgressData);
   void __fastcall GetData(TQueueItemProxy * Proxy);
-  virtual AnsiString __fastcall StartupDirectory() = 0; 
+  virtual AnsiString __fastcall StartupDirectory() = 0;
 };
 //---------------------------------------------------------------------------
 class TQueueItemProxy
@@ -183,6 +185,8 @@ public:
   bool __fastcall Move(TQueueItemProxy * BeforeItem);
   bool __fastcall ExecuteNow();
   bool __fastcall Delete();
+  bool __fastcall Pause();
+  bool __fastcall Resume();
 
   __property TFileOperationProgressType * ProgressData = { read = GetProgressData };
   __property TQueueItem::TInfo * Info = { read = FInfo };
@@ -243,7 +247,7 @@ protected:
   __fastcall TLocatedQueueItem(TTerminal * Terminal);
 
   virtual void __fastcall DoExecute(TTerminal * Terminal);
-  virtual AnsiString __fastcall StartupDirectory(); 
+  virtual AnsiString __fastcall StartupDirectory();
 
 private:
   AnsiString FCurrentDir;

+ 57 - 25
core/RemoteFiles.cpp

@@ -260,6 +260,7 @@ AnsiString __fastcall MakeFileList(TStrings * FileList)
     }
 
     AnsiString FileName = FileList->Strings[Index];
+    // currently this is used for local file only, so no delimiting is done
     if (FileName.Pos(" ") > 0)
     {
       Result += "\"" + FileName + "\"";
@@ -346,6 +347,7 @@ TRemoteFile * __fastcall TRemoteFile::Duplicate(bool Standalone)
     COPY_FP(LastAccess);
     COPY_FP(Group);
     COPY_FP(IconIndex);
+    COPY_FP(TypeName);
     COPY_FP(IsSymLink);
     COPY_FP(LinkTo);
     COPY_FP(Type);
@@ -365,36 +367,52 @@ TRemoteFile * __fastcall TRemoteFile::Duplicate(bool Standalone)
   return Result;
 }
 //---------------------------------------------------------------------------
+void __fastcall TRemoteFile::LoadTypeInfo()
+{
+  /* TODO : If file is link: Should be attributes taken from linked file? */
+  unsigned long Attrs = FILE_ATTRIBUTE_NORMAL;
+  if (IsDirectory) Attrs |= FILE_ATTRIBUTE_DIRECTORY;
+  if (IsHidden) Attrs |= FILE_ATTRIBUTE_HIDDEN;
+
+  TSHFileInfo SHFileInfo;
+  AnsiString DumbFileName = (IsSymLink && !LinkTo.IsEmpty() ? LinkTo : FileName);
+  // On Win2k we get icon of "ZIP drive" for ".." (parent directory)
+  if (DumbFileName == "..") DumbFileName = "dumb";
+  // this should be somewhere else, probably in TUnixDirView,
+  // as the "partial" overlay is added there too
+  if (AnsiSameText(UnixExtractFileExt(DumbFileName), PARTIAL_EXT))
+  {
+    static const size_t PartialExtLen = sizeof(PARTIAL_EXT) - 1;
+    DumbFileName.SetLength(DumbFileName.Length() - PartialExtLen);
+  }
+
+  SHGetFileInfo(DumbFileName.c_str(),
+    Attrs, &SHFileInfo, sizeof(SHFileInfo),
+    SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME);
+  FIconIndex = SHFileInfo.iIcon;
+  FTypeName = SHFileInfo.szTypeName;
+}
+//---------------------------------------------------------------------------
 Integer __fastcall TRemoteFile::GetIconIndex()
 {
   assert(FIconIndex >= -1);
   if (FIconIndex < 0)
   {
-    /* TODO : If file is link: Should be attributes taken from linked file? */
-    unsigned long Attrs = FILE_ATTRIBUTE_NORMAL;
-    if (IsDirectory) Attrs |= FILE_ATTRIBUTE_DIRECTORY;
-    if (IsHidden) Attrs |= FILE_ATTRIBUTE_HIDDEN;
-
-    TSHFileInfo SHFileInfo;
-    AnsiString DumbFileName = (IsSymLink && !LinkTo.IsEmpty() ? LinkTo : FileName);
-    // On Win2k we get icon of "ZIP drive" for ".." (parent directory)
-    if (DumbFileName == "..") DumbFileName = "dumb";
-    // this should be somewhere else, probably in TUnixDirView,
-    // as the "partial" overlay is added there too
-    if (AnsiSameText(UnixExtractFileExt(DumbFileName), PARTIAL_EXT))
-    {
-      static const size_t PartialExtLen = sizeof(PARTIAL_EXT) - 1;
-      DumbFileName.SetLength(DumbFileName.Length() - PartialExtLen);
-    }
-
-    SHGetFileInfo(DumbFileName.c_str(),
-      Attrs, &SHFileInfo, sizeof(SHFileInfo),
-      SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
-    FIconIndex = SHFileInfo.iIcon;
+    LoadTypeInfo();
   }
   return FIconIndex;
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TRemoteFile::GetTypeName()
+{
+  // check avilability of type info by icon index, because type name can be empty
+  if (FIconIndex < 0)
+  {
+    LoadTypeInfo();
+  }
+  return FTypeName;
+}
+//---------------------------------------------------------------------------
 Boolean __fastcall TRemoteFile::GetIsHidden()
 {
   bool Result;
@@ -488,7 +506,7 @@ bool __fastcall TRemoteFile::GetBrokenLink()
   assert(Terminal);
   // If file is symlink but we couldn't find linked file we assume broken link
   return (IsSymLink && (FCyclicLink || !FLinkedFile) &&
-    Terminal->SessionData->ResolveSymlinks && Terminal->IsCapable[fcResolveSymlink]);
+    Terminal->ResolvingSymlinks);
   // "!FLinkTo.IsEmpty()" removed because it does not work with SFTP
 }
 //---------------------------------------------------------------------------
@@ -800,8 +818,7 @@ void __fastcall TRemoteFile::SetListingStr(AnsiString value)
 void __fastcall TRemoteFile::Complete()
 {
   assert(Terminal != NULL);
-  if (IsSymLink && Terminal->SessionData->ResolveSymlinks &&
-      Terminal->IsCapable[fcResolveSymlink])
+  if (IsSymLink && Terminal->ResolvingSymlinks)
   {
     FindLinkedFile();
   }
@@ -844,7 +861,7 @@ void __fastcall TRemoteFile::FindLinkedFile()
   }
   else
   {
-    assert(Terminal->SessionData->ResolveSymlinks && Terminal->IsCapable[fcResolveSymlink]);
+    assert(Terminal->ResolvingSymlinks);
     Terminal->ExceptionOnFail = true;
     try
     {
@@ -1288,6 +1305,21 @@ void __fastcall TRemoteDirectoryChangesCache::ClearDirectoryChange(
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TRemoteDirectoryChangesCache::ClearDirectoryChangeTarget(
+  AnsiString TargetDir)
+{
+  for (int Index = 0; Index < Count; Index++)
+  {
+    AnsiString Name = Names[Index];
+    if ((Name.SubString(1, TargetDir.Length()) == TargetDir) ||
+        (Values[Name].SubString(1, TargetDir.Length()) == TargetDir))
+    {
+      Delete(Index);
+      Index--;
+    }
+  }
+}
+//---------------------------------------------------------------------------
 bool __fastcall TRemoteDirectoryChangesCache::GetDirectoryChange(
   const AnsiString SourceDir, const AnsiString Change, AnsiString & TargetDir)
 {

+ 5 - 0
core/RemoteFiles.h

@@ -40,6 +40,7 @@ private:
   bool FCyclicLink;
   AnsiString FFullFileName;
   int FIsHidden;
+  AnsiString FTypeName;
   int __fastcall GetAttr();
   bool __fastcall GetBrokenLink();
   bool __fastcall GetIsDirectory() const;
@@ -56,6 +57,7 @@ private:
   void __fastcall SetRights(TRights * value);
   AnsiString __fastcall GetFullFileName() const;
   int __fastcall GetIconIndex();
+  AnsiString __fastcall GetTypeName();
   bool __fastcall GetIsHidden();
   void __fastcall SetIsHidden(bool value);
   bool __fastcall GetIsParentDirectory() const;
@@ -63,6 +65,7 @@ private:
   bool __fastcall GetIsInaccesibleDirectory() const;
   AnsiString __fastcall GetExtension();
   AnsiString __fastcall GetUserModificationStr();
+  void __fastcall LoadTypeInfo();
 
 protected:
   void __fastcall FindLinkedFile();
@@ -100,6 +103,7 @@ public:
   __property bool Selected  = { read=FSelected, write=FSelected };
   __property AnsiString FullFileName  = { read = GetFullFileName, write = FFullFileName };
   __property int IconIndex = { read = GetIconIndex };
+  __property AnsiString TypeName = { read = GetTypeName };
   __property bool IsHidden = { read = GetIsHidden, write = SetIsHidden };
   __property bool IsParentDirectory = { read = GetIsParentDirectory };
   __property bool IsThisDirectory = { read = GetIsThisDirectory };
@@ -210,6 +214,7 @@ public:
   void __fastcall AddDirectoryChange(const AnsiString SourceDir,
     const AnsiString Change, const AnsiString TargetDir);
   void __fastcall ClearDirectoryChange(AnsiString SourceDir);
+  void __fastcall ClearDirectoryChangeTarget(AnsiString TargetDir);
   bool __fastcall GetDirectoryChange(const AnsiString SourceDir,
     const AnsiString Change, AnsiString & TargetDir);
   void __fastcall Clear();

+ 63 - 47
core/ScpFileSystem.cpp

@@ -274,6 +274,7 @@ __fastcall TSCPFileSystem::TSCPFileSystem(TTerminal * ATerminal):
   FLsFullTime = FTerminal->SessionData->SCPLsFullTime;
   FOutput = new TStringList();
   FProcessingCommand = false;
+  FOnCaptureOutput = NULL;
 }
 //---------------------------------------------------------------------------
 __fastcall TSCPFileSystem::~TSCPFileSystem()
@@ -372,7 +373,7 @@ void __fastcall TSCPFileSystem::AdditionalInfo(TStrings * AdditionalInfo,
     {
       try
       {
-        AnyCommand("uname -a");
+        AnyCommand("uname -a", NULL);
         for (int Index = 0; Index < Output->Count; Index++)
         {
           if (Index > 0)
@@ -411,22 +412,7 @@ AnsiString __fastcall TSCPFileSystem::DelimitStr(AnsiString Str)
 {
   if (!Str.IsEmpty())
   {
-    #define DELIMITCHAR(C) { \
-        Integer P, X = 0; \
-        while ((P = Str.SubString(X+1, Str.Length() - X).Pos(C)) != 0) { \
-          Str.Insert("\\", X+P); \
-          X += P+1; } \
-      }
-
-    DELIMITCHAR('\\');
-    DELIMITCHAR('`');
-    DELIMITCHAR('$');
-    DELIMITCHAR('"');
-    // #62 19.10.2001: delimiting ! doesn't work on BASH
-    //DELIMITCHAR('!');
-
-    #undef DELIMITCHAR
-
+    Str = ::DelimitStr(Str, "\\`$\"");
     if (Str[1] == '-') Str = "./"+Str;
   }
   return Str;
@@ -1143,27 +1129,45 @@ void __fastcall TSCPFileSystem::CustomCommandOnFile(const AnsiString FileName,
   {
     AnsiString Cmd = TRemoteCustomCommand(FileName, "").Complete(Command, true);
 
-    TLogAddLineEvent PrevAddLine = FTerminal->Log->OnAddLine;
-    if (OutputEvent != NULL)
-    {
-      FTerminal->Log->OnAddLine = OutputEvent;
-    }
-    
-    try
-    {
-      AnyCommand(Cmd);
-    }
-    __finally
-    {
-      FTerminal->Log->OnAddLine = PrevAddLine;
-    }  
+    AnyCommand(Cmd, OutputEvent);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TSCPFileSystem::CaptureOutput(TObject * Sender,
+  TLogLineType Type, const AnsiString AddedLine)
+{
+  assert((Type == llOutput || Type == llStdError));
+  int ReturnCode;
+  AnsiString Line = AddedLine;
+  if ((Type == llStdError) ||
+      !RemoveLastLine(Line, ReturnCode) ||
+      !Line.IsEmpty())
+  {
+    assert(FOnCaptureOutput != NULL);
+    FOnCaptureOutput(Sender, Type, AddedLine);
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSCPFileSystem::AnyCommand(const AnsiString Command)
+void __fastcall TSCPFileSystem::AnyCommand(const AnsiString Command,
+  TLogAddLineEvent OutputEvent)
 {
-  ExecCommand(fsAnyCommand, ARRAYOFCONST((Command)),
-    ecDefault | ecIgnoreWarnings);
+  assert(FTerminal->FOnCaptureOutput == NULL);
+  if (OutputEvent != NULL)
+  {
+    FTerminal->FOnCaptureOutput = CaptureOutput;
+    FOnCaptureOutput = OutputEvent;
+  }
+
+  try
+  {
+    ExecCommand(fsAnyCommand, ARRAYOFCONST((Command)),
+      ecDefault | ecIgnoreWarnings);
+  }
+  __finally
+  {
+    FOnCaptureOutput = NULL;
+    FTerminal->FOnCaptureOutput = NULL;
+  }
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall TSCPFileSystem::FileUrl(const AnsiString FileName)
@@ -1469,13 +1473,6 @@ void __fastcall TSCPFileSystem::SCPSource(const AnsiString FileName,
   const TCopyParamType * CopyParam, int Params,
   TFileOperationProgressType * OperationProgress, int Level)
 {
-  if (FLAGCLEAR(Params, cpDelete) &&
-      !CopyParam->AllowTransfer(FileName, osLocal))
-  {
-    FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
-    THROW_SKIP_FILE_NULL;  
-  }
-
   AnsiString DestFileName = CopyParam->ChangeFileName(
     ExtractFileName(FileName), osLocal, Level == 0);
 
@@ -1491,7 +1488,16 @@ void __fastcall TSCPFileSystem::SCPSource(const AnsiString FileName,
   FTerminal->OpenLocalFile(FileName, GENERIC_READ,
     &Attrs, &File, NULL, &MTime, &ATime, &Size);
 
-  if (Attrs & faDirectory)
+  bool Dir = FLAGSET(Attrs, faDirectory);
+
+  if (FLAGCLEAR(Params, cpDelete) &&
+      !CopyParam->AllowTransfer(FileName, osLocal, Dir))
+  {
+    FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
+    THROW_SKIP_FILE_NULL;
+  }
+
+  if (Dir)
   {
     SCPDirectorySource(FileName, CopyParam, Params, OperationProgress, Level);
   }
@@ -1528,8 +1534,16 @@ void __fastcall TSCPFileSystem::SCPSource(const AnsiString FileName,
         TFileBuffer BlockBuf;
 
         // This is crucial, if it fails during file transfer, it's fatal error
-        FILE_OPERATION_LOOP (FMTLOAD(READ_ERROR, (FileName)),
-          BlockBuf.LoadFile(File, OperationProgress->LocalBlockSize(), true);
+        FILE_OPERATION_LOOP_EX (!OperationProgress->TransferingFile,
+            FMTLOAD(READ_ERROR, (FileName)),
+          try
+          {
+            BlockBuf.LoadFile(File, OperationProgress->LocalBlockSize(), true);
+          }
+          catch(...)
+          {
+            RaiseLastOSError();
+          }
         );
 
         OperationProgress->AddLocalyUsed(BlockBuf.Size);
@@ -2108,9 +2122,10 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
           FTerminal->FatalError(LoadStr(ATTEMPT_TO_WRITE_TO_PARENT_DIR));
         }
 
+        bool Dir = (Ctrl == 'D');
         AnsiString SourceFullName = SourceDir + OperationProgress->FileName;
         if (FLAGCLEAR(Params, cpDelete) &&
-            !CopyParam->AllowTransfer(SourceFullName, osRemote))
+            !CopyParam->AllowTransfer(SourceFullName, osRemote, Dir))
         {
           FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer",
             (ErrorFileName)));
@@ -2126,7 +2141,7 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
         FileData.Attrs = FileGetAttr(DestFileName);
         // If getting attrs failes, we suppose, that file/folder doesn't exists
         FileData.Exists = (FileData.Attrs != -1);
-        if (Ctrl == 'D')
+        if (Dir)
         {
           if (FileData.Exists && !(FileData.Attrs & faDirectory))
           {
@@ -2202,7 +2217,8 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
                 }
               }
 
-              if (!FTerminal->CreateLocalFile(DestFileName, OperationProgress, &File))
+              if (!FTerminal->CreateLocalFile(DestFileName, OperationProgress,
+                     &File, FLAGSET(Params, cpNoConfirmation)))
               {
                 SkipConfirmed = true;
                 EXCEPTION;

+ 8 - 4
core/ScpFileSystem.h

@@ -14,7 +14,8 @@ public:
 
   virtual AnsiString __fastcall AbsolutePath(AnsiString Path);
   virtual void __fastcall KeepAlive();
-  virtual void __fastcall AnyCommand(const AnsiString Command);
+  virtual void __fastcall AnyCommand(const AnsiString Command,
+    TLogAddLineEvent OutputEvent);
   virtual void __fastcall ChangeDirectory(const AnsiString Directory);
   virtual void __fastcall CachedChangeDirectory(const AnsiString Directory);
   virtual void __fastcall ChangeFileProperties(const AnsiString FileName,
@@ -52,9 +53,6 @@ public:
   virtual AnsiString __fastcall FileUrl(const AnsiString FileName);
   virtual TStrings * __fastcall GetFixedPaths();
 
-  static bool __fastcall RemoveLastLine(AnsiString & Line,
-    int & ReturnCode, AnsiString LastLine = "");
-
 protected:
   __property TStrings * Output = { read = FOutput };
   __property int ReturnCode = { read = FReturnCode };
@@ -71,6 +69,7 @@ private:
   AnsiString FCachedDirectoryChange;
   bool FProcessingCommand;
   int FLsFullTime;
+  TLogAddLineEvent FOnCaptureOutput;
 
   void __fastcall AliasGroupList();
   void __fastcall ClearAliases();
@@ -104,6 +103,11 @@ private:
   void __fastcall UnsetNationalVars();
   TRemoteFile * __fastcall CreateRemoteFile(const AnsiString & ListingStr, 
   	TRemoteFile * LinkedByFile = NULL);
+  void __fastcall CaptureOutput(TObject * Sender, TLogLineType Type,
+    const AnsiString AddedLine);
+
+  static bool __fastcall RemoveLastLine(AnsiString & Line,
+    int & ReturnCode, AnsiString LastLine = "");
 };
 //---------------------------------------------------------------------------
 #endif // ScpFileSystemH

+ 14 - 5
core/ScpMain.cpp

@@ -241,8 +241,17 @@ long RegCloseWinSCPKey(HKEY Key)
   return R;
 }
 //---------------------------------------------------------------------------
-
-
-
-
-
+TKeyType KeyType(AnsiString FileName)
+{
+  assert(ktUnopenable == SSH_KEYTYPE_UNOPENABLE);
+  assert(ktSSHCom == SSH_KEYTYPE_SSHCOM);
+  Filename KeyFile;
+  ASCOPY(KeyFile.path, FileName);
+  return (TKeyType)key_type(&KeyFile);
+}
+//---------------------------------------------------------------------------
+AnsiString KeyTypeName(TKeyType KeyType)
+{
+  return key_type_to_str(KeyType);
+}
+//---------------------------------------------------------------------------

+ 4 - 0
core/ScpMain.h

@@ -12,6 +12,10 @@ extern TStoredSessionList *StoredSessions;
 void Initialize(const AnsiString IniFileName);
 void Finalize();
 //---------------------------------------------------------------------------
+enum TKeyType { ktUnopenable, ktUnknown, ktSSH1, ktSSH2, ktOpenSSH, ktSSHCom };
+TKeyType KeyType(AnsiString FileName);
+AnsiString KeyTypeName(TKeyType KeyType);
+//---------------------------------------------------------------------------
 class TCoreGuard : public TGuard
 {
 public:

+ 104 - 60
core/Script.cpp

@@ -10,7 +10,6 @@
 #include "Terminal.h"
 #include "SessionData.h"
 #include "ScpMain.h"
-#include "ScpFileSystem.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
@@ -306,7 +305,7 @@ __fastcall TScript::TScript(TTerminal * Terminal)
 //---------------------------------------------------------------------------
 void __fastcall TScript::Init()
 {
-  FBatch = false;
+  FBatch = BatchOff;
   FConfirm = true;
   FSynchronizeParams = 0;
   FOnPrint = NULL;
@@ -314,7 +313,6 @@ void __fastcall TScript::Init()
   FOnSynchronizeStartStop = NULL;
   FSynchronizeMode = -1;
   FKeepingUpToDate = false;
-  FLastPrintedLineTime = 0;
 
   FCommands = new TScriptCommands;
   FCommands->Register(";", 0, 0, &DummyProc, 0, -1);
@@ -339,9 +337,9 @@ void __fastcall TScript::Init()
   FCommands->Register("recv", 0, SCRIPT_GET_HELP, &GetProc, 1, -1);
   FCommands->Register("put", SCRIPT_PUT_DESC, SCRIPT_PUT_HELP, &PutProc, 1, -1);
   FCommands->Register("send", 0, SCRIPT_PUT_HELP, &PutProc, 1, -1);
-  FCommands->Register("option", SCRIPT_OPTION_DESC, SCRIPT_OPTION_HELP, &OptionProc, -1, 2);
-  FCommands->Register("ascii", 0, SCRIPT_OPTION_HELP, &AsciiProc, 0, 0);
-  FCommands->Register("binary", 0, SCRIPT_OPTION_HELP, &BinaryProc, 0, 0);
+  FCommands->Register("option", SCRIPT_OPTION_DESC, SCRIPT_OPTION_HELP2, &OptionProc, -1, 2);
+  FCommands->Register("ascii", 0, SCRIPT_OPTION_HELP2, &AsciiProc, 0, 0);
+  FCommands->Register("binary", 0, SCRIPT_OPTION_HELP2, &BinaryProc, 0, 0);
   FCommands->Register("synchronize", SCRIPT_SYNCHRONIZE_DESC, SCRIPT_SYNCHRONIZE_HELP, &SynchronizeProc, 1, 3);
   FCommands->Register("keepuptodate", SCRIPT_KEEPUPTODATE_DESC, SCRIPT_KEEPUPTODATE_HELP, &KeepUpToDateProc, 0, 2);
 }
@@ -353,7 +351,9 @@ void __fastcall TScript::SetCopyParam(const TCopyParamType & value)
 //---------------------------------------------------------------------------
 void __fastcall TScript::SetSynchronizeParams(int value)
 {
-  FSynchronizeParams = (value & TTerminal::spDelete);
+  FSynchronizeParams = (value &
+    (TTerminal::spDelete | TTerminal::spExistingOnly | TTerminal::spTimestamp |
+     TTerminal::spNotByTime | TTerminal::spBySize));
 }
 //---------------------------------------------------------------------------
 void __fastcall TScript::Command(const AnsiString Cmd)
@@ -620,8 +620,6 @@ void __fastcall TScript::Print(const AnsiString Str)
 //---------------------------------------------------------------------------
 void __fastcall TScript::PrintLine(const AnsiString Str)
 {
-  FLastPrintedLine = Str;
-  FLastPrintedLineTime = time(NULL);
   Print(Str + "\n");
 }
 //---------------------------------------------------------------------------
@@ -739,32 +737,17 @@ void __fastcall TScript::CallProc(TScriptProcParams * Parameters)
 
   if (EnsureCommandSessionFallback(fcAnyCommand))
   {
-    assert(FTerminal->Log->OnAddLine == NULL);
-    FTerminal->Log->OnAddLine = TerminalCaptureLog;
-    try
-    {
-      FTerminal->AnyCommand(Parameters->ParamsStr);
-    }
-    __finally
-    {
-      FTerminal->Log->OnAddLine = NULL;
-    }
+    FTerminal->AnyCommand(Parameters->ParamsStr, TerminalCaptureLog);
   }
 }
 //---------------------------------------------------------------------------
 void __fastcall TScript::TerminalCaptureLog(TObject* /*Sender*/,
   TLogLineType Type, const AnsiString AddedLine)
 {
-  if ((Type == llOutput) || (Type == llStdError))
-  {
-    AnsiString Line = AddedLine;
-    int ReturnCode;
-    if (!TSCPFileSystem::RemoveLastLine(Line, ReturnCode) ||
-        !Line.IsEmpty())
-    {
-      PrintLine(Line);
-    }
-  }
+  USEDPARAM(Type);
+  assert((Type == llOutput) || (Type == llStdError));
+
+  PrintLine(AddedLine);
 }
 //---------------------------------------------------------------------------
 void __fastcall TScript::PwdProc(TScriptProcParams * /*Parameters*/)
@@ -998,12 +981,16 @@ void __fastcall TScript::PutProc(TScriptProcParams * Parameters)
 //---------------------------------------------------------------------------
 void __fastcall TScript::OptionImpl(AnsiString OptionName, AnsiString ValueName)
 {
-  enum { Batch, Confirm, Transfer, SynchDelete };
-  static const char * Names[] = { "batch", "confirm", "transfer", "synchdelete" };
+  enum { Batch, Confirm, Transfer, SynchDelete, Exclude, Include };
+  static const char * Names[] = { "batch", "confirm", "transfer", "synchdelete",
+    "exclude", "include" };
 
   enum { Off, On };
   static const char * ToggleNames[] = { "off", "on" };
 
+  assert((BatchOff == 0) && (BatchOn == 1) && (BatchAbort == 2));
+  static const char * BatchModeNames[] = { "off", "on", "abort" };
+
   assert((tmBinary == 0) && (tmAscii == 1) && (tmAutomatic == 2));
   static const char * TransferModeNames[] = { "binary", "ascii", "automatic" };
 
@@ -1029,15 +1016,15 @@ void __fastcall TScript::OptionImpl(AnsiString OptionName, AnsiString ValueName)
   {
     if (SetValue)
     {
-      int Value = TScriptCommands::FindCommand(ToggleNames, LENOF(ToggleNames), ValueName);
+      int Value = TScriptCommands::FindCommand(BatchModeNames, LENOF(BatchModeNames), ValueName);
       if (Value < 0)
       {
         throw Exception(FMTLOAD(SCRIPT_VALUE_UNKNOWN, (ValueName, OptionName)));
       }
-      FBatch = (Value == On);
+      FBatch = (TBatchMode)Value;
     }
 
-    PrintLine(FORMAT(ListFormat, (Names[Batch], (ToggleNames[FBatch ? On : Off]))));
+    PrintLine(FORMAT(ListFormat, (Names[Batch], BatchModeNames[FBatch])));
   }
 
   if (OPT(Confirm))
@@ -1052,7 +1039,7 @@ void __fastcall TScript::OptionImpl(AnsiString OptionName, AnsiString ValueName)
       FConfirm = (Value == On);
     }
 
-    PrintLine(FORMAT(ListFormat, (Names[Confirm], (ToggleNames[FConfirm ? On : Off]))));
+    PrintLine(FORMAT(ListFormat, (Names[Confirm], ToggleNames[FConfirm ? On : Off])));
   }
 
   if (OPT(Transfer))
@@ -1088,7 +1075,41 @@ void __fastcall TScript::OptionImpl(AnsiString OptionName, AnsiString ValueName)
     }
 
     PrintLine(FORMAT(ListFormat, (Names[SynchDelete],
-      (ToggleNames[FLAGSET(FSynchronizeParams, TTerminal::spDelete) ? On : Off]))));
+      ToggleNames[FLAGSET(FSynchronizeParams, TTerminal::spDelete) ? On : Off])));
+  }
+
+  static const char * Clear = "clear";
+
+  if (OPT(Include))
+  {
+    if (SetValue)
+    {
+      FCopyParam.NegativeExclude = true;
+      FCopyParam.ExcludeFileMask =
+        (ValueName == Clear ? AnsiString() : ValueName);
+    }
+
+    if (SetValue ||
+        (FCopyParam.NegativeExclude && !FCopyParam.ExcludeFileMask.Masks.IsEmpty()))
+    {
+      PrintLine(FORMAT(ListFormat, (Names[Include], FCopyParam.ExcludeFileMask.Masks)));
+    }
+  }
+
+  if (OPT(Exclude))
+  {
+    if (SetValue)
+    {
+      FCopyParam.NegativeExclude = false;
+      FCopyParam.ExcludeFileMask =
+        (ValueName == Clear ? AnsiString() : ValueName);
+    }
+
+    if (SetValue ||
+        (!FCopyParam.NegativeExclude && !FCopyParam.ExcludeFileMask.Masks.IsEmpty()))
+    {
+      PrintLine(FORMAT(ListFormat, (Names[Exclude], FCopyParam.ExcludeFileMask.Masks)));
+    }
   }
 
   #undef OPT
@@ -1173,7 +1194,7 @@ void __fastcall TScript::SynchronizeProc(TScriptProcParams * Parameters)
     FTerminal->Synchronize(LocalDirectory, RemoteDirectory,
       static_cast<TTerminal::TSynchronizeMode>(FSynchronizeMode),
       &CopyParam, FSynchronizeParams | TTerminal::spNoConfirmation,
-      OnTerminalSynchronizeDirectory, NULL);
+      OnTerminalSynchronizeDirectory, NULL, NULL);
   }
   __finally
   {
@@ -1195,7 +1216,7 @@ void __fastcall TScript::Synchronize(const AnsiString LocalDirectory,
     FTerminal->Synchronize(LocalDirectory, RemoteDirectory, TTerminal::smRemote, &CopyParam,
       FSynchronizeParams | TTerminal::spNoConfirmation | TTerminal::spNoRecurse |
       TTerminal::spUseCache | TTerminal::spDelayProgress | TTerminal::spSubDirs,
-      OnTerminalSynchronizeDirectory, NULL);
+      OnTerminalSynchronizeDirectory, NULL, NULL);
 
     // to break line after the last transfer (if any); 
     Print("");    
@@ -1313,11 +1334,11 @@ void __fastcall TManagementScript::Input(const AnsiString Prompt,
   while (Str.Trim().IsEmpty() && !AllowEmpty);
 }
 //---------------------------------------------------------------------------
-void __fastcall TManagementScript::PrintProgress(const AnsiString Str)
+void __fastcall TManagementScript::PrintProgress(bool First, const AnsiString Str)
 {
   if (FOnPrintProgress != NULL)
   {
-    FOnPrintProgress(this, Str);
+    FOnPrintProgress(this, First, Str);
   }
 }
 //---------------------------------------------------------------------------
@@ -1336,10 +1357,6 @@ bool __fastcall TManagementScript::QueryCancel()
   if (OnQueryCancel != NULL)
   {
     OnQueryCancel(this, Result);
-    if (Result)
-    {
-      PrintLine(LoadStr(USER_TERMINATED));
-    }
   }
 
   return Result;
@@ -1366,6 +1383,23 @@ void __fastcall TManagementScript::TerminalPromptUser(TSecureShell * SecureShell
   }
 }
 //---------------------------------------------------------------------------
+bool __fastcall TManagementScript::Synchronizing()
+{
+  return (FKeepingUpToDate || (FSynchronizeMode >= 0));
+}
+//---------------------------------------------------------------------------
+void __fastcall TManagementScript::ShowPendingProgress()
+{
+  if (!FSynchronizeIntro.IsEmpty())
+  {
+    if (Synchronizing())
+    {
+      PrintLine(FSynchronizeIntro);
+    }
+    FSynchronizeIntro = "";
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TManagementScript::TerminalOperationProgress(
   TFileOperationProgressType & ProgressData, TCancelStatus & Cancel)
 {
@@ -1374,14 +1408,22 @@ void __fastcall TManagementScript::TerminalOperationProgress(
     if (ProgressData.InProgress  && !ProgressData.FileName.IsEmpty())
     {
       bool DoPrint = false;
-      if (ProgressData.FileName != FLastProgressFile)
+      bool First = false;
+      AnsiString ProgressFileName = ProgressData.FileName;
+      if (ProgressData.Side == osLocal)
       {
-        if (!FLastProgressFile.IsEmpty())
-        {
-          Print("");
-        }
+        ProgressFileName = ExcludeTrailingBackslash(ProgressFileName);
+      }
+      else
+      {
+        ProgressFileName = UnixExcludeTrailingBackslash(ProgressFileName);
+      }
+
+      if (ProgressFileName != FLastProgressFile)
+      {
+        First = true;
         DoPrint = true;
-        FLastProgressFile = ProgressData.FileName;
+        ShowPendingProgress();
       }
 
       if (!DoPrint && ((FLastProgressTime != time(NULL)) || ProgressData.IsTransferDone()))
@@ -1392,7 +1434,7 @@ void __fastcall TManagementScript::TerminalOperationProgress(
       if (DoPrint)
       {
         static int MaxFileName = 25;
-        AnsiString FileName = MinimizeName(ProgressData.FileName, MaxFileName,
+        AnsiString FileName = MinimizeName(ProgressFileName, MaxFileName,
           ProgressData.Side == osRemote);
         AnsiString ProgressMessage = FORMAT("%-*.*s | %10d kB | %6.1f kB/s | %-6.6s | %3d%%",
           (MaxFileName, MaxFileName, FileName,
@@ -1403,11 +1445,16 @@ void __fastcall TManagementScript::TerminalOperationProgress(
         if (FLastProgressMessage != ProgressMessage)
         {
           FLastProgressTime = time(NULL);
-          PrintProgress(ProgressMessage);
+          PrintProgress(First, ProgressMessage);
           FLastProgressMessage = ProgressMessage;
+          FLastProgressFile = ProgressFileName;
         }
       }
     }
+    else
+    {
+      FLastProgressFile = "";
+    }
   }
 
   if (QueryCancel())
@@ -1423,8 +1470,11 @@ void __fastcall TManagementScript::TerminalOperationFinished(
 {
   if (Success && (Operation != foCalculateSize) && (Operation != foCopy))
   {
-    if ((FKeepingUpToDate || (FSynchronizeMode >= 0)) && (Operation == foDelete))
+    ShowPendingProgress();
+    // For FKeepingUpToDate we should send events to synchronize controller eventuelly.
+    if (Synchronizing() && (Operation == foDelete))
     {
+      // Note that this is duplicated with "keep up to date" log.
       PrintLine(FMTLOAD(SCRIPT_SYNCHRONIZE_DELETED, (FileName)));
     }
     else
@@ -1460,15 +1510,9 @@ void __fastcall TManagementScript::TerminalSynchronizeDirectory(
       break;
   }
 
-  AnsiString Str = FMTLOAD(SCRIPT_SYNCHRONIZE, (ExcludeTrailingBackslash(LocalDirectory),
+  FSynchronizeIntro = FMTLOAD(SCRIPT_SYNCHRONIZE, (ExcludeTrailingBackslash(LocalDirectory),
     Arrow, UnixExcludeTrailingBackslash(RemoteDirectory)));
 
-  if ((Str != FLastPrintedLine) ||
-      (FLastPrintedLineTime < time(NULL) - 2))
-  {
-    PrintLine(Str);
-  }
-
   if (QueryCancel())
   {
     Continue = false;

+ 11 - 8
core/Script.h

@@ -40,6 +40,8 @@ private:
 class TScript
 {
 public:
+  enum TBatchMode { BatchOff, BatchOn, BatchAbort };
+
   __fastcall TScript();
   __fastcall TScript(TTerminal * Terminal);
   virtual __fastcall ~TScript();
@@ -56,7 +58,7 @@ public:
   __property TScriptSynchronizeStartStop OnSynchronizeStartStop = { read = FOnSynchronizeStartStop, write = FOnSynchronizeStartStop };
   __property TCopyParamType CopyParam = { read = FCopyParam, write = SetCopyParam };
   __property int SynchronizeParams = { read = FSynchronizeParams, write = SetSynchronizeParams };
-  __property bool Batch = { read = FBatch };
+  __property TBatchMode Batch = { read = FBatch };
   __property TTerminal * Terminal = { read = FTerminal };
 
 protected:
@@ -67,13 +69,12 @@ protected:
   TSynchronizeDirectory FOnTerminalSynchronizeDirectory;
   TScriptSynchronizeStartStop FOnSynchronizeStartStop;
   TCopyParamType FCopyParam;
-  bool FBatch;
+  TBatchMode FBatch;
   bool FConfirm;
   int FSynchronizeParams;
   int FSynchronizeMode;
   bool FKeepingUpToDate;
-  AnsiString FLastPrintedLine;
-  time_t FLastPrintedLineTime;
+  AnsiString FSynchronizeIntro;
 
   virtual void __fastcall ResetTransfer();
   virtual void __fastcall ConnectTerminal(TTerminal * Terminal);
@@ -129,11 +130,11 @@ private:
   void __fastcall Init();
   void __fastcall SetCopyParam(const TCopyParamType & value);
   void __fastcall SetSynchronizeParams(int value);
-  int __fastcall GetSynchronizeParams();
 };
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TScriptInputEvent)(TScript * Script, const AnsiString Prompt, AnsiString & Str);
 typedef void __fastcall (__closure *TScriptQueryCancelEvent)(TScript * Script, bool & Cancel);
+typedef void __fastcall (__closure *TScriptPrintProgressEvent)(TScript * Script, bool First, const AnsiString Str);
 //---------------------------------------------------------------------------
 class TManagementScript : public TScript
 {
@@ -148,7 +149,7 @@ public:
   __property TNotifyEvent OnTerminalUpdateStatus = { read = FOnTerminalUpdateStatus, write = FOnTerminalUpdateStatus };
   __property TPromptUserEvent OnTerminalPromptUser = { read = FOnTerminalPromptUser, write = FOnTerminalPromptUser };
   __property TQueryUserEvent OnTerminalQueryUser = { read = FOnTerminalQueryUser, write = FOnTerminalQueryUser };
-  __property TScriptPrintEvent OnPrintProgress = { read = FOnPrintProgress, write = FOnPrintProgress };
+  __property TScriptPrintProgressEvent OnPrintProgress = { read = FOnPrintProgress, write = FOnPrintProgress };
   __property bool Continue = { read = FContinue };
 
 protected:
@@ -157,7 +158,7 @@ protected:
   TNotifyEvent FOnTerminalUpdateStatus;
   TPromptUserEvent FOnTerminalPromptUser;
   TQueryUserEvent FOnTerminalQueryUser;
-  TScriptPrintEvent FOnPrintProgress;
+  TScriptPrintProgressEvent FOnPrintProgress;
   TStoredSessionList * FStoredSessions;
   TTerminalList * FTerminalList;
   AnsiString FLastProgressFile;
@@ -179,7 +180,7 @@ protected:
   void __fastcall PrintActiveSession();
   TTerminal * __fastcall FindSession(const AnsiString Index);
   void __fastcall FreeTerminal(TTerminal * Terminal);
-  void __fastcall PrintProgress(const AnsiString Str);
+  void __fastcall PrintProgress(bool First, const AnsiString Str);
   bool __fastcall QueryCancel();
   void __fastcall TerminalSynchronizeDirectory(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, bool & Continue);
@@ -190,6 +191,8 @@ protected:
   void __fastcall TerminalPromptUser(TSecureShell * SecureShell,
     AnsiString Prompt, TPromptKind Kind, AnsiString & Response, bool & Result,
     void * Arg);
+  inline bool __fastcall Synchronizing();
+  inline void __fastcall ShowPendingProgress();
 
   void __fastcall ExitProc(TScriptProcParams * Parameters);
   void __fastcall OpenProc(TScriptProcParams * Parameters);

+ 41 - 3
core/SecureShell.cpp

@@ -36,9 +36,11 @@ __fastcall TSecureShell::TSecureShell()
   FLog = new TSessionLog(this);
   FOnQueryUser = NULL;
   FOnPromptUser = NULL;
+  FOnDisplayBanner = NULL;
   FOnShowExtendedException = NULL;
   FOnUpdateStatus = NULL;
   FOnStdError = NULL;
+  FOnCaptureOutput = NULL;
   FOnClose = NULL;
   FCSCipher = cipWarn; // = not detected yet
   FSCCipher = cipWarn;
@@ -394,6 +396,10 @@ Integer __fastcall TSecureShell::Receive(char * Buf, Integer Len)
     if (Len <= 0) FatalError(LoadStr(LOST_CONNECTION));
   };
   FBytesReceived += Len;
+  if (Configuration->LogProtocol >= 1)
+  {
+    LogEvent(FORMAT("Received %u bytes", (static_cast<int>(Len))));
+  }
   return Len;
 }
 //---------------------------------------------------------------------------
@@ -434,7 +440,7 @@ AnsiString __fastcall TSecureShell::ReceiveLine()
 
   // We don't want end-of-line character
   Line.SetLength(Line.Length()-1);
-  Log->Add(llOutput, Line);
+  CaptureOutput(llOutput, Line);
   return Line;
 }
 //---------------------------------------------------------------------------
@@ -443,6 +449,7 @@ void __fastcall TSecureShell::SendSpecial(int Code)
   LogEvent(FORMAT("Sending special code: %d", (Code)));
   CheckConnection();
   FBackend->special(FBackendHandle, (Telnet_Special)Code);
+  CheckConnection();
   FLastDataSent = Now();
 }
 //---------------------------------------------------------------------------
@@ -457,6 +464,7 @@ void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
   FBufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
   if (Configuration->LogProtocol >= 1)
   {
+    LogEvent(FORMAT("Sent %u bytes", (static_cast<int>(Len))));
     LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
   }
   FLastDataSent = Now();
@@ -478,6 +486,7 @@ void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
       LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
     }
   }
+  CheckConnection();
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::SendNull()
@@ -531,7 +540,7 @@ void __fastcall TSecureShell::AddStdErrorLine(const AnsiString Str)
   {
     OnStdError(this, llStdError, Str);
   }
-  Log->Add(llStdError, Str);
+  CaptureOutput(llStdError, Str);
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::ClearStdError()
@@ -544,12 +553,22 @@ void __fastcall TSecureShell::ClearStdError()
       FAuthenticationLog +=
         (FAuthenticationLog.IsEmpty() ? "" : "\n") + FStdErrorTemp;
     }
-    Log->Add(llStdError, FStdErrorTemp);
+    CaptureOutput(llStdError, FStdErrorTemp);
     FStdErrorTemp = "";
   }
   StdError = "";
 }
 //---------------------------------------------------------------------------
+void __fastcall TSecureShell::CaptureOutput(TLogLineType Type,
+  const AnsiString & Line)
+{
+  if (FOnCaptureOutput != NULL)
+  {
+    FOnCaptureOutput(this, Type, Line);
+  }
+  Log->Add(Type, Line);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSecureShell::FatalError(Exception * E, AnsiString Msg)
 {
   if (FActive)
@@ -1117,6 +1136,25 @@ void __fastcall TSecureShell::AskAlg(const AnsiString AlgType,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TSecureShell::DoDisplayBanner(const AnsiString & Banner)
+{
+  if ((OnDisplayBanner != NULL) &&
+      Configuration->ShowBanner(SessionData->SessionKey, Banner))
+  {
+    bool NeverShowAgain = false;
+    OnDisplayBanner(this, SessionData->SessionName, Banner, NeverShowAgain);
+    if (NeverShowAgain)
+    {
+      Configuration->NeverShowBanner(SessionData->SessionKey, Banner);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TSecureShell::DisplayBanner(const AnsiString & Banner)
+{
+  DoDisplayBanner(Banner);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSecureShell::OldKeyfileWarning()
 {
   DoQueryUser(LoadStr(OLD_KEY), qaOK, NULL, qtWarning);

+ 10 - 0
core/SecureShell.h

@@ -31,6 +31,9 @@ typedef void __fastcall (__closure *TQueryUserEvent)
 typedef void __fastcall (__closure *TPromptUserEvent)
   (TSecureShell * SecureShell, AnsiString Prompt, TPromptKind Kind,
    AnsiString & Response, bool & Result, void * Arg);
+typedef void __fastcall (__closure *TDisplayBannerEvent)
+  (TSecureShell * SecureShell, AnsiString SessionName, const AnsiString & Banner,
+   bool & NeverShowAgain);
 typedef void __fastcall (__closure *TExtendedExceptionEvent)
   (TSecureShell * SecureShell, Exception * E, void * Arg);
 //---------------------------------------------------------------------------
@@ -122,6 +125,7 @@ private:
   TDateTime FLastDataSent;
   TQueryUserEvent FOnQueryUser;
   TPromptUserEvent FOnPromptUser;
+  TDisplayBannerEvent FOnDisplayBanner;
   TExtendedExceptionEvent FOnShowExtendedException;
   Backend * FBackend;
   void * FBackendHandle;
@@ -177,9 +181,12 @@ private:
   void __fastcall PoolForData(unsigned int & Result);
   TDateTime __fastcall GetIdleInterval();
   bool __fastcall GetStoredPasswordTried();
+  inline void __fastcall CaptureOutput(TLogLineType Type, const AnsiString & Line);
 
 protected:
   AnsiString StdError;
+  TLogAddLineEvent FOnCaptureOutput;
+
   void __fastcall Error(const AnsiString Error) const;
   virtual void __fastcall UpdateStatus(int Value);
   bool __fastcall SshFallbackCmd() const;
@@ -188,6 +195,7 @@ protected:
   int __fastcall RemainingSendBuffer();
   virtual void __fastcall KeepAlive();
   virtual void __fastcall SetSessionData(TSessionData * value);
+  virtual void __fastcall DoDisplayBanner(const AnsiString & Banner);
 
 public:
   __fastcall TSecureShell();
@@ -216,6 +224,7 @@ public:
   void __fastcall VerifyHostKey(const AnsiString Host, int Port,
     const AnsiString KeyType, const AnsiString KeyStr, const AnsiString Fingerprint);
   void __fastcall AskAlg(const AnsiString AlgType, const AnsiString AlgName);
+  void __fastcall DisplayBanner(const AnsiString & Banner);
   void __fastcall OldKeyfileWarning();
 
   virtual int __fastcall DoQueryUser(const AnsiString Query, TStrings * MoreMessages,
@@ -260,6 +269,7 @@ public:
   __property AnsiString HostKeyFingerprint = { read = FHostKeyFingerprint };
   __property TQueryUserEvent OnQueryUser = { read = FOnQueryUser, write = FOnQueryUser };
   __property TPromptUserEvent OnPromptUser = { read = FOnPromptUser, write = FOnPromptUser };
+  __property TDisplayBannerEvent OnDisplayBanner = { read = FOnDisplayBanner, write = FOnDisplayBanner };
   __property TExtendedExceptionEvent OnShowExtendedException = { read = FOnShowExtendedException, write = FOnShowExtendedException };
   __property TNotifyEvent OnUpdateStatus = { read = FOnUpdateStatus, write = FOnUpdateStatus };
   __property TLogAddLineEvent OnStdError = { read = FOnStdError, write = FOnStdError };

+ 33 - 9
core/SessionData.cpp

@@ -95,7 +95,7 @@ void __fastcall TSessionData::Default()
   PreserveDirectoryChanges = true;
   LockInHome = false;
   ResolveSymlinks = true;
-  ConsiderDST = false;
+  ConsiderDST = true;
   DeleteToRecycleBin = false;
   OverwrittenToRecycleBin = false;
   RecycleBinPath = "/tmp";
@@ -119,6 +119,7 @@ void __fastcall TSessionData::Default()
   SFTPUploadQueue = 4;
   SFTPListingQueue = 2;
   SFTPMaxVersion = 5;
+  SFTPMaxPacketSize = 0;
 
   for (int Index = 0; Index < LENOF(FSFTPBugs); Index++)
   {
@@ -219,6 +220,7 @@ void __fastcall TSessionData::Assign(TPersistent * Source)
     DUPL(SFTPUploadQueue);
     DUPL(SFTPListingQueue);
     DUPL(SFTPMaxVersion);
+    DUPL(SFTPMaxPacketSize);
 
     for (int Index = 0; Index < LENOF(FSFTPBugs); Index++)
     {
@@ -343,6 +345,8 @@ void __fastcall TSessionData::StoreToConfig(void * config)
       if (!Shell.IsEmpty())
       {
         ASCOPY(cfg->remote_cmd2, Shell);
+        // Putty reads only "ptr" member for fallback
+        cfg->remote_cmd_ptr2 = cfg->remote_cmd2;
       }
     }
     else
@@ -504,6 +508,7 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
     #undef READ_SFTP_BUG
 
     SFTPMaxVersion = Storage->ReadInteger("SFTPMaxVersion", SFTPMaxVersion);
+    SFTPMaxPacketSize = Storage->ReadInteger("SFTPMaxPacketSize", SFTPMaxPacketSize);
 
     // read only (used only on Import from Putty dialog)
     ProtocolStr = Storage->ReadString("Protocol", ProtocolStr);
@@ -651,7 +656,14 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
     else
     {
       // save password encrypted
-      WRITE_DATA_EX(String, "ProxyPasswordEnc", FProxyPassword, );
+      if (!ProxyPassword.IsEmpty())
+      {
+        WRITE_DATA_EX(String, "ProxyPasswordEnc", FProxyPassword, );
+      }
+      else
+      {
+        Storage->DeleteValue("ProxyPasswordEnc");
+      }
       Storage->DeleteValue("ProxyPassword");
     }
     WRITE_DATA(StringRaw, ProxyTelnetCommand);
@@ -689,6 +701,7 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage,
       #undef WRITE_SFTP_BUG
 
       WRITE_DATA(Integer, SFTPMaxVersion);
+      WRITE_DATA(Integer, SFTPMaxPacketSize);
 
       WRITE_DATA(String, CustomParam1);
       WRITE_DATA(String, CustomParam2);
@@ -856,7 +869,8 @@ AnsiString __fastcall TSessionData::GetSessionKey()
 //---------------------------------------------------------------------
 AnsiString __fastcall TSessionData::GetStorageKey()
 {
-  return MungeStr(Name);
+  // particularly OpenSessionInPutty expect that StorageKey always returns something
+  return MungeStr(Name.IsEmpty() ? SessionKey : Name);
 }
 //---------------------------------------------------------------------
 void __fastcall TSessionData::SetHostName(AnsiString value)
@@ -1410,6 +1424,11 @@ void __fastcall TSessionData::SetSFTPMaxVersion(int value)
   SET_SESSION_PROPERTY(SFTPMaxVersion);
 }
 //---------------------------------------------------------------------
+void __fastcall TSessionData::SetSFTPMaxPacketSize(unsigned long value)
+{
+  SET_SESSION_PROPERTY(SFTPMaxPacketSize);
+}
+//---------------------------------------------------------------------
 void __fastcall TSessionData::SetSFTPBug(TSftpBug Bug, TAutoSwitch value)
 {
   assert(Bug >= 0 && Bug < LENOF(FSFTPBugs));
@@ -1751,17 +1770,18 @@ TSessionData * __fastcall TStoredSessionList::ParseUrl(AnsiString Url,
   {
     if (!Url.IsEmpty())
     {
-      TSessionData * AData;
+      TSessionData * AData = NULL;
       // lookup stored session session even if protocol was defined
       // (this allows setting for example default username for host
       // by creating stored session named by host)
-      AnsiString AUrl(Url);
-      if (AUrl[AUrl.Length()] == '/')
+      AnsiString ConnectInfo;
+      AnsiString RemoteDirectory;
+      if (TSessionData::ParseUrl(Url, Params, &ConnectInfo, NULL, NULL, NULL,
+            NULL, &RemoteDirectory, FileName))
       {
-        AUrl.SetLength(AUrl.Length() - 1);
+        AData = dynamic_cast<TSessionData *>(FindByName(ConnectInfo, false));
       }
-      AData = dynamic_cast<TSessionData *>(FindByName(AUrl, false));
-    
+
       if (AData == NULL)
       {
         Data->Assign(DefaultSettings);
@@ -1779,6 +1799,10 @@ TSessionData * __fastcall TStoredSessionList::ParseUrl(AnsiString Url,
       {
         DefaultsOnly = false;
         Data->Assign(AData);
+        if (!RemoteDirectory.IsEmpty())
+        {
+          Data->RemoteDirectory = RemoteDirectory;
+        }
         if (IsHidden(AData))
         {
           AData->Remove();

+ 3 - 0
core/SessionData.h

@@ -102,6 +102,7 @@ private:
   int FSFTPUploadQueue;
   int FSFTPListingQueue;
   int FSFTPMaxVersion;
+  unsigned long FSFTPMaxPacketSize;
   bool FConsiderDST;
   TAutoSwitch FSFTPBugs[SFTP_BUG_COUNT];
   bool FDeleteToRecycleBin;
@@ -192,6 +193,7 @@ private:
   void __fastcall SetSFTPUploadQueue(int value);
   void __fastcall SetSFTPListingQueue(int value);
   void __fastcall SetSFTPMaxVersion(int value);
+  void __fastcall SetSFTPMaxPacketSize(unsigned long value);
   void __fastcall SetSFTPBug(TSftpBug Bug, TAutoSwitch value);
   TAutoSwitch __fastcall GetSFTPBug(TSftpBug Bug) const;
   void __fastcall SetSCPLsFullTime(TAutoSwitch value);
@@ -292,6 +294,7 @@ public:
   __property int SFTPUploadQueue = { read = FSFTPUploadQueue, write = SetSFTPUploadQueue };
   __property int SFTPListingQueue = { read = FSFTPListingQueue, write = SetSFTPListingQueue };
   __property int SFTPMaxVersion = { read = FSFTPMaxVersion, write = SetSFTPMaxVersion };
+  __property unsigned long SFTPMaxPacketSize = { read = FSFTPMaxPacketSize, write = SetSFTPMaxPacketSize };
   __property TAutoSwitch SFTPBug[TSftpBug Bug]  = { read=GetSFTPBug, write=SetSFTPBug };
   __property TAutoSwitch SCPLsFullTime = { read = FSCPLsFullTime, write = SetSCPLsFullTime };
   __property bool ConsiderDST = { read = FConsiderDST, write = SetConsiderDST };

+ 224 - 48
core/SftpFileSystem.cpp

@@ -124,6 +124,7 @@
 #define SFTP_EXT_OWNER_GROUP_REPLY "owner-group-query-reply@generic-extensions"
 #define SFTP_EXT_NEWLINE "newline"
 #define SFTP_EXT_SUPPORTED "supported"
+#define SFTP_EXT_SUPPORTED2 "supported2"
 #define SFTP_EXT_FSROOTS "[email protected]"
 #define SFTP_EXT_VENDOR_ID "vendor-id"
 //---------------------------------------------------------------------------
@@ -162,11 +163,40 @@ const int tfNewDirectory = 0x02;
 //---------------------------------------------------------------------------
 struct TSFTPSupport
 {
+  TSFTPSupport() :
+    AttribExtensions(new TStringList()),
+    Extensions(new TStringList())
+  {
+    Reset();
+  }
+
+  ~TSFTPSupport()
+  {
+    delete AttribExtensions;
+    delete Extensions;
+  }
+
+  void Reset()
+  {
+    AttributeMask = 0;
+    AttributeBits = 0;
+    OpenFlags = 0;
+    AccessMask = 0;
+    MaxReadSize = 0;
+    OpenBlockMasks = 0;
+    BlockMasks = 0;
+    AttribExtensions->Clear();
+    Extensions->Clear();
+  }
+
   unsigned int AttributeMask;
   unsigned int AttributeBits;
   unsigned int OpenFlags;
   unsigned int AccessMask;
   unsigned int MaxReadSize;
+  unsigned int OpenBlockMasks;
+  unsigned int BlockMasks;
+  TStrings * AttribExtensions;
   TStrings * Extensions;
 };
 //---------------------------------------------------------------------------
@@ -445,6 +475,15 @@ public:
     return Result;
   }
 
+  unsigned long GetSmallCardinal()
+  {
+    unsigned long Result;
+    Need(2);
+    Result = (FData[FPosition] << 8) + FData[FPosition + 1];
+    FPosition += 2;
+    return Result;
+  }
+
   __int64 GetInt64()
   {
     __int64 Hi = GetCardinal();
@@ -707,7 +746,7 @@ public:
     AnsiString Result;
     for (unsigned int Index = 0; Index < Length; Index++)
     {
-      Result += IntToHex(int((unsigned char)Data[Index]), 2) + ",";
+      Result += CharToHex(Data[Index]) + ",";
       if (((Index + 1) % 25) == 0)
       {
         Result += "\n";
@@ -1312,14 +1351,12 @@ __fastcall TSFTPFileSystem::TSFTPFileSystem(TTerminal * ATerminal):
   FUtfNever = false;
   FSignedTS = false;
   FSupport = new TSFTPSupport();
-  FSupport->Extensions = new TStringList();
   FExtensions = new TStringList();
   FFixedPaths = NULL;
 }
 //---------------------------------------------------------------------------
 __fastcall TSFTPFileSystem::~TSFTPFileSystem()
 {
-  delete FSupport->Extensions;
   delete FSupport;
   // there must be no valid packet reservation at the end
   for (int i = 0; i < FPacketReservations->Count; i++)
@@ -1551,13 +1588,23 @@ unsigned long __fastcall TSFTPFileSystem::TransferBlockSize(unsigned long Overhe
   // size + message number + type
   const unsigned long SFTPPacketOverhead = 4 + 4 + 1;
   unsigned long AMaxPacketSize = FTerminal->MaxPacketSize();
+  bool MaxPacketSizeValid = (AMaxPacketSize > 0);
   unsigned long Result = OperationProgress->CPS();
 
-  if ((MaxPacketSize > 0) && (MaxPacketSize < AMaxPacketSize))
+  if ((MaxPacketSize > 0) &&
+      ((MaxPacketSize < AMaxPacketSize) || !MaxPacketSizeValid))
   {
     AMaxPacketSize = MaxPacketSize;
+    MaxPacketSizeValid = true;
   }
-  
+
+  if ((FMaxPacketSize > 0) &&
+      ((FMaxPacketSize < AMaxPacketSize) || !MaxPacketSizeValid))
+  {
+    AMaxPacketSize = FMaxPacketSize;
+    MaxPacketSizeValid = true;
+  }
+
   if (Result == 0)
   {
     Result = OperationProgress->StaticBlockSize();
@@ -1568,12 +1615,22 @@ unsigned long __fastcall TSFTPFileSystem::TransferBlockSize(unsigned long Overhe
     Result = MinPacketSize;
   }
   
-  if (AMaxPacketSize > 0)
+  if (MaxPacketSizeValid)
   {
-    AMaxPacketSize -= SFTPPacketOverhead + Overhead;
-    if (Result > AMaxPacketSize)
+    Overhead += SFTPPacketOverhead;
+    if (AMaxPacketSize < Overhead)
+    {
+      // do not send another request
+      // (generally should happen only if upload buffer if full)
+      Result = 0;
+    }
+    else
     {
-      Result = AMaxPacketSize;
+      AMaxPacketSize -= Overhead;
+      if (Result > AMaxPacketSize)
+      {
+        Result = AMaxPacketSize;
+      }
     }
   }
   return Result;
@@ -1602,7 +1659,12 @@ unsigned long __fastcall TSFTPFileSystem::UploadBlockSize(const AnsiString & Han
 unsigned long __fastcall TSFTPFileSystem::DownloadBlockSize(
   TFileOperationProgressType * OperationProgress)
 {
-  return TransferBlockSize(sizeof(unsigned long), OperationProgress);
+  unsigned long Result = TransferBlockSize(sizeof(unsigned long), OperationProgress);
+  if ((FSupport->MaxReadSize > 0) && (Result > FSupport->MaxReadSize))
+  {
+    Result = FSupport->MaxReadSize;
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 void __fastcall TSFTPFileSystem::SendPacket(const TSFTPPacket * Packet)
@@ -1666,12 +1728,24 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
     SFTP_STATUS_NO_MEDIA,
     SFTP_STATUS_NO_SPACE_ON_FILESYSTEM,
     SFTP_STATUS_QUOTA_EXCEEDED,
-    SFTP_STATUS_UNKNOWN_PRINCIPLE
+    SFTP_STATUS_UNKNOWN_PRINCIPAL,
+    SFTP_STATUS_LOCK_CONFLICT,
+    SFTP_STATUS_DIR_NOT_EMPTY,
+    SFTP_STATUS_NOT_A_DIRECTORY,
+    SFTP_STATUS_INVALID_FILENAME,
+    SFTP_STATUS_LINK_LOOP,
+    SFTP_STATUS_CANNOT_DELETE,
+    SFTP_STATUS_INVALID_PARAMETER,
+    SFTP_STATUS_FILE_IS_A_DIRECTORY,
+    SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT,
+    SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED,
+    SFTP_STATUS_DELETE_PENDING,
+    SFTP_STATUS_FILE_CORRUPT
   };
   int Message;
   if ((AllowStatus & (0x01 << Code)) == 0)
   {
-    if (Code >= sizeof(Messages) / sizeof(*Messages))
+    if (Code >= LENOF(Messages))
     {
       Message = SFTP_STATUS_UNKNOWN;
     }
@@ -1689,7 +1763,7 @@ unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
       // (in other words, always use UTF unless server is know to be buggy)
       ServerMessage = Packet->GetString(!FUtfNever);
       LanguageTag = Packet->GetString();
-      if ((FVersion >= 5) && (Message == SFTP_STATUS_UNKNOWN_PRINCIPLE))
+      if ((FVersion >= 5) && (Message == SFTP_STATUS_UNKNOWN_PRINCIPAL))
       {
         while (Packet->GetNextData() != NULL)
         {
@@ -2171,35 +2245,59 @@ void __fastcall TSFTPFileSystem::DoStartup()
           FTerminal->FatalError(FMTLOAD(SFTP_INVALID_EOL, (ExtensionDisplayData)));
         }
       }
-      else if (ExtensionName == SFTP_EXT_SUPPORTED)
+      else if ((ExtensionName == SFTP_EXT_SUPPORTED) || (ExtensionName == SFTP_EXT_SUPPORTED2))
       {
+        FSupport->Reset();
         TSFTPPacket SupportedStruct(ExtensionData);
         FSupport->AttributeMask = SupportedStruct.GetCardinal();
         FSupport->AttributeBits = SupportedStruct.GetCardinal();
         FSupport->OpenFlags = SupportedStruct.GetCardinal();
         FSupport->AccessMask = SupportedStruct.GetCardinal();
         FSupport->MaxReadSize = SupportedStruct.GetCardinal();
-        AnsiString Extension;
-        FSupport->Extensions->Clear();
-        while (SupportedStruct.GetNextData() != NULL)
+        if (ExtensionName == SFTP_EXT_SUPPORTED)
+        {
+          while (SupportedStruct.GetNextData() != NULL)
+          {
+            FSupport->Extensions->Add(SupportedStruct.GetString());
+          }
+        }
+        else
         {
-          Extension = SupportedStruct.GetString();
-          FSupport->Extensions->Add(Extension);
+          FSupport->OpenBlockMasks = SupportedStruct.GetSmallCardinal();
+          FSupport->BlockMasks = SupportedStruct.GetSmallCardinal();
+          unsigned int ExtensionCount;
+          ExtensionCount = SupportedStruct.GetCardinal();
+          for (unsigned int i = 0; i < ExtensionCount; i++)
+          {
+            FSupport->AttribExtensions->Add(SupportedStruct.GetString());
+          }
+          ExtensionCount = SupportedStruct.GetCardinal();
+          for (unsigned int i = 0; i < ExtensionCount; i++)
+          {
+            FSupport->Extensions->Add(SupportedStruct.GetString());
+          }
         }
 
         if (FTerminal->IsLogging())
         {
           FTerminal->LogEvent(FORMAT(
             "Server support information:\n"
-            "  Attribute mask: %s, Attribute bits: %s, Open flags: %s\n"
-            "  Access mask: %s, Max read size: %s\n"
-            "  Extensions (%d)\n",
-            (IntToHex(__int64(FSupport->AttributeMask), 4),
-             IntToHex(__int64(FSupport->AttributeBits), 4),
-             IntToHex(__int64(FSupport->OpenFlags), 4),
-             IntToHex(__int64(FSupport->AccessMask), 4),
-             IntToStr(__int64(FSupport->MaxReadSize)),
-             FSupport->Extensions->Count)));
+            "  Attribute mask: %x, Attribute bits: %x, Open flags: %x\n"
+            "  Access mask: %x, Open block masks: %x, Block masks: %x, Max read size: %d\n",
+            (int(FSupport->AttributeMask),
+             int(FSupport->AttributeBits),
+             int(FSupport->OpenFlags),
+             int(FSupport->AccessMask),
+             int(FSupport->OpenBlockMasks),
+             int(FSupport->BlockMasks),
+             int(FSupport->MaxReadSize))));
+          FTerminal->LogEvent(FORMAT(   "  Attribute extensions (%d)\n", (FSupport->AttribExtensions->Count)));
+          for (int Index = 0; Index < FSupport->AttribExtensions->Count; Index++)
+          {
+            FTerminal->LogEvent(
+              FORMAT("    %s", (FSupport->AttribExtensions->Strings[Index])));
+          }
+          FTerminal->LogEvent(FORMAT(   "  Extensions (%d)\n", (FSupport->Extensions->Count)));
           for (int Index = 0; Index < FSupport->Extensions->Count; Index++)
           {
             FTerminal->LogEvent(
@@ -2303,6 +2401,17 @@ void __fastcall TSFTPFileSystem::DoStartup()
   {
     FTerminal->LogEvent("We will use UTF-8 strings for status messages only");
   }
+
+  FMaxPacketSize = FTerminal->SessionData->SFTPMaxPacketSize;
+  if (FMaxPacketSize == 0)
+  {
+    if (FTerminal->SshImplementation.Pos("OpenSSH") == 1)
+    {
+      FMaxPacketSize = 4 + (256 * 1024); // len + 256kB payload
+      FTerminal->LogEvent(FORMAT("Limiting packet size to OpenSSH limit of %d bytes",
+        (int(FMaxPacketSize))));
+    }
+  }
 }
 //---------------------------------------------------------------------------
 char * __fastcall TSFTPFileSystem::GetEOL() const
@@ -2757,10 +2866,24 @@ void __fastcall TSFTPFileSystem::CreateDirectory(const AnsiString DirName,
 
   // explicitly set permissions after directory creation,
   // permissions specified in SSH_FXP_MKDIR are ignored at least by OpenSSH
-  Packet.ChangeType(SSH_FXP_SETSTAT);
-  Packet.AddPathString(CanonifiedName, FUtfStrings);
-  Packet.AddProperties(Properties, 0, true, FVersion, FUtfStrings);
-  SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
+  try
+  {
+    Packet.ChangeType(SSH_FXP_SETSTAT);
+    Packet.AddPathString(CanonifiedName, FUtfStrings);
+    Packet.AddProperties(Properties, 0, true, FVersion, FUtfStrings);
+    SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
+  }
+  catch(Exception & E)
+  {
+    if (FTerminal->Active)
+    {
+      throw ECommand(&E, FMTLOAD(CHANGE_PROPERTIES_ERROR, (DirName)));
+    }
+    else
+    {
+      throw;
+    }
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TSFTPFileSystem::CreateLink(const AnsiString FileName,
@@ -2827,7 +2950,8 @@ void __fastcall TSFTPFileSystem::CustomCommandOnFile(const AnsiString /*FileName
   assert(false);
 }
 //---------------------------------------------------------------------------
-void __fastcall TSFTPFileSystem::AnyCommand(const AnsiString /*Command*/)
+void __fastcall TSFTPFileSystem::AnyCommand(const AnsiString /*Command*/,
+  TLogAddLineEvent /*OutputEvent*/)
 {
   assert(false);
 }
@@ -3066,13 +3190,6 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
   const AnsiString TargetDir, const TCopyParamType * CopyParam, int Params,
   TFileOperationProgressType * OperationProgress, unsigned int Flags)
 {
-  if (FLAGCLEAR(Params, cpDelete) &&
-      !CopyParam->AllowTransfer(FileName, osLocal))
-  {
-    FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
-    THROW_SKIP_FILE_NULL;
-  }
-
   FTerminal->LogEvent(FORMAT("File: \"%s\"", (FileName)));
 
   OperationProgress->SetFile(FileName);
@@ -3087,7 +3204,16 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
   FTerminal->OpenLocalFile(FileName, GENERIC_READ, &OpenParams.LocalFileAttrs,
     &File, NULL, &MTime, &ATime, &Size);
 
-  if (OpenParams.LocalFileAttrs & faDirectory)
+  bool Dir = FLAGSET(OpenParams.LocalFileAttrs, faDirectory);
+
+  if (FLAGCLEAR(Params, cpDelete) &&
+      !CopyParam->AllowTransfer(FileName, osLocal, Dir))
+  {
+    FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
+    THROW_SKIP_FILE_NULL;
+  }
+
+  if (Dir)
   {
     SFTPDirectorySource(IncludeTrailingBackslash(FileName), TargetDir,
       OpenParams.LocalFileAttrs, CopyParam, Params, OperationProgress, Flags);
@@ -3196,6 +3322,7 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
 
       }
 
+      // will the transfer be resumable?
       bool DoResume = (ResumeAllowed && (OpenParams.OverwriteMode == omOverwrite));
 
       if (DoResume && DestFileExists)
@@ -3318,8 +3445,10 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
           SFTPCloseRemote(OpenParams.RemoteFileHandle, DestFileName,
             OperationProgress, TransferFinished, false, &CloseRequest);
 
-          // delete file if transfer was not completed and resuming is not allowed
-          if (!TransferFinished && !DoResume)
+          // delete file if transfer was not completed, resuming was not allowed and
+          // we were not appending (incl. alternate resume),
+          // shortly after plain transfer completes (eq. !ResumeAllowed)
+          if (!TransferFinished && !DoResume && (OpenParams.OverwriteMode == omOverwrite))
           {
             DeleteFile(OpenParams.RemoteFileName);
           }
@@ -3465,7 +3594,7 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
 
       Success = true;
     }
-    catch(...)
+    catch(Exception & E)
     {
       if (!OpenParams->Confirmed && (OpenType & SSH_FXF_EXCL) && FTerminal->Active)
       {
@@ -3519,6 +3648,51 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
           FTerminal->RecycleFile(OpenParams->RemoteFileName, NULL);
         }
       }
+      else if (FTerminal->Active)
+      {
+        // if file overwritting was confirmed, it means that the file already exists,
+        // if not, check now
+        if (!OpenParams->Confirmed)
+        {
+          bool ThrowOriginal = false;
+
+          // When file does not exist, failure was probably caused by 'permission denied'
+          // or similar error. In this case throw original exception.
+          try
+          {
+            TRemoteFile * File;
+            AnsiString RealFileName = LocalCanonify(OpenParams->RemoteFileName);
+            ReadFile(RealFileName, File);
+            SAFE_DESTROY(File);
+          }
+          catch(...)
+          {
+            if (!FTerminal->Active)
+            {
+              throw;
+            }
+            else
+            {
+              ThrowOriginal = true;
+            }
+          }
+
+          if (ThrowOriginal)
+          {
+            throw;
+          }
+        }
+
+        // now we know that the file exists
+
+        if (FTerminal->FileOperationLoopQuery(E, OperationProgress,
+              FMTLOAD(SFTP_OVERWRITE_FILE_ERROR, (OpenParams->RemoteFileName)),
+              true, LoadStr(SFTP_OVERWRITE_DELETE_BUTTON)))
+        {
+          bool Recursive = false;
+          FTerminal->DeleteFile(OpenParams->RemoteFileName, NULL, &Recursive);
+        }
+      }
       else
       {
         throw;
@@ -3718,7 +3892,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
   AnsiString OnlyFileName = UnixExtractFileName(FileName);
 
   if (FLAGCLEAR(Params, cpDelete) &&
-      !CopyParam->AllowTransfer(FileName, osRemote))
+      !CopyParam->AllowTransfer(FileName, osRemote, File->IsDirectory))
   {
     FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
     THROW_SKIP_FILE_NULL;  
@@ -3808,6 +3982,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
     bool DeleteLocalFile = false;
     AnsiString RemoteHandle;
     AnsiString LocalFileName = DestFullName;
+    TSFTPOverwriteMode OverwriteMode = omOverwrite;
 
     try
     {
@@ -3860,7 +4035,6 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
           NULL, &LocalHandle, NULL, &MTime, NULL, &DestFileSize, false);
 
         FTerminal->LogEvent("Checking existence of file.");
-        TSFTPOverwriteMode OverwriteMode;
         TOverwriteFileParams FileParams;
         FileParams.SourceSize = OperationProgress->TransferSize;
         FileParams.SourceTimestamp = File->Modification;
@@ -3906,7 +4080,8 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
       // if not already opened (resume, append...), create new empty file
       if (!LocalHandle)
       {
-        if (!FTerminal->CreateLocalFile(LocalFileName, OperationProgress, &LocalHandle))
+        if (!FTerminal->CreateLocalFile(LocalFileName, OperationProgress,
+               &LocalHandle, FLAGSET(Params, cpNoConfirmation)))
         {
           THROW_SKIP_FILE_NULL;
         }
@@ -4115,7 +4290,8 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
     {
       if (LocalHandle) CloseHandle(LocalHandle);
       if (FileStream) delete FileStream;
-      if (DeleteLocalFile && (!ResumeAllowed || OperationProgress->LocalyUsed == 0))
+      if (DeleteLocalFile && (!ResumeAllowed || OperationProgress->LocalyUsed == 0) &&
+          (OverwriteMode == omOverwrite))
       {
         Sysutils::DeleteFile(LocalFileName);
       }

+ 3 - 1
core/SftpFileSystem.h

@@ -23,7 +23,8 @@ public:
 
   virtual AnsiString __fastcall AbsolutePath(AnsiString Path);
   virtual void __fastcall KeepAlive();
-  virtual void __fastcall AnyCommand(const AnsiString Command);
+  virtual void __fastcall AnyCommand(const AnsiString Command,
+    TLogAddLineEvent OutputEvent);
   virtual void __fastcall ChangeDirectory(const AnsiString Directory);
   virtual void __fastcall CachedChangeDirectory(const AnsiString Directory);
   virtual void __fastcall ChangeFileProperties(const AnsiString FileName,
@@ -79,6 +80,7 @@ protected:
   bool FUtfNever;
   bool FSignedTS;
   TStrings * FFixedPaths;
+  unsigned long FMaxPacketSize;
 
   void __fastcall CustomReadFile(const AnsiString FileName,
     TRemoteFile *& File, char Type, TRemoteFile * ALinkedByFile = NULL,

+ 230 - 61
core/Terminal.cpp

@@ -41,6 +41,21 @@ TSynchronizeStats::TSynchronizeStats()
   memset(this, 0, sizeof(*this));
 }
 //---------------------------------------------------------------------------
+TCalculateSizeStats::TCalculateSizeStats()
+{
+  memset(this, 0, sizeof(*this));
+}
+//---------------------------------------------------------------------------
+TSynchronizeOptions::TSynchronizeOptions()
+{
+  memset(this, 0, sizeof(*this));
+}
+//---------------------------------------------------------------------------
+TSynchronizeOptions::~TSynchronizeOptions()
+{
+  delete Filter;
+}
+//---------------------------------------------------------------------------
 __fastcall TTerminal::TTerminal(): TSecureShell()
 {
   FFiles = new TRemoteDirectory(this);
@@ -299,6 +314,83 @@ void __fastcall TTerminal::TerminalError(Exception * E, AnsiString Msg)
   throw ETerminal(E, Msg);
 }
 //---------------------------------------------------------------------------
+bool __fastcall TTerminal::FileOperationLoopQuery(Exception & E,
+  TFileOperationProgressType * OperationProgress, const AnsiString Message,
+  bool AllowSkip, AnsiString SpecialRetry)
+{
+  bool Result = false;
+  DoHandleExtendedException(&E);
+  int Answer;
+
+  if (AllowSkip && OperationProgress->SkipToAll)
+  {
+    Answer = qaSkip;
+  }
+  else
+  {
+    int Answers = qaRetry | qaAbort |
+      FLAGMASK(AllowSkip, (qaSkip | qaAll)) |
+      FLAGMASK(!SpecialRetry.IsEmpty(), qaYes);
+    TQueryParams Params(qpAllowContinueOnError | FLAGMASK(!AllowSkip, qpFatalAbort));
+    TQueryButtonAlias Aliases[2];
+    int AliasCount = 0;
+
+    if (FLAGSET(Answers, qaAll))
+    {
+      Aliases[AliasCount].Button = qaAll;
+      Aliases[AliasCount].Alias = LoadStr(SKIP_ALL_BUTTON);
+      AliasCount++;
+    }
+    if (FLAGSET(Answers, qaYes))
+    {
+      Aliases[AliasCount].Button = qaYes;
+      Aliases[AliasCount].Alias = SpecialRetry;
+      AliasCount++;
+    }
+    
+    if (AliasCount > 0)
+    {
+      Params.Aliases = Aliases;
+      Params.AliasesCount = AliasCount;
+    }
+
+    SUSPEND_OPERATION (
+      Answer = DoQueryUser(Message, &E, Answers, &Params, qtError);
+    );
+
+    if (Answer == qaAll)
+    {
+      OperationProgress->SkipToAll = true;
+      Answer = qaSkip;
+    }
+    if (Answer == qaYes)
+    {
+      Result = true;
+      Answer = qaRetry;
+    }
+  }
+
+  if (Answer != qaRetry)
+  {
+    if (Answer == qaAbort)
+    {
+      OperationProgress->Cancel = csCancel;
+    }
+
+    if (AllowSkip)
+    {
+      THROW_SKIP_FILE(&E, Message);
+    }
+    else
+    {
+      // this can happen only during file transfer with SCP
+      throw ExtException(&E, Message);
+    }
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
 int __fastcall TTerminal::FileOperationLoop(TFileOperationEvent CallBackFunc,
   TFileOperationProgressType * OperationProgress, bool AllowSkip,
   const AnsiString Message, void * Param1, void * Param2)
@@ -723,25 +815,55 @@ int __fastcall TTerminal::ConfirmFileOverwrite(const AnsiString FileName,
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::FileModified(const TRemoteFile * File,
-  const AnsiString FileName)
+  const AnsiString FileName, bool ClearDirectoryChange)
 {
-  if (SessionData->CacheDirectories)
+  AnsiString ParentDirectory;
+  AnsiString Directory;
+
+  if (SessionData->CacheDirectories || SessionData->CacheDirectoryChanges)
   {
     if ((File != NULL) && (File->Directory != NULL))
     {
       if (File->IsDirectory)
       {
-        // do not use UnixIncludeTrailingBackslash(CurrentDirectory)
-        DirectoryModified(
-          File->Directory->FullDirectory + File->FileName, true);
+        Directory = File->Directory->FullDirectory + File->FileName;
       }
-      DirectoryModified(File->Directory->Directory, false);
+      ParentDirectory = File->Directory->Directory;
     }
     else if (!FileName.IsEmpty())
     {
-      AnsiString Directory = UnixExtractFilePath(FileName);
-      DirectoryModified(
-        !Directory.IsEmpty() ? Directory : CurrentDirectory, false);
+      ParentDirectory = UnixExtractFilePath(FileName);
+      if (ParentDirectory.IsEmpty())
+      {
+        ParentDirectory = CurrentDirectory;
+      }
+
+      // this case for scripting
+      if ((File != NULL) && File->IsDirectory)
+      {
+        Directory = UnixIncludeTrailingBackslash(ParentDirectory) +
+          UnixExtractFileName(File->FileName);
+      }
+    }
+  }
+
+  if (SessionData->CacheDirectories)
+  {
+    if (!Directory.IsEmpty())
+    {
+      DirectoryModified(Directory, true);
+    }
+    if (!ParentDirectory.IsEmpty())
+    {
+      DirectoryModified(ParentDirectory, false);
+    }
+  }
+
+  if (SessionData->CacheDirectoryChanges && ClearDirectoryChange)
+  {
+    if (!Directory.IsEmpty())
+    {
+      FDirectoryChangesCache->ClearDirectoryChangeTarget(Directory);
     }
   }
 }
@@ -976,8 +1098,8 @@ TRemoteFileList * TTerminal::ReadDirectoryListing(AnsiString Directory, bool Use
     FileList = new TRemoteFileList();
     try
     {
-      bool LoadedFromCache = UseCache && SessionData->CacheDirectories &&
-        FDirectoryCache->HasFileList(Directory);
+      bool Cache = UseCache && SessionData->CacheDirectories;
+      bool LoadedFromCache = Cache && FDirectoryCache->HasFileList(Directory);
       if (LoadedFromCache)
       {
         LoadedFromCache = FDirectoryCache->GetFileList(Directory, FileList);
@@ -996,6 +1118,11 @@ TRemoteFileList * TTerminal::ReadDirectoryListing(AnsiString Directory, bool Use
         {
           ExceptionOnFail = false;
         }
+
+        if (Cache)
+        {
+          AddCachedFileList(FileList);
+        }
       }
     }
     catch(...)
@@ -1192,6 +1319,11 @@ TStrings * __fastcall TTerminal::GetFixedPaths()
   return FFileSystem->GetFixedPaths();
 }
 //---------------------------------------------------------------------------
+bool __fastcall TTerminal::GetResolvingSymlinks()
+{
+  return SessionData->ResolveSymlinks && IsCapable[fcResolveSymlink];
+}
+//---------------------------------------------------------------------------
 bool __fastcall TTerminal::IsRecycledFile(AnsiString FileName)
 {
   AnsiString Path = UnixExtractFilePath(FileName);
@@ -1243,7 +1375,7 @@ void __fastcall TTerminal::DeleteFile(AnsiString FileName,
   else
   {
     LogEvent(FORMAT("Deleting file \"%s\".", (FileName)));
-    if (File) FileModified(File, FileName);
+    if (File) FileModified(File, FileName, true);
     DoDeleteFile(FileName, File, Recursive);
     ReactOnCommand(fsDeleteFile);
   }
@@ -1335,20 +1467,14 @@ void __fastcall TTerminal::DoCustomCommandOnFile(AnsiString FileName,
       assert(CommandSessionOpened);
       assert(FCommandSession->FSProtocol == cfsSCP);
       LogEvent("Executing custom command on command session.");
-      // TODO: we should set FCommandSession->Log->OnAddLine to 
-      // Log->AddFromOtherLog always as in AnyCommand()
-      // to log shell output, but it collides with OutputEvent, so currenly
-      // there's no logging when OutputEvent is required
       assert(FCommandSession->Log->OnAddLine == NULL);
-      if (OutputEvent == NULL)
-      {
-        FCommandSession->Log->OnAddLine = Log->AddFromOtherLog;
-      }
-      
+      FCommandSession->Log->OnAddLine = Log->AddFromOtherLog;
+
       try
       {
         FCommandSession->CurrentDirectory = CurrentDirectory;
-        FCommandSession->FFileSystem->CustomCommandOnFile(FileName, File, Command, Params, OutputEvent);
+        FCommandSession->FFileSystem->CustomCommandOnFile(FileName, File, Command,
+          Params, OutputEvent);
       }
       __finally
       {
@@ -1392,21 +1518,12 @@ void __fastcall TTerminal::CustomCommandOnFiles(AnsiString Command,
           FileList += " ";
         }
 
-        FileList += "\"" + Files->Strings[i] + "\"";
+        FileList += "\"" + ShellDelimitStr(Files->Strings[i], '"') + "\"";
       }
     }
     
     AnsiString Cmd = TRemoteCustomCommand("", FileList).Complete(Command, true);
-    assert(Log->OnAddLine == NULL);
-    Log->OnAddLine = OutputEvent;
-    try
-    {
-      AnyCommand(Cmd);
-    }
-    __finally
-    {
-      Log->OnAddLine = NULL;
-    }  
+    AnyCommand(Cmd, OutputEvent);
   }
 }
 //---------------------------------------------------------------------------
@@ -1497,17 +1614,39 @@ void __fastcall TTerminal::CalculateFileSize(AnsiString FileName,
 
   if ((AParams->CopyParam == NULL) ||
       AParams->CopyParam->AllowTransfer(UnixExcludeTrailingBackslash(File->FullFileName),
-        osRemote))
+        osRemote, File->IsDirectory))
   {
-    if (File->IsDirectory && !File->IsSymLink)
+    if (File->IsDirectory)
     {
-      LogEvent(FORMAT("Getting size of directory \"%s\"", (FileName)));
-      // pass in full path so we get it back in file list for AllowTransfer() exclusion
-      DoCalculateDirectorySize(File->FullFileName, File, AParams);
+      if (!File->IsSymLink)
+      {
+        LogEvent(FORMAT("Getting size of directory \"%s\"", (FileName)));
+        // pass in full path so we get it back in file list for AllowTransfer() exclusion
+        DoCalculateDirectorySize(File->FullFileName, File, AParams);
+      }
+      else
+      {
+        AParams->Size += File->Size;
+      }
+
+      if (AParams->Stats != NULL)
+      {
+        AParams->Stats->Directories++;
+      }
     }
     else
     {
       AParams->Size += File->Size;
+
+      if (AParams->Stats != NULL)
+      {
+        AParams->Stats->Files++;
+      }
+    }
+
+    if ((AParams->Stats != NULL) && File->IsSymLink)
+    {
+      AParams->Stats->SymLinks++;
     }
   }
 
@@ -1539,12 +1678,14 @@ void __fastcall TTerminal::DoCalculateDirectorySize(const AnsiString FileName,
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::CalculateFilesSize(TStrings * FileList,
-  __int64 & Size, int Params, const TCopyParamType * CopyParam)
+  __int64 & Size, int Params, const TCopyParamType * CopyParam,
+  TCalculateSizeStats * Stats)
 {
   TCalculateSizeParams Param;
   Param.Size = 0;
   Param.Params = Params;
   Param.CopyParam = CopyParam;
+  Param.Stats = Stats;
   ProcessFiles(FileList, foCalculateSize, CalculateFileSize, &Param);
   Size = Param.Size;
 }
@@ -1904,6 +2045,7 @@ TTerminal * __fastcall TTerminal::GetCommandSession()
       FCommandSession->OnShowExtendedException = OnShowExtendedException;
       FCommandSession->OnProgress = OnProgress;
       FCommandSession->OnFinished = OnFinished;
+      // do not copy OnDisplayBanner to avoid it being displayed
     }
     catch(...)
     {
@@ -1915,7 +2057,8 @@ TTerminal * __fastcall TTerminal::GetCommandSession()
   return FCommandSession;
 }
 //---------------------------------------------------------------------------
-void __fastcall TTerminal::AnyCommand(const AnsiString Command)
+void __fastcall TTerminal::AnyCommand(const AnsiString Command,
+  TLogAddLineEvent OutputEvent)
 {
   assert(FFileSystem);
   try
@@ -1924,22 +2067,19 @@ void __fastcall TTerminal::AnyCommand(const AnsiString Command)
     if (IsCapable[fcAnyCommand])
     {
       LogEvent("Executing user defined command.");
-      FFileSystem->AnyCommand(Command);
+      FFileSystem->AnyCommand(Command, OutputEvent);
     }
     else
     {
       assert(CommandSessionOpened);
       assert(FCommandSession->FSProtocol == cfsSCP);
       LogEvent("Executing user defined command on command session.");
+      assert(FCommandSession->Log->OnAddLine == NULL);
       FCommandSession->Log->OnAddLine = Log->AddFromOtherLog;
-      TLogAddLineEvent PrevLog = Log->OnAddLine;
       try
       {
         FCommandSession->CurrentDirectory = CurrentDirectory;
-        FCommandSession->FFileSystem->AnyCommand(Command);
-
-        // ugly hack to prevent SFTP logging be displayed in console
-        Log->OnAddLine = NULL;
+        FCommandSession->FFileSystem->AnyCommand(Command, OutputEvent);
 
         FCommandSession->FFileSystem->ReadCurrentDirectory();
 
@@ -1948,7 +2088,6 @@ void __fastcall TTerminal::AnyCommand(const AnsiString Command)
       }
       __finally
       {
-        Log->OnAddLine = PrevLog;
         FCommandSession->Log->OnAddLine = NULL;
       }
     }
@@ -1962,7 +2101,8 @@ void __fastcall TTerminal::AnyCommand(const AnsiString Command)
 }
 //---------------------------------------------------------------------------
 bool __fastcall TTerminal::CreateLocalFile(const AnsiString FileName,
-  TFileOperationProgressType * OperationProgress, HANDLE * AHandle)
+  TFileOperationProgressType * OperationProgress, HANDLE * AHandle,
+  bool NoConfirmation)
 {
   assert(AHandle);
   bool Result = true;
@@ -1987,7 +2127,7 @@ bool __fastcall TTerminal::CreateLocalFile(const AnsiString FileName,
             {
               Result = false;
             }
-            else if (!OperationProgress->YesToAll)
+            else if (!OperationProgress->YesToAll && !NoConfirmation)
             {
               int Answer;
               SUSPEND_OPERATION
@@ -2155,10 +2295,11 @@ void __fastcall TTerminal::CalculateLocalFileSize(const AnsiString FileName,
 {
   TCalculateSizeParams * AParams = static_cast<TCalculateSizeParams*>(Params);
 
+  bool Dir = FLAGSET(Rec.Attr, faDirectory);
   if ((AParams->CopyParam == NULL) ||
-      AParams->CopyParam->AllowTransfer(FileName, osLocal))
+      AParams->CopyParam->AllowTransfer(FileName, osLocal, Dir))
   {
-    if (FLAGCLEAR(Rec.Attr, faDirectory))
+    if (!Dir)
     {
       AParams->Size +=
         (static_cast<__int64>(Rec.FindData.nFileSizeHigh) << 32) +
@@ -2228,6 +2369,7 @@ struct TSynchronizeFileData
   const TRemoteFile * MatchingRemoteFile;
 };
 //---------------------------------------------------------------------------
+const int sfFirstLevel = 0x01;
 struct TSynchronizeData
 {
   AnsiString LocalDirectory;
@@ -2236,6 +2378,8 @@ struct TSynchronizeData
   int Params;
   TSynchronizeDirectory OnSynchronizeDirectory;
   TSynchronizeStats * Stats;
+  TSynchronizeOptions * Options;
+  int Flags;
   TStringList * LocalFileList;
   TStringList * ModifiedRemoteFileList;
   TStringList * NewRemoteFileList;
@@ -2246,7 +2390,8 @@ struct TSynchronizeData
 void __fastcall TTerminal::Synchronize(const AnsiString LocalDirectory,
   const AnsiString RemoteDirectory, TSynchronizeMode Mode,
   const TCopyParamType * CopyParam, int Params,
-  TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats)
+  TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats,
+  TSynchronizeOptions * Options)
 {
   assert(CopyParam != NULL);
   TCopyParamType SyncCopyParam = *CopyParam;
@@ -2256,7 +2401,7 @@ void __fastcall TTerminal::Synchronize(const AnsiString LocalDirectory,
   try
   {
     DoSynchronizeDirectory(LocalDirectory, RemoteDirectory, Mode,
-      &SyncCopyParam, Params, OnSynchronizeDirectory, Stats);
+      &SyncCopyParam, Params, OnSynchronizeDirectory, Stats, Options, sfFirstLevel);
   }
   __finally
   {
@@ -2278,7 +2423,8 @@ void __fastcall TTerminal::DoSynchronizeProgress(const TSynchronizeData & Data)
 void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirectory,
   const AnsiString RemoteDirectory, TSynchronizeMode Mode,
   const TCopyParamType * CopyParam, int Params,
-  TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats)
+  TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats,
+  TSynchronizeOptions * Options, int Flags)
 {
   TSynchronizeData Data;
 
@@ -2292,6 +2438,8 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
   Data.ModifiedRemoteFileList = NULL;
   Data.CopyParam = CopyParam;
   Data.Stats = Stats;
+  Data.Options = Options;
+  Data.Flags = Flags;
   TStrings * LocalFileList = NULL;
 
   LogEvent(FORMAT("Synchronizing local directory '%s' with remote directory '%s', "
@@ -2330,10 +2478,15 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
           FileName = SearchRec.Name;
           // add dirs for recursive mode or when we are interested in newly
           // added subdirs
+          int FoundIndex;
           if ((FileName != ".") && (FileName != "..") &&
               (FLAGCLEAR(SearchRec.Attr, faDirectory) ||
                FLAGCLEAR(Params, spNoRecurse) || FLAGSET(Params, spSubDirs)) &&
-              CopyParam->AllowTransfer(Data.LocalDirectory + FileName, osLocal))
+              CopyParam->AllowTransfer(Data.LocalDirectory + FileName, osLocal,
+                FLAGSET(SearchRec.Attr, faDirectory)) &&
+              (FLAGCLEAR(Flags, sfFirstLevel) ||
+               (Options == NULL) || (Options->Filter == NULL) ||
+               Options->Filter->Find(FileName, FoundIndex)))
           {
             TSynchronizeFileData * FileData = new TSynchronizeFileData;
             FileData->Time = SearchRec.Time;
@@ -2439,7 +2592,10 @@ void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirector
 
       if (Upload || DeleteLocal || Download || DeleteRemote)
       {
-        if (Cached && FLAGSET(Params, spDelayProgress))
+        // Update progress whenever we did not before directory read.
+        // This makes progress show actual directory where the change occur,
+        // not the last directory scanned (subdirecttories).
+        if (FLAGCLEAR(Params, spDelayProgress) || Cached)
         {
           DoSynchronizeProgress(Data);
         }
@@ -2604,8 +2760,13 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
 {
   TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
 
+  int FoundIndex;
   if (Data->CopyParam->AllowTransfer(
-        UnixExcludeTrailingBackslash(File->FullFileName), osRemote))
+        UnixExcludeTrailingBackslash(File->FullFileName), osRemote,
+        File->IsDirectory) &&
+      (FLAGCLEAR(Data->Flags, sfFirstLevel) ||
+       (Data->Options == NULL) || (Data->Options->Filter == NULL) ||
+        Data->Options->Filter->Find(File->FileName, FoundIndex)))
   {
     bool Modified = false;
     int LocalIndex = Data->LocalFileList->IndexOf(File->FileName);
@@ -2635,7 +2796,14 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
         ReduceDateTimePrecision(LocalTime, File->ModificationFmt);
 
         bool LocalModified = false;
-        if (FLAGCLEAR(Data->Params, spNotByTime) &&
+        // for spTimestamp+spBySize require that the file sizes are the same
+        // before comparing file time
+        bool TimeCompare =
+          FLAGCLEAR(Data->Params, spNotByTime) &&
+          (FLAGCLEAR(Data->Params, spTimestamp) ||
+           FLAGCLEAR(Data->Params, spBySize) ||
+           (LocalData->Size != File->Size));
+        if (TimeCompare &&
             (CompareFileTime(LocalTime, RemoteTime) < 0))
         {
           if (FLAGCLEAR(Data->Params, spTimestamp) ||
@@ -2648,7 +2816,7 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
             LocalModified = true;
           }
         }
-        else if (FLAGCLEAR(Data->Params, spNotByTime) &&
+        else if (TimeCompare &&
                  (CompareFileTime(LocalTime, RemoteTime) > 0))
         {
           if (FLAGCLEAR(Data->Params, spTimestamp) ||
@@ -2662,7 +2830,8 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
           }
         }
         else if (FLAGSET(Data->Params, spBySize) &&
-                 (LocalData->Size != File->Size))
+                 (LocalData->Size != File->Size) &&
+                 FLAGCLEAR(Data->Params, spTimestamp))
         {
           Modified = true;
           LocalModified = true;
@@ -2680,7 +2849,7 @@ void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
           Data->LocalDirectory + File->FileName,
           Data->RemoteDirectory + File->FileName,
           Data->Mode, Data->CopyParam, Data->Params, Data->OnSynchronizeDirectory,
-          Data->Stats);
+          Data->Stats, Data->Options, (Data->Flags & ~sfFirstLevel));
       }
     }
     else

+ 41 - 40
core/Terminal.h

@@ -17,6 +17,8 @@ struct TCalculateSizeParams;
 struct TOverwriteFileParams;
 struct TSynchronizeData;
 struct TSynchronizeStats;
+struct TSynchronizeOptions;
+struct TCalculateSizeStats;
 typedef TStringList TUsersGroupsList;
 typedef void __fastcall (__closure *TReadDirectoryEvent)(System::TObject* Sender, Boolean ReloadOnly);
 typedef void __fastcall (__closure *TReadDirectoryProgressEvent)(
@@ -65,37 +67,9 @@ typedef int __fastcall (__closure *TDirectoryModifiedEvent)
       throw;                                                                \
     }                                                                       \
     catch (Exception & E)                                                   \
-    { \
-      TERMINAL->DoHandleExtendedException(&E); \
-      int Answer;                                                           \
-      if ((ALLOW_SKIP) && OperationProgress->SkipToAll)                     \
-        Answer = qaSkip;                                                    \
-      else                                                                  \
-      {                                                                     \
-        int Answers = qaRetry | qaAbort | ((ALLOW_SKIP) ? (qaSkip | qaAll) : 0); \
-        TQueryParams Params(qpAllowContinueOnError | (!(ALLOW_SKIP) ? qpFatalAbort : 0)); \
-        TQueryButtonAlias Aliases[1];                                       \
-        if (FLAGSET(Answers, qaAll))                                        \
-        {                                                                   \
-          Aliases[0].Button = qaAll;                                        \
-          Aliases[0].Alias = LoadStr(SKIP_ALL_BUTTON);                      \
-          Params.Aliases = Aliases;                                         \
-          Params.AliasesCount = LENOF(Aliases);                             \
-        }                                                                   \
-        SUSPEND_OPERATION (                                                 \
-          Answer = TERMINAL->DoQueryUser(MESSAGE, &E, Answers, &Params, qtError); \
-        );                                                                  \
-        if (Answer == qaAll)                                                \
-        {                                                                   \
-          OperationProgress->SkipToAll = true;                              \
-          Answer = qaSkip;                                                  \
-        }                                                                   \
-      }                                                                     \
-      DoRepeat = (Answer == qaRetry); \
-      if (Answer == qaAbort) OperationProgress->Cancel = csCancel; \
-      if (!DoRepeat && ALLOW_SKIP) THROW_SKIP_FILE(&E, MESSAGE); \
-        else \
-      if (!DoRepeat && !ALLOW_SKIP) throw; \
+    {                                                                       \
+      TERMINAL->FileOperationLoopQuery(E, OperationProgress, MESSAGE, ALLOW_SKIP); \
+      DoRepeat = true;                                                      \
     } \
   } while (DoRepeat); }
 
@@ -134,7 +108,7 @@ public:
   static const spSubDirs = 0x80; // cannot be combined with spTimestamp
   static const spTimestamp = 0x100;
   static const spNotByTime = 0x200; // cannot be combined with spTimestamp and smBoth
-  static const spBySize = 0x400; // cannot be combined with spTimestamp and smBoth
+  static const spBySize = 0x400; // cannot be combined with smBoth, has opposite meaning for spTimestamp
 
 // for TranslateLockedPath()
 friend class TRemoteFile;
@@ -188,6 +162,7 @@ private:
   void __fastcall AddCachedFileList(TRemoteFileList * FileList);
   bool __fastcall GetCommandSessionOpened();
   TTerminal * __fastcall GetCommandSession();
+  bool __fastcall GetResolvingSymlinks();
 
 protected:
   bool FReadCurrentDirectoryPending;
@@ -212,7 +187,8 @@ protected:
   void __fastcall DoChangeDirectory();
   void __fastcall EnsureNonExistence(const AnsiString FileName);
   void __fastcall LookupUsersGroups();
-  void __fastcall FileModified(const TRemoteFile * File, const AnsiString FileName);
+  void __fastcall FileModified(const TRemoteFile * File,
+    const AnsiString FileName, bool ClearDirectoryChange = false);
   int __fastcall FileOperationLoop(TFileOperationEvent CallBackFunc,
     TFileOperationProgressType * OperationProgress, bool AllowSkip,
     const AnsiString Message, void * Param1 = NULL, void * Param2 = NULL);
@@ -229,7 +205,8 @@ protected:
   void __fastcall CustomReadDirectory(TRemoteFileList * FileList);
   void __fastcall DoCreateLink(const AnsiString FileName, const AnsiString PointTo, bool Symbolic);
   bool __fastcall CreateLocalFile(const AnsiString FileName,
-    TFileOperationProgressType * OperationProgress, HANDLE * AHandle);
+    TFileOperationProgressType * OperationProgress, HANDLE * AHandle,
+    bool NoConfirmation);
   void __fastcall OpenLocalFile(const AnsiString FileName, int Access,
     int * Attrs, HANDLE * Handle, __int64 * ACTime, __int64 * MTime,
     __int64 * ATime, __int64 * Size, bool TryWriteReadOnly = true);
@@ -249,7 +226,8 @@ protected:
   void __fastcall DoSynchronizeDirectory(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, TSynchronizeMode Mode,
     const TCopyParamType * CopyParam, int Params,
-    TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats);
+    TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats,
+    TSynchronizeOptions * Options, int Level);
   void __fastcall SynchronizeFile(const AnsiString FileName,
     const TRemoteFile * File, /*TSynchronizeData*/ void * Param);
   void __fastcall SynchronizeRemoteTimestamp(const AnsiString FileName,
@@ -274,7 +252,7 @@ public:
   virtual void __fastcall DirectoryLoaded(TRemoteFileList * FileList);
   virtual void __fastcall Idle();
   bool __fastcall AllowedAnyCommand(const AnsiString Command);
-  void __fastcall AnyCommand(const AnsiString Command);
+  void __fastcall AnyCommand(const AnsiString Command, TLogAddLineEvent OutputEvent);
   void __fastcall CloseOnCompletion(const AnsiString Message = "");
   AnsiString __fastcall AbsolutePath(AnsiString Path);
   void __fastcall BeginTransaction();
@@ -296,7 +274,7 @@ public:
   bool __fastcall DeleteLocalFiles(TStrings * FileList);
   void __fastcall CustomCommandOnFile(AnsiString FileName,
     const TRemoteFile * File, void * AParams);
-  void __fastcall CustomCommandOnFiles(AnsiString Command, int Params, 
+  void __fastcall CustomCommandOnFiles(AnsiString Command, int Params,
     TStrings * Files, TLogAddLineEvent OutputEvent);
   void __fastcall ChangeDirectory(const AnsiString Directory);
   void __fastcall DoStartup();
@@ -321,17 +299,21 @@ public:
   bool __fastcall CopyFiles(TStrings * FileList, const AnsiString Target,
     const AnsiString FileMask);
   void __fastcall CalculateFilesSize(TStrings * FileList, __int64 & Size,
-    int Params, const TCopyParamType * CopyParam = NULL);
+    int Params, const TCopyParamType * CopyParam = NULL, TCalculateSizeStats * Stats = NULL);
   void __fastcall ClearCaches();
   void __fastcall Synchronize(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, TSynchronizeMode Mode,
     const TCopyParamType * CopyParam, int Params,
-    TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats);
+    TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats,
+    TSynchronizeOptions * Options);
   bool __fastcall DirectoryFileList(const AnsiString Path,
     TRemoteFileList *& FileList, bool CanLoad);
-  void __fastcall MakeLocalFileList(const AnsiString FileName, 
+  void __fastcall MakeLocalFileList(const AnsiString FileName,
     const TSearchRec Rec, void * Param);
   AnsiString __fastcall FileUrl(const AnsiString FileName);
+  bool __fastcall FileOperationLoopQuery(Exception & E,
+    TFileOperationProgressType * OperationProgress, const AnsiString Message,
+    bool AllowSkip, AnsiString SpecialRetry = "");
 
   static bool __fastcall IsAbsolutePath(const AnsiString Path);
   static AnsiString __fastcall ExpandFileName(AnsiString Path,
@@ -361,6 +343,7 @@ public:
   __property TTerminal * CommandSession = { read = GetCommandSession };
   __property bool AutoReadDirectory = { read = FAutoReadDirectory, write = FAutoReadDirectory };
   __property TStrings * FixedPaths = { read = GetFixedPaths };
+  __property bool ResolvingSymlinks = { read = GetResolvingSymlinks };
 };
 //---------------------------------------------------------------------------
 class TSecondaryTerminal : public TTerminal
@@ -409,11 +392,21 @@ struct TCustomCommandParams
   TLogAddLineEvent OutputEvent;
 };
 //---------------------------------------------------------------------------
+struct TCalculateSizeStats
+{
+  TCalculateSizeStats();
+
+  int Files;
+  int Directories;
+  int SymLinks;
+};
+//---------------------------------------------------------------------------
 struct TCalculateSizeParams
 {
   __int64 Size;
   int Params;
   const TCopyParamType * CopyParam;
+  TCalculateSizeStats * Stats;
 };
 //---------------------------------------------------------------------------
 struct TOverwriteFileParams
@@ -442,4 +435,12 @@ struct TSynchronizeStats
   int ObsoleteDirectories;
 };
 //---------------------------------------------------------------------------
+struct TSynchronizeOptions
+{
+  TSynchronizeOptions();
+  ~TSynchronizeOptions();
+
+  TStringList * Filter;
+};
+//---------------------------------------------------------------------------
 #endif

+ 38 - 20
forms/About.cpp

@@ -19,20 +19,31 @@ __fastcall TAboutDialog::TAboutDialog(TComponent* AOwner)
 {
   ThirdPartyBox->VertScrollBar->Position = 0;
   UseSystemSettings(this);
-  LinkLabel(HomepageLabel);
-  LinkLabel(ForumUrlLabel);
-  LinkLabel(PuttyLicenceLabel);
-  LinkLabel(PuttyHomepageLabel);
+  LinkLabel(HomepageLabel, LoadStr(HOMEPAGE_URL));
+  LinkLabel(ForumUrlLabel, LoadStr(FORUM_URL));
+  LinkLabel(PuttyLicenceLabel, "", FirstScrollingControlEnter);
+  LinkLabel(PuttyHomepageLabel, LoadStr(PUTTY_URL));
   LinkLabel(Toolbar2000HomepageLabel);
-  LinkLabel(TBXHomepageLabel);
+  LinkLabel(TBXHomepageLabel, "", LastScrollingControlEnter);
+  ApplicationLabel->ParentFont = true;
+  ApplicationLabel->Font->Style = ApplicationLabel->Font->Style << fsBold;
   ApplicationLabel->Caption = AppName;
-  HomepageLabel->Caption = LoadStr(HOMEPAGE_URL);
-  ForumUrlLabel->Caption = LoadStr(FORUM_URL);
-  PuttyHomepageLabel->Caption = LoadStr(PUTTY_URL);
   PuttyVersionLabel->Caption = FMTLOAD(PUTTY_BASED_ON, (LoadStr(PUTTY_VERSION)));
   PuttyCopyrightLabel->Caption = LoadStr(PUTTY_COPYRIGHT);
   WinSCPCopyrightLabel->Caption = LoadStr(WINSCP_COPYRIGHT);
-  TranslatorLabel->Caption = LoadStr(TRANSLATOR_INFO);
+  AnsiString Translator = LoadStr(TRANSLATOR_INFO);
+  if (Translator.IsEmpty())
+  {
+    TranslatorLabel->Visible = false;
+    TranslatorUrlLabel->Visible = false;
+    ClientHeight = ClientHeight -
+      (TranslatorLabel->Top - ProductSpecificMessageLabel->Top);
+  }
+  else
+  {
+    TranslatorLabel->Caption = LoadStr(TRANSLATOR_INFO);
+    LinkLabel(TranslatorUrlLabel, LoadStr(TRANSLATOR_URL));
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::SetConfiguration(TConfiguration * value)
@@ -46,17 +57,13 @@ void __fastcall TAboutDialog::SetConfiguration(TConfiguration * value)
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::LoadData()
 {
-  VersionLabel->Caption = Configuration->VersionStr;
-}
-//---------------------------------------------------------------------------
-void __fastcall TAboutDialog::HomepageLabelClick(TObject * Sender)
-{
-  OpenBrowser(((TLabel*)Sender)->Caption);
-}
-//---------------------------------------------------------------------------
-void __fastcall TAboutDialog::EmailLabelClick(TObject * Sender)
-{
-  OpenBrowser("mailto:" + ((TLabel*)Sender)->Caption);
+  AnsiString Version = Configuration->VersionStr;
+  if (Configuration->Version != Configuration->ProductVersion)
+  {
+    Version = FMTLOAD(ABOUT_BASED_ON_PRODUCT,
+      (Configuration->ProductName, Configuration->ProductVersion));
+  }
+  VersionLabel->Caption = Version;
 }
 //---------------------------------------------------------------------------
 void __fastcall TAboutDialog::DisplayLicence(TObject * Sender)
@@ -84,4 +91,15 @@ void __fastcall TAboutDialog::HelpButtonClick(TObject * /*Sender*/)
   FormHelp(this);
 }
 //---------------------------------------------------------------------------
+void __fastcall TAboutDialog::FirstScrollingControlEnter(TObject * /*Sender*/)
+{
+  ThirdPartyBox->VertScrollBar->Position = 0;
+}
+//---------------------------------------------------------------------------
+void __fastcall TAboutDialog::LastScrollingControlEnter(TObject * /*Sender*/)
+{
+  ThirdPartyBox->VertScrollBar->Position =
+    ThirdPartyBox->VertScrollBar->Range - ThirdPartyBox->ClientHeight;
+}
+//---------------------------------------------------------------------------
 

+ 183 - 253
forms/About.dfm

@@ -6,7 +6,7 @@ object AboutDialog: TAboutDialog
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'About application'
-  ClientHeight = 334
+  ClientHeight = 372
   ClientWidth = 388
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
@@ -18,29 +18,22 @@ object AboutDialog: TAboutDialog
   Position = poOwnerFormCenter
   DesignSize = (
     388
-    334)
+    372)
   PixelsPerInch = 96
   TextHeight = 13
   object ApplicationLabel: TLabel
     Left = 72
-    Top = 8
-    Width = 105
-    Height = 24
+    Top = 12
+    Width = 52
+    Height = 13
     Caption = 'Application'
-    Font.Charset = DEFAULT_CHARSET
-    Font.Color = clWindowText
-    Font.Height = -20
-    Font.Name = 'Arial'
-    Font.Style = [fsBold]
-    ParentFont = False
   end
   object VersionLabel: TLabel
     Left = 72
-    Top = 40
-    Width = 130
+    Top = 28
+    Width = 126
     Height = 13
-    Caption = 'Version 2.0.0 (Build 12) &&&'
-    ShowAccelChar = False
+    Caption = 'Version 2.0.0 (Build 12) XX'
   end
   object WinSCPCopyrightLabel: TLabel
     Left = 72
@@ -49,190 +42,166 @@ object AboutDialog: TAboutDialog
     Height = 13
     Caption = 'Copyright '#169' 2000-2003 Martin Prikryl'
   end
-  object HomepageLabel: TLabel
-    Left = 72
-    Top = 72
-    Width = 129
-    Height = 13
-    Cursor = crHandPoint
-    Caption = 'http://XXXXXXwinscp.net/'
-    Color = clBtnFace
-    Font.Charset = DEFAULT_CHARSET
-    Font.Color = clBlue
-    Font.Height = -11
-    Font.Name = 'MS Sans Serif'
-    Font.Style = [fsUnderline]
-    ParentColor = False
-    ParentFont = False
-    OnClick = HomepageLabelClick
-  end
   object ProductSpecificMessageLabel: TLabel
     Left = 72
-    Top = 97
+    Top = 100
     Width = 269
     Height = 13
     Caption = 'To send comments and report bugs use support forum at:'
   end
-  object ForumUrlLabel: TLabel
-    Left = 72
-    Top = 112
-    Width = 146
-    Height = 13
-    Cursor = crHandPoint
-    Caption = 'http://XXXXwinscp.net/forum/'
-    Font.Charset = DEFAULT_CHARSET
-    Font.Color = clBlue
-    Font.Height = -11
-    Font.Name = 'MS Sans Serif'
-    Font.Style = [fsUnderline]
-    ParentFont = False
-    OnClick = HomepageLabelClick
-  end
   object TranslatorLabel: TLabel
     Left = 72
-    Top = 137
+    Top = 144
     Width = 73
     Height = 13
     Caption = 'TranslatorLabel'
   end
-  object ImagePanel: TPanel
+  object Image: TImage
     Left = 8
-    Top = 8
+    Top = 12
     Width = 49
-    Height = 318
-    Anchors = [akLeft, akTop, akBottom]
-    BevelOuter = bvNone
-    Color = clTeal
+    Height = 32
+    Center = True
+    Picture.Data = {
+      055449636F6E0000010002002020000100000000A80800002600000020201000
+      00000000E8020000CE0800002800000020000000400000000100080000000000
+      0004000000000000000000000001000000000000000000000000800000800000
+      00808000800000008000800080800000C0C0C000C0DCC000F0CAA600F0FBFF00
+      A4A0A000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000
+      FFFFFF0060C0EE00582000000000000000000000000000000000000001000100
+      0000000068E0EE00E0DFEE0001000000000000000000000078544C0060C0EE00
+      2020000000000000A0D7EE0048E0EE00C848EC0000000000000000002000CC00
+      00000000D01F4200A8DFEE00AC1F4200A8DFEE00000000000000000000000000
+      0000000000000000000000000000000000000000A8DFEE002A07000056070000
+      78544C0060C0EE00B81F000008E14100E0DFEE00F014EC0018E0EE0004000000
+      78544C0060C0EE00981F00000000000000000000000000000000000000000000
+      20000000200000001000000001000400D490E300280000002000000020000000
+      0100040000000000000200000000000000000000100000000000000000000000
+      00000000000000000000000000000000000000000000000078544C0060C0EE00
+      201F000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000088E1EE00
+      000000000000000000010000010000000000000038E1EE0038E1EE0098010000
+      A86B430000E0EE00B80FEC000000000008000000600000000000000060E1EE00
+      60E1EE007001000000000000000000000000000000000000D478430000E0EE00
+      84E1EE0084E1EE004C01000000000000F0E1EE0018E2EE005CDFEE0000000000
+      000000002000CC00000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000E0EE00
+      0000000000000000ECE1EE00ECE1EE00E4000000E8E0410088E1EE00B80FEC00
+      C0E1EE000800000060000000280000002300000064B841000000000008E14100
+      88E1EE00F014EC00C0E1EE000400000034E2EE0034E2EE009C000000F017EC00
+      6874000048E2EE0048E2EE008800000000000000000000000000000000000000
+      D4784300649EEE006CE2EE006CE2EE00640000000000000000000000301BEC00
+      0000000088E2EE0088E2EE0048000000E8E0410070A0EE00B80FEC00A8A0EE00
+      0800000060000000280000002300000064B841000000000008E1410070A0EE00
+      F014EC00A8A0EE00700C00001F00000090E746000000000000000000F0E2EE00
+      0000000000000000ECE2EE00ECE2EE005C060000000000000000000018000000
+      04E3EE0004E3EE00440600004C616200140000003F0100001873420000000000
+      0000000000000000000000000000000000000000000000000800010000000000
+      807B43001CE3EE000800000040000000200000004E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4EEE4E4E4E4E4E4E4E4E4E4E014E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E0101030303030303030303C40C4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E0103030303030303030303D001D04E4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E01C4C4C4C4C4C4C4C4C4C42503014E4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E01C490EBEB90FC262614D0ABC4010C4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E014E010101010101010101012503EE4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4ED001030101010101010101010103D0014E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E0C0103C4D0D0D0252525C403C40101014E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4EEE01010C0C0C0C0C0CC4ABC4C4034E634E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E0101EE0B0B0C0CEEEE23C4D0C4C4010C4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E01010B07070B0C0CEEEEC4D0C4C4010C4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E4E4E4EE5070B0CEEEEC4D0C4C4010C4E4E4E4E4E4E4E4E4E4EEEEEEE
+      EEEEEEEEEEEE4E4E1D070CEEEEC4C4D0C4010C4E4E4E4E4E4E4E4E4E4EEE4E7C
+      7C7C7C7C7CEE4E07070BEEEEEEC4C4D0C401EE4E4E4E0B63232323234EEE4E0F
+      EEEEEE0F7CEE4E0101010101010303D0C401EE4E4E4EEE7B898989894EEE4E0F
+      0FEE0F0F7CEE4EC4C4C4C4C4C4C4C425C401EE4E4E4EEE89898989894EEE4E0F
+      EEEEEE0F7CEE4EBCFCEB90B782722D1DD001EE4E4E4EEE0B090909094EEE4E0F
+      0FEE0F0F7CEE4ED0D0D0D0D0D0D0D0D01DC4EE4E4E4EEE63212121154EEE4E4E
+      4E4E4E4E4EEE4ED0D0C4D0D0D0D0D0D0D025EE4E4E4E217B7B7B7B894EEEEEEE
+      EEEEEEEEEEEE4E0101010101010101010101EE4E4E4E217B0B0B0B0B0B4EEE4E
+      214E4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0B15210B0C0CEE234EEE4E
+      21EE4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0C150B08070B0CEE4EEE4E
+      7BEE4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E21150713E5070CEE0C4EEE
+      4E4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E21210BE51D070CEE21894E
+      EEEEEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E2189212121212121218909
+      4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E21090909090909090909A5
+      7B214E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0C0C8F090909098F8F8FE2
+      07154E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0C0C0909090909090909
+      E20C4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4EEEEEEEEEEEEEEEEE15
+      21EE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
+      4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4EFFFFFFFFFFFF000FFFFF0007
+      FFFF0003FFFF0003FFFF0001FFFF0001FFFE0001FFFE0001FFFE0001FFFE0001
+      FFFE0001FFFFE001FF803001FFA02001C0A02001C0A02001C0A02001C0A02001
+      C0BFA001C0802001C0537FFF80537FFF80537FFF802EFFFF8011FFFF800FFFFF
+      8003FFFF8003FFFFC003FFFFE003FFFFFFFFFFFF280000002000000040000000
+      0100040000000000000200000000000000000000100000000000000000000000
+      00008000008000000080800080000000800080008080000080808000C0C0C000
+      0000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF0000000000
+      0000000000000000000000000000000000000000711111111111000000000000
+      0000000011111711171870000000000000000000177777777778180000000000
+      000000001777777777787100000000000000000018BBBBBBBB88717000000000
+      0000000011111111111187100000000000000007111111111111181000000000
+      0000000717888888887811100000000000000001117777777888111000000000
+      0000000117777774188871700000000000000001178887771888717000000000
+      0000000000088777488871700000000004747474770888777878717000000000
+      070BBBBBB70887771778717000711111070B477BB40111111178717000777777
+      040BB7BBB70888888888717000777877070B777BB70BBBBBB888811000778888
+      070BB7BBB7088888888888100071444407000000070888888888887000477777
+      0777777774011111111111700047788886704100700000000000000007477777
+      4640740040000000000000000748887746707400700000000000000007488887
+      4767000700000000000000000778888777767470000000000000000004777774
+      7778000000000000000000000488888888887400000000000000000007788888
+      8888840000000000000000000077888888888700000000000000000000074774
+      44444700000000000000000000000000000000000000000000000000FFFFFFFF
+      FFFF000FFFFF0007FFFF0003FFFF0003FFFF0001FFFF0001FFFE0001FFFE0001
+      FFFE0001FFFE0001FFFE0001FFFFE001FF802001FFA02001C0A02001C0A02001
+      C0A02001C0A02001C0BFA001C0802001C0137FFF80137FFF80137FFF800EFFFF
+      8001FFFF800FFFFF8003FFFF8003FFFFC003FFFFE003FFFFFFFFFFFF}
+    Transparent = True
+  end
+  object HomepageLabel: TStaticText
+    Left = 72
+    Top = 72
+    Width = 133
+    Height = 17
+    Caption = 'http://XXXXXXwinscp.net/'
+    TabOrder = 2
+    TabStop = True
+  end
+  object ForumUrlLabel: TStaticText
+    Left = 72
+    Top = 116
+    Width = 150
+    Height = 17
+    Caption = 'http://XXXXwinscp.net/forum/'
     TabOrder = 3
-    DesignSize = (
-      49
-      318)
-    object ImageShape: TShape
-      Left = 0
-      Top = 0
-      Width = 49
-      Height = 318
-      Align = alClient
-      Brush.Style = bsClear
-      Pen.Width = 0
-    end
-    object Image: TImage
-      Left = 0
-      Top = 0
-      Width = 49
-      Height = 65
-      Anchors = [akLeft, akTop, akRight]
-      Center = True
-      Picture.Data = {
-        055449636F6E0000010002002020000100000000A80800002600000020201000
-        00000000E8020000CE0800002800000020000000400000000100080000000000
-        0004000000000000000000000001000000000000000000000000800000800000
-        00808000800000008000800080800000C0C0C000C0DCC000F0CAA600F0FBFF00
-        A4A0A000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000
-        FFFFFF0060C0EE00582000000000000000000000000000000000000001000100
-        0000000068E0EE00E0DFEE0001000000000000000000000078544C0060C0EE00
-        2020000000000000A0D7EE0048E0EE00C848EC0000000000000000002000CC00
-        00000000D01F4200A8DFEE00AC1F4200A8DFEE00000000000000000000000000
-        0000000000000000000000000000000000000000A8DFEE002A07000056070000
-        78544C0060C0EE00B81F000008E14100E0DFEE00F014EC0018E0EE0004000000
-        78544C0060C0EE00981F00000000000000000000000000000000000000000000
-        20000000200000001000000001000400D490E300280000002000000020000000
-        0100040000000000000200000000000000000000100000000000000000000000
-        00000000000000000000000000000000000000000000000078544C0060C0EE00
-        201F000000000000000000000000000000000000000000000000000000000000
-        0000000000000000000000000000000000000000000000000000000088E1EE00
-        000000000000000000010000010000000000000038E1EE0038E1EE0098010000
-        A86B430000E0EE00B80FEC000000000008000000600000000000000060E1EE00
-        60E1EE007001000000000000000000000000000000000000D478430000E0EE00
-        84E1EE0084E1EE004C01000000000000F0E1EE0018E2EE005CDFEE0000000000
-        000000002000CC00000000000000000000000000000000000000000000000000
-        0000000000000000000000000000000000000000000000000000000000E0EE00
-        0000000000000000ECE1EE00ECE1EE00E4000000E8E0410088E1EE00B80FEC00
-        C0E1EE000800000060000000280000002300000064B841000000000008E14100
-        88E1EE00F014EC00C0E1EE000400000034E2EE0034E2EE009C000000F017EC00
-        6874000048E2EE0048E2EE008800000000000000000000000000000000000000
-        D4784300649EEE006CE2EE006CE2EE00640000000000000000000000301BEC00
-        0000000088E2EE0088E2EE0048000000E8E0410070A0EE00B80FEC00A8A0EE00
-        0800000060000000280000002300000064B841000000000008E1410070A0EE00
-        F014EC00A8A0EE00700C00001F00000090E746000000000000000000F0E2EE00
-        0000000000000000ECE2EE00ECE2EE005C060000000000000000000018000000
-        04E3EE0004E3EE00440600004C616200140000003F0100001873420000000000
-        0000000000000000000000000000000000000000000000000800010000000000
-        807B43001CE3EE000800000040000000200000004E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4EEE4E4E4E4E4E4E4E4E4E4E014E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E0101030303030303030303C40C4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E0103030303030303030303D001D04E4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E01C4C4C4C4C4C4C4C4C4C42503014E4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E01C490EBEB90FC262614D0ABC4010C4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E014E010101010101010101012503EE4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4ED001030101010101010101010103D0014E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E0C0103C4D0D0D0252525C403C40101014E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4EEE01010C0C0C0C0C0CC4ABC4C4034E634E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E0101EE0B0B0C0CEEEE23C4D0C4C4010C4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E01010B07070B0C0CEEEEC4D0C4C4010C4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E4E4E4EE5070B0CEEEEC4D0C4C4010C4E4E4E4E4E4E4E4E4E4EEEEEEE
-        EEEEEEEEEEEE4E4E1D070CEEEEC4C4D0C4010C4E4E4E4E4E4E4E4E4E4EEE4E7C
-        7C7C7C7C7CEE4E07070BEEEEEEC4C4D0C401EE4E4E4E0B63232323234EEE4E0F
-        EEEEEE0F7CEE4E0101010101010303D0C401EE4E4E4EEE7B898989894EEE4E0F
-        0FEE0F0F7CEE4EC4C4C4C4C4C4C4C425C401EE4E4E4EEE89898989894EEE4E0F
-        EEEEEE0F7CEE4EBCFCEB90B782722D1DD001EE4E4E4EEE0B090909094EEE4E0F
-        0FEE0F0F7CEE4ED0D0D0D0D0D0D0D0D01DC4EE4E4E4EEE63212121154EEE4E4E
-        4E4E4E4E4EEE4ED0D0C4D0D0D0D0D0D0D025EE4E4E4E217B7B7B7B894EEEEEEE
-        EEEEEEEEEEEE4E0101010101010101010101EE4E4E4E217B0B0B0B0B0B4EEE4E
-        214E4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0B15210B0C0CEE234EEE4E
-        21EE4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0C150B08070B0CEE4EEE4E
-        7BEE4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E21150713E5070CEE0C4EEE
-        4E4E4EEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E21210BE51D070CEE21894E
-        EEEEEE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E2189212121212121218909
-        4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E21090909090909090909A5
-        7B214E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0C0C8F090909098F8F8FE2
-        07154E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E0C0C0909090909090909
-        E20C4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4EEEEEEEEEEEEEEEEE15
-        21EE4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E
-        4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4EFFFFFFFFFFFF000FFFFF0007
-        FFFF0003FFFF0003FFFF0001FFFF0001FFFE0001FFFE0001FFFE0001FFFE0001
-        FFFE0001FFFFE001FF803001FFA02001C0A02001C0A02001C0A02001C0A02001
-        C0BFA001C0802001C0537FFF80537FFF80537FFF802EFFFF8011FFFF800FFFFF
-        8003FFFF8003FFFFC003FFFFE003FFFFFFFFFFFF280000002000000040000000
-        0100040000000000000200000000000000000000100000000000000000000000
-        00008000008000000080800080000000800080008080000080808000C0C0C000
-        0000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF0000000000
-        0000000000000000000000000000000000000000711111111111000000000000
-        0000000011111711171870000000000000000000177777777778180000000000
-        000000001777777777787100000000000000000018BBBBBBBB88717000000000
-        0000000011111111111187100000000000000007111111111111181000000000
-        0000000717888888887811100000000000000001117777777888111000000000
-        0000000117777774188871700000000000000001178887771888717000000000
-        0000000000088777488871700000000004747474770888777878717000000000
-        070BBBBBB70887771778717000711111070B477BB40111111178717000777777
-        040BB7BBB70888888888717000777877070B777BB70BBBBBB888811000778888
-        070BB7BBB7088888888888100071444407000000070888888888887000477777
-        0777777774011111111111700047788886704100700000000000000007477777
-        4640740040000000000000000748887746707400700000000000000007488887
-        4767000700000000000000000778888777767470000000000000000004777774
-        7778000000000000000000000488888888887400000000000000000007788888
-        8888840000000000000000000077888888888700000000000000000000074774
-        44444700000000000000000000000000000000000000000000000000FFFFFFFF
-        FFFF000FFFFF0007FFFF0003FFFF0003FFFF0001FFFF0001FFFE0001FFFE0001
-        FFFE0001FFFE0001FFFE0001FFFFE001FF802001FFA02001C0A02001C0A02001
-        C0A02001C0A02001C0BFA001C0802001C0137FFF80137FFF80137FFF800EFFFF
-        8001FFFF800FFFFF8003FFFF8003FFFFC003FFFFE003FFFFFFFFFFFF}
-      Transparent = True
-    end
+    TabStop = True
+  end
+  object TranslatorUrlLabel: TStaticText
+    Left = 72
+    Top = 160
+    Width = 150
+    Height = 17
+    Caption = 'http://XXXXwinscp.net/forum/'
+    TabOrder = 4
+    TabStop = True
   end
   object ThirdPartyBox: TScrollBox
     Left = 72
-    Top = 155
+    Top = 185
     Width = 306
-    Height = 141
+    Height = 145
+    HorzScrollBar.Range = 289
     HorzScrollBar.Visible = False
+    VertScrollBar.Range = 309
     VertScrollBar.Smooth = True
+    VertScrollBar.Tracking = True
     Anchors = [akLeft, akRight, akBottom]
-    TabOrder = 4
+    AutoScroll = False
+    TabOrder = 5
     DesignSize = (
       285
-      137)
+      141)
     object Label3: TLabel
       Left = 8
       Top = 8
@@ -266,23 +235,6 @@ object AboutDialog: TAboutDialog
       Height = 13
       Caption = 'Copyright '#169' xxx Simon Tatham'
     end
-    object PuttyHomepageLabel: TLabel
-      Left = 8
-      Top = 120
-      Width = 277
-      Height = 13
-      Cursor = crHandPoint
-      Caption = 'http://XXXwww.chiark.greenend.org.uk/~sgtatham/putty/'
-      Color = clBtnFace
-      Font.Charset = DEFAULT_CHARSET
-      Font.Color = clBlue
-      Font.Height = -11
-      Font.Name = 'MS Sans Serif'
-      Font.Style = [fsUnderline]
-      ParentColor = False
-      ParentFont = False
-      OnClick = HomepageLabelClick
-    end
     object Label8: TLabel
       Left = 8
       Top = 264
@@ -297,32 +249,6 @@ object AboutDialog: TAboutDialog
       Height = 13
       Caption = 'Copyright '#169' 1999 Ingo Eckel'
     end
-    object ProlongBoxLabel: TLabel
-      Left = 8
-      Top = 296
-      Width = 63
-      Height = 13
-      Caption = '                     '
-      Transparent = True
-    end
-    object PuttyLicenceLabel: TLabel
-      Tag = 1
-      Left = 8
-      Top = 104
-      Width = 71
-      Height = 13
-      Cursor = crHandPoint
-      Caption = 'Display licence'
-      Color = clBtnFace
-      Font.Charset = DEFAULT_CHARSET
-      Font.Color = clBlue
-      Font.Height = -11
-      Font.Name = 'MS Sans Serif'
-      Font.Style = [fsUnderline]
-      ParentColor = False
-      ParentFont = False
-      OnClick = DisplayLicence
-    end
     object Label1: TLabel
       Left = 8
       Top = 152
@@ -337,23 +263,6 @@ object AboutDialog: TAboutDialog
       Height = 13
       Caption = 'Copyright '#169' 1998-2005 Jordan Russell'
     end
-    object Toolbar2000HomepageLabel: TLabel
-      Left = 8
-      Top = 184
-      Width = 176
-      Height = 13
-      Cursor = crHandPoint
-      Caption = 'http://www.jrsoftware.org/tb2kdl.php'
-      Color = clBtnFace
-      Font.Charset = DEFAULT_CHARSET
-      Font.Color = clBlue
-      Font.Height = -11
-      Font.Name = 'MS Sans Serif'
-      Font.Style = [fsUnderline]
-      ParentColor = False
-      ParentFont = False
-      OnClick = HomepageLabelClick
-    end
     object Label5: TLabel
       Left = 8
       Top = 208
@@ -368,27 +277,48 @@ object AboutDialog: TAboutDialog
       Height = 13
       Caption = 'Copyright '#169' 2001-2005 Alex A. Denisov'
     end
-    object TBXHomepageLabel: TLabel
+    object PuttyLicenceLabel: TStaticText
+      Tag = 1
+      Left = 8
+      Top = 104
+      Width = 75
+      Height = 17
+      Caption = 'Display licence'
+      TabOrder = 0
+      TabStop = True
+      OnClick = DisplayLicence
+    end
+    object PuttyHomepageLabel: TStaticText
+      Left = 8
+      Top = 120
+      Width = 281
+      Height = 17
+      Caption = 'http://XXXwww.chiark.greenend.org.uk/~sgtatham/putty/'
+      TabOrder = 1
+      TabStop = True
+    end
+    object Toolbar2000HomepageLabel: TStaticText
+      Left = 8
+      Top = 184
+      Width = 180
+      Height = 17
+      Caption = 'http://www.jrsoftware.org/tb2kdl.php'
+      TabOrder = 2
+      TabStop = True
+    end
+    object TBXHomepageLabel: TStaticText
       Left = 8
       Top = 240
-      Width = 118
-      Height = 13
-      Cursor = crHandPoint
+      Width = 122
+      Height = 17
       Caption = 'http://www.g32.org/tbx/'
-      Color = clBtnFace
-      Font.Charset = DEFAULT_CHARSET
-      Font.Color = clBlue
-      Font.Height = -11
-      Font.Name = 'MS Sans Serif'
-      Font.Style = [fsUnderline]
-      ParentColor = False
-      ParentFont = False
-      OnClick = HomepageLabelClick
+      TabOrder = 3
+      TabStop = True
     end
   end
   object OKButton: TButton
     Left = 221
-    Top = 303
+    Top = 339
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
@@ -400,17 +330,17 @@ object AboutDialog: TAboutDialog
   end
   object LicenceButton: TButton
     Left = 72
-    Top = 303
+    Top = 339
     Width = 75
     Height = 25
-    Anchors = [akRight, akBottom]
+    Anchors = [akLeft, akBottom]
     Caption = '&Licence...'
-    TabOrder = 2
+    TabOrder = 6
     OnClick = LicenceButtonClick
   end
   object HelpButton: TButton
     Left = 303
-    Top = 303
+    Top = 339
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]

+ 10 - 12
forms/About.h

@@ -18,37 +18,33 @@
 class TAboutDialog : public TForm
 {
 __published:
-  TPanel *ImagePanel;
-  TShape *ImageShape;
-  TImage *Image;
   TLabel *ApplicationLabel;
   TLabel *VersionLabel;
   TLabel *WinSCPCopyrightLabel;
-  TLabel *HomepageLabel;
+  TStaticText *HomepageLabel;
   TLabel *ProductSpecificMessageLabel;
-  TLabel *ForumUrlLabel;
+  TStaticText *ForumUrlLabel;
   TScrollBox *ThirdPartyBox;
   TLabel *Label3;
   TLabel *PuttyVersionLabel;
   TLabel *PuttyCopyrightLabel;
-  TLabel *PuttyHomepageLabel;
+  TStaticText *PuttyHomepageLabel;
   TLabel *Label7;
   TLabel *Label8;
   TLabel *Label10;
-  TLabel *ProlongBoxLabel;
   TButton *OKButton;
   TButton *LicenceButton;
-  TLabel *PuttyLicenceLabel;
+  TStaticText *PuttyLicenceLabel;
   TLabel *TranslatorLabel;
   TLabel *Label1;
   TLabel *Label2;
-  TLabel *Toolbar2000HomepageLabel;
+  TStaticText *Toolbar2000HomepageLabel;
   TLabel *Label5;
   TLabel *Label6;
-  TLabel *TBXHomepageLabel;
+  TStaticText *TBXHomepageLabel;
   TButton *HelpButton;
-  void __fastcall HomepageLabelClick(TObject *Sender);
-  void __fastcall EmailLabelClick(TObject *Sender);
+  TImage *Image;
+  TStaticText *TranslatorUrlLabel;
   void __fastcall DisplayLicence(TObject *Sender);
   void __fastcall LicenceButtonClick(TObject *Sender);
   bool __fastcall GetAllowLicence();
@@ -57,6 +53,8 @@ private:
   TConfiguration * FConfiguration;
   void __fastcall SetConfiguration(TConfiguration * value);
   void __fastcall SetAllowLicence(bool value);
+  void __fastcall FirstScrollingControlEnter(TObject * Sender);
+  void __fastcall LastScrollingControlEnter(TObject * Sender);
 public:
   virtual __fastcall TAboutDialog(TComponent* AOwner);
   void __fastcall LoadData();

+ 54 - 0
forms/Banner.cpp

@@ -0,0 +1,54 @@
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include <Common.h>
+
+#include <VCLCommon.h>
+#include "WinInterface.h"
+#include "Banner.h"
+//---------------------------------------------------------------------------
+#pragma package(smart_init)
+#pragma resource "*.dfm"
+//---------------------------------------------------------------------------
+void __fastcall DoBannerDialog(AnsiString SessionName, const AnsiString & Banner,
+  bool & NeverShowAgain)
+{
+  TBannerDialog * BannerDialog = NULL;
+  try
+  {
+    BannerDialog = new TBannerDialog(Application, SessionName, Banner);
+    BannerDialog->Execute(NeverShowAgain);
+  }
+  __finally
+  {
+    delete BannerDialog;
+  }
+}
+//---------------------------------------------------------------------------
+__fastcall TBannerDialog::TBannerDialog(TComponent * Owner,
+  AnsiString SessionName, const AnsiString & Banner)
+  : TForm(Owner)
+{
+  UseSystemSettings(this);
+  Caption = FORMAT("%s - %s", (Caption, SessionName));
+  BannerMemo->Lines->Text = Banner;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TBannerDialog::Execute(bool & NeverShowAgain)
+{
+  NeverShowAgainCheck->Checked = NeverShowAgain;
+  bool Result = (ShowModal() == mrOk);
+  if (Result)
+  {
+    NeverShowAgain = NeverShowAgainCheck->Checked;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TBannerDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
+

+ 64 - 0
forms/Banner.dfm

@@ -0,0 +1,64 @@
+object BannerDialog: TBannerDialog
+  Left = 413
+  Top = 230
+  HelpType = htKeyword
+  HelpKeyword = 'ui_banner'
+  ActiveControl = CloseButton
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsDialog
+  Caption = 'Authentication Banner'
+  ClientHeight = 291
+  ClientWidth = 440
+  Color = clBtnFace
+  ParentFont = True
+  OldCreateOrder = True
+  Position = poOwnerFormCenter
+  DesignSize = (
+    440
+    291)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object CloseButton: TButton
+    Left = 273
+    Top = 256
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Cancel = True
+    Caption = 'Close'
+    Default = True
+    ModalResult = 1
+    TabOrder = 0
+  end
+  object BannerMemo: TMemo
+    Left = 8
+    Top = 8
+    Width = 425
+    Height = 244
+    Anchors = [akLeft, akTop, akRight, akBottom]
+    Color = clBtnFace
+    ReadOnly = True
+    ScrollBars = ssVertical
+    TabOrder = 2
+    WantReturns = False
+  end
+  object NeverShowAgainCheck: TCheckBox
+    Left = 15
+    Top = 262
+    Width = 250
+    Height = 17
+    Anchors = [akLeft, akRight, akBottom]
+    Caption = '&Never show this banner again'
+    TabOrder = 3
+  end
+  object HelpButton: TButton
+    Left = 357
+    Top = 256
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 1
+    OnClick = HelpButtonClick
+  end
+end

+ 28 - 0
forms/Banner.h

@@ -0,0 +1,28 @@
+//---------------------------------------------------------------------------
+#ifndef BannerH
+#define BannerH
+//---------------------------------------------------------------------------
+#include <Classes.hpp>
+#include <Controls.hpp>
+#include <StdCtrls.hpp>
+#include <Forms.hpp>
+#include <Mask.hpp>
+
+#include "WinInterface.h"
+//---------------------------------------------------------------------------
+class TBannerDialog : public TForm
+{
+__published:
+  TButton *CloseButton;
+  TMemo *BannerMemo;
+  TCheckBox *NeverShowAgainCheck;
+  TButton *HelpButton;
+  void __fastcall HelpButtonClick(TObject *Sender);
+
+public:
+  __fastcall TBannerDialog(TComponent * Owner, AnsiString SessionName,
+    const AnsiString & Banner);
+  bool __fastcall Execute(bool & NeverShowAgain);
+};
+//---------------------------------------------------------------------------
+#endif

+ 9 - 33
forms/Console.cpp

@@ -9,7 +9,6 @@
 
 #include <TextsWin.h>
 #include <Interface.h>
-#include <ScpFileSystem.h>
 #include <ScpMain.h>
 
 #include <VCLCommon.h>
@@ -38,10 +37,8 @@ __fastcall TConsoleDialog::TConsoleDialog(TComponent* AOwner)
     : TForm(AOwner)
 {
   FTerminal = NULL;
-  FOldLogAddLine = NULL;
   FOldChangeDirectory = NULL;
   FLastTerminal = NULL;
-  FAddOutput = false;
   OutputMemo->Color = clBlack;
   OutputMemo->Font->Color = (TColor)0x00BBBBBB; //clGray;
   UseSystemSettings(this);
@@ -67,10 +64,7 @@ void __fastcall TConsoleDialog::SetTerminal(TTerminal * value)
     {
       assert(FTerminal->OnChangeDirectory == DoChangeDirectory);
       FTerminal->OnChangeDirectory = FOldChangeDirectory;
-      assert(FTerminal->Log->OnAddLine == DoLogAddLine);
-      FTerminal->Log->OnAddLine = FOldLogAddLine;
       FOldChangeDirectory = NULL;
-      FOldLogAddLine = NULL;
       FTerminal->EndTransaction();
     }
     FTerminal = value;
@@ -79,8 +73,6 @@ void __fastcall TConsoleDialog::SetTerminal(TTerminal * value)
       OutputMemo->Clear();
       FOldChangeDirectory = FTerminal->OnChangeDirectory;
       FTerminal->OnChangeDirectory = DoChangeDirectory;
-      FOldLogAddLine = FTerminal->Log->OnAddLine;
-      FTerminal->Log->OnAddLine = DoLogAddLine;
       // avoid reloading directory after each change of current directory from console
       FTerminal->BeginTransaction();
       FLastTerminal = FTerminal;
@@ -115,15 +107,7 @@ bool __fastcall TConsoleDialog::Execute(const AnsiString Command,
   
   try
   {
-    TStrings * CommandsHistory = CustomWinConfiguration->History["Commands"];
-    if ((CommandsHistory != NULL) && (CommandsHistory->Count > 0))
-    {
-      CommandEdit->Items = CommandsHistory;
-    }
-    else
-    {
-      CommandEdit->Items->Clear();
-    }
+    CommandEdit->Items = CustomWinConfiguration->History["Commands"];
 
     if (Log != NULL)
     {
@@ -155,6 +139,7 @@ bool __fastcall TConsoleDialog::Execute(const AnsiString Command,
     {
       assert(FTerminal->OnClose == TerminalClose);
       FTerminal->OnClose = FPrevTerminalClose;
+      CommandEdit->SaveToHistory();
       CustomWinConfiguration->History["Commands"] = CommandEdit->Items;
     }
   }
@@ -183,13 +168,11 @@ void __fastcall TConsoleDialog::DoExecuteCommand()
   try
   {
     AnsiString Command = CommandEdit->Text;
-    OutputMemo->Lines->Add(FORMAT("$ %s", ((Command))));
-    FAddOutput = true;
-    FTerminal->AnyCommand(Command);
+    OutputMemo->Lines->Add(FORMAT("%s$ %s", (FTerminal->CurrentDirectory, Command)));
+    FTerminal->AnyCommand(Command, DoLogAddLine);
   }
   __finally
   {
-    FAddOutput = false;
     if (FTerminal)
     {
       FTerminal->ExceptionOnFail = false;
@@ -222,22 +205,15 @@ void __fastcall TConsoleDialog::CommandEditChange(TObject * /*Sender*/)
 void __fastcall TConsoleDialog::DoLogAddLine(TObject* /*Sender*/,
   TLogLineType Type, const AnsiString AddedLine)
 {
-  if (FAddOutput)
-  {
-    AddLine(Type, AddedLine);
-  }
+  AddLine(Type, AddedLine);
 }
 //---------------------------------------------------------------------------
-void __fastcall TConsoleDialog::AddLine(TLogLineType Type, AnsiString Line)
+void __fastcall TConsoleDialog::AddLine(TLogLineType Type, const AnsiString & Line)
 {
-  if (!Line.IsEmpty() && (Type == llOutput || Type == llStdError))
+  assert((Type == llOutput) || (Type == llStdError));
+  if (!Line.IsEmpty())
   {
-    int ReturnCode;
-    if (!TSCPFileSystem::RemoveLastLine(Line, ReturnCode) ||
-        !Line.IsEmpty())
-    {
-      OutputMemo->Lines->Add(Line);
-    }
+    OutputMemo->Lines->Add(Line);
   }
 }
 //---------------------------------------------------------------------------

+ 3 - 3
forms/Console.dfm

@@ -15,7 +15,7 @@ object ConsoleDialog: TConsoleDialog
   Position = poMainFormCenter
   DesignSize = (
     559
-    397)
+    404)
   PixelsPerInch = 96
   TextHeight = 13
   object Bevel1: TBevel
@@ -63,7 +63,7 @@ object ConsoleDialog: TConsoleDialog
     Left = 0
     Top = 78
     Width = 559
-    Height = 319
+    Height = 326
     TabStop = False
     Align = alClient
     Color = clBtnFace
@@ -111,7 +111,7 @@ object ConsoleDialog: TConsoleDialog
     Top = 42
     Width = 75
     Height = 25
-    Anchors = [akRight, akBottom]
+    Anchors = [akTop, akRight]
     Caption = '&Help'
     TabOrder = 3
     OnClick = HelpButtonClick

+ 1 - 3
forms/Console.h

@@ -39,15 +39,13 @@ private:
   TTerminal * FTerminal;
   TTerminal * FLastTerminal;
   TNotifyEvent FOldChangeDirectory;
-  TLogAddLineEvent FOldLogAddLine;
-  bool FAddOutput;
   TNotifyEvent FPrevTerminalClose;
   
   void __fastcall DoExecuteCommand();
   void __fastcall ExecuteCommand();
   void __fastcall SetTerminal(TTerminal * value);
   void __fastcall TerminalClose(TObject * Sender);
-  void __fastcall AddLine(TLogLineType Type, AnsiString Line);
+  inline void __fastcall AddLine(TLogLineType Type, const AnsiString & Line);
 
 protected:
   void __fastcall DoChangeDirectory(TObject * Sender);

+ 122 - 48
forms/Copy.cpp

@@ -34,14 +34,14 @@ bool __fastcall DoCopyDialog(bool ToRemote,
     }
     CopyDialog->ToRemote = ToRemote;
     CopyDialog->Options = Options;
-    CopyDialog->Directory = TargetDirectory;
-    CopyDialog->FileList = FileList;
-    CopyDialog->Params = *Params;
-    CopyDialog->Move = Move;
     if (OutputOptions != NULL)
     {
       CopyDialog->OutputOptions = *OutputOptions;
     }
+    CopyDialog->Directory = TargetDirectory;
+    CopyDialog->FileList = FileList;
+    CopyDialog->Params = *Params;
+    CopyDialog->Move = Move;
     Result = CopyDialog->Execute();
     if (Result)
     {
@@ -81,53 +81,70 @@ __fastcall TCopyDialog::~TCopyDialog()
   delete FPresetsMenu;
 }
 //---------------------------------------------------------------------------
-void __fastcall TCopyDialog::AdjustControls()
+void __fastcall TCopyDialog::AdjustTransferControls()
 {
-  if (FLAGSET(Options, coDoNotShowAgain))
-  {
-    SaveSettingsCheck->Caption = LoadStr(NEVER_SHOW_DIALOG_AGAIN);
-  }
-  RemoteDirectoryEdit->Visible = false;
-  LocalDirectoryEdit->Visible = false;
-  DirectoryEdit->Visible = FLAGCLEAR(Options, coTemp);
-  EnableControl(DirectoryEdit, FLAGCLEAR(Options, coDisableDirectory));
-  EnableControl(DirectoryLabel, DirectoryEdit->Enabled);
-  EnableControl(LocalDirectoryBrowseButton, DirectoryEdit->Enabled);
-  DirectoryLabel->FocusControl = DirectoryEdit;
-  CopyParamsFrame->Direction = !ToRemote ? pdToLocal : pdToRemote;
-  CopyParamsFrame->Options =
-    FLAGMASK(FLAGCLEAR(Options, coDisableTransferMode), cfAllowTransferMode) |
-    FLAGMASK(!Move, cfAllowExcludeMask) |
-    FLAGMASK(!Move && ToRemote, cfAllowClearArchive);
-  EnableControl(NewerOnlyCheck, FLAGCLEAR(Options, coDisableNewerOnly));
-
   if (FileList && FileList->Count)
   {
-    AnsiString TransferStr = LoadStr(!Move ? COPY_COPY : COPY_MOVE);
-    // currently the copy dialog is shown when downloading to temp folder
-    // only for drag&drop downloads, for we dare to display d&d specific prompt
-    AnsiString DirectionStr =
-      LoadStr(((Options & coTemp) != 0) ? COPY_TODROP :
-        (ToRemote ? COPY_TOREMOTE : COPY_TOLOCAL));
-
-    if (FileList->Count == 1)
+    if (!ToRemote && !Move && FLAGSET(FOutputOptions, cooRemoteTransfer))
     {
-      AnsiString FileName;
-      if (!ToRemote) FileName = UnixExtractFileName(FFileList->Strings[0]);
-        else FileName = ExtractFileName(FFileList->Strings[0]);
-      DirectoryLabel->Caption = FMTLOAD(COPY_FILE,
-        (TransferStr, FileName, DirectionStr));
+      AnsiString Label;
+      if (FileList->Count == 1)
+      {
+        AnsiString FileName;
+        if (!ToRemote) FileName = UnixExtractFileName(FFileList->Strings[0]);
+          else FileName = ExtractFileName(FFileList->Strings[0]);
+        Label = FMTLOAD(REMOTE_COPY_FILE, (FileName));
+      }
+      else
+      {
+        Label = FMTLOAD(REMOTE_COPY_FILES, (FFileList->Count));
+      }
+
+      // hack to remove trailing colon used for "duplicate" prompt
+      if (!Label.IsEmpty() &&
+          (Label.ByteType(Label.Length()) == mbSingleByte) &&
+          (Label[Label.Length()] == ':'))
+      {
+        Label.SetLength(Label.Length() - 1);
+      }
+
+      DirectoryLabel->Caption = Label;
     }
     else
     {
-      DirectoryLabel->Caption = FMTLOAD(COPY_FILES,
-        (TransferStr, FFileList->Count, DirectionStr));
+      AnsiString TransferStr = LoadStr(!Move ? COPY_COPY : COPY_MOVE);
+      // currently the copy dialog is shown when downloading to temp folder
+      // only for drag&drop downloads, for we dare to display d&d specific prompt
+      AnsiString DirectionStr =
+        LoadStr(((Options & coTemp) != 0) ? COPY_TODROP :
+          (RemotePaths() ? COPY_TOREMOTE : COPY_TOLOCAL));
+
+      if (FileList->Count == 1)
+      {
+        AnsiString FileName;
+        if (!ToRemote) FileName = UnixExtractFileName(FFileList->Strings[0]);
+          else FileName = ExtractFileName(FFileList->Strings[0]);
+        DirectoryLabel->Caption = FMTLOAD(COPY_FILE,
+          (TransferStr, FileName, DirectionStr));
+      }
+      else
+      {
+        DirectoryLabel->Caption = FMTLOAD(COPY_FILES,
+          (TransferStr, FFileList->Count, DirectionStr));
+      }
     }
   }
 
   if (!Move)
   {
-    Caption = LoadStr(COPY_COPY_CAPTION);
+    if (!ToRemote && FLAGSET(FOutputOptions, cooRemoteTransfer))
+    {
+      Caption = LoadStr(REMOTE_COPY_TITLE);
+    }
+    else
+    {
+      Caption = LoadStr(COPY_COPY_CAPTION);
+    }
     CopyButton->Caption = LoadStr(COPY_COPY_BUTTON);
   }
   else
@@ -136,6 +153,36 @@ void __fastcall TCopyDialog::AdjustControls()
     CopyButton->Caption = LoadStr(COPY_MOVE_BUTTON);
   }
 
+  bool RemoteTransfer = FLAGSET(FOutputOptions, cooRemoteTransfer);
+  assert(FLAGSET(Options, coAllowRemoteTransfer) || !RemoteTransfer);
+
+  EnableControl(CopyParamsFrame, !RemoteTransfer);
+  EnableControl(NewerOnlyCheck, FLAGCLEAR(Options, coDisableNewerOnly) && !RemoteTransfer);
+  EnableControl(PresetsButton, !RemoteTransfer);
+}
+//---------------------------------------------------------------------------
+void __fastcall TCopyDialog::AdjustControls()
+{
+  if (FLAGSET(Options, coDoNotShowAgain))
+  {
+    SaveSettingsCheck->Caption = LoadStr(NEVER_SHOW_DIALOG_AGAIN);
+  }
+  RemoteDirectoryEdit->Visible = false;
+  LocalDirectoryEdit->Visible = false;
+  DirectoryEdit->Visible = FLAGCLEAR(Options, coTemp);
+  EnableControl(DirectoryEdit, FLAGCLEAR(Options, coDisableDirectory));
+  EnableControl(DirectoryLabel, DirectoryEdit->Enabled);
+  EnableControl(LocalDirectoryBrowseButton, DirectoryEdit->Enabled);
+  DirectoryLabel->FocusControl = DirectoryEdit;
+  CopyParamsFrame->Direction = !ToRemote ? pdToLocal : pdToRemote;
+  CopyParamsFrame->Options =
+    FLAGMASK(FLAGCLEAR(Options, coDisableTransferMode), cfAllowTransferMode) |
+    FLAGMASK(!Move, cfAllowExcludeMask) |
+    FLAGMASK(!Move && ToRemote, cfAllowClearArchive);
+  PresetsButton->Visible = FLAGCLEAR(Options, coDoNotUsePresets);
+
+  AdjustTransferControls();
+
   LocalDirectoryBrowseButton->Visible = !ToRemote &&
     ((Options & coTemp) == 0);
 
@@ -184,9 +231,14 @@ THistoryComboBox * __fastcall TCopyDialog::GetDirectoryEdit()
   return ToRemote ? RemoteDirectoryEdit : LocalDirectoryEdit;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TCopyDialog::RemotePaths()
+{
+  return (ToRemote || FLAGSET(FOutputOptions, cooRemoteTransfer));
+}
+//---------------------------------------------------------------------------
 AnsiString __fastcall TCopyDialog::GetFileMask()
 {
-  return ToRemote ? UnixExtractFileName(DirectoryEdit->Text) :
+  return RemotePaths() ? UnixExtractFileName(DirectoryEdit->Text) :
     ExtractFileName(DirectoryEdit->Text);
 }
 //---------------------------------------------------------------------------
@@ -215,8 +267,8 @@ void __fastcall TCopyDialog::SetDirectory(AnsiString value)
 {
   if (!value.IsEmpty())
   {
-    value = ToRemote ? UnixIncludeTrailingBackslash(value) :
-      IncludeTrailingBackslash(value);
+    value = RemotePaths() ?
+      UnixIncludeTrailingBackslash(value) : IncludeTrailingBackslash(value);
   }
   DirectoryEdit->Text = value + GetFileMask();
 }
@@ -226,7 +278,7 @@ AnsiString __fastcall TCopyDialog::GetDirectory()
   assert(DirectoryEdit);
 
   AnsiString Result = DirectoryEdit->Text;
-  if (ToRemote)
+  if (RemotePaths())
   {
     Result = UnixExtractFilePath(Result);
     if (!Result.IsEmpty())
@@ -256,12 +308,27 @@ void __fastcall TCopyDialog::SetFileList(TStrings * value)
 //---------------------------------------------------------------------------
 void __fastcall TCopyDialog::UpdateControls()
 {
+  if (!ToRemote && FLAGSET(Options, coAllowRemoteTransfer))
+  {
+    AnsiString Directory = DirectoryEdit->Text;
+    bool RemoteTransfer = (Directory.Pos("\\") == 0) && (Directory.Pos("/") > 0);
+    if (RemoteTransfer != FLAGSET(FOutputOptions, cooRemoteTransfer))
+    {
+      FOutputOptions =
+        (FOutputOptions & ~cooRemoteTransfer) |
+        FLAGMASK(RemoteTransfer, cooRemoteTransfer);
+      AdjustTransferControls();
+    }
+  }
+
+  bool RemoteTransfer = FLAGSET(FOutputOptions, cooRemoteTransfer);
   EnableControl(QueueCheck,
-    (Options & (coDisableQueue | coTemp)) == 0);
+    ((Options & (coDisableQueue | coTemp)) == 0) && !RemoteTransfer);
   EnableControl(QueueNoConfirmationCheck,
-    ((Options & coTemp) == 0) && QueueCheck->Checked);
+    (((Options & coTemp) == 0) && QueueCheck->Checked) && !RemoteTransfer);
   QueueNoConfirmationCheck->Visible = MoreButton->Expanded;
-  EnableControl(SaveSettingsCheck, FLAGCLEAR(Options, coDisableSaveSettings));
+  EnableControl(SaveSettingsCheck, FLAGCLEAR(Options, coDisableSaveSettings) &&
+    !RemoteTransfer);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCopyDialog::SetMove(bool value)
@@ -333,7 +400,7 @@ void __fastcall TCopyDialog::FormCloseQuery(TObject * /*Sender*/,
 {
   if (ModalResult != mrCancel)
   {
-    if (!ToRemote && ((Options & coTemp) == 0))
+    if (!RemotePaths() && ((Options & coTemp) == 0))
     {
       AnsiString Dir = Directory;
       AnsiString Drive = ExtractFileDrive(Dir);
@@ -372,10 +439,17 @@ void __fastcall TCopyDialog::LocalDirectoryBrowseButtonClick(
   TObject * /*Sender*/)
 {
   assert(!ToRemote);
-  AnsiString Directory = LocalDirectoryEdit->Text;
+  AnsiString Directory;
+  // if we are duplicating, we have remote path there
+  if (RemotePaths())
+  {
+    Directory = LocalDirectoryEdit->Text;
+  }
+
   if (SelectDirectory(Directory, LoadStr(SELECT_LOCAL_DIRECTORY), true))
   {
     LocalDirectoryEdit->Text = Directory;
+    UpdateControls();
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 0
forms/Copy.dfm

@@ -39,6 +39,7 @@ object CopyDialog: TCopyDialog
     ItemHeight = 13
     TabOrder = 0
     Text = 'LocalDirectoryEdit'
+    OnChange = ControlChange
   end
   object RemoteDirectoryEdit: THistoryComboBox
     Left = 8
@@ -51,6 +52,7 @@ object CopyDialog: TCopyDialog
     MaxLength = 1000
     TabOrder = 2
     Text = 'RemoteDirectoryEdit'
+    OnChange = ControlChange
   end
   object MoreButton: TMoreButton
     Left = 176

+ 3 - 0
forms/Copy.h

@@ -40,6 +40,7 @@ __published:
   void __fastcall PresetsButtonClick(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);
 private:
+  bool FDefaultToRemote;
   bool FToRemote;
   TStrings * FFileList;
   bool FMove;
@@ -64,6 +65,8 @@ private:
 protected:
   void __fastcall UpdateControls();
   void __fastcall AdjustControls();
+  void __fastcall AdjustTransferControls();
+  bool __fastcall RemotePaths();
 public:
   __fastcall TCopyDialog(TComponent* Owner);
   virtual __fastcall ~TCopyDialog();

+ 4 - 1
forms/CopyParamCustom.dfm

@@ -2,7 +2,7 @@ object CopyParamCustomDialog: TCopyParamCustomDialog
   Left = 264
   Top = 122
   HelpType = htKeyword
-  HelpKeyword = 'ui_transfer_preset'
+  HelpKeyword = 'ui_transfer_custom'
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'Transfer settings'
@@ -141,6 +141,9 @@ object CopyParamCustomDialog: TCopyParamCustomDialog
       inherited ExcludeFileMaskCombo: THistoryComboBox
         Width = 217
       end
+      inherited ExcludeFileMaskHintText: TStaticText
+        Left = 256
+      end
     end
   end
   object HelpButton: TButton

+ 10 - 0
forms/CopyParamPreset.cpp

@@ -6,6 +6,7 @@
 #include <TextsWin.h>
 #include <GUIConfiguration.h>
 #include <GUITools.h>
+#include <Tools.h>
 #include "CopyParamPreset.h"
 #include "VCLCommon.h"
 //---------------------------------------------------------------------------
@@ -43,6 +44,7 @@ __fastcall TCopyParamPresetDialog::TCopyParamPresetDialog(TComponent * Owner,
   InstallPathWordBreakProc(UserNameEdit);
   InstallPathWordBreakProc(RemoteDirectoryEdit);
   InstallPathWordBreakProc(LocalDirectoryEdit);
+  HintLabel(RuleMaskHintText, LoadStr(MASK_HINT));
 }
 //---------------------------------------------------------------------------
 void __fastcall TCopyParamPresetDialog::UpdateControls()
@@ -194,6 +196,8 @@ void __fastcall TCopyParamPresetDialog::FormCloseQuery(TObject * /*Sender*/,
       DescriptionEdit->SetFocus();
       throw Exception(FMTLOAD(COPY_PARAM_DUPLICATE, (Description)));
     }
+
+    ExitActiveControl(this);
   }
 }
 //---------------------------------------------------------------------------
@@ -209,3 +213,9 @@ void __fastcall TCopyParamPresetDialog::HelpButtonClick(TObject * /*Sender*/)
   FormHelp(this);
 }
 //---------------------------------------------------------------------------
+void __fastcall TCopyParamPresetDialog::MaskEditExit(TObject * Sender)
+{
+  ValidateMaskEdit(dynamic_cast<TEdit*>(Sender));
+}
+//---------------------------------------------------------------------------
+

+ 19 - 0
forms/CopyParamPreset.dfm

@@ -158,6 +158,9 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
       inherited ExcludeFileMaskCombo: THistoryComboBox
         Width = 217
       end
+      inherited ExcludeFileMaskHintText: TStaticText
+        Left = 256
+      end
     end
   end
   object RuleGroup: TXPGroupBox
@@ -212,6 +215,7 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
       MaxLength = 250
       TabOrder = 0
       OnChange = ControlChange
+      OnExit = MaskEditExit
     end
     object UserNameEdit: TEdit
       Left = 10
@@ -222,6 +226,7 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
       MaxLength = 250
       TabOrder = 1
       OnChange = ControlChange
+      OnExit = MaskEditExit
     end
     object RemoteDirectoryEdit: TEdit
       Left = 10
@@ -232,6 +237,7 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
       MaxLength = 250
       TabOrder = 2
       OnChange = ControlChange
+      OnExit = MaskEditExit
     end
     object LocalDirectoryEdit: TEdit
       Left = 10
@@ -242,6 +248,7 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
       MaxLength = 250
       TabOrder = 3
       OnChange = ControlChange
+      OnExit = MaskEditExit
     end
     object CurrentRuleButton: TButton
       Left = 10
@@ -252,6 +259,18 @@ object CopyParamPresetDialog: TCopyParamPresetDialog
       TabOrder = 4
       OnClick = CurrentRuleButtonClick
     end
+    object RuleMaskHintText: TStaticText
+      Left = 127
+      Top = 207
+      Width = 97
+      Height = 17
+      Alignment = taRightJustify
+      Anchors = [akTop, akRight]
+      AutoSize = False
+      Caption = 'mask hints'
+      TabOrder = 5
+      TabStop = True
+    end
   end
   object HasRuleCheck: TCheckBox
     Left = 395

+ 2 - 0
forms/CopyParamPreset.h

@@ -31,11 +31,13 @@ __published:
   TEdit *LocalDirectoryEdit;
   TButton *CurrentRuleButton;
   TButton *HelpButton;
+  TStaticText *RuleMaskHintText;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall FormShow(TObject *Sender);
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
   void __fastcall CurrentRuleButtonClick(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);
+  void __fastcall MaskEditExit(TObject *Sender);
 
 private:
   TCopyParamPresetMode FMode;

+ 22 - 8
forms/CopyParams.cpp

@@ -33,6 +33,8 @@ __fastcall TCopyParamsFrame::TCopyParamsFrame(TComponent* Owner)
 
   InstallPathWordBreakProc(AsciiFileMaskCombo);
   InstallPathWordBreakProc(ExcludeFileMaskCombo);
+  HintLabel(ExcludeFileMaskHintText,
+    FORMAT("%s\n \n%s",(LoadStr(MASK_HINT), LoadStr(PATH_MASK_HINT))));
 }
 //---------------------------------------------------------------------------
 __fastcall TCopyParamsFrame::~TCopyParamsFrame()
@@ -60,7 +62,8 @@ void __fastcall TCopyParamsFrame::SetParams(TCopyParamType value)
     case ncLowerCaseShort: CCLowerCaseShortButton->Checked = True; break;
   }
 
-  ReplaceInvalidCharsCheck->Checked = value.ReplaceInvalidChars;
+  ReplaceInvalidCharsCheck->Checked =
+    (value.InvalidCharsReplacement != TCopyParamType::NoReplacement);
 
   RightsFrame->AddXToDirectories = value.AddXToDirectories;
   RightsFrame->Rights = value.Rights;
@@ -135,18 +138,29 @@ TCheckBox * __fastcall TCopyParamsFrame::GetPreserveTimeCheck()
 //---------------------------------------------------------------------------
 void __fastcall TCopyParamsFrame::UpdateControls()
 {
+  EnableControl(CommonPropertiesGroup, FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
+  EnableControl(LocalPropertiesGroup, FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
+  EnableControl(RemotePropertiesGroup, FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
   EnableControl(TransferModeGroup,
-    FLAGSET(Options, cfAllowTransferMode) && Enabled);
+    FLAGSET(Options, cfAllowTransferMode) &&
+    FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
   EnableControl(AsciiFileMaskLabel,
-    FLAGSET(Options, cfAllowTransferMode) && TMAutomaticButton->Checked && Enabled);
+    TransferModeGroup->Enabled && TMAutomaticButton->Checked);
   EnableControl(AsciiFileMaskCombo,
-    FLAGSET(Options, cfAllowTransferMode) && TMAutomaticButton->Checked && Enabled);
-  EnableControl(RightsFrame, PreserveRightsCheck->Checked && Enabled);
-  EnableControl(ExcludeFileMaskCombo, FLAGSET(Options, cfAllowExcludeMask));
+    TransferModeGroup->Enabled && TMAutomaticButton->Checked);
+  EnableControl(PreserveRightsCheck, FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
+  EnableControl(RightsFrame, PreserveRightsCheck->Checked &&
+    PreserveRightsCheck->Enabled);
+  EnableControl(ExcludeFileMaskCombo,
+    (FLAGSET(Options, cfAllowExcludeMask) || FLAGSET(Options, cfAllowExcludeMaskOnly)) &&
+    Enabled);
   EnableControl(ExclusionFileMaskLabel, ExcludeFileMaskCombo->Enabled);
   EnableControl(NegativeExcludeCombo, ExcludeFileMaskCombo->Enabled);
-  EnableControl(ClearArchiveCheck, FLAGSET(Options, cfAllowClearArchive));
-  EnableControl(PreserveTimeCheck, FLAGCLEAR(Options, cfDisablePreserveTime));
+  EnableControl(ClearArchiveCheck, FLAGSET(Options, cfAllowClearArchive) &&
+    FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
+  EnableControl(PreserveTimeCheck, FLAGCLEAR(Options, cfDisablePreserveTime) &&
+    FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
+  EnableControl(ChangeCaseGroup, FLAGCLEAR(Options, cfAllowExcludeMaskOnly) && Enabled);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCopyParamsFrame::SetDirection(TParamsForDirection value)

+ 13 - 1
forms/CopyParams.dfm

@@ -272,7 +272,7 @@ object CopyParamsFrame: TCopyParamsFrame
       Width = 231
       Height = 17
       Caption = 'Clear source file '#39'Archi&ve'#39' attribute'
-      TabOrder = 2
+      TabOrder = 3
     end
     object NegativeExcludeCombo: TComboBox
       Left = 10
@@ -286,5 +286,17 @@ object CopyParamsFrame: TCopyParamsFrame
         'Exclude'
         'Include')
     end
+    object ExcludeFileMaskHintText: TStaticText
+      Left = 397
+      Top = 39
+      Width = 97
+      Height = 17
+      Alignment = taRightJustify
+      Anchors = [akTop, akRight]
+      AutoSize = False
+      Caption = 'mask hints'
+      TabOrder = 2
+      TabStop = True
+    end
   end
 end

+ 2 - 0
forms/CopyParams.h

@@ -18,6 +18,7 @@ const int cfAllowTransferMode = 0x01;
 const int cfAllowExcludeMask =  0x02;
 const int cfAllowClearArchive = 0x04;
 const int cfDisablePreserveTime = 0x08;
+const int cfAllowExcludeMaskOnly = 0x10;
 //---------------------------------------------------------------------------
 class TCopyParamsFrame : public TFrame
 {
@@ -50,6 +51,7 @@ __published:
   TRadioButton *CCLowerCaseShortButton;
   TCheckBox *ClearArchiveCheck;
   TComboBox *NegativeExcludeCombo;
+  TStaticText *ExcludeFileMaskHintText;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall ValidateMaskComboExit(TObject *Sender);
 private:

+ 8 - 2
forms/CustomCommand.cpp

@@ -54,6 +54,7 @@ __fastcall TCustomCommandDialog::TCustomCommandDialog(TComponent* Owner)
   FMode = ccmEdit;
   FOnValidate = NULL;
   InstallPathWordBreakProc(CommandEdit);
+  HintLabel(HintText, LoadStr(CUSTOM_COMMAND_PATTERNS_HINT));
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomCommandDialog::UpdateControls()
@@ -87,6 +88,7 @@ void __fastcall TCustomCommandDialog::UpdateControls()
   EnableControl(RecursiveCheck, AllowRecursive);
   EnableControl(ApplyToDirectoriesCheck, AllowApplyToDirectories);
   EnableControl(ShowResultsCheck, RemoteCommand);
+  EnableControl(CopyResultsCheck, RemoteCommand);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomCommandDialog::SetCommand(AnsiString value)
@@ -117,16 +119,19 @@ void __fastcall TCustomCommandDialog::SetParams(int value)
   RecursiveCheck->Checked = FLAGSET(value, ccRecursive);
   (FLAGSET(value, ccLocal) ? LocalCommandButton : RemoteCommandButton)->Checked = true;
   ShowResultsCheck->Checked = FLAGSET(value, ccShowResults);
+  CopyResultsCheck->Checked = FLAGSET(value, ccCopyResults);
 }
 //---------------------------------------------------------------------------
 int __fastcall TCustomCommandDialog::GetParams()
 {
   return
-    (FParams & ~(ccApplyToDirectories | ccRecursive | ccLocal | ccShowResults)) |
+    (FParams & ~(ccApplyToDirectories | ccRecursive | ccLocal |
+       ccShowResults | ccCopyResults)) |
     FLAGMASK(!RemoteCommandButton->Checked, ccLocal) |
     FLAGMASK(ApplyToDirectoriesCheck->Checked, ccApplyToDirectories) |
     FLAGMASK(RecursiveCheck->Checked && RecursiveCheck->Enabled, ccRecursive) |
-    FLAGMASK(ShowResultsCheck->Checked && ShowResultsCheck->Enabled, ccShowResults);
+    FLAGMASK(ShowResultsCheck->Checked && ShowResultsCheck->Enabled, ccShowResults) |
+    FLAGMASK(CopyResultsCheck->Checked && CopyResultsCheck->Enabled, ccCopyResults);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomCommandDialog::ControlChange(TObject * /*Sender*/)
@@ -148,6 +153,7 @@ bool __fastcall TCustomCommandDialog::Execute()
   bool Result = (ShowModal() == mrOk);
   if (Result)
   {
+    CommandEdit->SaveToHistory();
     CustomWinConfiguration->History["CustomCommand"] = CommandEdit->Items;
   }
   return Result;

+ 42 - 35
forms/CustomCommand.dfm

@@ -6,7 +6,7 @@ object CustomCommandDialog: TCustomCommandDialog
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'CustomCommandDialog'
-  ClientHeight = 305
+  ClientHeight = 255
   ClientWidth = 396
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
@@ -20,19 +20,19 @@ object CustomCommandDialog: TCustomCommandDialog
   OnShow = FormShow
   DesignSize = (
     396
-    305)
+    255)
   PixelsPerInch = 96
   TextHeight = 13
   object Group: TXPGroupBox
     Left = 8
     Top = 8
     Width = 380
-    Height = 255
+    Height = 205
     Anchors = [akLeft, akTop, akRight, akBottom]
     TabOrder = 0
     DesignSize = (
       380
-      255)
+      205)
     object DescriptionLabel: TLabel
       Left = 11
       Top = 16
@@ -51,20 +51,6 @@ object CustomCommandDialog: TCustomCommandDialog
       Caption = '&Custom command:'
       FocusControl = CommandEdit
     end
-    object CustomCommandsPatternsLabel: TLabel
-      Left = 16
-      Top = 189
-      Width = 353
-      Height = 61
-      Anchors = [akLeft, akTop, akRight]
-      AutoSize = False
-      Caption = 
-        'Patterns:'#160'!!'#160'-'#160'exclamation mark; !'#160'-'#160'file name; !?prompt?default' +
-        '!'#160'-'#160'prompts user for parameter value; !&&'#160'-'#160'selected files list'#160 +
-        '(quoted, space-delimited)'#13#10'Local command patterns: !^!'#160'-'#160'file na' +
-        'me from local panel'
-      WordWrap = True
-    end
     object DescriptionEdit: TEdit
       Left = 11
       Top = 32
@@ -88,53 +74,74 @@ object CustomCommandDialog: TCustomCommandDialog
     end
     object ApplyToDirectoriesCheck: TCheckBox
       Left = 16
-      Top = 136
-      Width = 145
+      Top = 149
+      Width = 161
       Height = 17
       Caption = '&Apply to directories'
-      TabOrder = 4
+      TabOrder = 5
       OnClick = ControlChange
     end
     object RecursiveCheck: TCheckBox
       Left = 184
-      Top = 136
-      Width = 145
+      Top = 149
+      Width = 185
       Height = 17
       Caption = '&Execute recursively'
-      TabOrder = 5
+      TabOrder = 6
       OnClick = ControlChange
     end
     object LocalCommandButton: TRadioButton
       Left = 184
-      Top = 109
-      Width = 161
+      Top = 122
+      Width = 185
       Height = 17
       Caption = '&Local command'
-      TabOrder = 3
+      TabOrder = 4
       OnClick = ControlChange
     end
     object RemoteCommandButton: TRadioButton
       Left = 16
-      Top = 109
+      Top = 122
       Width = 161
       Height = 17
       Caption = '&Remote command'
-      TabOrder = 2
+      TabOrder = 3
       OnClick = ControlChange
     end
     object ShowResultsCheck: TCheckBox
       Left = 16
-      Top = 163
-      Width = 353
+      Top = 176
+      Width = 161
       Height = 17
       Caption = '&Show results in terminal'
-      TabOrder = 6
+      TabOrder = 7
       OnClick = ControlChange
     end
+    object CopyResultsCheck: TCheckBox
+      Left = 184
+      Top = 176
+      Width = 185
+      Height = 17
+      Caption = 'Copy results to clip&board'
+      TabOrder = 8
+      OnClick = ControlChange
+    end
+    object HintText: TStaticText
+      Left = 290
+      Top = 103
+      Width = 79
+      Height = 16
+      Alignment = taRightJustify
+      Anchors = [akTop, akRight]
+      AutoSize = False
+      Caption = '&patterns'
+      TabOrder = 2
+      TabStop = True
+    end
   end
   object OkButton: TButton
     Left = 144
-    Top = 272
+    Top = 222
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
@@ -145,7 +152,7 @@ object CustomCommandDialog: TCustomCommandDialog
   end
   object CancelButton: TButton
     Left = 228
-    Top = 272
+    Top = 222
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]
@@ -156,7 +163,7 @@ object CustomCommandDialog: TCustomCommandDialog
   end
   object HelpButton: TButton
     Left = 312
-    Top = 272
+    Top = 222
     Width = 75
     Height = 25
     Anchors = [akRight, akBottom]

+ 2 - 1
forms/CustomCommand.h

@@ -23,11 +23,12 @@ __published:
   THistoryComboBox *CommandEdit;
   TCheckBox *ApplyToDirectoriesCheck;
   TCheckBox *RecursiveCheck;
-  TLabel *CustomCommandsPatternsLabel;
   TRadioButton *LocalCommandButton;
   TRadioButton *RemoteCommandButton;
   TCheckBox *ShowResultsCheck;
   TButton *HelpButton;
+  TCheckBox *CopyResultsCheck;
+  TStaticText *HintText;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall FormShow(TObject *Sender);
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);

File diff suppressed because it is too large
+ 427 - 159
forms/CustomScpExplorer.cpp


+ 11 - 1
forms/CustomScpExplorer.dfm

@@ -10,6 +10,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
   OldCreateOrder = False
   Position = poDefaultPosOnly
   OnCloseQuery = FormCloseQuery
+  OnConstrainedResize = FormConstrainedResize
   PixelsPerInch = 96
   TextHeight = 13
   object QueueSplitter: TSplitter
@@ -18,6 +19,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
     Width = 628
     Height = 3
     Cursor = crVSplit
+    Hint = 'Drag to resize queue list. Double click to hide queue list.'
     Align = alBottom
     AutoSnap = False
     MinSize = 70
@@ -73,8 +75,9 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       ViewStyle = vsReport
       OnColumnRightClick = DirViewColumnRightClick
       OnEnter = RemoteDirViewEnter
-      NortonLike = False
+      NortonLike = nlOff
       UnixColProperties.ExtWidth = 20
+      UnixColProperties.TypeVisible = False
       OnDDDragFileName = RemoteFileControlDDDragFileName
       OnGetSelectFilter = RemoteDirViewGetSelectFilter
       OnLoaded = DirViewLoaded
@@ -181,6 +184,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       OnStartDrag = QueueViewStartDrag
     end
     object QueueDock: TTBXDock
+      Tag = 1
       Left = 0
       Top = 0
       Width = 628
@@ -206,6 +210,12 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
         object TBXItem204: TTBXItem
           Action = NonVisualDataModule.QueueItemExecuteAction
         end
+        object TBXItem195: TTBXItem
+          Action = NonVisualDataModule.QueueItemPauseAction
+        end
+        object TBXItem194: TTBXItem
+          Action = NonVisualDataModule.QueueItemResumeAction
+        end
         object TBXItem205: TTBXItem
           Action = NonVisualDataModule.QueueItemDeleteAction
         end

+ 41 - 21
forms/CustomScpExplorer.h

@@ -36,11 +36,12 @@ class TQueueItemProxy;
 class TQueueController;
 class TSynchronizeController;
 class TEditorManager;
+class TEditorPreferences;
 struct TEditedFileData;
 //---------------------------------------------------------------------------
 enum TActionAllowed { aaShortCut, aaUpdate, aaExecute };
 enum TActionFlag { afLocal = 1, afRemote = 2, afExplorer = 4 , afCommander = 8 };
-enum TExecuteFileBy { efDefault, efEditor, efAlternativeEditor };
+enum TExecuteFileBy { efShell = 1, efInternalEditor = 2, efExternalEditor = 3, efDefaultEditor = 100 };
 enum TPanelExport { pePath, peFileList, peFullFileList, peUrl };
 enum TPanelExportDestination { pedClipboard, pedCommandLine };
 //---------------------------------------------------------------------------
@@ -68,6 +69,8 @@ __published:
   TTBXItem *TBXItem208;
   TUnixDriveView *RemoteDriveView;
   TSplitter *RemotePanelSplitter;
+  TTBXItem *TBXItem194;
+  TTBXItem *TBXItem195;
   void __fastcall RemoteDirViewContextPopup(TObject *Sender,
     const TPoint &MousePos, bool &Handled);
   void __fastcall RemoteDirViewGetSelectFilter(
@@ -119,13 +122,15 @@ __published:
   void __fastcall RemoteDirViewEnter(TObject *Sender);
   void __fastcall RemoteDriveViewEnter(TObject *Sender);
   void __fastcall DirViewMatchMask(TObject *Sender,
-          AnsiString FileName, AnsiString Masks, bool &Matches);
+    AnsiString FileName, bool Directory, AnsiString Masks, bool &Matches);
   void __fastcall RemoteDirViewGetOverlay(TObject *Sender, TListItem *Item,
-          WORD &Indexes);
+    WORD &Indexes);
   void __fastcall DirViewHistoryChange(TCustomDirView *Sender);
   void __fastcall RemoteStatusBarClick(TObject *Sender);
   void __fastcall DirViewLoaded(TObject *Sender);
   void __fastcall AddressToolbarGetBaseSize(TTBCustomToolbar * Toolbar, TPoint & ASize);
+  void __fastcall FormConstrainedResize(TObject * Sender, int & MinWidth,
+    int  &MinHeight, int  &MaxWidth, int  &MaxHeight);
 
 private:
   TTerminal * FTerminal;
@@ -163,6 +168,9 @@ private:
   AnsiString FCopyParamDefault;
   AnsiString FCopyParamAutoSelected;
   bool FEditingFocusedAdHocCommand;
+  TList * FDocks;
+  TSynchronizeController * FSynchronizeController;
+  AnsiString FTransferComboHint;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side);
@@ -181,6 +189,8 @@ private:
   void __fastcall UpdateHistoryMenu(TOperationSide Side, bool Back);
   void __fastcall AdHocCustomCommandValidate(const AnsiString & Command,
     int Params);
+  void __fastcall SetDockAllowDrag(bool value);
+  void __fastcall QueueSplitterDblClick(TObject * Sender);
 
 protected:
   TOperationSide FCurrentSide;
@@ -228,7 +238,7 @@ protected:
   void __fastcall OperationComplete(const TDateTime & StartTime);
   void __fastcall ExecutedFileChanged(const AnsiString FileName,
     const TEditedFileData & Data, HANDLE UploadCompleteEvent);
-  void __fastcall ExecutedFileEarlyClosed(const AnsiString FileName,
+  void __fastcall ExecutedFileEarlyClosed(const TEditedFileData & Data,
     bool * CloseFlag, bool & KeepOpen);
   void __fastcall CMAppSysCommand(TMessage & Message);
   void __fastcall WMAppCommand(TMessage & Message);
@@ -242,12 +252,17 @@ protected:
   void __fastcall DoSynchronize(TSynchronizeController * Sender,
     const AnsiString LocalDirectory, const AnsiString RemoteDirectory,
     const TCopyParamType & CopyParam, const TSynchronizeParamType & Params,
-    TSynchronizeStats * Stats, bool Full);
+    TSynchronizeStats * Stats, TSynchronizeOptions * Options, bool Full);
   void __fastcall DoSynchronizeInvalid(TSynchronizeController * Sender,
     const AnsiString Directory, const AnsiString ErrorStr);
+  void __fastcall DoSynchronizeTooManyDirectories(TSynchronizeController * Sender,
+    int & MaxDirectories);
   void __fastcall Synchronize(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, TSynchronizeMode Mode,
-    const TCopyParamType & CopyParam, int Params, TSynchronizeStats * Stats);
+    const TCopyParamType & CopyParam, int Params, TSynchronizeStats * Stats,
+    TSynchronizeOptions * Options);
+  void __fastcall GetSynchronizeOptions(int Params, TSynchronizeOptions & Options);
+  bool __fastcall SynchronizeAllowSelectedOnly();
   virtual void __fastcall BatchStart(void *& Storage);
   virtual void __fastcall BatchEnd(void * Storage);
   void __fastcall ExecuteFileOperation(TFileOperation Operation, TOperationSide Side,
@@ -262,7 +277,7 @@ protected:
   void __fastcall QueueListUpdate(TTerminalQueue * Queue);
   void __fastcall QueueItemUpdate(TTerminalQueue * Queue, TQueueItem * Item);
   void __fastcall UpdateQueueStatus();
-  TQueueItemProxy * __fastcall RefreshQueueItems();
+  void __fastcall RefreshQueueItems(bool AppIdle);
   virtual int __fastcall GetStaticComponentsHeight();
   void __fastcall FillQueueViewItem(TListItem * Item,
     TQueueItemProxy * QueueItem, bool Detail);
@@ -283,10 +298,17 @@ protected:
     TFileOperation Operation, bool NoConfirmation, void * Param);
   bool __fastcall EnsureCommandSessionFallback(TFSCapability Capability);
   void __fastcall FileTerminalClosed(const AnsiString FileName,
-    TEditedFileData & Data, void * Arg);
+    TEditedFileData & Data, TObject * Token, void * Arg);
+  void __fastcall FileConfigurationChanged(const AnsiString FileName,
+    TEditedFileData & Data, TObject * Token, void * Arg);
   void __fastcall CustomExecuteFile(TOperationSide Side,
-    TExecuteFileBy ExecuteFileBy, AnsiString FileName, AnsiString OriginalFileName);
-  bool __fastcall RemoteExecuteForceText(TExecuteFileBy ExecuteFileBy);
+    TExecuteFileBy ExecuteFileBy, AnsiString FileName, AnsiString OriginalFileName,
+    const TEditorPreferences * ExternalEditor);
+  bool __fastcall RemoteExecuteForceText(TExecuteFileBy ExecuteFileBy,
+    const TEditorPreferences * ExternalEditor);
+  void __fastcall ExecuteFileNormalize(TExecuteFileBy & ExecuteFileBy,
+    const TEditorPreferences *& ExternalEditor, const AnsiString & FileName,
+    bool Local);
   void __fastcall TemporarilyDownloadFiles(TStrings * FileList, bool ForceText,
     AnsiString & TempDir, bool AllFiles, bool GetTargetNames);
   TTBXPopupMenu * __fastcall HistoryMenu(TOperationSide Side, bool Back);
@@ -298,14 +320,10 @@ protected:
   void __fastcall TransferPresetAutoSelect();
   virtual void __fastcall GetTransferPresetAutoSelectData(TCopyParamRuleData & Data);
   int __fastcall CustomCommandState(const AnsiString & Command, int Params, bool OnFocused);
-
-  #pragma warn -inl
-  BEGIN_MESSAGE_MAP
-    VCL_MESSAGE_HANDLER(CM_APPSYSCOMMAND, TMessage, CMAppSysCommand)
-    VCL_MESSAGE_HANDLER(_WM_APPCOMMAND, TMessage, WMAppCommand)
-    VCL_MESSAGE_HANDLER(WM_SYSCOMMAND, TMessage, WMSysCommand)
-  END_MESSAGE_MAP(TForm)
-  #pragma warn +inl
+  void __fastcall LoadToolbarsLayoutStr(AnsiString LayoutStr);
+  AnsiString __fastcall GetToolbarsLayoutStr();
+  virtual void __fastcall Dispatch(void * Message);
+  void __fastcall PostComponentHide(unsigned short Component);
 
 public:
   virtual __fastcall ~TCustomScpExplorerForm();
@@ -331,16 +349,16 @@ public:
   virtual void __fastcall CompareDirectories();
   void __fastcall ExecuteCurrentFile();
   virtual void __fastcall OpenConsole(AnsiString Command = "");
-  void __fastcall OpenInPutty();
   virtual void __fastcall UpdateSessionData(TSessionData * Data = NULL);
   virtual void __fastcall SynchronizeDirectories();
-  virtual void __fastcall FullSynchronizeDirectories();
+  virtual void __fastcall FullSynchronizeDirectories() = 0;
   virtual void __fastcall ExploreLocalDirectory();
   virtual void __fastcall GoToCommandLine();
   virtual void __fastcall GoToTree();
   virtual void __fastcall PanelExport(TOperationSide Side, TPanelExport Export,
     TPanelExportDestination Destination, bool OnFocused = false);
-  void __fastcall ExecuteFile(TOperationSide Side, TExecuteFileBy ExecuteFileBy);
+  void __fastcall ExecuteFile(TOperationSide Side, TExecuteFileBy ExecuteFileBy,
+    const TEditorPreferences * ExternalEditor = NULL);
   void __fastcall EditNew(TOperationSide Side);
   bool __fastcall AllowQueueOperation(TQueueOperation Operation);
   void __fastcall ExecuteQueueOperation(TQueueOperation Operation);
@@ -364,6 +382,8 @@ public:
   virtual AnsiString __fastcall PathForCaption();
   void __fastcall FileListFromClipboard();
   void __fastcall PreferencesDialog(TPreferencesMode APreferencesMode);
+  void __fastcall WhatsThis();
+  virtual void __fastcall BeforeAction();
 
   __property bool ComponentVisible[Word Component] = { read = GetComponentVisible, write = SetComponentVisible };
   __property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation };

+ 155 - 67
forms/Editor.cpp

@@ -10,6 +10,8 @@
 #include <ScpMain.h>
 #include "VCLCommon.h"
 #include "WinConfiguration.h"
+#include "HelpWin.h"
+#include <CommDlg.hpp>
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 #pragma link "TB2Dock"
@@ -43,6 +45,71 @@ TForm * __fastcall ShowEditorForm(const AnsiString FileName, TCustomForm * Paren
   return Dialog;
 }
 //---------------------------------------------------------------------------
+void __fastcall ReconfigureEditorForm(TForm * Form)
+{
+  TEditorForm * Editor = dynamic_cast<TEditorForm *>(Form);
+  assert(Editor != NULL);
+  Editor->ApplyConfiguration();
+}
+//---------------------------------------------------------------------------
+class TFindDialogEx : public TFindDialog
+{
+public:
+    __fastcall virtual TFindDialogEx(TComponent * AOwner) : TFindDialog(AOwner)
+  {
+    FHelpMsg = RegisterWindowMessage(HELPMSGSTRING);
+  }
+  
+protected:
+  unsigned int FHelpMsg;
+
+    virtual bool __fastcall MessageHook(TMessage & Msg)
+  {
+    bool Result = false;
+    if (Msg.Msg == FHelpMsg)
+    {
+      Application->HelpKeyword(HELP_EDITOR_FIND);
+      Result = true;
+    }
+
+    if (!Result)
+    {
+      Result = TFindDialog::MessageHook(Msg);
+    }
+
+    return Result;
+  }
+};
+//---------------------------------------------------------------------------
+class TReplaceDialogEx : public TReplaceDialog
+{
+public:
+    __fastcall virtual TReplaceDialogEx(TComponent * AOwner) : TReplaceDialog(AOwner)
+  {
+    FHelpMsg = RegisterWindowMessage(HELPMSGSTRING);
+  }
+  
+protected:
+  unsigned int FHelpMsg;
+
+    virtual bool __fastcall MessageHook(TMessage & Msg)
+  {
+    bool Result = false;
+    if (Msg.Msg == FHelpMsg)
+    {
+      Application->HelpKeyword(HELP_EDITOR_REPLACE);
+      Result = true;
+    }
+
+    if (!Result)
+    {
+      Result = TReplaceDialog::MessageHook(Msg);
+    }
+
+    return Result;
+  }
+};
+//---------------------------------------------------------------------------
 __fastcall TEditorForm::TEditorForm(TComponent* Owner)
   : TForm(Owner)
 {
@@ -51,44 +118,22 @@ __fastcall TEditorForm::TEditorForm(TComponent* Owner)
   FCaretPos.y = -1;
   FLastFindDialog = NULL;
   FShowWindowButton = false;
+  FCloseAnnounced = false;
   ApplyConfiguration();
-  FindDialog->FindText = WinConfiguration->Editor.FindText;
-  TFindOptions Options = FindDialog->Options;
-  if (WinConfiguration->Editor.FindMatchCase)
-  {
-    Options << frMatchCase;
-  }
-  if (WinConfiguration->Editor.FindWholeWord)
-  {
-    Options << frWholeWord;
-  }
-  FindDialog->Options = Options;
-  ReplaceDialog->FindText = FindDialog->FindText;
-  ReplaceDialog->Options = FindDialog->Options;
-  ReplaceDialog->ReplaceText = WinConfiguration->Editor.ReplaceText;
+  FFindDialog = new TFindDialogEx(this);
+  FFindDialog->OnFind = FindDialogFind;
+  FReplaceDialog = new TReplaceDialogEx(this);
+  FReplaceDialog->OnFind = FindDialogFind;
+  FReplaceDialog->OnReplace = FindDialogFind;
   UseSystemSettings(this);
 }
 //---------------------------------------------------------------------------
 __fastcall TEditorForm::~TEditorForm()
 {
-  if (FOnWindowClose != NULL)
+  // see FormClose for explanation
+  if (!FCloseAnnounced)
   {
-    try
-    {
-      FOnWindowClose(this);
-    }
-    catch(Exception & E)
-    {
-      ShowExtendedException(&E);
-    }
-  }
-
-  if (FLastFindDialog)
-  {
-    WinConfiguration->Editor.FindText = FLastFindDialog->FindText;
-    WinConfiguration->Editor.ReplaceText = ReplaceDialog->ReplaceText;
-    WinConfiguration->Editor.FindMatchCase = FLastFindDialog->Options.Contains(frMatchCase);
-    WinConfiguration->Editor.FindWholeWord = FLastFindDialog->Options.Contains(frWholeWord);
+    DoWindowClose();
   }
 }
 //---------------------------------------------------------------------------
@@ -127,7 +172,7 @@ void __fastcall TEditorForm::EditorActionsUpdate(TBasicAction *Action,
   else if (Action == FindNextAction)
   {
     FindNextAction->Enabled =
-      FLastFindDialog != NULL || !FindDialog->FindText.IsEmpty();
+      FLastFindDialog != NULL || !FFindDialog->FindText.IsEmpty();
   }
   else if (Action == PreferencesAction || Action == CloseAction ||
     Action == FindAction || Action == ReplaceAction || Action == GoToLineAction ||
@@ -158,10 +203,7 @@ void __fastcall TEditorForm::EditorActionsExecute(TBasicAction *Action,
   }
   else if (Action == PreferencesAction)
   {
-    if (DoPreferencesDialog(pmEditor))
-    {
-      ApplyConfiguration();
-    }
+    DoPreferencesDialog(pmEditor);
   }
   else if (Action == CloseAction)
   {
@@ -175,7 +217,7 @@ void __fastcall TEditorForm::EditorActionsExecute(TBasicAction *Action,
   {
     if (!FLastFindDialog)
     {
-      FLastFindDialog = FindDialog;
+      FLastFindDialog = FFindDialog;
     }
     Find();
   }
@@ -317,33 +359,40 @@ void __fastcall TEditorForm::Find()
 
     // length condition is there to improve performance when large
     // block is selected in editor
-    if (FLastFindDialog == ReplaceDialog &&
-        (ReplaceDialog->Options.Contains(frReplace) ||
-         ReplaceDialog->Options.Contains(frReplaceAll)) &&
-        ReplaceDialog->FindText.Length() == EditorMemo->SelLength &&
-        AnsiSameText(ReplaceDialog->FindText, EditorMemo->SelText))
+    if (FLastFindDialog == FReplaceDialog &&
+        (FReplaceDialog->Options.Contains(frReplace) ||
+         FReplaceDialog->Options.Contains(frReplaceAll)) &&
+        FReplaceDialog->FindText.Length() == EditorMemo->SelLength &&
+        AnsiSameText(FReplaceDialog->FindText, EditorMemo->SelText))
     {
-      EditorMemo->SelText = ReplaceDialog->ReplaceText;
+      EditorMemo->SelText = FReplaceDialog->ReplaceText;
       Replacements++;
     }
 
-    if (FLastFindDialog->Options.Contains(frMatchCase))
+    TEditorConfiguration EditorConfiguration = WinConfiguration->Editor;
+    EditorConfiguration.FindText = FLastFindDialog->FindText;
+    EditorConfiguration.ReplaceText = FReplaceDialog->ReplaceText;
+    EditorConfiguration.FindMatchCase = FLastFindDialog->Options.Contains(frMatchCase);
+    EditorConfiguration.FindWholeWord = FLastFindDialog->Options.Contains(frWholeWord);
+    WinConfiguration->Editor = EditorConfiguration;
+
+    if (EditorConfiguration.FindMatchCase)
     {
       SearchTypes << stMatchCase;
     }
-    if (FLastFindDialog->Options.Contains(frWholeWord))
+    if (EditorConfiguration.FindWholeWord)
     {
       SearchTypes << stWholeWord;
     }
 
-    NewPos = EditorMemo->FindText(FLastFindDialog->FindText,
+    NewPos = EditorMemo->FindText(EditorConfiguration.FindText,
       EditorMemo->SelLength ? EditorMemo->SelStart+1 : EditorMemo->SelStart,
       EditorMemo->Text.Length(), SearchTypes);
 
     if (NewPos >= 0)
     {
       EditorMemo->SelStart = NewPos;
-      EditorMemo->SelLength = FLastFindDialog->FindText.Length();
+      EditorMemo->SelLength = EditorConfiguration.FindText.Length();
     }
 
     if (FLastFindDialog->Handle)
@@ -353,18 +402,30 @@ void __fastcall TEditorForm::Find()
 
     if (NewPos < 0)
     {
-      if (!Replacements)
-      {
-        MessageDialog(FMTLOAD(EDITOR_FIND_END, (FLastFindDialog->FindText)), qtInformation, qaOK, HELP_NONE);
-      }
-      else
+      if ((Replacements == 0) || FReplaceDialog->Options.Contains(frReplaceAll))
       {
-        MessageDialog(FMTLOAD(EDITOR_REPLACE_END, (Replacements)), qtInformation, qaOK, HELP_NONE);
+        // now Screen->ActiveForm can be NULL when other form was meanwhile
+        // activated and then focus was returned back to "find" dialog
+        // (non VCL form)
+        if (Screen->ActiveForm != this)
+        {
+          SetFocus();
+          FLastFindDialog->Execute();
+        }
+
+        if (Replacements == 0)
+        {
+          MessageDialog(FMTLOAD(EDITOR_FIND_END, (EditorConfiguration.FindText)), qtInformation, qaOK, HELP_NONE);
+        }
+        else if (FReplaceDialog->Options.Contains(frReplaceAll))
+        {
+          MessageDialog(FMTLOAD(EDITOR_REPLACE_END, (Replacements)), qtInformation, qaOK, HELP_NONE);
+        }
       }
     }
   }
-  while (NewPos >= 0 && FLastFindDialog == ReplaceDialog &&
-         ReplaceDialog->Options.Contains(frReplaceAll));
+  while (NewPos >= 0 && FLastFindDialog == FReplaceDialog &&
+         FReplaceDialog->Options.Contains(frReplaceAll));
 }
 //---------------------------------------------------------------------------
 void __fastcall TEditorForm::FormShow(TObject * /*Sender*/)
@@ -416,25 +477,23 @@ void __fastcall TEditorForm::PositionFindDialog(bool VerticalOnly)
   assert(FLastFindDialog);
   if (!VerticalOnly)
   {
-    FLastFindDialog->Left = EditorMemo->Left + EditorMemo->Width / 2 - 100;
+    FLastFindDialog->Left = Left + EditorMemo->Left + EditorMemo->Width / 2 - 100;
   }
-  FLastFindDialog->Top = EditorMemo->Top + (EditorMemo->Height / 4) +
-    (CursorInUpperPart() ? (EditorMemo->Height / 2) : 0) - 30;
+  FLastFindDialog->Top = Top + EditorMemo->Top + (EditorMemo->Height / 4) +
+    (CursorInUpperPart() ? (EditorMemo->Height / 2) : 0) - 40;
 }
 //---------------------------------------------------------------------------
 void __fastcall TEditorForm::StartFind(bool Find)
 {
   AnsiString Text = EditorMemo->SelText;
   TFindOptions Options;
-  if (FLastFindDialog)
+  Options << frHideUpDown; // not implemented
+  Options << frShowHelp;
+  if (Text.IsEmpty())
   {
-    Options = FLastFindDialog->Options;
-    if (Text.IsEmpty())
-    {
-      Text = FLastFindDialog->FindText;
-    }
+    Text = WinConfiguration->Editor.FindText;
   }
-  TFindDialog * Dialog = Find ? FindDialog : ReplaceDialog;
+  TFindDialog * Dialog = Find ? FFindDialog : FReplaceDialog;
   if (FLastFindDialog && Dialog != FLastFindDialog && FLastFindDialog->Handle)
   {
     FLastFindDialog->CloseDialog();
@@ -444,10 +503,16 @@ void __fastcall TEditorForm::StartFind(bool Find)
   {
     FLastFindDialog->FindText = Text;
   }
-  if (!Options.Empty())
+  FReplaceDialog->ReplaceText = WinConfiguration->Editor.ReplaceText;
+  if (WinConfiguration->Editor.FindMatchCase)
+  {
+    Options << frMatchCase;
+  }
+  if (WinConfiguration->Editor.FindWholeWord)
   {
-    FLastFindDialog->Options = Options;
+    Options << frWholeWord;
   }
+  FLastFindDialog->Options = Options;
   if (!FLastFindDialog->Handle)
   {
     PositionFindDialog(false);
@@ -475,9 +540,32 @@ void __fastcall TEditorForm::GoToLine()
 void __fastcall TEditorForm::FormClose(TObject * /*Sender*/,
   TCloseAction & Action)
 {
+  // Preferably announce closure here as this is called from within TForm::Close(),
+  // so the annoucement will be synchronous (and editor manager thus
+  // will consider the form to be really closed and will not block
+  // application closure).
+  // However FormClose is not called when form is closed due to
+  // application exit, so there is last resort call from destructor.
+  DoWindowClose();
+  FCloseAnnounced = true;
   Action = caFree;
 }
 //---------------------------------------------------------------------------
+void __fastcall TEditorForm::DoWindowClose()
+{
+  if (FOnWindowClose != NULL)
+  {
+    try
+    {
+      FOnWindowClose(this);
+    }
+    catch(Exception & E)
+    {
+      ShowExtendedException(&E);
+    }
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TEditorForm::SetShowWindowButton(bool value)
 {
   if (value != ShowWindowButton)

+ 2 - 15
forms/Editor.dfm

@@ -104,7 +104,7 @@ object EditorForm: TEditorForm
     Left = 0
     Top = 26
     Width = 617
-    Height = 337
+    Height = 344
     Align = alClient
     HideSelection = False
     PlainText = True
@@ -118,7 +118,7 @@ object EditorForm: TEditorForm
   end
   object StatusBar: TTBXStatusBar
     Left = 0
-    Top = 363
+    Top = 370
     Width = 617
     Panels = <
       item
@@ -918,19 +918,6 @@ object EditorForm: TEditorForm
       8001FDDF81FFF87FFFFFFFFFFFFFFFFF00000000000000000000000000000000
       000000000000}
   end
-  object FindDialog: TFindDialog
-    Options = [frDown, frHideUpDown]
-    OnFind = FindDialogFind
-    Left = 480
-    Top = 96
-  end
-  object ReplaceDialog: TReplaceDialog
-    Options = [frDown, frHideUpDown]
-    OnFind = FindDialogFind
-    OnReplace = FindDialogFind
-    Left = 536
-    Top = 96
-  end
   object EditorPopup: TTBXPopupMenu
     Images = EditorImages
     Left = 480

+ 5 - 3
forms/Editor.h

@@ -59,8 +59,6 @@ __published:
   TTBXItem *TBXItem12;
   TTBXSeparatorItem *TBXSeparatorItem4;
   TTBXItem *TBXItem13;
-  TFindDialog *FindDialog;
-  TReplaceDialog *ReplaceDialog;
   TTBXPopupMenu *EditorPopup;
   TTBXItem *Undo1;
   TTBXSeparatorItem *N1;
@@ -98,19 +96,22 @@ private:
   TFindDialog * FLastFindDialog;
   TPoint FCaretPos;
   bool FShowWindowButton;
+  TFindDialog * FFindDialog;
+  TReplaceDialog * FReplaceDialog;
+  bool FCloseAnnounced;
   void __fastcall SetFileName(const AnsiString value);
   void __fastcall SetParentForm(TCustomForm * value);
   void __fastcall SetShowWindowButton(bool value);
 public:
   __fastcall TEditorForm(TComponent* Owner);
   virtual __fastcall ~TEditorForm();
+  void __fastcall ApplyConfiguration();
   __property AnsiString FileName = { read = FFileName, write = SetFileName };
   __property TNotifyEvent OnFileChanged = { read = FOnFileChanged, write = FOnFileChanged };
   __property TNotifyEvent OnWindowClose = { read = FOnWindowClose, write = FOnWindowClose };
   __property TCustomForm * ParentForm = { read = FParentForm, write = SetParentForm };
   __property bool ShowWindowButton = { read = FShowWindowButton, write = SetShowWindowButton };
 protected:
-  void __fastcall ApplyConfiguration();
   bool __fastcall CursorInUpperPart();
   void __fastcall Find();
   void __fastcall GoToLine();
@@ -118,6 +119,7 @@ protected:
   void __fastcall PositionFindDialog(bool VerticalOnly);
   void __fastcall StartFind(bool Find);
   void __fastcall UpdateControls();
+  void __fastcall DoWindowClose();
   virtual void __fastcall CreateParams(TCreateParams & Params);
 };
 //---------------------------------------------------------------------------

+ 182 - 0
forms/EditorPreferences.cpp

@@ -0,0 +1,182 @@
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include <Common.h>
+#include <WinConfiguration.h>
+#include <WinInterface.h>
+#include <VCLCommon.h>
+#include <TextsWin.h>
+#include <Tools.h>
+#include <ScpMain.h>
+#include "EditorPreferences.h"
+//---------------------------------------------------------------------------
+#pragma package(smart_init)
+#pragma link "XPThemes"
+#pragma link "HistoryComboBox"
+#pragma resource "*.dfm"
+//---------------------------------------------------------------------------
+bool __fastcall DoEditorPreferencesDialog(TEditorPreferences * Editor,
+  TEditorPreferencesMode Mode)
+{
+  bool Result;
+  TEditorPreferencesDialog * Dialog = new TEditorPreferencesDialog(Application, Mode);
+  try
+  {
+    Result = Dialog->Execute(Editor);
+  }
+  __finally
+  {
+    delete Dialog;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+__fastcall TEditorPreferencesDialog::TEditorPreferencesDialog(
+  TComponent * Owner, TEditorPreferencesMode Mode) : 
+  TForm(Owner)
+{
+  UseSystemSettings(this);
+
+  FMode = Mode;
+
+  Caption = LoadStr(Mode == epmEdit ? EDITOR_EDIT : EDITOR_ADD);
+
+  InstallPathWordBreakProc(ExternalEditorEdit);
+}
+//---------------------------------------------------------------------------
+bool __fastcall TEditorPreferencesDialog::Execute(TEditorPreferences * Editor)
+{
+  EditorInternalButton->Checked = (Editor->Data.Editor == edInternal);
+  EditorExternalButton->Checked = (Editor->Data.Editor == edExternal);
+  AnsiString ExternalEditor = Editor->Data.ExternalEditor;
+  if (!ExternalEditor.IsEmpty())
+  {
+    TWinConfiguration::ReformatFileNameCommand(ExternalEditor);
+  }
+  ExternalEditorEdit->Text = ExternalEditor;
+  ExternalEditorEdit->Items = CustomWinConfiguration->History["ExternalEditor"];
+  MaskEdit->Text = Editor->Data.FileMask.Masks;
+  MaskEdit->Items = CustomWinConfiguration->History["Mask"];
+  ExternalEditorTextCheck->Checked = Editor->Data.ExternalEditorText;
+  MDIExternalEditorCheck->Checked = Editor->Data.MDIExternalEditor;
+
+  bool Result = (ShowModal() == mrOk);
+
+  if (Result)
+  {
+    Editor->Data.Editor = (EditorInternalButton->Checked ? edInternal : edExternal);
+    Editor->Data.ExternalEditor = ExternalEditorEdit->Text;
+    ExternalEditorEdit->SaveToHistory();
+    CustomWinConfiguration->History["ExternalEditor"] = ExternalEditorEdit->Items;
+    Editor->Data.FileMask = MaskEdit->Text;
+    MaskEdit->SaveToHistory();
+    CustomWinConfiguration->History["Mask"] = MaskEdit->Items;
+    Editor->Data.ExternalEditorText = ExternalEditorTextCheck->Checked;
+    Editor->Data.MDIExternalEditor = MDIExternalEditorCheck->Checked;
+  }
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::ExternalEditorEditChange(
+  TObject * Sender)
+{
+  // duplicated in TPreferencesDialog::FilenameEditChange
+  if (FAfterFilenameEditDialog)
+  {
+    FAfterFilenameEditDialog = false;
+    ExternalEditorEditExit(Sender);
+  }
+  else
+  {
+    ControlChange(Sender);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::ExternalEditorEditExit(
+  TObject * Sender)
+{
+  // duplicated in TPreferencesDialog::FilenameEditExit
+  THistoryComboBox * FilenameEdit = dynamic_cast<THistoryComboBox *>(Sender);
+  try
+  {
+    AnsiString Filename = FilenameEdit->Text;
+    if (!Filename.IsEmpty())
+    {
+      TWinConfiguration::ReformatFileNameCommand(Filename);
+      FilenameEdit->Text = Filename;
+    }
+    ControlChange(Sender);
+  }
+  catch(...)
+  {
+    FilenameEdit->SelectAll();
+    FilenameEdit->SetFocus();
+    throw;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::ExternalEditorBrowseButtonClick(
+  TObject * /*Sender*/)
+{
+  AnsiString ExternalEditor, Program, Params, Dir;
+  ExternalEditor = ExternalEditorEdit->Text;
+  TWinConfiguration::ReformatFileNameCommand(ExternalEditor);
+  SplitCommand(ExternalEditor, Program, Params, Dir);
+
+  TOpenDialog * FileDialog = new TOpenDialog(this);
+  try
+  {
+    FileDialog->FileName = Program;
+    FileDialog->Filter = LoadStr(PREFERENCES_EXTERNAL_EDITOR_FILTER);
+    FileDialog->Title = LoadStr(PREFERENCES_SELECT_EXTERNAL_EDITOR);
+
+    if (FileDialog->Execute())
+    {
+      FAfterFilenameEditDialog = true;
+      ExternalEditorEdit->Text = FormatCommand(FileDialog->FileName, Params);
+      FAfterFilenameEditDialog = false;
+      ExternalEditorEditExit(ExternalEditorEdit);
+    }
+  }
+  __finally
+  {
+    delete FileDialog;
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::HelpButtonClick(TObject * /*Sender*/)
+{
+  FormHelp(this);
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::ControlChange(TObject * /*Sender*/)
+{
+  UpdateControls();
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::UpdateControls()
+{
+  EnableControl(OkButton,
+    EditorInternalButton->Checked || !ExternalEditorEdit->Text.IsEmpty());
+  EnableControl(ExternalEditorEdit, EditorExternalButton->Checked);  
+  EnableControl(ExternalEditorBrowseButton, EditorExternalButton->Checked);  
+  EnableControl(ExternalEditorGroup, EditorExternalButton->Checked);  
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::FormCloseQuery(TObject * /*Sender*/,
+  bool & /*CanClose*/)
+{
+  if (ModalResult != mrCancel)
+  {
+    ExitActiveControl(this);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TEditorPreferencesDialog::MaskEditExit(TObject * /*Sender*/)
+{
+  ValidateMaskEdit(MaskEdit);
+}
+//---------------------------------------------------------------------------
+

+ 166 - 0
forms/EditorPreferences.dfm

@@ -0,0 +1,166 @@
+object EditorPreferencesDialog: TEditorPreferencesDialog
+  Left = 303
+  Top = 145
+  HelpType = htKeyword
+  HelpKeyword = 'ui_editor_preferences'
+  BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
+  BorderStyle = bsDialog
+  Caption = 'EditorPreferencesDialog'
+  ClientHeight = 310
+  ClientWidth = 403
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'MS Sans Serif'
+  Font.Style = []
+  OldCreateOrder = False
+  Position = poMainFormCenter
+  OnCloseQuery = FormCloseQuery
+  DesignSize = (
+    403
+    310)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object ExternalEditorGroup: TXPGroupBox
+    Left = 8
+    Top = 196
+    Width = 388
+    Height = 73
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'External editor options (affects editing remote files only)'
+    TabOrder = 2
+    object ExternalEditorTextCheck: TCheckBox
+      Left = 16
+      Top = 45
+      Width = 337
+      Height = 17
+      Caption = 'Force &text transfer mode for files edited in external editor'
+      TabOrder = 1
+    end
+    object MDIExternalEditorCheck: TCheckBox
+      Left = 16
+      Top = 21
+      Width = 337
+      Height = 17
+      Caption = 'E&xternal editor opens multiple files in one window (process)'
+      TabOrder = 0
+    end
+  end
+  object EditorGroup: TXPGroupBox
+    Left = 8
+    Top = 8
+    Width = 388
+    Height = 101
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'Editor type'
+    TabOrder = 0
+    DesignSize = (
+      388
+      101)
+    object EditorInternalButton: TRadioButton
+      Left = 16
+      Top = 21
+      Width = 145
+      Height = 17
+      Caption = '&Internal editor'
+      TabOrder = 0
+      OnClick = ControlChange
+    end
+    object EditorExternalButton: TRadioButton
+      Left = 16
+      Top = 45
+      Width = 145
+      Height = 17
+      Caption = '&External editor'
+      TabOrder = 1
+      OnClick = ControlChange
+    end
+    object ExternalEditorEdit: THistoryComboBox
+      Left = 32
+      Top = 69
+      Width = 267
+      Height = 21
+      Anchors = [akLeft, akTop, akRight]
+      ItemHeight = 13
+      TabOrder = 2
+      Text = 'ExternalEditorEdit'
+      OnChange = ExternalEditorEditChange
+      OnExit = ExternalEditorEditExit
+    end
+    object ExternalEditorBrowseButton: TButton
+      Left = 305
+      Top = 67
+      Width = 75
+      Height = 25
+      Anchors = [akTop, akRight]
+      Caption = 'B&rowse...'
+      TabOrder = 3
+      OnClick = ExternalEditorBrowseButtonClick
+    end
+  end
+  object MaskGroup: TXPGroupBox
+    Left = 7
+    Top = 116
+    Width = 388
+    Height = 73
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'Editor autoselection'
+    TabOrder = 1
+    DesignSize = (
+      388
+      73)
+    object Label1: TLabel
+      Left = 11
+      Top = 20
+      Width = 150
+      Height = 13
+      Caption = 'Use this editor for &following files:'
+      FocusControl = MaskEdit
+    end
+    object MaskEdit: THistoryComboBox
+      Left = 11
+      Top = 39
+      Width = 367
+      Height = 21
+      Anchors = [akLeft, akTop, akRight]
+      ItemHeight = 13
+      MaxLength = 1000
+      TabOrder = 0
+      Text = '*.*'
+      OnExit = MaskEditExit
+    end
+  end
+  object OkButton: TButton
+    Left = 151
+    Top = 277
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = 'OK'
+    Default = True
+    ModalResult = 1
+    TabOrder = 3
+  end
+  object CancelButton: TButton
+    Left = 235
+    Top = 277
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Cancel = True
+    Caption = 'Cancel'
+    ModalResult = 2
+    TabOrder = 4
+  end
+  object HelpButton: TButton
+    Left = 319
+    Top = 277
+    Width = 75
+    Height = 25
+    Anchors = [akRight, akBottom]
+    Caption = '&Help'
+    TabOrder = 5
+    OnClick = HelpButtonClick
+  end
+end

+ 50 - 0
forms/EditorPreferences.h

@@ -0,0 +1,50 @@
+//---------------------------------------------------------------------------
+#ifndef EditorPreferencesH
+#define EditorPreferencesH
+//---------------------------------------------------------------------------
+#include <Classes.hpp>
+#include <Controls.hpp>
+#include <StdCtrls.hpp>
+#include <Forms.hpp>
+#include "HistoryComboBox.hpp"
+#include "XPThemes.hpp"
+//---------------------------------------------------------------------------
+class TEditorPreferencesDialog : public TForm
+{
+__published:
+  TXPGroupBox *ExternalEditorGroup;
+  TCheckBox *ExternalEditorTextCheck;
+  TCheckBox *MDIExternalEditorCheck;
+  TXPGroupBox *EditorGroup;
+  TRadioButton *EditorInternalButton;
+  TRadioButton *EditorExternalButton;
+  THistoryComboBox *ExternalEditorEdit;
+  TButton *ExternalEditorBrowseButton;
+  TXPGroupBox *MaskGroup;
+  TLabel *Label1;
+  THistoryComboBox *MaskEdit;
+  TButton *OkButton;
+  TButton *CancelButton;
+  TButton *HelpButton;
+  void __fastcall ExternalEditorEditChange(TObject *Sender);
+  void __fastcall ExternalEditorEditExit(TObject *Sender);
+  void __fastcall ExternalEditorBrowseButtonClick(TObject *Sender);
+  void __fastcall HelpButtonClick(TObject *Sender);
+  void __fastcall ControlChange(TObject *Sender);
+  void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
+  void __fastcall MaskEditExit(TObject *Sender);
+
+public:
+  __fastcall TEditorPreferencesDialog(TComponent * Owner,
+    TEditorPreferencesMode Mode);
+
+  bool __fastcall Execute(TEditorPreferences * Editor);
+
+private:
+  TEditorPreferencesMode FMode;
+  bool FAfterFilenameEditDialog;
+  
+  void __fastcall UpdateControls();
+};
+//---------------------------------------------------------------------------
+#endif

+ 35 - 15
forms/FullSynchronize.cpp

@@ -6,6 +6,7 @@
 
 #include "WinInterface.h"
 #include "FullSynchronize.h"
+#include "CopyParams.h"
 #include "VCLCommon.h"
 
 #include <ScpMain.h>
@@ -64,6 +65,7 @@ __fastcall TFullSynchronizeDialog::TFullSynchronizeDialog(TComponent* Owner)
   FPresetsMenu = new TPopupMenu(this);
   InstallPathWordBreakProc(LocalDirectoryEdit);
   InstallPathWordBreakProc(RemoteDirectoryEdit);
+  FSynchronizeBySizeCaption = SynchronizeBySizeCheck->Caption;
 }
 //---------------------------------------------------------------------------
 __fastcall TFullSynchronizeDialog::~TFullSynchronizeDialog()
@@ -78,31 +80,37 @@ void __fastcall TFullSynchronizeDialog::UpdateControls()
   {
     SynchronizeExistingOnlyCheck->Checked = true;
     SynchronizePreviewChangesCheck->Checked = false;
-    SynchronizeNoConfirmationCheck->Checked = false;
   }
-  if (SynchronizeTimestampCheck->Checked || SynchronizeBothButton->Checked)
+  if (SynchronizeTimestampCheck->Checked)
   {
     SynchronizeDeleteCheck->Checked = false;
     SynchronizeByTimeCheck->Checked = true;
+  }
+  if (SynchronizeBothButton->Checked)
+  {
     SynchronizeBySizeCheck->Checked = false;
   }
   EnableControl(SynchronizeDeleteCheck, !SynchronizeBothButton->Checked && 
     !SynchronizeTimestampCheck->Checked);
   EnableControl(SynchronizeExistingOnlyCheck, !SynchronizeTimestampCheck->Checked);
   EnableControl(SynchronizePreviewChangesCheck, !SynchronizeTimestampCheck->Checked);
-  EnableControl(SynchronizeNoConfirmationCheck, !SynchronizeTimestampCheck->Checked);
   EnableControl(SynchronizeByTimeCheck, !SynchronizeBothButton->Checked && 
     !SynchronizeTimestampCheck->Checked);
   EnableControl(SynchronizeBySizeCheck, !SynchronizeBothButton->Checked && 
     !SynchronizeTimestampCheck->Checked);
+  EnableControl(SynchronizeBySizeCheck, !SynchronizeBothButton->Checked);
+  EnableControl(SynchronizeSelectedOnlyCheck, FLAGSET(FOptions, fsoAllowSelectedOnly));
   EnableControl(OkButton, !LocalDirectoryEdit->Text.IsEmpty() &&
     !RemoteDirectoryEdit->Text.IsEmpty());
 
-  AnsiString InfoStr = FCopyParams.GetInfoStr("; ");
+  AnsiString InfoStr = FCopyParams.GetInfoStr("; ",
+    FLAGMASK(SynchronizeTimestampCheck->Checked, TCopyParamType::cpiExcludeMaskOnly));
   CopyParamLabel->Caption = InfoStr;
   CopyParamLabel->Hint = InfoStr;
   CopyParamLabel->ShowHint =
     (CopyParamLabel->Canvas->TextWidth(InfoStr) > (CopyParamLabel->Width * 3 / 2));
+  SynchronizeBySizeCheck->Caption = SynchronizeTimestampCheck->Checked ?
+    LoadStr(SYNCHRONIZE_SAME_SIZE) : FSynchronizeBySizeCaption;
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::ControlChange(TObject * /*Sender*/)
@@ -188,12 +196,12 @@ TSynchronizeMode __fastcall TFullSynchronizeDialog::GetMode()
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::SetParams(int value)
 {
-  FParams = value & ~(spDelete | spNoConfirmation | spExistingOnly |
-    spPreviewChanges | spTimestamp | spNotByTime | spBySize);
+  FParams = value & ~(spDelete | spExistingOnly |
+    spPreviewChanges | spTimestamp | spNotByTime | spBySize | spSelectedOnly);
   SynchronizeDeleteCheck->Checked = FLAGSET(value, spDelete);
-  SynchronizeNoConfirmationCheck->Checked = FLAGSET(value, spNoConfirmation);
   SynchronizeExistingOnlyCheck->Checked = FLAGSET(value, spExistingOnly);
   SynchronizePreviewChangesCheck->Checked = FLAGSET(value, spPreviewChanges);
+  SynchronizeSelectedOnlyCheck->Checked = FLAGSET(value, spSelectedOnly);
   SynchronizeTimestampCheck->Checked = FLAGSET(value, spTimestamp) &&
     FLAGCLEAR(Options, fsoDisableTimestamp);
   SynchronizeByTimeCheck->Checked = FLAGCLEAR(value, spNotByTime);
@@ -205,9 +213,9 @@ int __fastcall TFullSynchronizeDialog::GetParams()
 {
   return FParams |
     FLAGMASK(SynchronizeDeleteCheck->Checked, spDelete) |
-    FLAGMASK(SynchronizeNoConfirmationCheck->Checked, spNoConfirmation) |
     FLAGMASK(SynchronizeExistingOnlyCheck->Checked, spExistingOnly) |
     FLAGMASK(SynchronizePreviewChangesCheck->Checked, spPreviewChanges) |
+    FLAGMASK(SynchronizeSelectedOnlyCheck->Checked, spSelectedOnly) |
     FLAGMASK(SynchronizeTimestampCheck->Checked && FLAGCLEAR(Options, fsoDisableTimestamp),
       spTimestamp) |
     FLAGMASK(!SynchronizeByTimeCheck->Checked, spNotByTime) |
@@ -250,13 +258,21 @@ void __fastcall TFullSynchronizeDialog::SetOptions(int value)
 void __fastcall TFullSynchronizeDialog::TransferSettingsButtonClick(
   TObject * /*Sender*/)
 {
-  CopyParamListPopup(
-    TransferSettingsButton->ClientToScreen(TPoint(0, TransferSettingsButton->Height)),
-    FPresetsMenu, FCopyParams, FPreset, CopyParamClick, cplCustomize);
+  if (FLAGCLEAR(FOptions, fsoDoNotUsePresets))
+  {
+    CopyParamListPopup(
+      TransferSettingsButton->ClientToScreen(TPoint(0, TransferSettingsButton->Height)),
+      FPresetsMenu, FCopyParams, FPreset, CopyParamClick, cplCustomize);
+  }
+  else
+  {
+    CopyParamGroupDblClick(NULL);
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::CopyParamClick(TObject * Sender)
 {
+  assert(FLAGCLEAR(FOptions, fsoDoNotUsePresets));
   if (CopyParamListPopupClick(Sender, FCopyParams, FPreset))
   {
     UpdateControls();
@@ -308,15 +324,19 @@ void __fastcall TFullSynchronizeDialog::SetCopyParams(const TCopyParamType & val
 void __fastcall TFullSynchronizeDialog::CopyParamGroupContextPopup(
   TObject * /*Sender*/, TPoint & MousePos, bool & Handled)
 {
-  CopyParamListPopup(CopyParamGroup->ClientToScreen(MousePos), FPresetsMenu,
-    FCopyParams, FPreset, CopyParamClick, cplCustomize | cplCustomizeDefault);
-  Handled = true;
+  if (FLAGCLEAR(FOptions, fsoDoNotUsePresets))
+  {
+    CopyParamListPopup(CopyParamGroup->ClientToScreen(MousePos), FPresetsMenu,
+      FCopyParams, FPreset, CopyParamClick, cplCustomize | cplCustomizeDefault);
+    Handled = true;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TFullSynchronizeDialog::CopyParamGroupDblClick(
   TObject * /*Sender*/)
 {
-  if (DoCopyParamCustomDialog(FCopyParams))
+  if (DoCopyParamCustomDialog(FCopyParams,
+       (SynchronizeTimestampCheck->Checked ? cfAllowExcludeMaskOnly : -1)))
   {
     UpdateControls();
   }

+ 4 - 4
forms/FullSynchronize.dfm

@@ -127,12 +127,12 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       TabOrder = 1
       OnClick = ControlChange
     end
-    object SynchronizeNoConfirmationCheck: TCheckBox
+    object SynchronizeSelectedOnlyCheck: TCheckBox
       Left = 139
       Top = 68
       Width = 123
       Height = 17
-      Caption = '&No confirmations'
+      Caption = 'S&elected files only'
       TabOrder = 4
       OnClick = ControlChange
     end
@@ -180,7 +180,7 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
     Width = 417
     Height = 49
     Anchors = [akLeft, akTop, akRight]
-    Caption = 'Direction'
+    Caption = 'Direction/Target directory'
     TabOrder = 1
     object SynchronizeBothButton: TRadioButton
       Left = 11
@@ -231,7 +231,7 @@ object FullSynchronizeDialog: TFullSynchronizeDialog
       Top = 44
       Width = 121
       Height = 17
-      Caption = '&File size'
+      Caption = 'File si&ze'
       TabOrder = 1
       OnClick = SynchronizeByTimeSizeCheckClick
     end

+ 2 - 1
forms/FullSynchronize.h

@@ -23,7 +23,7 @@ __published:
   THistoryComboBox *LocalDirectoryEdit;
   TXPGroupBox *OptionsGroup;
   TCheckBox *SynchronizeDeleteCheck;
-  TCheckBox *SynchronizeNoConfirmationCheck;
+  TCheckBox *SynchronizeSelectedOnlyCheck;
   TButton *LocalDirectoryBrowseButton;
   TCheckBox *SynchronizeExistingOnlyCheck;
   TButton *TransferSettingsButton;
@@ -59,6 +59,7 @@ private:
   TCopyParamType FCopyParams;
   TPopupMenu * FPresetsMenu;
   AnsiString FPreset;
+  AnsiString FSynchronizeBySizeCaption;
   void __fastcall SetRemoteDirectory(const AnsiString value);
   AnsiString __fastcall GetRemoteDirectory();
   void __fastcall SetLocalDirectory(const AnsiString value);

+ 2 - 2
forms/GeneralSettings.dfm

@@ -18,7 +18,7 @@ object GeneralSettingsFrame: TGeneralSettingsFrame
     DesignSize = (
       306
       202)
-    object CommanderDescriptionLabel: TLabel
+    object CommanderDescriptionLabel2: TLabel
       Left = 132
       Top = 19
       Width = 167
@@ -29,7 +29,7 @@ object GeneralSettingsFrame: TGeneralSettingsFrame
         '- two panels (left for local directory, right for remote directo' +
         'ry)'#13#10'- keyboard shortcuts like in Norton Commander (and other si' +
         'milar programs as Total Commander, Midnight Commander...)'#13#10'- dra' +
-        'g && drop to/from both panels'#13#10'- synchronization'
+        'g && drop to/from both panels'
       WordWrap = True
       OnClick = CommanderClick
     end

+ 1 - 1
forms/GeneralSettings.h

@@ -14,7 +14,7 @@ class TGeneralSettingsFrame : public TFrame
 {
 __published:
   TXPGroupBox *InterfaceGroup;
-  TLabel *CommanderDescriptionLabel;
+  TLabel *CommanderDescriptionLabel2;
   TLabel *ExplorerDescriptionLabel;
   TImage *CommanderInterfacePicture;
   TImage *ExplorerInterfacePicture;

+ 368 - 104
forms/Glyphs.dfm

@@ -8,9 +8,265 @@ object GlyphsModule: TGlyphsModule
     Left = 32
     Top = 16
     Bitmap = {
-      494C010153005400040010001000FFFFFFFFFF00FFFFFFFFFFFFFFFF424D3600
-      0000000000003600000028000000400000005001000001002000000000000050
+      494C010157005900040010001000FFFFFFFFFF00FFFFFFFFFFFFFFFF424D3600
+      0000000000003600000028000000400000007001000001002000000000000070
       0100000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000004C4C
+      4C004C4C4C004C4C4C004C4C4C004C4C4C0000000000000000004C4C4C004C4C
+      4C004C4C4C004C4C4C004C4C4C00000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000FF00000000000000FF0000000000
+      0000FF00000000000000FF00000000000000FF00000000000000FF0000000000
+      0000FF00000000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000004C4C
+      4C009F705B009A705B009F705B004C4C4C0000000000000000004C4C4C009F70
+      5B009A705B009F705B004C4C4C00000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C004C4C4C004C4C
+      4C00A4705B00A4705B00A4705B004C4C4C004C4C4C004C4C4C004C4C4C00A470
+      5B00A4705B00A4705B004C4C4C00000000000000000000000000000000000000
+      0000000000000000000066CC3300000000000000000000000000000000000000
+      000000000000000000000000000000000000FF00000000000000000000000000
+      0000000000000000000000008000000080000000800000000000000000000000
+      00000000000000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C009F705B004C4C
+      4C00AF755B00AF755B00AF755B004C4C4C004C4C4C009F705B004C4C4C00AF75
+      5B00AF755B00AF755B004C4C4C00000000000000000000000000000000000000
+      0000000000000066330066FF330066CC33000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000080000000800000000000000000000000000000008000000080000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00A4705B004C4C
+      4C00BA7A5B00BA7A5B00BA7A5B004C4C4C004C4C4C00A4705B004C4C4C00BA7A
+      5B00BA7A5B00BA7A5B004C4C4C00000000000000000000000000000000000000
+      000066CC33000066330066FF330066FF330066CC330000000000000000000000
+      000000000000000000000000000000000000FF00000000000000000000000000
+      8000000000000000000000000000000000000000000000000000000000000000
+      80000000000000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00AF755B004C4C
+      4C00BA7A5B00BF7A5B00BA7A5B004C4C4C004C4C4C00AF755B004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C00000000000000000000000000000000000066
+      330066FF33000066330066FF330066FF330066FF330066CC3300000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      8000000000000000000000000000000000000000000000000000000000000000
+      8000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00BA7A5B004C4C
+      4C00BA7A5B00BF7A5B00BA7A5B004C4C4C004C4C4C00BA7A5B004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C00000000000000000000000000000000000066
+      330066FF33000066330099FFCC0066FF330066FF330066FF330066CC33000000
+      000000000000000000000000000000000000FF00000000000000000080000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000800000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00BA7A5B004C4C
+      4C00BF7F6000BF7F6000BF7F60004C4C4C004C4C4C00BA7A5B004C4C4C00BF7F
+      6000BF7F6000BF7F60004C4C4C00000000000000000000000000000000000066
+      330066FF33000066330099FFCC0099FFCC0066FF330066FF3300000000000000
+      0000000000000000000000000000000000000000000000000000000080000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000080000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00BA7A5B004C4C
+      4C00C58A6A00C58A7000C58A6A004C4C4C004C4C4C00BA7A5B004C4C4C00C58A
+      6A00C58A7000C58A6A004C4C4C00000000000000000000000000000000000066
+      330099FFCC0000663300FFFFFF0099FFCC0099FFCC0000000000000000000000
+      000000000000000000000000000000000000FF00000000000000000080000000
+      0000000000000000000000000000000000000000000000008000000000000000
+      00000000800000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00BF7F60004C4C
+      4C00CA947500CF9A7F00CA947A004C4C4C004C4C4C00BF7F60004C4C4C00CA94
+      7500CF9A7F00CA947A004C4C4C00000000000000000000000000000000000066
+      330099FFCC0000663300FFFFFF00FFFFFF000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      8000000000000000000000000000000000000000000000008000000080000000
+      8000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00C58A6A004C4C
+      4C00CF9F8500CF9F8500CF9F85004C4C4C004C4C4C00C58A6A004C4C4C00CF9F
+      8500CF9F8500CF9F85004C4C4C00000000000000000000000000000000000066
+      3300FFFFFF0000663300FFFFFF00000000000000000000000000000000000000
+      000000000000000000000000000000000000FF00000000000000000000000000
+      8000000000000000000000000000000000000000000000008000000080000000
+      80000000000000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00CA9475004C4C
+      4C00CF9F8500CF9F8500CF9F85004C4C4C004C4C4C00CA9475004C4C4C00CF9F
+      8500CF9F8500CF9F85004C4C4C00000000000000000000000000000000000066
+      3300FFFFFF000066330000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000080000000800000000000000000000000000000008000000080000000
+      8000000080000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00CF9F85004C4C
+      4C004C4C4C004C4C4C004C4C4C004C4C4C004C4C4C00CF9F85004C4C4C004C4C
+      4C004C4C4C004C4C4C004C4C4C00000000000000000000000000000000000066
+      3300FFFFFF000066330000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000FF00000000000000000000000000
+      0000000000000000000000008000000080000000000000000000000000000000
+      00000000000000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C00CF9F8500CF9F
+      8500CF9F85004C4C4C0000000000000000004C4C4C00CF9F8500CF9F8500CF9F
+      85004C4C4C000000000000000000000000000000000000000000000000000066
+      3300000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000000000004C4C4C004C4C4C004C4C
+      4C004C4C4C004C4C4C0000000000000000004C4C4C004C4C4C004C4C4C004C4C
+      4C004C4C4C000000000000000000000000000000000000000000000000000066
+      3300000000000000000000000000000000000000000000000000000000000000
+      000000000000000000000000000000000000FF00000000000000FF0000000000
+      0000FF00000000000000FF00000000000000FF00000000000000FF0000000000
+      0000FF00000000000000FF000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000008080800000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000008080800000000000000000000000
@@ -24,121 +280,121 @@ object GlyphsModule: TGlyphsModule
       0000808080008000000080000000808080000000000080808000800000008080
       80000000000000000000000000000000000080808000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF0000000000C0C0C000C0C0C000C0C0C000C0C0C000C0C0
-      C000C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000008080
+      C000C0C0C00000000000000000000000000000000000000000004C4C4C004C4C
+      4C004C4C4C004C4C4C004C4C4C0000000000000000004C4C4C004C4C4C004C4C
+      4C004C4C4C004C4C4C0000000000000000000000000000000000000000008080
       800080000000FF000000FF000000FF0000000000000000000000808080008000
       0000808080000000000000000000000000000000000000000000000000008080
       800080000000FF000000FF000000FF0000000000000000000000808080008000
       00008080800000000000000000000000000080808000FFFFFF0000000000FFFF
       FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000808080008000
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C009F70
+      5B009A705B009F705B004C4C4C0000000000000000004C4C4C009F705B009A70
+      5B009F705B004C4C4C0000000000000000000000000000000000808080008000
       0000FF000000FF000000FF000000FF000000000000000000FF00000000008080
       8000800000008080800000000000000000000000000000000000808080008000
       0000FF000000FF000000FF000000FF0000000000000080808000000000008080
       80008000000080808000000000000000000080808000FFFFFF00000000008080
       800000000000FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF000000FF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      000000000000000000000000000000000000000000000000000080000000FF00
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00A470
+      5B00A4705B00A4705B004C4C4C0000000000000000004C4C4C00A4705B00A470
+      5B00A4705B004C4C4C000000000000000000000000000000000080000000FF00
       0000FF00000000808000000000000000000000000000000000000000FF000000
       000080808000800000000000000000000000000000000000000080000000FF00
       0000FF0000000080800000000000000000000000000000000000808080000000
       00008080800080000000000000000000000080808000FFFFFF00FFFFFF000000
       000000000000FFFFFF00000000000000FF000000FF000000FF000000FF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF000080
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00AF75
+      5B00AF755B00AF755B004C4C4C0000000000000000004C4C4C00AF755B00AF75
+      5B00AF755B004C4C4C0000000000000000000000000080808000FFFFFF000080
       800000808000FFFFFF00000000000000FF000000FF008000FF000000FF000000
       FF00000000008080800080808000000000000000000080808000FFFFFF000080
       800000808000FFFFFF0000000000808080008080800080808000808080008080
       80000000000080808000808080000000000080808000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF0000000000FFFFFF000000FF00FFFFFF000000FF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00BA7A
+      5B00BA7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BA7A
+      5B00BA7A5B004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF0000808000FF000000000000008000FF000000FF00000000000000
       0000000000000000000080000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF0000808000FF000000000000008080800080808000000000000000
       0000000000000000000080000000000000008080800080808000808080008080
       8000808080008080800000000000FFFFFF00FFFFFF000000FF000000FF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF0000808000FF000000000000000000FF008000FF000000FF000000
       000080808000FFFFFF0080000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF0000808000FF000000000000008080800080808000808080000000
       000080808000FFFFFF0080000000000000000000000080808000FFFFFF00FF00
       0000FF000000FF000000FF000000FF000000FF000000FFFFFF000000FF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF0000000000000000000000000000000000000000000000FF008000FF000000
       FF00000000008080800080000000000000000000000080808000FFFFFF00FFFF
       FF00000000000000000000000000000000000000000080808000808080008080
       8000000000008080800080000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF00000000008000FF000000FF008000FF000000FF008000FF000000FF008000
       FF000000FF000000000080808000000000000000000080808000FFFFFF00FFFF
       FF00000000008080800080808000808080008080800080808000808080008080
       8000808080000000000080808000000000000000000080808000FFFFFF000000
       00000000000000000000FFFFFF00FF000000FF000000FF000000FF000000FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      000000000000000000000000000000000000000000008080800080808000FFFF
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C000000000000000000000000008080800080808000FFFF
       FF00FFFFFF00000000008000FF000000FF008000FF000000FF00000000000000
       000000000000000000008080800000000000000000008080800080808000FFFF
       FF00FFFFFF000000000080808000808080008080800080808000000000000000
       0000000000000000000080808000000000000000000080808000FFFFFF000000
       FF000000FF0080808000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000808080000080
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00BF7F
+      6000BF7F6000BF7F60004C4C4C0000000000000000004C4C4C00BF7F6000BF7F
+      6000BF7F60004C4C4C0000000000000000000000000000000000808080000080
       800000808000000000000000FF008000FF000000FF008000FF000000FF000000
       0000808080008000000000000000000000000000000000000000808080000080
       8000008080000000000080808000808080008080800080808000808080000000
       0000808080008000000000000000000000000000000080808000FFFFFF00FF00
       0000FFFFFF0080808000FFFFFF00FF000000FF000000FF000000FF000000FFFF
-      FF00C0C0C0000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000808080008080
+      FF00C0C0C00000000000000000000000000000000000000000004C4C4C00C58A
+      6A00C58A7000C58A6A004C4C4C0000000000000000004C4C4C00C58A6A00C58A
+      7000C58A6A004C4C4C0000000000000000000000000000000000808080008080
       8000FF00000000808000000000000000FF008000FF000000FF008000FF000000
       FF00000000008080800000000000000000000000000000000000808080008080
       8000FF0000000080800000000000808080008080800080808000808080008080
       8000000000008080800000000000000000000000000080808000FFFFFF00FF00
       0000FFFFFF0080808000FFFFFF00FFFFFF00FFFFFF00FFFFFF00000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000008080
+      00000000000000000000000000000000000000000000000000004C4C4C00CA94
+      7500CF9A7F00CA947A004C4C4C0000000000000000004C4C4C00CA947500CF9A
+      7F00CA947A004C4C4C0000000000000000000000000000000000000000008080
       800080808000FF000000000000008000FF008000FF008000FF000000FF008000
       FF008000FF000000000080808000000000000000000000000000000000008080
       800080808000FF00000000000000808080008080800080808000808080008080
       8000808080000000000080808000000000000000000080808000FFFFFF008080
       80008080800080808000FFFFFF00FFFFFF00FFFFFF00FFFFFF00C0C0C000FFFF
-      FF00808080000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      FF008080800000000000000000000000000000000000000000004C4C4C00CF9F
+      8500CF9F8500CF9F85004C4C4C0000000000000000004C4C4C00CF9F8500CF9F
+      8500CF9F85004C4C4C0000000000000000000000000000000000000000000000
       0000808080008080800080808000000000008000FF000000FF008000FF008000
       FF000000FF008000FF0000000000808080000000000000000000000000000000
       0000808080008080800080808000000000008080800080808000808080008080
       8000808080008080800000000000808080000000000080808000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C0C0C0008080
-      8000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      80000000000000000000000000000000000000000000000000004C4C4C00CF9F
+      8500CF9F8500CF9F85004C4C4C0000000000000000004C4C4C00CF9F8500CF9F
+      8500CF9F85004C4C4C0000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000080808000808080008080
       8000808080008080800080808000808080008080800080808000808080000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000004C4C4C004C4C
+      4C004C4C4C004C4C4C004C4C4C0000000000000000004C4C4C004C4C4C004C4C
+      4C004C4C4C004C4C4C0000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -2699,12 +2955,20 @@ object GlyphsModule: TGlyphsModule
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       000000000000000000000000000000000000424D3E000000000000003E000000
-      2800000040000000500100000100010000000000800A00000000000000000000
-      000000000000000000000000FFFFFF00FE7FFE7F00030000F00FF00F00030000
-      E007E00700030000C003C00300030000C003C003000300008001800100030000
-      8001800100030000800180018003000080018001800300008001800180030000
-      8001800180030000C003C00380030000C003C00380030000E001E00180070000
-      F000F000800F0000FE00FE00801F0000FF81FFFFFFFFFE7FFF81000FFFFFF83F
+      2800000040000000700100000100010000000000800B00000000000000000000
+      000000000000000000000000FFFFFF0000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000FFFFFFFFFFFF0000E0C1FBFF55550000
+      E0C1F9FFFFFF00008001E8FF7C7D00008001E07FF39F00008001E03F6FED0000
+      8001E01FEFEF00008001E00F5FF500008001E01FDFF700008001E03F5FB50000
+      8001E07FEF8F00008001E0FF6F8D00008001E1FFF38700008001E3FF7CFD0000
+      8307E7FFFFFF00008307EFFF55550000FE7FFE7F0003FFFFF00FF00F0003C183
+      E007E0070003C183C003C0030003C183C003C0030003C183800180010003C183
+      800180010003C183800180018003C183800180018003C183800180018003C183
+      800180018003C183C003C0038003C183C003C0038003C183E001E0018007C183
+      F000F000800FC183FE00FE00801FC183FF81FFFFFFFFFE7FFF81000FFFFFF83F
       F081000FAABFE01FF781000FD57F800FF78F000FA8010007F7FF000FD4010003
       F781000FA8010001F081000FD4010000F781000FA8010000F7810007D4018000
       FF8F0007A801C00081FF0003D403E00081FF0003FC07F00181FF0001FC0FF807
@@ -3197,7 +3461,7 @@ object GlyphsModule: TGlyphsModule
     Left = 136
     Top = 80
     Bitmap = {
-      494C010107000900040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
+      494C010108000900040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
       0000000000003600000028000000400000003000000001002000000000000030
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -3341,121 +3605,121 @@ object GlyphsModule: TGlyphsModule
       0000808080000000800000008000000080000000800000008000808080008080
       8000808080000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000004C4C4C004C4C
+      4C004C4C4C004C4C4C004C4C4C0000000000000000004C4C4C004C4C4C004C4C
+      4C004C4C4C004C4C4C0000000000000000000000000000000000000000000000
       000000000000000000000000000000000000FFFFFF0000000000808080000000
       0000000000000000000000000000000000000000000000000000000000000000
       8000000080000000FF000000FF000000FF000000FF000000FF00000080000000
       8000808080008080800000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000004C4C4C009F70
+      5B009A705B009F705B004C4C4C0000000000000000004C4C4C009F705B009A70
+      5B009F705B004C4C4C0000000000000000000000000000000000000000000000
       0000C0C0C0000000000000000000C0C0C000FFFFFF0000000000808080008080
       8000C0C0C0000000000000000000000000000000000000000000000080000000
       FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000
       FF000000800080808000808080000000000086868600CCCCCC00CCCCCC00CCCC
       CC00CCCCCC00CCCCCC00CCCCCC00CCCCCC00CCCCCC00CCCCCC00CCCCCC00CCCC
-      CC00CCCCCC00CCCCCC00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      CC00CCCCCC00CCCCCC00CCCCCC000000000000000000000000004C4C4C00A470
+      5B00A4705B00A4705B004C4C4C0000000000000000004C4C4C00A4705B00A470
+      5B00A4705B004C4C4C0000000000000000000000000000000000000000000000
       000000000000C0C0C000FFFFFF00FFFFFF00FFFFFF00C0C0C000000000000000
       00008080800080808000000000000000000000000000000080000000FF000000
       FF00FFFFFF000000FF000000FF000000FF000000FF000000FF00FFFFFF000000
       FF000000FF0000008000808080000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      000000000000000000000000000000000000000000000000000000000000C0C0
+      FF00FFFFFF00FFFFFF00CCCCCC000000000000000000000000004C4C4C00AF75
+      5B00AF755B00AF755B004C4C4C0000000000000000004C4C4C00AF755B00AF75
+      5B00AF755B004C4C4C000000000000000000000000000000000000000000C0C0
       C000FFFFFF00FFFFFF00FFFFFF00FF000000FF000000FFFFFF00FFFFFF00C0C0
       C0000000000080808000808080000000000000000000000080000000FF00FFFF
       FF00FFFFFF00FFFFFF000000FF000000FF000000FF00FFFFFF00FFFFFF00FFFF
       FF000000FF0000008000808080008080800086868600FFFFFF00FFFFFF009933
       0000993300009933000099330000FFFFFF00FFFFFF0099330000993300009933
-      0000FFFFFF00FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      0000FFFFFF00FFFFFF00CCCCCC000000000000000000000000004C4C4C00BA7A
+      5B00BA7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BA7A
+      5B00BA7A5B004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF0080808000FF000000FFFFFF00FFFFFF00FFFF
       FF00C0C0C0000000000080808000C0C0C000000080000000FF000000FF000000
       FF00FFFFFF00FFFFFF00FFFFFF000000FF00FFFFFF00FFFFFF00FFFFFF000000
       FF000000FF000000FF00000080008080800086868600FFFFFF00FF0000009933
       0000FFFFFF00FF00000099330000FFFFFF00FF00000099330000FFFFFF009933
-      000099330000FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000080808000C0C0C000FFFFFF00FFFF
+      000099330000FFFFFF00CCCCCC000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C00000000000000000080808000C0C0C000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00C0C0C0000000000080808000000080000000FF000000FF000000
       FF000000FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FF000000
       FF000000FF000000FF00000080008080800086868600FFFFFF00FF0000009933
       0000FFFFFF00FF00000099330000FFFFFF00FF00000099330000FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000080808000FFFFFF00FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00CCCCCC000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C00000000000000000080808000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FF000000FF000000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF000000000080808000000080000000FF000000FF000000
       FF000000FF000000FF00FFFFFF00FFFFFF00FFFFFF000000FF000000FF000000
       FF000000FF000000FF00000080008080800086868600FFFFFF00FFFFFF00FF00
       0000FF000000FF00000099330000FFFFFF00FF00000099330000993300009933
-      000099330000FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000080808000FFFFFF00FFFFFF00FFFF
+      000099330000FFFFFF00CCCCCC000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C00000000000000000080808000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF0080808000FF000000FF000000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF000000000080808000000080000000FF000000FF000000
       FF000000FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000FF000000
       FF000000FF000000FF00000080008080800086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FF00000099330000FFFFFF00FF00000099330000FFFFFF00FF00
-      000099330000FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000080808000FFFFFF00FFFFFF00FFFF
+      000099330000FFFFFF00CCCCCC000000000000000000000000004C4C4C00BA7A
+      5B00BF7A5B00BA7A5B004C4C4C0000000000000000004C4C4C00BA7A5B00BF7A
+      5B00BA7A5B004C4C4C00000000000000000080808000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0080808000FF00000080808000FFFF
       FF00FFFFFF00FFFFFF000000000080808000000080000000FF000000FF000000
       FF00FFFFFF00FFFFFF00FFFFFF000000FF00FFFFFF00FFFFFF00FFFFFF000000
       FF000000FF000000FF00000080000000000086868600FFFFFF00FFFFFF00FF00
       0000FF000000FF000000FFFFFF00FFFFFF00FFFFFF00FF000000FF000000FF00
-      0000FFFFFF00FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000000080808000FFFFFF00FFFFFF00FFFF
+      0000FFFFFF00FFFFFF00CCCCCC000000000000000000000000004C4C4C00BF7F
+      6000BF7F6000BF7F60004C4C4C0000000000000000004C4C4C00BF7F6000BF7F
+      6000BF7F60004C4C4C00000000000000000080808000FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FF000000FF000000FFFFFF00FFFFFF00FF000000FF000000FFFF
       FF00FFFFFF00C0C0C000000000000000000000000000000080000000FF00FFFF
       FF00FFFFFF00FFFFFF000000FF000000FF000000FF00FFFFFF00FFFFFF00FFFF
       FF000000FF0000008000808080000000000086868600FFFFFF00FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
-      FF00FFFFFF00FFFFFF00CCCCCC00000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      FF00FFFFFF00FFFFFF00CCCCCC000000000000000000000000004C4C4C00C58A
+      6A00C58A7000C58A6A004C4C4C0000000000000000004C4C4C00C58A6A00C58A
+      7000C58A6A004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF0080808000FF000000FFFFFF00FFFFFF00FF000000FF000000FFFF
       FF00FFFFFF0000000000808080000000000000000000000080000000FF000000
       FF00FFFFFF000000FF000000FF000000FF000000FF000000FF00FFFFFF000000
       FF000000FF000000800000000000000000008000000080000000800000008000
       0000800000008000000080000000800000008000000080000000800000008000
-      0000800000008000000080000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000080808000FFFFFF00FFFF
+      00008000000080000000800000000000000000000000000000004C4C4C00CA94
+      7500CF9A7F00CA947A004C4C4C0000000000000000004C4C4C00CA947500CF9A
+      7F00CA947A004C4C4C0000000000000000000000000080808000FFFFFF00FFFF
       FF00FFFFFF00FFFFFF00FF000000FF000000FF000000FF000000FFFFFF00FFFF
       FF00C0C0C0000000000000000000000000000000000000000000000080000000
       FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000
       FF0000008000000000000000000000000000FF000000FF993300FF663300FF66
       3300FF663300FF663300FF663300FF663300FF663300FF663300FF663300FF66
-      3300FF663300FF663300FF663300000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000808080008080
+      3300FF663300FF663300FF6633000000000000000000000000004C4C4C00CF9F
+      8500CF9F8500CF9F85004C4C4C0000000000000000004C4C4C00CF9F8500CF9F
+      8500CF9F85004C4C4C0000000000000000000000000000000000808080008080
       8000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000
       0000000000000000000000000000000000000000000000000000000000000000
       8000000080000000FF000000FF000000FF000000FF000000FF00000080000000
       800000000000000000000000000000000000FF000000FF000000FF000000FF00
       0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF00
-      0000FF000000FF000000FF000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      0000FF000000FF000000FF0000000000000000000000000000004C4C4C00CF9F
+      8500CF9F8500CF9F85004C4C4C0000000000000000004C4C4C00CF9F8500CF9F
+      8500CF9F85004C4C4C0000000000000000000000000000000000000000000000
       0000808080008080800080808000808080008080800080808000808080000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000800000008000000080000000800000008000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+      00000000000000000000000000000000000000000000000000004C4C4C004C4C
+      4C004C4C4C004C4C4C004C4C4C0000000000000000004C4C4C004C4C4C004C4C
+      4C004C4C4C004C4C4C0000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -3589,11 +3853,11 @@ object GlyphsModule: TGlyphsModule
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      00000000000000000000000000000000FF9FFC1FFFFF0000FF1FF007FFFF0000
-      FE1FE00300000000F007C00100000000E003800100000000C001800000000000
-      8000000000000000000000000000000000000000000000000000000000000000
-      0000000100000000000180010000000080018003000000008003C00700000000
-      C007E00F00000000F01FF83FFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFC01FC01
+      00000000000000000000000000000000FF9FFC1FFFFFFFFFFF1FF007FFFFC183
+      FE1FE0030000C183F007C0010000C183E00380010000C183C00180000000C183
+      800000000000C183000000000000C183000000000000C183000000000000C183
+      000000010000C183000180010000C183800180030000C1838003C0070000C183
+      C007E00F0000C183F01FF83FFFFFC183FFFFFFFFFFFFFFFFFFFFFFFFFC01FC01
       FFFFFFFFFC01FC01F9FFFF3FFC01FC01F1FFFF1F00015401E113910F0001A801
       C113910700015401811391030001A801C113910700035403E113910F0007A807
       F1FFFF1F000F500FF9FFFF3F00F7AAF7FFFFFFFF01E355E3FFFFFFFF03C1ABC1

+ 3 - 2
forms/InputDlg.cpp

@@ -2,6 +2,7 @@
 #include <vcl.h>
 #pragma hdrstop
 
+#include "WinInterface.h"
 #include <VCLCommon.h>
 #include <Windows.hpp>
 #include <Consts.hpp>
@@ -26,7 +27,7 @@ bool __fastcall InputDialog(const AnsiString ACaption,
   TPoint DialogUnits;
   int ButtonTop, ButtonWidth, ButtonHeight;
   bool Result = False;
-  Form = new TForm(Application);
+  Form = new TForm(Application, 0); // bypass the VCL streaming (for Salamander)
   try
   {
     SetCorrectFormParent(Form);
@@ -120,7 +121,7 @@ bool __fastcall InputDialog(const AnsiString ACaption,
       Button->ModalResult = mrNone;
       Button->SetBounds(ButtonsStart + 2 * (ButtonWidth + ButtonSpace), ButtonTop,
         ButtonWidth, ButtonHeight);
-      TNotifyEvent OnClick;  
+      TNotifyEvent OnClick;
       ((TMethod*)&OnClick)->Code = InputDialogHelp;
       Button->OnClick = OnClick;
     }

+ 98 - 26
forms/LocationProfiles.cpp

@@ -203,6 +203,11 @@ void __fastcall TLocationProfilesDialog::LoadBookmarks()
       Parent = dynamic_cast<TTreeNode *>(FFolders->Objects[FFolders->IndexOf(Bookmark->Node)]);
     }
     ProfilesView->Items->AddChildObject(Parent, Bookmark->Name, Bookmark);
+    if ((Parent != NULL) && (Parent->Count == 1))
+    {
+      // only now, when folder node has its first child, we can eventually expand it
+      Parent->Expanded = FBookmarkList->NodeOpened[Parent->Text];
+    }
   }
 }
 //---------------------------------------------------------------------------
@@ -249,41 +254,92 @@ bool __fastcall TLocationProfilesDialog::AddAsBookmark()
   {
     BookmarkName = RemoteDirectory;
   }
-  Result = InputDialog(LoadStr(ADD_BOOKMARK_CAPTION), LoadStr(ADD_BOOKMARK_PROMPT),
-    BookmarkName, HELP_LOCATION_PROFILE_ADD);
-  if (Result)
+
+  TTreeNode * Selected = ProfilesView->Selected;
+  TBookmark * SelectedBookmark = NULL;
+  AnsiString SelectedNode;
+  if (Selected != NULL)
   {
-    if (BookmarkName.IsEmpty() || (StrToIntDef(BookmarkName, -123) != -123))
+    SelectedBookmark = (TBookmark *)Selected->Data;
+    if (SelectedBookmark != NULL)
     {
-      throw Exception(FMTLOAD(BOOKMARK_INVALID_NAME, (BookmarkName)));
+      SelectedNode = SelectedBookmark->Node;
     }
-
-    TBookmark * Bookmark = new TBookmark();
-    Bookmark->Name = BookmarkName;
-    Bookmark->Local = LocalDirectory;
-    Bookmark->Remote = RemoteDirectory;
-    TTreeNode * Selected = ProfilesView->Selected;
-    if (Selected && Selected->Data)
+    else
     {
-      TBookmark * SelectedBookmark = (TBookmark *)Selected->Data;
-      Bookmark->Node = SelectedBookmark->Node;
-      FBookmarkList->InsertBefore(SelectedBookmark, Bookmark);
-      Selected = ProfilesView->Items->InsertObject(Selected, Bookmark->Name, Bookmark);
+      SelectedNode = Selected->Text;
     }
-    else if (Selected && !Selected->Data)
+  }
+  
+  TStrings * PeerBookmarks = new TStringList();
+  try
+  {
+    for (int Index = 0; Index < FBookmarkList->Count; Index++)
     {
-      // must be a folder
-      assert(!Selected->Parent); // more than one level of folders is not supported
-      Bookmark->Node = Selected->Text;
-      FBookmarkList->Add(Bookmark);
-      Selected = ProfilesView->Items->AddChildObject(Selected, Bookmark->Name, Bookmark);
+      TBookmark * Bookmark = FBookmarkList->Bookmarks[Index];
+      if (Bookmark->Node == SelectedNode)
+      {
+        PeerBookmarks->Add(Bookmark->Name);
+      }
     }
-    else
+
+    Result = DoComboInputDialog(LoadStr(ADD_BOOKMARK_CAPTION), LoadStr(ADD_BOOKMARK_PROMPT),
+      BookmarkName, PeerBookmarks, NULL, false, HELP_LOCATION_PROFILE_ADD);
+    if (Result)
     {
-      FBookmarkList->Add(Bookmark);
-      Selected = ProfilesView->Items->AddObject(NULL, Bookmark->Name, Bookmark);
+      if (BookmarkName.IsEmpty() || (StrToIntDef(BookmarkName, -123) != -123))
+      {
+        throw Exception(FMTLOAD(BOOKMARK_INVALID_NAME, (BookmarkName)));
+      }
+
+      TBookmark * Bookmark = FBookmarkList->FindByName(SelectedNode, BookmarkName);
+      if (Bookmark != NULL)
+      {
+        Bookmark->Local = LocalDirectory;
+        Bookmark->Remote = RemoteDirectory;
+
+        for (int Index = 0; Index < ProfilesView->Items->Count; Index++)
+        {
+          TTreeNode * Node = ProfilesView->Items->Item[Index];
+          if (Node->Data == Bookmark)
+          {
+            Selected = Node;
+            break;
+          }
+        }
+      }
+      else
+      {
+        Bookmark = new TBookmark();
+        Bookmark->Name = BookmarkName;
+        Bookmark->Local = LocalDirectory;
+        Bookmark->Remote = RemoteDirectory;
+        if (SelectedBookmark != NULL)
+        {
+          Bookmark->Node = SelectedBookmark->Node;
+          FBookmarkList->InsertBefore(SelectedBookmark, Bookmark);
+          Selected = ProfilesView->Items->InsertObject(Selected, Bookmark->Name, Bookmark);
+        }
+        else if ((Selected != NULL) && (SelectedBookmark == NULL))
+        {
+          // must be a folder
+          assert(!Selected->Parent); // more than one level of folders is not supported
+          Bookmark->Node = Selected->Text;
+          FBookmarkList->Add(Bookmark);
+          Selected = ProfilesView->Items->AddChildObject(Selected, Bookmark->Name, Bookmark);
+        }
+        else
+        {
+          FBookmarkList->Add(Bookmark);
+          Selected = ProfilesView->Items->AddObject(NULL, Bookmark->Name, Bookmark);
+        }
+      }
+      ProfilesView->Selected = Selected;
     }
-    ProfilesView->Selected = Selected;
+  }
+  __finally
+  {
+    delete PeerBookmarks;
   }
 
   UpdateControls();
@@ -644,4 +700,20 @@ void __fastcall TLocationProfilesDialog::HelpButtonClick(TObject * /*Sender*/)
   FormHelp(this);
 }
 //---------------------------------------------------------------------------
+void __fastcall TLocationProfilesDialog::ProfilesViewCollapsed(
+  TObject * /*Sender*/, TTreeNode * Node)
+{
+  assert(Node != NULL);
+  assert(Node->Data == NULL);
+  FBookmarkList->NodeOpened[Node->Text] = false;
+}
+//---------------------------------------------------------------------------
+void __fastcall TLocationProfilesDialog::ProfilesViewExpanded(
+  TObject * /*Sender*/, TTreeNode * Node)
+{
+  assert(Node != NULL);
+  assert(Node->Data == NULL);
+  FBookmarkList->NodeOpened[Node->Text] = true;
+}
+//---------------------------------------------------------------------------
 

+ 3 - 0
forms/LocationProfiles.dfm

@@ -76,11 +76,14 @@ object LocationProfilesDialog: TLocationProfilesDialog
       HideSelection = False
       Images = BookmarkImageList
       Indent = 19
+      ReadOnly = True
       TabOrder = 0
       OnChange = ProfilesViewChange
+      OnCollapsed = ProfilesViewCollapsed
       OnDblClick = ProfilesViewDblClick
       OnDragDrop = ProfilesViewDragDrop
       OnDragOver = ProfilesViewDragOver
+      OnExpanded = ProfilesViewExpanded
       OnGetImageIndex = ProfilesViewGetImageIndex
       OnGetSelectedIndex = ProfilesViewGetSelectedIndex
       OnKeyDown = ProfilesViewKeyDown

+ 2 - 0
forms/LocationProfiles.h

@@ -70,6 +70,8 @@ __published:
   void __fastcall LocalDirectoryBrowseButtonClick(TObject *Sender);
   void __fastcall SwitchButtonClick(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);
+  void __fastcall ProfilesViewCollapsed(TObject *Sender, TTreeNode *Node);
+  void __fastcall ProfilesViewExpanded(TObject *Sender, TTreeNode *Node);
 
 public:
   __fastcall TLocationProfilesDialog(TComponent* AOwner);

+ 9 - 9
forms/LogSettings.dfm

@@ -48,7 +48,7 @@ object LoggingFrame: TLoggingFrame
       Width = 193
       Height = 17
       Caption = 'Log to &file:'
-      TabOrder = 0
+      TabOrder = 1
       OnClick = LogToFileCheckChange
     end
     object LogFileNameEdit: TFilenameEdit
@@ -63,7 +63,7 @@ object LoggingFrame: TLoggingFrame
       DialogTitle = 'Select file for session log.'
       ClickKey = 16397
       Anchors = [akLeft, akTop, akRight]
-      TabOrder = 1
+      TabOrder = 2
       Text = 'LogFileNameEdit'
       OnChange = DataChange
     end
@@ -73,7 +73,7 @@ object LoggingFrame: TLoggingFrame
       Width = 129
       Height = 17
       Caption = 'Show log &window:'
-      TabOrder = 3
+      TabOrder = 4
       OnClick = DataChange
     end
     object LogWindowCompleteButton: TRadioButton
@@ -82,7 +82,7 @@ object LoggingFrame: TLoggingFrame
       Width = 233
       Height = 17
       Caption = 'Display &complete session'
-      TabOrder = 4
+      TabOrder = 5
       OnClick = DataChange
     end
     object LogWindowLinesButton: TRadioButton
@@ -91,7 +91,7 @@ object LoggingFrame: TLoggingFrame
       Width = 136
       Height = 17
       Caption = 'Display only &last '
-      TabOrder = 5
+      TabOrder = 6
       OnClick = DataChange
     end
     object LogWindowLinesEdit: TUpDownEdit
@@ -103,7 +103,7 @@ object LoggingFrame: TLoggingFrame
       Increment = 50
       MaxValue = 10000
       MinValue = 50
-      TabOrder = 6
+      TabOrder = 7
       OnChange = DataChange
     end
     object LogFilePanel: TPanel
@@ -113,7 +113,7 @@ object LoggingFrame: TLoggingFrame
       Height = 25
       Anchors = [akLeft, akTop, akRight]
       BevelOuter = bvNone
-      TabOrder = 2
+      TabOrder = 3
       object LogFileAppendButton: TRadioButton
         Left = 0
         Top = 4
@@ -135,13 +135,13 @@ object LoggingFrame: TLoggingFrame
     end
     object LogProtocolCombo: TComboBox
       Left = 112
-      Top = 15
+      Top = 17
       Width = 113
       Height = 21
       Style = csDropDownList
       Anchors = [akLeft, akTop, akRight]
       ItemHeight = 13
-      TabOrder = 7
+      TabOrder = 0
       Items.Strings = (
         'Normal'
         'Debug 1'

+ 80 - 13
forms/Login.cpp

@@ -5,6 +5,7 @@
 #include <ScpMain.h>
 #include <Common.h>
 #include <TextsWin.h>
+#include <TextsCore.h>
 #include <HelpWin.h>
 #include <VCLCommon.h>
 
@@ -566,15 +567,6 @@ void __fastcall TLoginDialog::UpdateControls()
 
       EnableControl(ShellIconsButton, SessionListView->Selected);
 
-      if (!PrivateKeyEdit->Text.IsEmpty())
-      {
-        PasswordEdit->Clear();
-      }
-      EnableControl(PasswordEdit, PrivateKeyEdit->Text.IsEmpty());
-
-      if (!PasswordEdit->Text.IsEmpty()) PrivateKeyEdit->Clear();
-      EnableControl(PrivateKeyEdit, PasswordEdit->Text.IsEmpty());
-
       EnableControl(PingIntervalSecEdit, !PingOffButton->Checked);
 
       EnableControl(SessionListView, SessionListView->Items->Count);
@@ -616,7 +608,10 @@ void __fastcall TLoginDialog::UpdateControls()
       EnableControl(ProxySettingsGroup, !ProxyNoneButton->Checked);
       EnableControl(ProxyTelnetCommandEdit, ProxyTelnetButton->Checked);
 
-      EnableControl(PreserveDirectoryChangesCheck, CacheDirectoryChangesCheck->Checked);
+      EnableControl(CacheDirectoryChangesCheck,
+        !SCPonlyButton->Checked || CacheDirectoriesCheck->Checked);
+      EnableControl(PreserveDirectoryChangesCheck,
+        CacheDirectoryChangesCheck->Enabled && CacheDirectoryChangesCheck->Checked);
 
       EnableControl(OverwrittenToRecycleBinCheck, !SCPonlyButton->Checked);
       EnableControl(RecycleBinPathEdit,
@@ -847,7 +842,9 @@ void __fastcall TLoginDialog::SaveSessionActionExecute(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 void __fastcall TLoginDialog::DeleteSessionActionExecute(TObject * /*Sender*/)
 {
-  if (SelectedSession)
+  if (SelectedSession &&
+      (MessageDialog(FMTLOAD(CONFIRM_DELETE_SESSION, (SelectedSession->SessionName)),
+         qtConfirmation, qaOK | qaCancel, HELP_DELETE_SESSION) == qaOK))
   {
     int PrevSelectedIndex = SessionListView->Selected->Index;
     SelectedSession->Remove();
@@ -1192,7 +1189,7 @@ void __fastcall TLoginDialog::DesktopIconActionExecute(TObject * /*Sender*/)
   {
     assert(SelectedSession);
     CreateDesktopShortCut(SelectedSession->Name, Application->ExeName,
-      FORMAT("\"%s\"", (SelectedSession->Name)),
+      FORMAT("\"%s\" /UploadIfAny", (SelectedSession->Name)),
       FMTLOAD(SHORTCUT_INFO_TIP, (SelectedSession->Name, SelectedSession->InfoTip)));
   }
 }
@@ -1205,7 +1202,7 @@ void __fastcall TLoginDialog::SendToHookActionExecute(TObject * /*Sender*/)
     assert(SelectedSession);
     CreateDesktopShortCut(FMTLOAD(SESSION_SENDTO_HOOK_NAME, (SelectedSession->Name)),
       Application->ExeName,
-      FORMAT("\"%s\" /upload", (SelectedSession->Name)), "",
+      FORMAT("\"%s\" /Upload", (SelectedSession->Name)), "",
       CSIDL_SENDTO);
   }
 }
@@ -1306,4 +1303,74 @@ void __fastcall TLoginDialog::FormKeyDown(TObject * /*Sender*/, WORD &Key,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TLoginDialog::VerifyKey(AnsiString FileName, bool TypeOnly)
+{
+  if (!FileName.Trim().IsEmpty())
+  {
+    TKeyType Type = KeyType(FileName);
+    AnsiString Message;
+    switch (Type)
+    {
+      case ktOpenSSH:
+        Message = FMTLOAD(KEY_TYPE_UNSUPPORTED, (FileName, "OpenSSH SSH-2"));
+        break;
+
+      case ktSSHCom:
+        Message = FMTLOAD(KEY_TYPE_UNSUPPORTED, (FileName, "ssh.com SSH-2"));
+        break;
+
+      case ktSSH1:
+      case ktSSH2:
+        // on file select do not check for SSH version as user may
+        // inted to change it only after he/she selects key file
+        if (!TypeOnly)
+        {
+          TSessionData * Data = SessionData;
+          if ((Type == ktSSH1) !=
+                ((Data->SshProt == ssh1only) || (Data->SshProt == ssh1)))
+          {
+            Message = FMTLOAD(KEY_TYPE_DIFFERENT_SSH,
+              (FileName, (Type == ktSSH1 ? "SSH-1" : "PuTTY SSH-2")));
+          }
+        }
+        break;
+
+      default:
+        assert(false);
+        // fallthru
+      case ktUnopenable:
+      case ktUnknown:
+        Message = FMTLOAD(KEY_TYPE_UNKNOWN, (FileName));
+        break;
+    }
+
+    if (!Message.IsEmpty())
+    {
+      if (MessageDialog(Message, qtWarning, qaIgnore | qaAbort,
+           HELP_LOGIN_KEY_TYPE) == qaAbort)
+      {
+        Abort();
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TLoginDialog::PrivateKeyEditAfterDialog(TObject * /*Sender*/,
+  AnsiString & Name, bool & /*Action*/)
+{
+  if (Name != PrivateKeyEdit->Text)
+  {
+    VerifyKey(Name, true);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TLoginDialog::FormCloseQuery(TObject * /*Sender*/,
+  bool & /*CanClose*/)
+{
+  if (ModalResult != mrCancel)
+  {
+    VerifyKey(SessionData->PublicKeyFile, false);
+  }
+}
+//---------------------------------------------------------------------------
 

+ 6 - 2
forms/Login.dfm

@@ -13,6 +13,7 @@ object LoginDialog: TLoginDialog
   KeyPreview = True
   OldCreateOrder = True
   Position = poMainFormCenter
+  OnCloseQuery = FormCloseQuery
   OnKeyDown = FormKeyDown
   OnShow = FormShow
   DesignSize = (
@@ -95,6 +96,7 @@ object LoginDialog: TLoginDialog
       Style = tsButtons
       TabIndex = 0
       TabOrder = 1
+      TabStop = False
       OnChange = PageControlChange
       object SessionListSheet: TTabSheet
         Tag = 1
@@ -253,7 +255,7 @@ object LoginDialog: TLoginDialog
             Width = 226
             Height = 21
             Anchors = [akLeft, akTop, akRight]
-            MaxLength = 50
+            MaxLength = 100
             TabOrder = 0
             Text = 'HostNameEdit'
             OnChange = DataChange
@@ -296,6 +298,7 @@ object LoginDialog: TLoginDialog
             Width = 323
             Height = 21
             AcceptFiles = True
+            OnAfterDialog = PrivateKeyEditAfterDialog
             Filter = 'PuTTY Private Key Files (*.ppk)|*.ppk|All files (*.*)|*.*'
             DialogOptions = [ofReadOnly, ofPathMustExist, ofFileMustExist]
             DialogTitle = 'Select private key file'
@@ -688,6 +691,7 @@ object LoginDialog: TLoginDialog
             Anchors = [akLeft, akTop, akRight]
             Caption = 'Cache &visited remote directories'
             TabOrder = 1
+            OnClick = DataChange
           end
           object ResolveSymlinksCheck: TCheckBox
             Left = 11
@@ -1143,7 +1147,7 @@ object LoginDialog: TLoginDialog
           TabOrder = 0
           inherited InterfaceGroup: TXPGroupBox
             Width = 345
-            inherited CommanderDescriptionLabel: TLabel
+            inherited CommanderDescriptionLabel2: TLabel
               Width = 206
             end
             inherited ExplorerDescriptionLabel: TLabel

+ 4 - 0
forms/Login.h

@@ -280,6 +280,9 @@ __published:
   void __fastcall HelpButtonClick(TObject *Sender);
   void __fastcall FormKeyDown(TObject *Sender, WORD &Key,
           TShiftState Shift);
+  void __fastcall PrivateKeyEditAfterDialog(TObject *Sender,
+          AnsiString &Name, bool &Action);
+  void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
 
 private:
   int NoUpdate;
@@ -329,6 +332,7 @@ protected:
   void __fastcall Init();
   void __fastcall InitControls();
   void __fastcall ShowTabs(bool Show);
+  void __fastcall VerifyKey(AnsiString FileName, bool TypeOnly);
 
   __property TTreeView * NavigationTree = { read=GetNavigationTree };
 

+ 1 - 1
forms/MessageDlg.cpp

@@ -119,7 +119,7 @@ const ResourceString * ButtonCaptions[ButtonCount] = {
   &_SMsgDlgYes, &_SMsgDlgNo, &_SMsgDlgOK, &_SMsgDlgCancel, &_SMsgDlgAbort,
   &_SMsgDlgRetry, &_SMsgDlgIgnore, &_SMsgDlgAll, &_SMsgDlgNoToAll, &_SMsgDlgYesToAll,
   &_SMsgDlgHelp };
-const int ModalResults[ButtonCount] = {
+extern const int ModalResults[ButtonCount] = {
   mrYes, mrNo, mrOk, mrCancel, mrAbort, mrRetry, mrIgnore, mrAll, mrNoToAll,
   mrYesToAll, 0 };
 const int mcHorzMargin = 8;

+ 142 - 27
forms/NonVisual.cpp

@@ -78,7 +78,7 @@ __fastcall TNonVisualDataModule::TNonVisualDataModule(TComponent* Owner)
 {
   FListColumn = NULL;
   FSessionIdleTimerExecuting = false;
-  FIdle = true;
+  FIdle = 0;
 }
 //---------------------------------------------------------------------------
 __fastcall TNonVisualDataModule::~TNonVisualDataModule()
@@ -103,8 +103,7 @@ void __fastcall TNonVisualDataModule::LogActionsUpdate(
 void __fastcall TNonVisualDataModule::LogActionsExecute(
       TBasicAction *Action, bool &Handled)
 {
-  assert(FIdle);
-  FIdle = false;
+  FIdle--;
 
   try
   {
@@ -120,7 +119,8 @@ void __fastcall TNonVisualDataModule::LogActionsExecute(
   }
   __finally
   {
-    FIdle = true;
+    assert(FIdle < 0);
+    FIdle++;
   }
   
   DoIdle();
@@ -156,14 +156,13 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
     !DirView(osCurrent)->ItemIsDirectory(DirView(osCurrent)->ItemFocused))
   UPD(CurrentEditAlternativeAction, EnableFocusedOperation &&
     !WinConfiguration->DisableOpenEdit &&
-    !DirView(osCurrent)->ItemIsDirectory(DirView(osCurrent)->ItemFocused) &&
-    (WinConfiguration->Editor.Editor == edExternal || !WinConfiguration->Editor.ExternalEditor.IsEmpty()))
+    !DirView(osCurrent)->ItemIsDirectory(DirView(osCurrent)->ItemFocused))
   UPD(CurrentOpenAction, EnableFocusedOperation &&
     !WinConfiguration->DisableOpenEdit &&
     !DirView(osCurrent)->ItemIsDirectory(DirView(osCurrent)->ItemFocused))
   UPD(AddEditLinkAction, ScpExplorer->Terminal &&
     (DirView(osCurrent) != DirView(osRemote) ||
-     (ScpExplorer->Terminal->IsCapable[fcResolveSymlink] &&
+     (ScpExplorer->Terminal->ResolvingSymlinks &&
       ScpExplorer->Terminal->IsCapable[fcSymbolicLink])))
   // selected operaton
   UPD(CurrentCopyAction, EnableSelectedOperation)
@@ -188,6 +187,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPD(SelectAllAction, DirView(osCurrent)->FilesCount)
   UPD(InvertSelectionAction, DirView(osCurrent)->FilesCount)
   UPD(ClearSelectionAction, DirView(osCurrent)->SelCount)
+  UPD(RestoreSelectionAction, DirView(osCurrent)->SelectedNamesSaved)
   UPD(PasteAction, ScpExplorer->CanPasteFromClipBoard())
 
   //style
@@ -278,6 +278,8 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
     ShowHiddenFilesAction->Checked = WinConfiguration->ShowHiddenFiles, )
   UPD(PreferencesAction, true)
   UPD(PresetsPreferencesAction, true)
+  UPDEX(LockToolbarsAction, true,
+    LockToolbarsAction->Checked = WinConfiguration->LockToolbars, )
 
   // SORT
   UPDSORTA(Local)
@@ -295,13 +297,12 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPDSORT(Remote, uv, Rights)
   UPDSORT(Remote, uv, Owner)
   UPDSORT(Remote, uv, Group)
+  UPDSORT(Remote, uv, Type)
   UPDSORTA(Current)
   UPDSORTC(dv, Name, uv, Name)
   UPDSORTC(dv, Ext, uv, Ext)
   UPDSORTC(dv, Size, uv, Size)
-  #define uvType uvName /* no type columns on remote panel */
   UPDSORTC(dv, Type, uv, Type)
-  #undef uvType
   UPDSORTC(dv, Changed, uv, Changed)
   UPDSORTC(dv, Attr, uv, Rights)
   UPDSORTC(dv, Name, uv, Owner)
@@ -328,6 +329,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPDSHCOL(Remote, uv, Owner)
   UPDSHCOL(Remote, uv, Group)
   UPDSHCOL(Remote, uv, LinkTarget)
+  UPDSHCOL(Remote, uv, Type)
   UPD(HideColumnAction, (ListColumn != NULL))
   UPD(BestFitColumnAction, (ListColumn != NULL))
 
@@ -343,12 +345,13 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPD(SynchronizeAction, true)
   UPD(FullSynchronizeAction, true)
   UPD(ConsoleAction, true)
-  UPD(PuttyAction, true)
+  UPD(PuttyAction, TTerminalManager::Instance()->CanOpenInPutty())
   UPD(SynchronizeBrowsingAction, true)
   UPD(CloseApplicationAction, true)
   UPD(FileSystemInfoAction, true)
   UPD(ClearCachesAction, (ScpExplorer->Terminal != NULL) && !ScpExplorer->Terminal->AreCachesEmpty)
   UPD(EditNewAction, !WinConfiguration->DisableOpenEdit)
+  UPD(EditorListCustomizeAction, true)
 
   // CUSTOM COMMANDS
   UPD(CustomCommandsAction, true)
@@ -367,9 +370,18 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
   UPDEX(QueueItemPromptAction, ScpExplorer->AllowQueueOperation(qoItemPrompt),
     ((TAction *)Action)->Visible = true, ((TAction *)Action)->Visible = false)
   UPDQUEUE(ItemDelete)
-  UPDQUEUE(ItemExecute)
+  UPDEX(QueueItemExecuteAction, ScpExplorer->AllowQueueOperation(qoItemExecute),
+    ((TAction *)Action)->Visible = true, ((TAction *)Action)->Visible = 
+      !ScpExplorer->AllowQueueOperation(qoItemPause) &&
+      !ScpExplorer->AllowQueueOperation(qoItemResume))
+  UPDEX(QueueItemPauseAction, ScpExplorer->AllowQueueOperation(qoItemPause),
+    ((TAction *)Action)->Visible = true, ((TAction *)Action)->Visible = false)
+  UPDEX(QueueItemResumeAction, ScpExplorer->AllowQueueOperation(qoItemResume),
+    ((TAction *)Action)->Visible = true, ((TAction *)Action)->Visible = false)
   UPDQUEUE(ItemUp)
   UPDQUEUE(ItemDown)
+  UPDQUEUE(PauseAll)
+  UPDQUEUE(ResumeAll)
   #undef UPDQUEUE
   UPDACT(QueueToggleShowAction,
     ((TAction *)Action)->Checked = ScpExplorer->ComponentVisible[fcQueueView])
@@ -395,9 +407,9 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     Handled = true;
     return;
   }
+  ScpExplorer->BeforeAction();
   
-  assert(FIdle);
-  FIdle = false;
+  FIdle--;
   try
   {
     // focused operation
@@ -410,8 +422,8 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     // operation
     EXE(CurrentCopyAction, ScpExplorer->ExecuteFileOperation(foCopy, osCurrent, false))
     EXE(CurrentMoveAction, ScpExplorer->ExecuteFileOperation(foMove, osCurrent, false))
-    EXE(CurrentEditAction, ScpExplorer->ExecuteFile(osCurrent, efEditor))
-    EXE(CurrentEditAlternativeAction, ScpExplorer->ExecuteFile(osCurrent, efAlternativeEditor))
+    EXE(CurrentEditAction, ScpExplorer->ExecuteFile(osCurrent, efDefaultEditor))
+    EXE(CurrentEditAlternativeAction, CreateEditorListMenu(CurrentEditAlternativeAction))
     EXE(CurrentOpenAction, ScpExplorer->ExecuteCurrentFile())
     EXE(AddEditLinkAction, ScpExplorer->AddEditLink())
     EXE(CurrentRenameAction, ScpExplorer->ExecuteFileOperation(foRename, osCurrent, false))
@@ -433,6 +445,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXE(SelectAllAction, DirView(osCurrent)->SelectAll(smAll))
     EXE(InvertSelectionAction, DirView(osCurrent)->SelectAll(smInvert))
     EXE(ClearSelectionAction, DirView(osCurrent)->SelectAll(smNone))
+    EXE(RestoreSelectionAction, DirView(osCurrent)->RestoreSelectedNames())
     EXE(PasteAction, ScpExplorer->PasteFromClipBoard())
 
     // style
@@ -514,6 +527,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXE(ShowHiddenFilesAction, WinConfiguration->ShowHiddenFiles = !WinConfiguration->ShowHiddenFiles)
     EXE(PreferencesAction, PreferencesDialog(pmDefault) )
     EXE(PresetsPreferencesAction, PreferencesDialog(pmPresets) )
+    EXE(LockToolbarsAction, WinConfiguration->LockToolbars = !WinConfiguration->LockToolbars)
 
     #define COLVIEWPROPS ((TCustomDirViewColProperties*)(((TCustomDirView*)(((TListColumns*)(ListColumn->Collection))->Owner()))->ColProperties))
     // SORT
@@ -532,11 +546,12 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXESORT(Remote, uv, Rights)
     EXESORT(Remote, uv, Owner)
     EXESORT(Remote, uv, Group)
+    EXESORT(Remote, uv, Type)
     EXESORTA(Current)
     EXESORTC(Name, dvName, uvName)
     EXESORTC(Ext, dvExt, uvExt)
     EXESORTC(Size, dvSize, uvSize)
-    EXESORTC(Type, dvType, uvName)
+    EXESORTC(Type, dvType, uvType)
     EXESORTC(Changed, dvChanged, uvChanged)
     EXESORTC(Rights, dvAttr, uvRights)
     EXESORTC(Owner, dvName, uvOwner)
@@ -561,6 +576,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXESHCOL(Remote, uv, Owner)
     EXESHCOL(Remote, uv, Group)
     EXESHCOL(Remote, uv, LinkTarget)
+    EXESHCOL(Remote, uv, Type)
     EXE(HideColumnAction, assert(ListColumn);
       COLVIEWPROPS->Visible[ListColumn->Index] = false; ListColumn = NULL )
     EXE(BestFitColumnAction, assert(ListColumn); ListColumn = NULL ) // TODO
@@ -578,12 +594,13 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXE(SynchronizeAction, ScpExplorer->SynchronizeDirectories())
     EXE(FullSynchronizeAction, ScpExplorer->FullSynchronizeDirectories())
     EXE(ConsoleAction, ScpExplorer->OpenConsole())
-    EXE(PuttyAction, ScpExplorer->OpenInPutty())
+    EXE(PuttyAction, TTerminalManager::Instance()->OpenInPutty())
     EXE(SynchronizeBrowsingAction, )
     EXE(CloseApplicationAction, ScpExplorer->Close())
     EXE(FileSystemInfoAction, DoFileSystemInfoDialog(ScpExplorer->Terminal))
     EXE(ClearCachesAction, ScpExplorer->Terminal->ClearCaches())
-    EXE(EditNewAction, ScpExplorer->EditNew(osCurrent));
+    EXE(EditNewAction, ScpExplorer->EditNew(osCurrent))
+    EXE(EditorListCustomizeAction, PreferencesDialog(pmEditor))
 
     // CUSTOM COMMANDS
     EXE(CustomCommandsAction, CreateCustomCommandsMenu(CustomCommandsAction))
@@ -601,8 +618,12 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
     EXEQUEUE(ItemPrompt)
     EXEQUEUE(ItemDelete)
     EXEQUEUE(ItemExecute)
+    EXEQUEUE(ItemPause)
+    EXEQUEUE(ItemResume)
     EXEQUEUE(ItemUp)
     EXEQUEUE(ItemDown)
+    EXEQUEUE(PauseAll)
+    EXEQUEUE(ResumeAll)
     #undef EXEQUEUE
     EXE(QueueToggleShowAction, ScpExplorer->ToggleQueueVisibility())
     #define QUEUEACTION(SHOW) EXE(Queue ## SHOW ## Action, \
@@ -619,7 +640,8 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
   }
   __finally
   {
-    FIdle = true;
+    assert(FIdle < 0);
+    FIdle++;
   }
 
   DoIdle();
@@ -647,7 +669,6 @@ void __fastcall TNonVisualDataModule::ExplorerShortcuts()
   // File operation
   CurrentRenameAction->ShortCut = ShortCut(VK_F2, NONE);
   CurrentEditAction->ShortCut = ShortCut('E', CTRL);
-  CurrentEditAlternativeAction->ShortCut = ShortCut('E', CTRLSHIFT);
   AddEditLinkAction->ShortCut = ShortCut('L', CTRLALT);
   // Focused operation
   CurrentCopyFocusedAction->ShortCut = ShortCut('C', CTRL);
@@ -671,6 +692,9 @@ void __fastcall TNonVisualDataModule::ExplorerShortcuts()
   SelectAllAction->ShortCut = ShortCut('A', CTRL);
   InvertSelectionAction->ShortCut = ShortCut(VK_MULTIPLY, NONE);
   ClearSelectionAction->ShortCut = ShortCut('L', CTRL);
+  RestoreSelectionAction->ShortCut = ShortCut('R', CTRLALT);
+  // commands
+  EditNewAction->ShortCut = ShortCut('E', CTRLSHIFT);
 
   CloseApplicationAction->ShortCut = ShortCut(VK_F4, ALT);
 }
@@ -682,7 +706,6 @@ void __fastcall TNonVisualDataModule::CommanderShortcuts()
   // File operation
   CurrentRenameAction->ShortCut = ShortCut(VK_F2, NONE);
   CurrentEditAction->ShortCut = ShortCut(VK_F4, NONE);
-  CurrentEditAlternativeAction->ShortCut = ShortCut(VK_F4, SHIFT);
   AddEditLinkAction->ShortCut = ShortCut(VK_F6, ALT);
   // Focused operation
   CurrentCopyFocusedAction->ShortCut = ShortCut(VK_F5, NONE);
@@ -717,8 +740,11 @@ void __fastcall TNonVisualDataModule::CommanderShortcuts()
   SelectAllAction->ShortCut = ShortCut('A', CTRL);
   InvertSelectionAction->ShortCut = ShortCut(VK_MULTIPLY, NONE);
   ClearSelectionAction->ShortCut = ShortCut('L', CTRL);
+  RestoreSelectionAction->ShortCut = ShortCut('R', CTRLALT);
   // commands
-  EditNewAction->ShortCut = ShortCut(VK_F4, CTRLSHIFT);
+  EditNewAction->ShortCut = ShortCut(VK_F4, SHIFT);
+  // legacy shortcut (can be removed when necessary)
+  EditNewAction->SecondaryShortCuts->Add(ShortCutToText(ShortCut(VK_F4, CTRLSHIFT)));
 
   CloseApplicationAction->ShortCut = ShortCut(VK_F10, NONE);
 }
@@ -746,7 +772,7 @@ void __fastcall TNonVisualDataModule::DoIdle()
     try
     {
       assert(ScpExplorer);
-      ScpExplorer->Idle(FIdle);
+      ScpExplorer->Idle(FIdle >= 0);
     }
     __finally
     {
@@ -790,10 +816,7 @@ void __fastcall TNonVisualDataModule::CreateCustomCommandsMenu(TAction * Action)
     Item->Action = CustomCommandsEnterAction;
     Menu->Add(Item);
 
-    Item = new TTBXSeparatorItem(Menu);
-    Item->Caption = "-";
-    Item->Hint = "E";
-    Menu->Add(Item);
+    AddMenuSeparator(Menu);
 
     Item = new TTBXItem(Menu);
     Item->Action = CustomCommandsCustomizeAction;
@@ -890,6 +913,90 @@ void __fastcall TNonVisualDataModule::OpenedSessionItemClick(TObject * Sender)
   TTerminalManager::Instance()->ActiveTerminal = (TTerminal*)(((TMenuItem *)Sender)->Tag);
 }
 //---------------------------------------------------------------------------
+void __fastcall TNonVisualDataModule::CreateEditorListMenu(TAction * Action)
+{
+  assert(Action != NULL);
+  TTBCustomItem * Menu = dynamic_cast<TTBCustomItem *>(Action->ActionComponent);
+  if (Menu != NULL)
+  {
+    int PrevCount = Menu->Count;
+
+    TTBCustomItem * Item = new TTBXItem(Menu);
+    Item->Caption = LoadStr(INTERNAL_EDITOR_NAME);
+    Item->Tag = -1;
+    Item->Hint = LoadStr(INTERNAL_EDITOR_HINT);
+    Item->OnClick = EditorItemClick;
+    Menu->Add(Item);
+
+    AddMenuSeparator(Menu);
+
+    TStringList * UsedEditors = new TStringList();
+    try
+    {
+      UsedEditors->CaseSensitive = false;
+      UsedEditors->Sorted = true;
+
+      bool AnyExternal = false;
+      const TEditorList * EditorList = WinConfiguration->EditorList;
+      for (int Index = 0; Index < EditorList->Count; Index++)
+      {
+        const TEditorPreferences * Editor = EditorList->Editors[Index];
+      
+        if ((Editor->Data.Editor == edExternal) &&
+            (UsedEditors->IndexOf(Editor->Data.ExternalEditor) < 0))
+        {
+          UsedEditors->Add(Editor->Data.ExternalEditor);
+
+          TTBCustomItem * Item = new TTBXItem(Menu);
+          Item->Caption = Editor->Name;
+          Item->Tag = Index;
+          Item->Hint = FMTLOAD(EXTERNAL_EDITOR_HINT, (Editor->Name));
+          Item->OnClick = EditorItemClick;
+          Menu->Add(Item);
+
+          AnyExternal = true;
+        }
+      }
+
+      if (AnyExternal)
+      {
+        AddMenuSeparator(Menu);
+      }
+
+      Item = new TTBXItem(Menu);
+      Item->Action = EditorListCustomizeAction;
+      Menu->Add(Item);
+
+      for (int Index = 0; Index < PrevCount; Index++)
+      {
+        Menu->Delete(0);
+      }
+    }
+    __finally
+    {
+      delete UsedEditors;
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TNonVisualDataModule::EditorItemClick(TObject * Sender)
+{
+  int Tag = dynamic_cast<TTBXItem*>(Sender)->Tag;
+  if (Tag < 0)
+  {
+    ScpExplorer->ExecuteFile(osCurrent, efInternalEditor);
+  }
+  else
+  {
+    const TEditorList * EditorList = WinConfiguration->EditorList;
+    // sanity check
+    if (Tag < EditorList->Count)
+    {
+      ScpExplorer->ExecuteFile(osCurrent, efExternalEditor, EditorList->Editors[Tag]);
+    }
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TNonVisualDataModule::QueuePopupPopup(TObject * /*Sender*/)
 {
   TAction * Action = NULL;
@@ -911,6 +1018,14 @@ void __fastcall TNonVisualDataModule::QueuePopupPopup(TObject * /*Sender*/)
     case qoItemExecute:
       Action = QueueItemExecuteAction;
       break;
+
+    case qoItemPause:
+      Action = QueueItemPauseAction;
+      break;
+
+    case qoItemResume:
+      Action = QueueItemResumeAction;
+      break;
   }
 
   TTBCustomItem * Item;

+ 121 - 24
forms/NonVisual.dfm

@@ -66,7 +66,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object CurrentOpenMenuItem: TTBXItem
       Action = CurrentOpenAction
     end
-    object CurentEditMenuItem: TTBXItem
+    object CurrentEditMenuItem: TTBXItem
       Action = CurrentEditAction
     end
     object CurrentCopyMenuItem: TTBXItem
@@ -134,7 +134,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object GoToTreeAction: TAction
       Tag = 15
       Category = 'View'
-      Caption = 'Go To Tree'
+      Caption = 'Go to Tree'
       HelpKeyword = 'ui_file_panel#directory_tree'
       Hint = 'Go to tree'
       ImageIndex = 76
@@ -185,7 +185,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object GoToCommandLineAction: TAction
       Tag = 11
       Category = 'View'
-      Caption = 'Go To Comma&nd Line'
+      Caption = 'Go to Comma&nd Line'
       HelpKeyword = 'ui_commander#command_line'
       Hint = 'Go to command line'
       ShortCut = 49230
@@ -193,7 +193,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object QueueItemDeleteAction: TAction
       Tag = 12
       Category = 'Queue'
-      Caption = '&Delete'
+      Caption = '&Cancel'
       HelpKeyword = 'ui_queue#managing_the_queue'
       Hint = 'Remove selected queue item'
       ImageIndex = 71
@@ -259,7 +259,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object CurrentCopyAction: TAction
       Tag = 15
       Category = 'Toolbar Operation (selected + rename + mkdir + close)'
-      Caption = '&Copy ...'
+      Caption = '&Copy...'
       HelpKeyword = 'task_download'
       Hint = 'Copy|Copy selected file(s)'
       ImageIndex = 0
@@ -267,7 +267,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object CurrentMoveAction: TAction
       Tag = 15
       Category = 'Toolbar Operation (selected + rename + mkdir + close)'
-      Caption = '&Move ...'
+      Caption = '&Move...'
       HelpKeyword = 'task_download'
       Hint = 'Move|Move selected file(s)'
       ImageIndex = 1
@@ -315,7 +315,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object CurrentCopyFocusedAction: TAction
       Tag = 12
       Category = 'Focused Operation'
-      Caption = '&Copy ...'
+      Caption = '&Copy...'
       HelpKeyword = 'task_download'
       Hint = 'Copy|Copy selected file(s) to local directory'
       ImageIndex = 0
@@ -323,14 +323,14 @@ object NonVisualDataModule: TNonVisualDataModule
     object RemoteMoveToAction: TAction
       Tag = 14
       Category = 'Selected Operation'
-      Caption = 'Mo&ve to ...'
+      Caption = 'Mo&ve to...'
       HelpKeyword = 'task_move_duplicate#moving_remote_files'
       Hint = 'Move|Move selected file(s) to remote directory'
     end
     object CurrentMoveFocusedAction: TAction
       Tag = 12
       Category = 'Focused Operation'
-      Caption = '&Move ...'
+      Caption = '&Move...'
       HelpKeyword = 'task_download'
       Hint = 'Move|Move selected file(s) to local directory'
       ImageIndex = 1
@@ -434,7 +434,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object AboutAction: TAction
       Tag = 15
       Category = 'Help'
-      Caption = '&About ...'
+      Caption = '&About...'
       HelpKeyword = 'ui_about'
       Hint = 'About|Show About box'
       ImageIndex = 17
@@ -479,7 +479,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object SelectAction: TAction
       Tag = 15
       Category = 'Selection'
-      Caption = 'Sele&ct Files'
+      Caption = 'Sele&ct Files...'
       HelpKeyword = 'ui_select'
       Hint = 'Select|Select files by mask'
       ImageIndex = 19
@@ -487,7 +487,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object UnselectAction: TAction
       Tag = 15
       Category = 'Selection'
-      Caption = '&Unselect Files'
+      Caption = '&Unselect Files...'
       HelpKeyword = 'ui_select'
       Hint = 'Unselect|Unselect files by mask'
       ImageIndex = 20
@@ -904,6 +904,14 @@ object NonVisualDataModule: TNonVisualDataModule
       ImageIndex = 39
       ShortCut = 16504
     end
+    object RemoteSortByTypeAction: TAction
+      Tag = 14
+      Category = 'Sort'
+      Caption = 'By &Type'
+      HelpKeyword = 'ui_file_panel#sorting_files'
+      Hint = 'Sort by type|Sort remote panel by file type'
+      ImageIndex = 34
+    end
     object CurrentSortAscendingAction: TAction
       Tag = 15
       Category = 'Sort'
@@ -942,7 +950,7 @@ object NonVisualDataModule: TNonVisualDataModule
       ShortCut = 16501
     end
     object CurrentSortByTypeAction: TAction
-      Tag = 9
+      Tag = 15
       Category = 'Sort'
       Caption = 'By &Type'
       HelpKeyword = 'ui_file_panel#sorting_files'
@@ -1098,6 +1106,14 @@ object NonVisualDataModule: TNonVisualDataModule
         'el'
       ImageIndex = 82
     end
+    object ShowHideRemoteTypeColumnAction: TAction
+      Tag = 15
+      Category = 'Columns'
+      Caption = '&Type'
+      HelpKeyword = 'ui_file_panel#selecting_columns'
+      Hint = 'Show/hide type|Show/hide type column on remote panel'
+      ImageIndex = 46
+    end
     object ShowHideLocalNameColumnAction: TAction
       Tag = 15
       Category = 'Columns'
@@ -1162,7 +1178,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object SynchronizeAction: TAction
       Tag = 15
       Category = 'Command'
-      Caption = '&Keep Remote Directory Up To Date'
+      Caption = '&Keep Remote Directory up to Date...'
       HelpKeyword = 'task_keep_up_to_date'
       Hint = 
         'Keep remote directory up to date|Keep remote directory up to dat' +
@@ -1179,7 +1195,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object LocalAddBookmarkAction: TAction
       Tag = 9
       Category = 'Local Directory'
-      Caption = '&Add To Bookmarks'
+      Caption = '&Add to Bookmarks'
       HelpKeyword = 'task_navigate#bookmarks'
       Hint = 'Add to bookmarks|Add current local directory to bookmark list'
       ImageIndex = 54
@@ -1188,7 +1204,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object RemoteAddBookmarkAction: TAction
       Tag = 14
       Category = 'Remote Directory'
-      Caption = '&Add To Bookmarks'
+      Caption = '&Add to Bookmarks'
       HelpKeyword = 'task_navigate#bookmarks'
       Hint = 'Add to bookmarks|Add current remote directory to bookmark list'
       ImageIndex = 54
@@ -1305,7 +1321,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object CheckForUpdatesAction: TAction
       Tag = 15
       Category = 'Help'
-      Caption = '&Check For Updates'
+      Caption = '&Check for Updates'
       HelpKeyword = 'updates'
       Hint = 'Queries application homepage for updates'
       ImageIndex = 63
@@ -1319,7 +1335,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object FileSystemInfoAction: TAction
       Tag = 15
       Category = 'Command'
-      Caption = '&Server/protocol Information'
+      Caption = '&Server/Protocol Information'
       HelpKeyword = 'ui_fsinfo'
       Hint = 'Display server/protocol information'
       ImageIndex = 17
@@ -1334,7 +1350,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object FullSynchronizeAction: TAction
       Tag = 15
       Category = 'Command'
-      Caption = '&Synchronize'
+      Caption = '&Synchronize...'
       HelpKeyword = 'task_synchronize_full'
       Hint = 'Synchronize local directory with remote directory'
       ImageIndex = 66
@@ -1343,14 +1359,14 @@ object NonVisualDataModule: TNonVisualDataModule
     object RemoteMoveToFocusedAction: TAction
       Tag = 14
       Category = 'Focused Operation'
-      Caption = 'Mo&ve to ...'
+      Caption = 'Mo&ve to...'
       HelpKeyword = 'task_move_duplicate#moving_remote_files'
       Hint = 'Move|Move selected file(s) to remote directory'
     end
     object ShowHiddenFilesAction: TAction
       Tag = 15
       Category = 'View'
-      Caption = 'Show/hide &hidden files'
+      Caption = 'Show/Hide &Hidden Files'
       HelpKeyword = 'ui_file_panel#special_files'
       Hint = 'Toggle showing hidden files in panel(s)'
       ShortCut = 49224
@@ -1472,15 +1488,23 @@ object NonVisualDataModule: TNonVisualDataModule
     object EditNewAction: TAction
       Tag = 15
       Category = 'Command'
-      Caption = 'Edit &new file ...'
+      Caption = 'Edit &New File...'
       HelpKeyword = 'task_edit'
       Hint = 'Edit new file|Create new file and open it in editor'
       ImageIndex = 77
     end
+    object EditorListCustomizeAction: TAction
+      Tag = 15
+      Category = 'Command'
+      Caption = '&Configure...'
+      HelpKeyword = 'ui_pref_editor'
+      Hint = 'Customize editors'
+      ImageIndex = 28
+    end
     object RemoteCopyToFocusedAction: TAction
       Tag = 14
       Category = 'Focused Operation'
-      Caption = '&Duplicate ...'
+      Caption = '&Duplicate...'
       HelpKeyword = 'task_move_duplicate#duplicating_remote_files'
       Hint = 'Duplicate|Duplicate selected file(s) to remote directory'
       ImageIndex = 78
@@ -1488,7 +1512,7 @@ object NonVisualDataModule: TNonVisualDataModule
     object RemoteCopyToAction: TAction
       Tag = 14
       Category = 'Selected Operation'
-      Caption = '&Duplicate ...'
+      Caption = '&Duplicate...'
       HelpKeyword = 'task_move_duplicate#duplicating_remote_files'
       Hint = 'Duplicate|Duplicate selected file(s) to remote directory'
       ImageIndex = 78
@@ -1549,6 +1573,53 @@ object NonVisualDataModule: TNonVisualDataModule
       Hint = 'Configure transfers settings presets'
       ImageIndex = 28
     end
+    object LockToolbarsAction: TAction
+      Tag = 15
+      Category = 'View'
+      Caption = '&Lock Toolbars'
+      HelpKeyword = 'ui_toolbars'
+      Hint = 'Prevents moving and (un)docking of all toolbars'
+    end
+    object QueueItemPauseAction: TAction
+      Tag = 12
+      Category = 'Queue'
+      Caption = '&Suspend'
+      HelpKeyword = 'ui_queue#managing_the_queue'
+      Hint = 'Suspend selected queue item'
+      ImageIndex = 83
+    end
+    object QueueItemResumeAction: TAction
+      Tag = 12
+      Category = 'Queue'
+      Caption = '&Resume'
+      HelpKeyword = 'ui_queue#managing_the_queue'
+      Hint = 'Resume selected suspended queue item'
+      ImageIndex = 70
+    end
+    object QueuePauseAllAction: TAction
+      Tag = 12
+      Category = 'Queue'
+      Caption = '&Suspend All'
+      HelpKeyword = 'ui_queue#managing_the_queue'
+      Hint = 'Suspend all running queue items'
+      ImageIndex = 84
+    end
+    object QueueResumeAllAction: TAction
+      Tag = 12
+      Category = 'Queue'
+      Caption = '&Resume All'
+      HelpKeyword = 'ui_queue#managing_the_queue'
+      Hint = 'Resume all suspended queue items'
+      ImageIndex = 85
+    end
+    object RestoreSelectionAction: TAction
+      Tag = 15
+      Category = 'Selection'
+      Caption = '&Restore Selection'
+      HelpKeyword = 'ui_file_panel#selecting_files'
+      Hint = 'Restore previous selection'
+      ImageIndex = 86
+    end
   end
   object ExplorerBarPopup: TTBXPopupMenu
     Images = GlyphsModule.ExplorerImages
@@ -1578,6 +1649,9 @@ object NonVisualDataModule: TNonVisualDataModule
     object TBXItem4: TTBXItem
       Action = ExplorerTransferBandAction
     end
+    object TBXItem7: TTBXItem
+      Action = LockToolbarsAction
+    end
     object N5: TTBXSeparatorItem
       Hint = 'E'
     end
@@ -1655,6 +1729,9 @@ object NonVisualDataModule: TNonVisualDataModule
     object TBXItem5: TTBXItem
       Action = CommanderTransferBandAction
     end
+    object TBXItem6: TTBXItem
+      Action = LockToolbarsAction
+    end
     object N26: TTBXSeparatorItem
       Hint = 'E'
     end
@@ -1881,6 +1958,9 @@ object NonVisualDataModule: TNonVisualDataModule
       object Size4: TTBXItem
         Action = ShowHideRemoteSizeColumnAction
       end
+      object TBXItem8: TTBXItem
+        Action = ShowHideRemoteTypeColumnAction
+      end
       object Modification4: TTBXItem
         Action = ShowHideRemoteChangedColumnAction
       end
@@ -1918,6 +1998,12 @@ object NonVisualDataModule: TNonVisualDataModule
     object ExecuteNow1: TTBXItem
       Action = QueueItemExecuteAction
     end
+    object TBXItem9: TTBXItem
+      Action = QueueItemPauseAction
+    end
+    object TBXItem10: TTBXItem
+      Action = QueueItemResumeAction
+    end
     object Delete4: TTBXItem
       Action = QueueItemDeleteAction
     end
@@ -1933,6 +2019,17 @@ object NonVisualDataModule: TNonVisualDataModule
     object N67: TTBXSeparatorItem
       Hint = 'E'
     end
+    object TBXSubmenuItem1: TTBXSubmenuItem
+      Caption = '&All'
+      HelpKeyword = 'ui_queue#managing_the_queue'
+      Hint = 'Mass queue management commands'
+      object TBXItem11: TTBXItem
+        Action = QueuePauseAllAction
+      end
+      object TBXItem12: TTBXItem
+        Action = QueueResumeAllAction
+      end
+    end
     object Queue2: TTBXSubmenuItem
       Caption = '&Options'
       HelpKeyword = 'ui_queue'

+ 21 - 3
forms/NonVisual.h

@@ -289,7 +289,7 @@ __published:    // IDE-managed Components
   TTBXItem *StatusBar10;
   TTBXPopupMenu *RemoteFilePopup;
   TTBXItem *CurrentOpenMenuItem;
-  TTBXItem *CurentEditMenuItem;
+  TTBXItem *CurrentEditMenuItem;
   TTBXItem *CurrentCopyMenuItem;
   TTBXItem *Duplicate3;
   TTBXItem *Moveto1;
@@ -416,6 +416,23 @@ __published:    // IDE-managed Components
   TAction *PresetsPreferencesAction;
   TAction *BestFitColumnAction;
   TAction *CustomCommandsEnterAction;
+  TAction *LockToolbarsAction;
+  TTBXItem *TBXItem6;
+  TTBXItem *TBXItem7;
+  TAction *ShowHideRemoteTypeColumnAction;
+  TTBXItem *TBXItem8;
+  TAction *RemoteSortByTypeAction;
+  TAction *QueueItemPauseAction;
+  TTBXItem *TBXItem9;
+  TAction *QueueItemResumeAction;
+  TTBXItem *TBXItem10;
+  TAction *QueuePauseAllAction;
+  TAction *QueueResumeAllAction;
+  TTBXSubmenuItem *TBXSubmenuItem1;
+  TTBXItem *TBXItem11;
+  TTBXItem *TBXItem12;
+  TAction *EditorListCustomizeAction;
+  TAction *RestoreSelectionAction;
   void __fastcall LogActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall LogActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);
@@ -426,7 +443,7 @@ private:
   TListColumn * FListColumn;
   TCustomScpExplorerForm * FScpExplorer;
   bool FSessionIdleTimerExecuting;
-  bool FIdle;
+  int FIdle;
 
   void __fastcall SetScpExplorer(TCustomScpExplorerForm * value);
 protected:
@@ -437,6 +454,8 @@ protected:
   void __fastcall SessionItemClick(TObject * Sender);
   void __fastcall OpenedSessionItemClick(TObject * Sender);
   void __fastcall CustomCommandClick(TObject * Sender);
+  void __fastcall CreateEditorListMenu(TAction * Action);
+  void __fastcall EditorItemClick(TObject * Sender);
   void __fastcall DoIdle();
   inline void __fastcall ShowUpdatesUpdate();
   void __fastcall PreferencesDialog(TPreferencesMode APreferencesMode);
@@ -451,7 +470,6 @@ public:
 
   __property TListColumn * ListColumn = { read = FListColumn, write = FListColumn };
   __property TCustomScpExplorerForm * ScpExplorer = { read = FScpExplorer, write = SetScpExplorer };
-  __property bool Idle = { read = FIdle };
 };
 //---------------------------------------------------------------------------
 extern PACKAGE TNonVisualDataModule *NonVisualDataModule;

+ 29 - 30
forms/Password.cpp

@@ -41,14 +41,10 @@ __fastcall TPasswordDialog::TPasswordDialog(TComponent* AOwner)
 {
   UseSystemSettings(this);
   Kind = pkPassword;
-  FApplicationShowHint = Application->OnShowHint;
-  Application->OnShowHint = ApplicationShowHint;
 }
 //---------------------------------------------------------------------
 __fastcall TPasswordDialog::~TPasswordDialog()
 {
-  assert(Application->OnShowHint == ApplicationShowHint);
-  Application->OnShowHint = FApplicationShowHint;
 }
 //---------------------------------------------------------------------
 void __fastcall TPasswordDialog::SetPasswordCaption(const AnsiString value)
@@ -68,22 +64,41 @@ void __fastcall TPasswordDialog::SetPasswordCaption(const AnsiString value)
     Caption.SetLength(P - 1);
   }
 
-  bool NeedTrim = MultiLine ||
-    (PasswordLabel->Canvas->TextWidth(Caption) > PasswordLabel->Width);
-  if (NeedTrim)
+  bool NeedTrim;
+  TControlCanvas * PasswordLabelCanvas = new TControlCanvas();
+  try
   {
-    static AnsiString Ellipsis(" ...");
-    while (PasswordLabel->Canvas->TextWidth(Caption + Ellipsis) >
-        PasswordLabel->Width)
+    PasswordLabelCanvas->Control = PasswordLabel;
+
+    NeedTrim = MultiLine ||
+      (PasswordLabelCanvas->TextWidth(Caption) > PasswordLabel->Width);
+    if (NeedTrim)
     {
-      Caption.SetLength(Caption.Length() - 1);
+      static AnsiString Ellipsis(" ...");
+      while (PasswordLabelCanvas->TextWidth(Caption + Ellipsis) >
+          PasswordLabel->Width)
+      {
+        Caption.SetLength(Caption.Length() - 1);
+      }
+      Caption = Caption + Ellipsis;
     }
-    Caption = Caption + Ellipsis;
+  }
+  __finally
+  {
+    delete PasswordLabelCanvas;
   }
 
   PasswordLabel->Caption = Caption;
-  PasswordLabel->Hint = value;
-  PasswordLabel->ShowHint = NeedTrim;
+  if (NeedTrim)
+  {
+    HintLabel(PasswordLabel, value);
+    PasswordLabel->TabStop = true;
+  }
+  else
+  {
+    // just to make GetPasswordCaption(), i.e. useless
+    PasswordLabel->Hint = value;
+  }
 }
 //---------------------------------------------------------------------
 AnsiString __fastcall TPasswordDialog::GetPasswordCaption()
@@ -126,22 +141,6 @@ void __fastcall TPasswordDialog::HideTypingCheckClick(TObject * /*Sender*/)
   PasswordEdit->Password = HideTypingCheck->Checked;
 }
 //---------------------------------------------------------------------------
-void __fastcall TPasswordDialog::ApplicationShowHint(AnsiString & HintStr,
-  bool & CanShow, THintInfo & HintInfo)
-{
-  if (FApplicationShowHint != NULL)
-  {
-    FApplicationShowHint(HintStr, CanShow, HintInfo);
-  }
-
-  if (HintInfo.HintControl == PasswordLabel)
-  {
-    HintInfo.HintPos.x = PasswordLabel->ClientOrigin.x - 3;
-    HintInfo.HintPos.y = PasswordLabel->ClientOrigin.y - 3;
-    HintInfo.HideTimeout = 2500;
-  }
-}
-//---------------------------------------------------------------------------
 void __fastcall TPasswordDialog::HelpButtonClick(TObject * /*Sender*/)
 {
   FormHelp(this);

+ 4 - 2
forms/Password.dfm

@@ -3,6 +3,7 @@ object PasswordDialog: TPasswordDialog
   Top = 77
   HelpType = htKeyword
   HelpKeyword = 'ui_password'
+  ActiveControl = PasswordEdit
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   BorderStyle = bsDialog
   Caption = 'Enter password'
@@ -50,7 +51,7 @@ object PasswordDialog: TPasswordDialog
     DesignSize = (
       404
       50)
-    object PasswordLabel: TLabel
+    object PasswordLabel: TStaticText
       Left = 8
       Top = 8
       Width = 389
@@ -59,6 +60,7 @@ object PasswordDialog: TPasswordDialog
       AutoSize = False
       Caption = '&Password:'
       FocusControl = PasswordEdit
+      TabOrder = 0
     end
     object PasswordEdit: TPasswordEdit
       Left = 8
@@ -67,7 +69,7 @@ object PasswordDialog: TPasswordDialog
       Height = 21
       Anchors = [akLeft, akTop, akRight]
       MaxLength = 250
-      TabOrder = 0
+      TabOrder = 1
     end
   end
   object ServerPromptPanel: TPanel

Some files were not shown because too many files changed in this diff