mirror of
https://github.com/winscp/winscp.git
synced 2026-10-24 02:28:55 +03:00
Bug 99: When saving edited/opened file, warn if it was modified meanwhile externally
https://winscp.net/tracker/99 Source commit: 992032004bbf1599bc5e02c91ba036fb7ecb68f7
This commit is contained in:
@@ -3317,7 +3317,7 @@ void __fastcall TCustomScpExplorerForm::EditNew(TOperationSide Side)
|
||||
false, MaskParams);
|
||||
|
||||
CustomExecuteFile(Side, ExecuteFileBy, LocalFileName, TargetFileName,
|
||||
ExternalEditor, RootTempDir, RemoteDirectory, NewFile);
|
||||
ExternalEditor, RootTempDir, RemoteDirectory, NewFile, TDateTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3338,7 +3338,7 @@ bool __fastcall TCustomScpExplorerForm::RemoteExecuteForceText(
|
||||
void __fastcall TCustomScpExplorerForm::CustomExecuteFile(TOperationSide Side,
|
||||
TExecuteFileBy ExecuteFileBy, UnicodeString FileName, UnicodeString OriginalFileName,
|
||||
const TEditorData * ExternalEditor, UnicodeString LocalRootDirectory,
|
||||
UnicodeString RemoteDirectory, bool NewFile)
|
||||
UnicodeString RemoteDirectory, bool NewFile, const TDateTime & SourceTimestamp)
|
||||
{
|
||||
DebugAssert(!WinConfiguration->DisableOpenEdit);
|
||||
DebugAssert((ExecuteFileBy == efExternalEditor) ==
|
||||
@@ -3359,6 +3359,11 @@ void __fastcall TCustomScpExplorerForm::CustomExecuteFile(TOperationSide Side,
|
||||
Data->LocalRootDirectory = LocalRootDirectory;
|
||||
Data->OriginalFileName = OriginalFileName;
|
||||
Data->Command = L""; // will be changed later for external editor
|
||||
// Empty SourceTimestamp indicates that checking was disabled when the file was opened
|
||||
if (WinConfiguration->EditorCheckNotModified)
|
||||
{
|
||||
Data->SourceTimestamp = SourceTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
if (ExecuteFileBy == efInternalEditor)
|
||||
@@ -3784,7 +3789,7 @@ void __fastcall TCustomScpExplorerForm::ExecuteFile(TOperationSide Side,
|
||||
Configuration->Usage->Inc(Counter);
|
||||
|
||||
CustomExecuteFile(Side, ExecuteFileBy, LocalFileName, OriginalFileName,
|
||||
ExternalEditor, LocalRootDirectory, RemoteDirectory, false);
|
||||
ExternalEditor, LocalRootDirectory, RemoteDirectory, false, MaskParams.Modification);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -3865,6 +3870,11 @@ void __fastcall TCustomScpExplorerForm::TemporaryFileCopyParam(TCopyParamType &
|
||||
CopyParam.FileMask = L"";
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool TCustomScpExplorerForm::EditorCheckNotModified(const TEditedFileData * Data)
|
||||
{
|
||||
return WinConfiguration->EditorCheckNotModified && (Data->SourceTimestamp != TDateTime());
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(
|
||||
const UnicodeString & FileName, TEditedFileData * Data, HANDLE UploadCompleteEvent, bool & Retry)
|
||||
{
|
||||
@@ -3927,6 +3937,25 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(
|
||||
}
|
||||
}
|
||||
|
||||
if (EditorCheckNotModified(Data))
|
||||
{
|
||||
UnicodeString RemoteFilePath = UnixCombinePaths(Data->RemoteDirectory, Data->OriginalFileName);
|
||||
std::unique_ptr<TRemoteFile> File(Terminal->TryReadFile(RemoteFilePath));
|
||||
if (File.get() != NULL)
|
||||
{
|
||||
AppLogFmt(L"Edited remote file timestamp: %s, Original timestamp: %s", (StandardTimestamp(File->Modification), StandardTimestamp(Data->SourceTimestamp)));
|
||||
|
||||
if (File->Modification != Data->SourceTimestamp)
|
||||
{
|
||||
UnicodeString Message = MainInstructions(LoadStr(EDIT_CHANGED_EXTERNALLY));
|
||||
if (MessageDialog(Message, qtConfirmation, qaOK | qaCancel) != qaOK)
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TStrings * FileList = new TStringList();
|
||||
try
|
||||
{
|
||||
@@ -3981,7 +4010,7 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::ExecutedFileReload(
|
||||
const UnicodeString FileName, const TEditedFileData * Data)
|
||||
const UnicodeString & FileName, TEditedFileData * Data)
|
||||
{
|
||||
// Sanity check, we should not be busy otherwise user would not be able to click Reload button.
|
||||
DebugAssert(!NonVisualDataModule->Busy);
|
||||
@@ -4021,6 +4050,11 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileReload(
|
||||
|
||||
TemporarilyDownloadFiles(FileList.get(), Data->ForceText, RootTempDir, TempDir, true, true, false);
|
||||
|
||||
if (EditorCheckNotModified(Data))
|
||||
{
|
||||
Data->SourceTimestamp = File->Modification;
|
||||
}
|
||||
|
||||
// sanity check, the target file name should be still the same
|
||||
DebugAssert(ExtractFileName(FileName) == FileList->Strings[0]);
|
||||
}
|
||||
@@ -4156,8 +4190,19 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileEarlyClosed(
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::ExecutedFileUploadComplete(TObject * Sender)
|
||||
void __fastcall TCustomScpExplorerForm::ExecutedFileUploadComplete(TEditedFileData * Data, TObject * Sender)
|
||||
{
|
||||
if (EditorCheckNotModified(Data))
|
||||
{
|
||||
UnicodeString RemoteFilePath = UnixCombinePaths(Data->RemoteDirectory, Data->OriginalFileName);
|
||||
std::unique_ptr<TRemoteFile> File(Terminal->TryReadFile(RemoteFilePath));
|
||||
if (File.get() != NULL)
|
||||
{
|
||||
Data->SourceTimestamp = File->Modification;
|
||||
AppLogFmt(L"Uploaded edited remote file timestamp: %s: ", (StandardTimestamp(Data->SourceTimestamp)));
|
||||
}
|
||||
}
|
||||
|
||||
EditorFormFileUploadComplete(DebugNotNull(dynamic_cast<TForm *>(Sender)));
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -461,11 +461,11 @@ protected:
|
||||
void __fastcall OperationComplete(const TDateTime & StartTime);
|
||||
void __fastcall ExecutedFileChanged(
|
||||
const UnicodeString & FileName, TEditedFileData * Data, HANDLE UploadCompleteEvent, bool & Retry);
|
||||
void __fastcall ExecutedFileReload(const UnicodeString FileName,
|
||||
const TEditedFileData * Data);
|
||||
void __fastcall ExecutedFileReload(const UnicodeString & FileName, TEditedFileData * Data);
|
||||
void __fastcall ExecutedFileEarlyClosed(const TEditedFileData * Data,
|
||||
bool & KeepOpen);
|
||||
void __fastcall ExecutedFileUploadComplete(TObject * Sender);
|
||||
void __fastcall ExecutedFileUploadComplete(TEditedFileData * Data, TObject * Sender);
|
||||
bool EditorCheckNotModified(const TEditedFileData * Data);
|
||||
void __fastcall CMDialogChar(TMessage & AMessage);
|
||||
inline void __fastcall WMAppCommand(TMessage & Message);
|
||||
inline void __fastcall WMSysCommand(TMessage & Message);
|
||||
@@ -558,7 +558,7 @@ protected:
|
||||
void __fastcall CustomExecuteFile(TOperationSide Side,
|
||||
TExecuteFileBy ExecuteFileBy, UnicodeString FileName, UnicodeString OriginalFileName,
|
||||
const TEditorData * ExternalEditor, UnicodeString LocalRootDirectory,
|
||||
UnicodeString RemoteDirectory, bool NewFile);
|
||||
UnicodeString RemoteDirectory, bool NewFile, const TDateTime & SourceTimestamp);
|
||||
void __fastcall ExecuteFile(TOperationSide Side,
|
||||
TExecuteFileBy ExecuteFileBy, const TEditorData * ExternalEditor,
|
||||
UnicodeString FullFileName, TObject * Object,
|
||||
|
||||
@@ -397,6 +397,7 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
|
||||
FEditorBackgroundColor = WinConfiguration->Editor.BackgroundColor;
|
||||
(*FEditorList) = *WinConfiguration->EditorList;
|
||||
UpdateEditorListView();
|
||||
BOOLPROP(EditorCheckNotModified);
|
||||
|
||||
FCopyParams = GUIConfiguration->DefaultCopyParam;
|
||||
ResumeOnButton->Checked = GUIConfiguration->DefaultCopyParam.ResumeSupport == rsOn;
|
||||
@@ -788,6 +789,7 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
|
||||
WinConfiguration->Editor.FontColor = FEditorFont->Color;
|
||||
WinConfiguration->Editor.BackgroundColor = FEditorBackgroundColor;
|
||||
WinConfiguration->EditorList = FEditorList;
|
||||
BOOLPROP(EditorCheckNotModified);
|
||||
|
||||
// overwrites only TCopyParamType fields
|
||||
CopyParam = FCopyParams;
|
||||
|
||||
@@ -1073,18 +1073,18 @@ object PreferencesDialog: TPreferencesDialog
|
||||
Left = 8
|
||||
Top = 8
|
||||
Width = 389
|
||||
Height = 404
|
||||
Height = 344
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
Caption = 'Editor preference'
|
||||
TabOrder = 0
|
||||
DesignSize = (
|
||||
389
|
||||
404)
|
||||
344)
|
||||
object EditorListView3: TListView
|
||||
Left = 16
|
||||
Top = 24
|
||||
Width = 356
|
||||
Height = 307
|
||||
Height = 247
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
Columns = <
|
||||
item
|
||||
@@ -1120,7 +1120,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object AddEditorButton: TButton
|
||||
Left = 16
|
||||
Top = 337
|
||||
Top = 277
|
||||
Width = 83
|
||||
Height = 25
|
||||
Anchors = [akLeft, akBottom]
|
||||
@@ -1130,7 +1130,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object EditEditorButton: TButton
|
||||
Left = 112
|
||||
Top = 337
|
||||
Top = 277
|
||||
Width = 83
|
||||
Height = 25
|
||||
Anchors = [akLeft, akBottom]
|
||||
@@ -1140,7 +1140,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object UpEditorButton: TButton
|
||||
Left = 290
|
||||
Top = 337
|
||||
Top = 277
|
||||
Width = 83
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
@@ -1150,7 +1150,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object DownEditorButton: TButton
|
||||
Left = 290
|
||||
Top = 368
|
||||
Top = 308
|
||||
Width = 83
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
@@ -1160,7 +1160,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object RemoveEditorButton: TButton
|
||||
Left = 16
|
||||
Top = 368
|
||||
Top = 308
|
||||
Width = 83
|
||||
Height = 25
|
||||
Anchors = [akLeft, akBottom]
|
||||
@@ -1169,6 +1169,27 @@ object PreferencesDialog: TPreferencesDialog
|
||||
OnClick = RemoveEditorButtonClick
|
||||
end
|
||||
end
|
||||
object EditingOptionsGroup: TGroupBox
|
||||
Left = 8
|
||||
Top = 358
|
||||
Width = 389
|
||||
Height = 54
|
||||
Caption = 'Editing options'
|
||||
TabOrder = 1
|
||||
DesignSize = (
|
||||
389
|
||||
54)
|
||||
object EditorCheckNotModifiedCheck: TCheckBox
|
||||
Left = 16
|
||||
Top = 21
|
||||
Width = 357
|
||||
Height = 17
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = '&Check that edited remote file has not changed before saving it'
|
||||
TabOrder = 0
|
||||
OnClick = ControlChange
|
||||
end
|
||||
end
|
||||
end
|
||||
object IntegrationSheet: TTabSheet
|
||||
Tag = 9
|
||||
|
||||
@@ -342,6 +342,8 @@ __published:
|
||||
TUpDownEdit *LocalPortNumberMaxEdit;
|
||||
TCheckBox *AlwaysSortDirectoriesByNameCheck;
|
||||
TLabel *LogProtocolHintLabel;
|
||||
TGroupBox *EditingOptionsGroup;
|
||||
TCheckBox *EditorCheckNotModifiedCheck;
|
||||
void __fastcall FormShow(TObject *Sender);
|
||||
void __fastcall ControlChange(TObject *Sender);
|
||||
void __fastcall EditorFontButtonClick(TObject *Sender);
|
||||
|
||||
@@ -161,6 +161,7 @@
|
||||
#define CLOSE_SESSION_WORKSPACE 1371
|
||||
#define CLOSE_WORKSPACE 1372
|
||||
#define TOO_MANY_SESSIONS 1373
|
||||
#define EDIT_CHANGED_EXTERNALLY 1374
|
||||
|
||||
#define WIN_INFORMATION_STRINGS 1400
|
||||
#define COMPARE_NO_DIFFERENCES 1402
|
||||
|
||||
@@ -165,6 +165,7 @@ BEGIN
|
||||
CLOSE_SESSION_WORKSPACE, "Terminate session '%s' and close application without saving a workspace?"
|
||||
CLOSE_WORKSPACE, "Close application without saving a workspace?"
|
||||
TOO_MANY_SESSIONS, "**Do you really want to open another tab?**\nYou have %d tabs opened already. Please consider closing some tabs first to free resources of your computer."
|
||||
EDIT_CHANGED_EXTERNALLY, "The remote file has been changed while you were editing it. Do you want to overwrite it anyway?"
|
||||
|
||||
WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
|
||||
COMPARE_NO_DIFFERENCES, "No differences found."
|
||||
|
||||
@@ -403,7 +403,7 @@ void __fastcall TEditorManager::UploadComplete(int Index)
|
||||
}
|
||||
else if ((FileData->Token != NULL) && (FOnFileUploadComplete != NULL))
|
||||
{
|
||||
FOnFileUploadComplete(FileData->Token);
|
||||
FOnFileUploadComplete(FileData->Data, FileData->Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,17 @@ struct TEditedFileData
|
||||
UnicodeString SessionName;
|
||||
UnicodeString OriginalFileName;
|
||||
UnicodeString Command;
|
||||
TDateTime SourceTimestamp;
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
typedef void __fastcall (__closure * TEditedFileChangedEvent)
|
||||
(const UnicodeString & FileName, TEditedFileData * Data, HANDLE CompleteEvent, bool & Retry);
|
||||
typedef void __fastcall (__closure * TEditedFileReloadEvent)
|
||||
(const UnicodeString FileName, const TEditedFileData * Data);
|
||||
(const UnicodeString & FileName, TEditedFileData * Data);
|
||||
typedef void __fastcall (__closure * TEditedFileEarlyClosedEvent)
|
||||
(const TEditedFileData * Data, bool & KeepOpen);
|
||||
typedef void __fastcall (__closure * TEditedFileUploadComplete)
|
||||
(TObject * Token);
|
||||
(TEditedFileData * Data, TObject * Token);
|
||||
//---------------------------------------------------------------------------
|
||||
typedef void __fastcall (__closure * TEditedFileProcessEvent)
|
||||
(const UnicodeString FileName, TEditedFileData * Data, TObject * Token, void * Arg);
|
||||
|
||||
@@ -628,6 +628,7 @@ void __fastcall TWinConfiguration::Default()
|
||||
AllowWindowPrint = false;
|
||||
StoreTransition = stInit;
|
||||
QueueTransferLimitMax = 9;
|
||||
EditorCheckNotModified = false;
|
||||
FirstRun = StandardDatestamp();
|
||||
|
||||
FEditor.Font.FontName = DefaultFixedWidthFontName;
|
||||
@@ -1086,6 +1087,7 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool & SessionList)
|
||||
KEY(Bool, AllowWindowPrint); \
|
||||
KEY(Integer, StoreTransition); \
|
||||
KEY(Integer, QueueTransferLimitMax); \
|
||||
KEY(Bool, EditorCheckNotModified); \
|
||||
KEY(String, FirstRun); \
|
||||
); \
|
||||
BLOCK(L"Interface\\Editor", CANCREATE, \
|
||||
@@ -2784,6 +2786,11 @@ void TWinConfiguration::SetQueueTransferLimitMax(int value)
|
||||
SET_CONFIG_PROPERTY(QueueTransferLimitMax);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TWinConfiguration::SetEditorCheckNotModified(bool value)
|
||||
{
|
||||
SET_CONFIG_PROPERTY(EditorCheckNotModified);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TWinConfiguration::SetFirstRun(const UnicodeString & value)
|
||||
{
|
||||
SET_CONFIG_PROPERTY(FirstRun);
|
||||
|
||||
@@ -477,6 +477,7 @@ private:
|
||||
bool FAllowWindowPrint;
|
||||
TStoreTransition FStoreTransition;
|
||||
int FQueueTransferLimitMax;
|
||||
bool FEditorCheckNotModified;
|
||||
UnicodeString FFirstRun;
|
||||
int FDontDecryptPasswords;
|
||||
int FMasterPasswordSession;
|
||||
@@ -597,6 +598,7 @@ private:
|
||||
void __fastcall SetAllowWindowPrint(bool value);
|
||||
void SetStoreTransition(TStoreTransition value);
|
||||
void SetQueueTransferLimitMax(int value);
|
||||
void SetEditorCheckNotModified(bool value);
|
||||
void SetFirstRun(const UnicodeString & value);
|
||||
int __fastcall GetLocaleCompletenessTreshold();
|
||||
|
||||
@@ -799,6 +801,7 @@ public:
|
||||
__property bool AllowWindowPrint = { read = FAllowWindowPrint, write = SetAllowWindowPrint };
|
||||
__property TStoreTransition StoreTransition = { read = FStoreTransition, write = SetStoreTransition };
|
||||
__property int QueueTransferLimitMax = { read = FQueueTransferLimitMax, write = SetQueueTransferLimitMax };
|
||||
__property bool EditorCheckNotModified = { read = FEditorCheckNotModified, write = SetEditorCheckNotModified };
|
||||
__property UnicodeString FirstRun = { read = FFirstRun, write = SetFirstRun };
|
||||
__property LCID DefaultLocale = { read = FDefaultLocale };
|
||||
__property int LocaleCompletenessTreshold = { read = GetLocaleCompletenessTreshold };
|
||||
|
||||
Reference in New Issue
Block a user