1
0
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:
Martin Prikryl
2023-03-23 16:00:49 +00:00
parent 9dcaa8a7f3
commit 10b9d6638a
11 changed files with 103 additions and 20 deletions

View File

@@ -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)));
}
//---------------------------------------------------------------------------

View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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."

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 };