mirror of
https://github.com/winscp/winscp.git
synced 2026-10-24 02:28:55 +03:00
Issue 2232 – Automatically reconnect inactive sessions
https://winscp.net/tracker/2232 Source commit: 083cfa045a8291cdfcc1fe8ce1e510a7d7619e7e
This commit is contained in:
@@ -429,12 +429,14 @@ __fastcall EOSExtException::EOSExtException(UnicodeString Msg, int LastError) :
|
||||
//---------------------------------------------------------------------------
|
||||
__fastcall EFatal::EFatal(Exception* E, UnicodeString Msg, UnicodeString HelpKeyword) :
|
||||
ExtException(Msg, E, HelpKeyword),
|
||||
FReopenQueried(false)
|
||||
ReopenQueried(false),
|
||||
InactiveTerminationMessage(false)
|
||||
{
|
||||
EFatal * F = dynamic_cast<EFatal *>(E);
|
||||
if (F != NULL)
|
||||
{
|
||||
FReopenQueried = F->ReopenQueried;
|
||||
ReopenQueried = F->ReopenQueried;
|
||||
InactiveTerminationMessage = F->InactiveTerminationMessage;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -160,13 +160,11 @@ public:
|
||||
// fatal errors are always copied, new message is only appended
|
||||
__fastcall EFatal(Exception* E, UnicodeString Msg, UnicodeString HelpKeyword = L"");
|
||||
|
||||
__property bool ReopenQueried = { read = FReopenQueried, write = FReopenQueried };
|
||||
bool ReopenQueried;
|
||||
bool InactiveTerminationMessage;
|
||||
|
||||
virtual ExtException * __fastcall Clone();
|
||||
virtual void __fastcall Rethrow();
|
||||
|
||||
private:
|
||||
bool FReopenQueried;
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
#define DERIVE_FATAL_EXCEPTION(NAME, BASE) \
|
||||
|
||||
@@ -2566,6 +2566,12 @@ __fastcall TTerminalThread::~TTerminalThread()
|
||||
FTerminal->OnReadDirectoryProgress = FOnReadDirectoryProgress;
|
||||
FTerminal->OnInitializeLog = FOnInitializeLog;
|
||||
|
||||
while (!FNonInteractiveInformation.empty())
|
||||
{
|
||||
delete FNonInteractiveInformation.front();
|
||||
FNonInteractiveInformation.pop_front();
|
||||
}
|
||||
|
||||
delete FSection;
|
||||
if (FAbandoned)
|
||||
{
|
||||
@@ -2605,6 +2611,63 @@ void __fastcall TTerminalThread::Idle()
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalThread::StartTerminalReopenNonInteractive()
|
||||
{
|
||||
FNonInteractive = true;
|
||||
StartAction(TerminalReopenEvent);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
TTerminalReopenResult TTerminalThread::IsTerminalReopenComplete()
|
||||
{
|
||||
DebugAssert(!FCancel);
|
||||
DebugAssert(!FAbandoned);
|
||||
DebugAssert(!FCancelled);
|
||||
|
||||
TTerminalReopenResult Result;
|
||||
if (WaitForActionEvent(0))
|
||||
{
|
||||
if (FNeedsInteraction)
|
||||
{
|
||||
Result = trrNeedsInteraction;
|
||||
}
|
||||
else if (FException != NULL)
|
||||
{
|
||||
Result = trrFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = trrSucceeded;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = trrPending;
|
||||
}
|
||||
|
||||
if (Result != trrPending)
|
||||
{
|
||||
DiscardException();
|
||||
}
|
||||
|
||||
DebugAssert(FUserAction == NULL);
|
||||
|
||||
return Result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalThread::ContinueTerminalReopenInteractive()
|
||||
{
|
||||
FNonInteractive = false;
|
||||
|
||||
while (!FNonInteractiveInformation.empty())
|
||||
{
|
||||
std::unique_ptr<TInformationUserAction> Action(FNonInteractiveInformation.front());
|
||||
FNonInteractiveInformation.pop_front();
|
||||
Action->Execute(NULL);
|
||||
}
|
||||
|
||||
CompleteAction();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalOpen()
|
||||
{
|
||||
RunAction(TerminalOpenEvent);
|
||||
@@ -2620,72 +2683,103 @@ void TTerminalThread::DiscardException()
|
||||
SAFE_DESTROY(FException);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::RunAction(TNotifyEvent Action)
|
||||
void TTerminalThread::StartAction(TNotifyEvent Action)
|
||||
{
|
||||
DebugAssert(FAction == NULL);
|
||||
DebugAssert(FException == NULL);
|
||||
DebugAssert(FIdleException == NULL);
|
||||
DebugAssert(FOnIdle != NULL);
|
||||
|
||||
FCancelled = false;
|
||||
FAction = Action;
|
||||
FNeedsInteraction = false;
|
||||
TriggerEvent();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool TTerminalThread::WaitForActionEvent(unsigned int Wait)
|
||||
{
|
||||
switch (WaitForSingleObject(FActionEvent, Wait))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
return true;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw Exception(L"Error waiting for background session task to complete");
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool TTerminalThread::Abandoned()
|
||||
{
|
||||
bool Result = false;
|
||||
if (AllowAbandon && FCancel && (Now() >= FCancelAfter))
|
||||
{
|
||||
TGuard Guard(FSection);
|
||||
if (WaitForSingleObject(FActionEvent, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
FAbandoned = true;
|
||||
FCancelled = true;
|
||||
Result = true;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalThread::RunAction(TNotifyEvent Action)
|
||||
{
|
||||
FNonInteractive = false;
|
||||
StartAction(Action);
|
||||
CompleteAction();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalThread::CompleteAction()
|
||||
{
|
||||
DebugAssert(FOnIdle != NULL);
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
TriggerEvent();
|
||||
|
||||
bool Done = false;
|
||||
const unsigned int MaxWait = 50;
|
||||
unsigned int Wait = MaxWait;
|
||||
|
||||
do
|
||||
{
|
||||
switch (WaitForSingleObject(FActionEvent, Wait))
|
||||
if (WaitForActionEvent(Wait))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
Done = true;
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
if (FUserAction != NULL)
|
||||
Done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FUserAction != NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
FUserAction->Execute(NULL);
|
||||
}
|
||||
catch (Exception & E)
|
||||
{
|
||||
SaveException(E, FException);
|
||||
}
|
||||
|
||||
FUserAction = NULL;
|
||||
TriggerEvent();
|
||||
Wait = 0;
|
||||
FUserAction->Execute(NULL);
|
||||
}
|
||||
else
|
||||
catch (Exception & E)
|
||||
{
|
||||
if (FOnIdle != NULL)
|
||||
{
|
||||
FOnIdle(NULL);
|
||||
}
|
||||
Wait = std::min(Wait + 10, MaxWait);
|
||||
SaveException(E, FException);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Exception(L"Error waiting for background session task to complete");
|
||||
FUserAction = NULL;
|
||||
TriggerEvent();
|
||||
Wait = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FOnIdle != NULL)
|
||||
{
|
||||
FOnIdle(NULL);
|
||||
}
|
||||
Wait = std::min(Wait + 10, MaxWait);
|
||||
}
|
||||
}
|
||||
|
||||
if (AllowAbandon && !Done && FCancel && (Now() >= FCancelAfter))
|
||||
if (!Done && Abandoned())
|
||||
{
|
||||
TGuard Guard(FSection);
|
||||
if (WaitForSingleObject(FActionEvent, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
FAbandoned = true;
|
||||
FCancelled = true;
|
||||
FatalAbort();
|
||||
}
|
||||
FatalAbort();
|
||||
}
|
||||
}
|
||||
while (!Done);
|
||||
@@ -2792,6 +2886,7 @@ void __fastcall TTerminalThread::FatalAbort()
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::CheckCancel()
|
||||
{
|
||||
// Should we really fatally abort when e.g. just cancelling password prompt?
|
||||
if (FCancel && !FCancelled)
|
||||
{
|
||||
FCancelled = true;
|
||||
@@ -2801,6 +2896,7 @@ void __fastcall TTerminalThread::CheckCancel()
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::WaitForUserAction(TUserAction * UserAction)
|
||||
{
|
||||
DebugAssert(!FNonInteractive);
|
||||
DWORD Thread = GetCurrentThreadId();
|
||||
// we can get called from the main thread from within Idle,
|
||||
// should be only to call HandleExtendedException
|
||||
@@ -2898,13 +2994,25 @@ void __fastcall TTerminalThread::WaitForUserAction(TUserAction * UserAction)
|
||||
void __fastcall TTerminalThread::TerminalInformation(
|
||||
TTerminal * Terminal, const UnicodeString & Str, int Phase, const UnicodeString & Additional)
|
||||
{
|
||||
TInformationUserAction Action(FOnInformation);
|
||||
Action.Terminal = Terminal;
|
||||
Action.Str = Str;
|
||||
Action.Phase = Phase;
|
||||
Action.Additional = Additional;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
std::unique_ptr<TInformationUserAction> Action(new TInformationUserAction(FOnInformation));
|
||||
Action->Terminal = Terminal;
|
||||
Action->Str = Str;
|
||||
Action->Phase = Phase;
|
||||
Action->Additional = Additional;
|
||||
if (!FNonInteractive)
|
||||
{
|
||||
WaitForUserAction(Action.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
FNonInteractiveInformation.push_back(Action.release());
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalThread::NeedsInteraction()
|
||||
{
|
||||
FNeedsInteraction = true;
|
||||
Cancel();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalQueryUser(TObject * Sender,
|
||||
@@ -2913,36 +3021,42 @@ void __fastcall TTerminalThread::TerminalQueryUser(TObject * Sender,
|
||||
{
|
||||
DebugUsedParam(Arg);
|
||||
DebugAssert(Arg == NULL);
|
||||
if (FNonInteractive)
|
||||
{
|
||||
NeedsInteraction();
|
||||
Answer = AbortAnswer(Answers);
|
||||
}
|
||||
else
|
||||
{
|
||||
// note about TQueryParams::TimerEvent
|
||||
// So far there is only one use for this, the TSecureShell::SendBuffer,
|
||||
// which should be thread-safe, as long as the terminal thread,
|
||||
// is stopped waiting for OnQueryUser to finish.
|
||||
|
||||
// note about TQueryParams::TimerEvent
|
||||
// So far there is only one use for this, the TSecureShell::SendBuffer,
|
||||
// which should be thread-safe, as long as the terminal thread,
|
||||
// is stopped waiting for OnQueryUser to finish.
|
||||
// note about TQueryButtonAlias::OnClick
|
||||
// So far there is only one use for this, the TClipboardHandler,
|
||||
// which is thread-safe.
|
||||
|
||||
// note about TQueryButtonAlias::OnClick
|
||||
// So far there is only one use for this, the TClipboardHandler,
|
||||
// which is thread-safe.
|
||||
TQueryUserAction Action(FOnQueryUser);
|
||||
Action.Sender = Sender;
|
||||
Action.Query = Query;
|
||||
Action.MoreMessages = MoreMessages;
|
||||
Action.Answers = Answers;
|
||||
Action.Params = Params;
|
||||
Action.Answer = Answer;
|
||||
Action.Type = Type;
|
||||
|
||||
TQueryUserAction Action(FOnQueryUser);
|
||||
Action.Sender = Sender;
|
||||
Action.Query = Query;
|
||||
Action.MoreMessages = MoreMessages;
|
||||
Action.Answers = Answers;
|
||||
Action.Params = Params;
|
||||
Action.Answer = Answer;
|
||||
Action.Type = Type;
|
||||
WaitForUserAction(&Action);
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
|
||||
Answer = Action.Answer;
|
||||
Answer = Action.Answer;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalInitializeLog(TObject * Sender)
|
||||
{
|
||||
if (FOnInitializeLog != NULL)
|
||||
// never used, so not tested either
|
||||
if (DebugAlwaysFalse(FOnInitializeLog != NULL) && DebugAlwaysTrue(!FNonInteractive))
|
||||
{
|
||||
// never used, so not tested either
|
||||
DebugFail();
|
||||
TNotifyAction Action(FOnInitializeLog);
|
||||
Action.Sender = Sender;
|
||||
|
||||
@@ -2955,91 +3069,121 @@ void __fastcall TTerminalThread::TerminalPromptUser(TTerminal * Terminal,
|
||||
TStrings * Results, bool & Result, void * Arg)
|
||||
{
|
||||
DebugUsedParam(Arg);
|
||||
DebugAssert(Arg == NULL);
|
||||
if (FNonInteractive)
|
||||
{
|
||||
NeedsInteraction();
|
||||
Result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugAssert(Arg == NULL);
|
||||
|
||||
TPromptUserAction Action(FOnPromptUser);
|
||||
Action.Terminal = Terminal;
|
||||
Action.Kind = Kind;
|
||||
Action.Name = Name;
|
||||
Action.Instructions = Instructions;
|
||||
Action.Prompts = Prompts;
|
||||
Action.Results->AddStrings(Results);
|
||||
TPromptUserAction Action(FOnPromptUser);
|
||||
Action.Terminal = Terminal;
|
||||
Action.Kind = Kind;
|
||||
Action.Name = Name;
|
||||
Action.Instructions = Instructions;
|
||||
Action.Prompts = Prompts;
|
||||
Action.Results->AddStrings(Results);
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
|
||||
Results->Clear();
|
||||
Results->AddStrings(Action.Results);
|
||||
Result = Action.Result;
|
||||
Results->Clear();
|
||||
Results->AddStrings(Action.Results);
|
||||
Result = Action.Result;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalShowExtendedException(
|
||||
TTerminal * Terminal, Exception * E, void * Arg)
|
||||
{
|
||||
DebugUsedParam(Arg);
|
||||
DebugAssert(Arg == NULL);
|
||||
if (FNonInteractive)
|
||||
{
|
||||
NeedsInteraction();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugAssert(Arg == NULL);
|
||||
|
||||
TShowExtendedExceptionAction Action(FOnShowExtendedException);
|
||||
Action.Terminal = Terminal;
|
||||
Action.E = E;
|
||||
TShowExtendedExceptionAction Action(FOnShowExtendedException);
|
||||
Action.Terminal = Terminal;
|
||||
Action.E = E;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalDisplayBanner(TTerminal * Terminal,
|
||||
UnicodeString SessionName, const UnicodeString & Banner,
|
||||
bool & NeverShowAgain, int Options, unsigned int & Params)
|
||||
{
|
||||
TDisplayBannerAction Action(FOnDisplayBanner);
|
||||
Action.Terminal = Terminal;
|
||||
Action.SessionName = SessionName;
|
||||
Action.Banner = Banner;
|
||||
Action.NeverShowAgain = NeverShowAgain;
|
||||
Action.Options = Options;
|
||||
Action.Params = Params;
|
||||
if (!FNonInteractive)
|
||||
{
|
||||
TDisplayBannerAction Action(FOnDisplayBanner);
|
||||
Action.Terminal = Terminal;
|
||||
Action.SessionName = SessionName;
|
||||
Action.Banner = Banner;
|
||||
Action.NeverShowAgain = NeverShowAgain;
|
||||
Action.Options = Options;
|
||||
Action.Params = Params;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
|
||||
NeverShowAgain = Action.NeverShowAgain;
|
||||
Params = Action.Params;
|
||||
NeverShowAgain = Action.NeverShowAgain;
|
||||
Params = Action.Params;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalChangeDirectory(TObject * Sender)
|
||||
{
|
||||
TNotifyAction Action(FOnChangeDirectory);
|
||||
Action.Sender = Sender;
|
||||
if (!FNonInteractive)
|
||||
{
|
||||
TNotifyAction Action(FOnChangeDirectory);
|
||||
Action.Sender = Sender;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalReadDirectory(TObject * Sender, Boolean ReloadOnly)
|
||||
{
|
||||
TReadDirectoryAction Action(FOnReadDirectory);
|
||||
Action.Sender = Sender;
|
||||
Action.ReloadOnly = ReloadOnly;
|
||||
if (!FNonInteractive)
|
||||
{
|
||||
TReadDirectoryAction Action(FOnReadDirectory);
|
||||
Action.Sender = Sender;
|
||||
Action.ReloadOnly = ReloadOnly;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalStartReadDirectory(TObject * Sender)
|
||||
{
|
||||
TNotifyAction Action(FOnStartReadDirectory);
|
||||
Action.Sender = Sender;
|
||||
if (!FNonInteractive)
|
||||
{
|
||||
TNotifyAction Action(FOnStartReadDirectory);
|
||||
Action.Sender = Sender;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalThread::TerminalReadDirectoryProgress(
|
||||
TObject * Sender, int Progress, int ResolvedLinks, bool & Cancel)
|
||||
{
|
||||
TReadDirectoryProgressAction Action(FOnReadDirectoryProgress);
|
||||
Action.Sender = Sender;
|
||||
Action.Progress = Progress;
|
||||
Action.ResolvedLinks = ResolvedLinks;
|
||||
Action.Cancel = Cancel;
|
||||
if (!FNonInteractive)
|
||||
{
|
||||
TReadDirectoryProgressAction Action(FOnReadDirectoryProgress);
|
||||
Action.Sender = Sender;
|
||||
Action.Progress = Progress;
|
||||
Action.ResolvedLinks = ResolvedLinks;
|
||||
Action.Cancel = Cancel;
|
||||
|
||||
WaitForUserAction(&Action);
|
||||
WaitForUserAction(&Action);
|
||||
|
||||
Cancel = Action.Cancel;
|
||||
Cancel = Action.Cancel;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool __fastcall TTerminalThread::Release()
|
||||
|
||||
@@ -404,6 +404,8 @@ private:
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
class TUserAction;
|
||||
class TInformationUserAction;
|
||||
enum TTerminalReopenResult { trrPending, trrSucceeded, trrFailed, trrNeedsInteraction };
|
||||
class TTerminalThread : public TSignalThread
|
||||
{
|
||||
public:
|
||||
@@ -412,6 +414,10 @@ public:
|
||||
|
||||
void __fastcall TerminalOpen();
|
||||
void __fastcall TerminalReopen();
|
||||
void StartTerminalReopenNonInteractive();
|
||||
TTerminalReopenResult IsTerminalReopenComplete();
|
||||
void ContinueTerminalReopenInteractive();
|
||||
bool Abandoned();
|
||||
|
||||
void __fastcall Cancel();
|
||||
bool __fastcall Release();
|
||||
@@ -454,12 +460,17 @@ private:
|
||||
bool FCancelled;
|
||||
bool FPendingIdle;
|
||||
bool FAllowAbandon;
|
||||
bool FNonInteractive;
|
||||
bool FNeedsInteraction;
|
||||
typedef std::list<TInformationUserAction *> TInformationList;
|
||||
TInformationList FNonInteractiveInformation;
|
||||
|
||||
DWORD FMainThread;
|
||||
TCriticalSection * FSection;
|
||||
|
||||
void __fastcall WaitForUserAction(TUserAction * UserAction);
|
||||
void __fastcall RunAction(TNotifyEvent Action);
|
||||
void StartAction(TNotifyEvent Action);
|
||||
void RunAction(TNotifyEvent Action);
|
||||
|
||||
static void __fastcall SaveException(Exception & E, Exception *& Exception);
|
||||
static void __fastcall Rethrow(Exception *& Exception);
|
||||
@@ -488,6 +499,9 @@ private:
|
||||
void __fastcall TerminalReadDirectoryProgress(TObject * Sender, int Progress, int ResolvedLinks, bool & Cancel);
|
||||
void __fastcall TerminalInitializeLog(TObject * Sender);
|
||||
void DiscardException();
|
||||
bool WaitForActionEvent(unsigned int Wait);
|
||||
void CompleteAction();
|
||||
void NeedsInteraction();
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
enum TQueueFileState { qfsQueued = 0, qfsProcessed = 1 };
|
||||
|
||||
@@ -3973,17 +3973,40 @@ void TCustomScpExplorerForm::EditedFileUploaded(TTerminal * ATerminal, HANDLE Up
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(
|
||||
const UnicodeString & FileName, TEditedFileData * Data, HANDLE UploadCompleteEvent, bool & Retry)
|
||||
const UnicodeString & FileName, const TDateTime & Timestamp, TEditedFileData * Data,
|
||||
HANDLE UploadCompleteEvent, bool & Retry)
|
||||
{
|
||||
TTerminalManager * Manager = TTerminalManager::Instance();
|
||||
|
||||
bool IsInactive = !IsActiveTerminal(Data->Terminal);
|
||||
if ((Manager->ActiveTerminal == Data->Terminal) &&
|
||||
Manager->ScheduleTerminalReconnnect(Data->Terminal))
|
||||
{
|
||||
AppLog(L"Scheduled session reconnect, will retry the upload later");
|
||||
Retry = true;
|
||||
}
|
||||
else if (!IsActiveTerminal(Data->Terminal))
|
||||
else if (Manager->IsReconnectingTerminal(Data->Terminal))
|
||||
{
|
||||
AppLog(L"Session is being reconnected, will retry the upload later");
|
||||
Retry = true;
|
||||
}
|
||||
else if (IsInactive &&
|
||||
GUIConfiguration->SessionReopenAutoInactive &&
|
||||
(Data->Terminal->NextInactiveReconnect != TDateTime()))
|
||||
{
|
||||
if (Data->Terminal->LastInactiveSave != Timestamp)
|
||||
{
|
||||
AppLog(L"Inactive session reconnect is pending, rescheduling it for as soon as possible to retry the upload");
|
||||
Data->Terminal->NextInactiveReconnect = Now();
|
||||
Data->Terminal->LastInactiveSave = Timestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppLog(L"Inactive session reconnect is pending, will retry the upload later");
|
||||
}
|
||||
Retry = true;
|
||||
}
|
||||
else if (IsInactive)
|
||||
{
|
||||
if (!NonVisualDataModule->Busy)
|
||||
{
|
||||
@@ -4018,7 +4041,16 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(
|
||||
UnicodeString Message =
|
||||
MainInstructions(
|
||||
FMTLOAD(EDIT_SESSION_RECONNECT, (Data->SessionName, FileNameOnly)));
|
||||
if (MessageDialog(Message, qtConfirmation, qaOK | qaCancel) == qaOK)
|
||||
TMessageParams Params(FLAGMASK(!GUIConfiguration->SessionReopenAutoInactive, mpNeverAskAgainCheck));
|
||||
Params.NeverAskAgainTitle = LoadStr(ALWAYS_RECONNECT);
|
||||
unsigned int Answer = MessageDialog(Message, qtConfirmation, qaOK | qaCancel, HELP_NONE, &Params);
|
||||
if (Answer == qaNeverAskAgain)
|
||||
{
|
||||
GUIConfiguration->SessionReopenAutoInactive = true;
|
||||
Answer = qaOK;
|
||||
}
|
||||
|
||||
if (Answer == qaOK)
|
||||
{
|
||||
if (Terminal->Disconnected)
|
||||
{
|
||||
@@ -7989,25 +8021,23 @@ void __fastcall TCustomScpExplorerForm::SessionReady()
|
||||
UpdateSessionTab(SessionsPageControl->Pages[ActiveSessionIndex]);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::InactiveTerminalException(
|
||||
TTerminal * Terminal, Exception * E)
|
||||
void TCustomScpExplorerForm::InactiveTerminalNotify(
|
||||
TManagedTerminal * Terminal, const UnicodeString & Message, TQueryType Type, Exception * E)
|
||||
{
|
||||
Notify(Terminal, L"", qtError, false, NULL, NULL, E);
|
||||
Notify(Terminal, Message, Type, false, NULL, NULL, E);
|
||||
|
||||
if (!Terminal->Active)
|
||||
int Index = TTerminalManager::Instance()->IndexOf(Terminal);
|
||||
if (DebugAlwaysTrue((Index >= 0) && (Index < SessionsPageControl->PageCount)))
|
||||
{
|
||||
int Index = TTerminalManager::Instance()->IndexOf(Terminal);
|
||||
if (DebugAlwaysTrue((Index >= 0) && (Index < SessionsPageControl->PageCount)))
|
||||
{
|
||||
UpdateSessionTab(SessionsPageControl->Pages[Index]);
|
||||
}
|
||||
UpdateSessionTab(SessionsPageControl->Pages[Index]);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::Notify(TTerminal * Terminal,
|
||||
UnicodeString Message, TQueryType Type,
|
||||
void TCustomScpExplorerForm::Notify(
|
||||
TManagedTerminal * Terminal, const UnicodeString & AMessage, TQueryType Type,
|
||||
bool Important, TNotifyEvent OnClick, TObject * UserData, Exception * E)
|
||||
{
|
||||
UnicodeString Message = AMessage;
|
||||
if ((E == NULL) ||
|
||||
ExceptionMessage(E, Message))
|
||||
{
|
||||
@@ -8020,9 +8050,7 @@ void __fastcall TCustomScpExplorerForm::Notify(TTerminal * Terminal,
|
||||
UnicodeString NoteMessage(UnformatMessage(Message));
|
||||
if (Terminal != NULL)
|
||||
{
|
||||
TManagedTerminal * Session = DebugNotNull(dynamic_cast<TManagedTerminal *>(Terminal));
|
||||
NoteMessage = FORMAT(L"%s: %s",
|
||||
(TTerminalManager::Instance()->GetSessionTitle(Session, true), NoteMessage));
|
||||
NoteMessage = TTerminalManager::Instance()->FormatTerminalNoteMessage(Terminal, NoteMessage);
|
||||
}
|
||||
|
||||
if (WinConfiguration->BalloonNotifications)
|
||||
@@ -9573,7 +9601,7 @@ void __fastcall TCustomScpExplorerForm::UpdatesChecked()
|
||||
if (!New && (Type != qtWarning))
|
||||
{
|
||||
AppLog(L"Posting non-critical updates note");
|
||||
PostNote(UnformatMessage(Message), 0, UpdatesNoteClicked, NULL);
|
||||
PostNote(UnformatMessage(Message), 0, UpdatesNoteClicked);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -10228,6 +10256,14 @@ bool __fastcall TCustomScpExplorerForm::CancelNote(bool Force)
|
||||
return Result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TCustomScpExplorerForm::CancelNote(const UnicodeString & Note)
|
||||
{
|
||||
if (FNote == Note)
|
||||
{
|
||||
CancelNote(true);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::NoteTimer(TObject * /*Sender*/)
|
||||
{
|
||||
DebugAssert(FNoteTimer->Enabled);
|
||||
@@ -10261,9 +10297,10 @@ void __fastcall TCustomScpExplorerForm::AddNote(UnicodeString Note, bool UpdateN
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::PostNote(UnicodeString Note,
|
||||
unsigned int Seconds, TNotifyEvent OnNoteClick, TObject * NoteData)
|
||||
void TCustomScpExplorerForm::PostNote(
|
||||
const UnicodeString & ANote, unsigned int Seconds, TNotifyEvent OnNoteClick, TObject * NoteData)
|
||||
{
|
||||
UnicodeString Note = ANote;
|
||||
int P = Note.Pos(L"\n");
|
||||
if (P > 0)
|
||||
{
|
||||
@@ -10289,7 +10326,7 @@ void __fastcall TCustomScpExplorerForm::PostNote(UnicodeString Note,
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::ReadDirectoryCancelled()
|
||||
{
|
||||
PostNote(LoadStr(DIRECTORY_READING_CANCELLED), 0, NULL, NULL);
|
||||
PostNote(LoadStr(DIRECTORY_READING_CANCELLED));
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::SynchronizeBrowsingChanged()
|
||||
@@ -10301,8 +10338,7 @@ void __fastcall TCustomScpExplorerForm::SynchronizeBrowsingChanged()
|
||||
|
||||
PostNote(FORMAT(LoadStrPart(SYNC_DIR_BROWSE_TOGGLE, 1),
|
||||
(LoadStrPart(SYNC_DIR_BROWSE_TOGGLE,
|
||||
(NonVisualDataModule->SynchronizeBrowsingAction2->Checked ? 2 : 3)))),
|
||||
0, NULL, NULL);
|
||||
(NonVisualDataModule->SynchronizeBrowsingAction2->Checked ? 2 : 3)))));
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::ToggleShowHiddenFiles()
|
||||
@@ -10310,7 +10346,7 @@ void __fastcall TCustomScpExplorerForm::ToggleShowHiddenFiles()
|
||||
bool NewShowHiddenFiles = !WinConfiguration->ShowHiddenFiles;
|
||||
UnicodeString Note =
|
||||
FORMAT(LoadStrPart(SHOW_HIDDEN_FILES_TOGGLE, 1), (LoadStrPart(SHOW_HIDDEN_FILES_TOGGLE, (NewShowHiddenFiles ? 2 : 3))));
|
||||
PostNote(Note, 0, NULL, NULL);
|
||||
PostNote(Note);
|
||||
GetComponent(fcStatusBar)->Repaint(); // toggling ShowHiddenFiles takes time, force repaint beforehand
|
||||
WinConfiguration->ShowHiddenFiles = NewShowHiddenFiles;
|
||||
}
|
||||
@@ -10325,7 +10361,7 @@ void __fastcall TCustomScpExplorerForm::ToggleAutoReadDirectoryAfterOp()
|
||||
Configuration->AutoReadDirectoryAfterOp = !Configuration->AutoReadDirectoryAfterOp;
|
||||
PostNote(FORMAT(LoadStrPart(AUTO_READ_DIRECTORY_TOGGLE, 1),
|
||||
(LoadStrPart(AUTO_READ_DIRECTORY_TOGGLE,
|
||||
(Configuration->AutoReadDirectoryAfterOp ? 2 : 3)))), 0, NULL, NULL);
|
||||
(Configuration->AutoReadDirectoryAfterOp ? 2 : 3)))));
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TCustomScpExplorerForm::StatusBarPanelDblClick(
|
||||
@@ -11985,7 +12021,7 @@ void __fastcall TCustomScpExplorerForm::CloseApp()
|
||||
//---------------------------------------------------------------------------
|
||||
bool __fastcall TCustomScpExplorerForm::IsActiveTerminal(TTerminal * Terminal)
|
||||
{
|
||||
return (Terminal != NULL) && Terminal->Active;
|
||||
return TTerminalManager::Instance()->IsActiveTerminal(Terminal);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool __fastcall TCustomScpExplorerForm::HasManagedSession()
|
||||
|
||||
@@ -472,7 +472,8 @@ protected:
|
||||
void __fastcall OperationComplete(const TDateTime & StartTime);
|
||||
void EditedFileUploaded(TTerminal * ATerminal, HANDLE UploadCompleteEvent);
|
||||
void __fastcall ExecutedFileChanged(
|
||||
const UnicodeString & FileName, TEditedFileData * Data, HANDLE UploadCompleteEvent, bool & Retry);
|
||||
const UnicodeString & FileName, const TDateTime & Timestamp, TEditedFileData * Data,
|
||||
HANDLE UploadCompleteEvent, bool & Retry);
|
||||
void __fastcall ExecutedFileReload(const UnicodeString & FileName, TEditedFileData * Data);
|
||||
void __fastcall ExecutedFileEarlyClosed(const TEditedFileData * Data,
|
||||
bool & KeepOpen);
|
||||
@@ -627,8 +628,6 @@ protected:
|
||||
void __fastcall SetSessionColor(TColor value);
|
||||
void __fastcall NoteTimer(TObject * Sender);
|
||||
void __fastcall AddNote(UnicodeString Note, bool UpdateNow = true);
|
||||
void __fastcall PostNote(UnicodeString Note, unsigned int Seconds,
|
||||
TNotifyEvent OnNoteClick, TObject * NoteData);
|
||||
bool __fastcall CancelNote(bool Force);
|
||||
void __fastcall UpdateNoteHints();
|
||||
void __fastcall UpdatesChecked();
|
||||
@@ -638,9 +637,6 @@ protected:
|
||||
bool AllowNeverAskAgain);
|
||||
void __fastcall UpdateTrayIcon();
|
||||
void __fastcall TrayIconClick(TObject * Sender);
|
||||
void __fastcall Notify(TTerminal * Terminal, UnicodeString Message,
|
||||
TQueryType Type, bool Important = false, TNotifyEvent OnClick = NULL,
|
||||
TObject * UserData = NULL, Exception * E = NULL);
|
||||
virtual void __fastcall UpdateSessionData(TSessionData * Data);
|
||||
virtual void __fastcall UpdateRemotePathComboBox(bool TextOnly);
|
||||
virtual void __fastcall ToolbarItemResize(TTBXCustomDropDownItem * Item, int Width);
|
||||
@@ -873,7 +869,8 @@ public:
|
||||
void __fastcall OperationProgress(TFileOperationProgressType & ProgressData);
|
||||
UnicodeString __fastcall GetProgressTitle();
|
||||
void __fastcall ShowExtendedException(TTerminal * Terminal, Exception * E);
|
||||
void __fastcall InactiveTerminalException(TTerminal * Terminal, Exception * E);
|
||||
void InactiveTerminalNotify(
|
||||
TManagedTerminal * Terminal, const UnicodeString & Message, TQueryType Type, Exception * E = NULL);
|
||||
void __fastcall SessionReady();
|
||||
void __fastcall QueueEvent(TManagedTerminal * Terminal, TTerminalQueue * Queue, TQueueEvent Event);
|
||||
void __fastcall QueueEmptyNoteClicked(TObject * Sender);
|
||||
@@ -937,6 +934,11 @@ public:
|
||||
void PostThumbnailVisibleQueueQuery(int Index, const UnicodeString & FileName);
|
||||
void PostThumbnailDrawRequest(int Index);
|
||||
void ChangeDirViewStyle(TOperationSide Side, TDirViewStyle DirViewStyle);
|
||||
void PostNote(const UnicodeString & Note, unsigned int Seconds = 0, TNotifyEvent OnNoteClick = NULL, TObject * NoteData = NULL);
|
||||
void CancelNote(const UnicodeString & Note);
|
||||
void Notify(
|
||||
TManagedTerminal * Terminal, const UnicodeString & Message, TQueryType Type,
|
||||
bool Important = false, TNotifyEvent OnClick = NULL, TObject * UserData = NULL, Exception * E = NULL);
|
||||
|
||||
__property bool ComponentVisible[Byte Component] = { read = GetComponentVisible, write = SetComponentVisible };
|
||||
__property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation, index = 0 };
|
||||
|
||||
@@ -427,9 +427,12 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
|
||||
SessionReopenAutoCheck->Checked = (Configuration->SessionReopenAuto > 0);
|
||||
SessionReopenAutoEdit->Value = (Configuration->SessionReopenAuto > 0 ?
|
||||
(Configuration->SessionReopenAuto / MSecsPerSec) : 5);
|
||||
SessionReopenAutoIdleCheck->Checked = (GUIConfiguration->SessionReopenAutoIdle > 0);
|
||||
SessionReopenAutoIdleEdit->Value = (GUIConfiguration->SessionReopenAutoIdle > 0 ?
|
||||
(GUIConfiguration->SessionReopenAutoIdle / MSecsPerSec) : 5);
|
||||
SessionReopenAutoIdleCheck->Checked =
|
||||
GUIConfiguration->SessionReopenAutoIdleOn && (GUIConfiguration->SessionReopenAutoIdle > 0);
|
||||
SessionReopenAutoInactiveCheck->Checked = GUIConfiguration->SessionReopenAutoInactive;
|
||||
int SessionReopenAutoIdle =
|
||||
(GUIConfiguration->SessionReopenAutoIdle > 0 ? GUIConfiguration->SessionReopenAutoIdle : SessionReopenAutoIdleDefault);
|
||||
SessionReopenAutoIdleEdit->Value = (SessionReopenAutoIdle / MSecsPerSec);
|
||||
SessionReopenAutoStallCheck->Checked = (Configuration->SessionReopenAutoStall > 0);
|
||||
SessionReopenAutoStallEdit->Value = (Configuration->SessionReopenAutoStall > 0 ?
|
||||
(Configuration->SessionReopenAutoStall / MSecsPerSec) : SecsPerMin);
|
||||
@@ -829,8 +832,9 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
|
||||
|
||||
Configuration->SessionReopenAuto =
|
||||
(SessionReopenAutoCheck->Checked ? (SessionReopenAutoEdit->AsInteger * MSecsPerSec) : 0);
|
||||
GUIConfiguration->SessionReopenAutoIdle =
|
||||
(SessionReopenAutoIdleCheck->Checked ? (SessionReopenAutoIdleEdit->AsInteger * MSecsPerSec) : 0);
|
||||
GUIConfiguration->SessionReopenAutoIdleOn = SessionReopenAutoIdleCheck->Checked;
|
||||
GUIConfiguration->SessionReopenAutoInactive = SessionReopenAutoInactiveCheck->Checked;
|
||||
GUIConfiguration->SessionReopenAutoIdle = (SessionReopenAutoIdleEdit->AsInteger * MSecsPerSec);
|
||||
Configuration->SessionReopenAutoStall =
|
||||
(SessionReopenAutoStallCheck->Checked ? (SessionReopenAutoStallEdit->AsInteger * MSecsPerSec) : 0);
|
||||
Configuration->SessionReopenTimeout = (SessionReopenTimeoutEdit->AsInteger * MSecsPerSec);
|
||||
@@ -1289,7 +1293,7 @@ void __fastcall TPreferencesDialog::UpdateControls()
|
||||
EnableControl(SessionReopenAutoEdit, SessionReopenAutoCheck->Checked);
|
||||
EnableControl(SessionReopenAutoLabel, SessionReopenAutoEdit->Enabled);
|
||||
EnableControl(SessionReopenAutoSecLabel, SessionReopenAutoEdit->Enabled);
|
||||
EnableControl(SessionReopenAutoIdleEdit, SessionReopenAutoIdleCheck->Checked);
|
||||
EnableControl(SessionReopenAutoIdleEdit, SessionReopenAutoIdleCheck->Checked || SessionReopenAutoInactiveCheck->Checked);
|
||||
EnableControl(SessionReopenAutoIdleLabel, SessionReopenAutoIdleEdit->Enabled);
|
||||
EnableControl(SessionReopenAutoIdleSecLabel, SessionReopenAutoIdleEdit->Enabled);
|
||||
EnableControl(SessionReopenAutoStallEdit, SessionReopenAutoStallCheck->Checked);
|
||||
|
||||
@@ -2010,13 +2010,13 @@ object PreferencesDialog: TPreferencesDialog
|
||||
Left = 8
|
||||
Top = 132
|
||||
Width = 445
|
||||
Height = 211
|
||||
Height = 234
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = 'Automatic reconnect'
|
||||
TabOrder = 1
|
||||
DesignSize = (
|
||||
445
|
||||
211)
|
||||
234)
|
||||
object SessionReopenAutoLabel: TLabel
|
||||
Left = 27
|
||||
Top = 48
|
||||
@@ -2035,7 +2035,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenTimeoutLabel: TLabel
|
||||
Left = 9
|
||||
Top = 181
|
||||
Top = 204
|
||||
Width = 120
|
||||
Height = 15
|
||||
Caption = '&Keep reconnecting for:'
|
||||
@@ -2043,7 +2043,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenTimeoutSecLabel: TLabel
|
||||
Left = 286
|
||||
Top = 181
|
||||
Top = 204
|
||||
Width = 43
|
||||
Height = 15
|
||||
Caption = 'seconds'
|
||||
@@ -2051,7 +2051,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenAutoStallLabel: TLabel
|
||||
Left = 27
|
||||
Top = 152
|
||||
Top = 175
|
||||
Width = 86
|
||||
Height = 15
|
||||
Caption = 'Re&connect after:'
|
||||
@@ -2059,7 +2059,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenAutoStallSecLabel: TLabel
|
||||
Left = 286
|
||||
Top = 152
|
||||
Top = 175
|
||||
Width = 43
|
||||
Height = 15
|
||||
Caption = 'seconds'
|
||||
@@ -2067,7 +2067,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenAutoIdleLabel: TLabel
|
||||
Left = 27
|
||||
Top = 100
|
||||
Top = 123
|
||||
Width = 86
|
||||
Height = 15
|
||||
Caption = 'Reco&nnect after:'
|
||||
@@ -2075,7 +2075,7 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenAutoIdleSecLabel: TLabel
|
||||
Left = 286
|
||||
Top = 100
|
||||
Top = 123
|
||||
Width = 43
|
||||
Height = 15
|
||||
Caption = 'seconds'
|
||||
@@ -2116,30 +2116,30 @@ object PreferencesDialog: TPreferencesDialog
|
||||
end
|
||||
object SessionReopenTimeoutEdit: TUpDownEdit
|
||||
Left = 183
|
||||
Top = 178
|
||||
Top = 201
|
||||
Width = 97
|
||||
Height = 23
|
||||
Alignment = taRightJustify
|
||||
Increment = 30.000000000000000000
|
||||
MaxValue = 86400.000000000000000000
|
||||
MaxLength = 5
|
||||
TabOrder = 6
|
||||
TabOrder = 7
|
||||
OnGetValue = SessionReopenTimeoutEditGetValue
|
||||
OnSetValue = SessionReopenTimeoutEditSetValue
|
||||
end
|
||||
object SessionReopenAutoStallCheck: TCheckBox
|
||||
Left = 11
|
||||
Top = 126
|
||||
Top = 149
|
||||
Width = 425
|
||||
Height = 17
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = 'Automatically reconnect session, if it &stalls'
|
||||
TabOrder = 4
|
||||
TabOrder = 5
|
||||
OnClick = ControlChange
|
||||
end
|
||||
object SessionReopenAutoStallEdit: TUpDownEdit
|
||||
Left = 183
|
||||
Top = 149
|
||||
Top = 172
|
||||
Width = 97
|
||||
Height = 23
|
||||
Alignment = taRightJustify
|
||||
@@ -2148,11 +2148,11 @@ object PreferencesDialog: TPreferencesDialog
|
||||
MinValue = 1.000000000000000000
|
||||
Value = 5.000000000000000000
|
||||
MaxLength = 3
|
||||
TabOrder = 5
|
||||
TabOrder = 6
|
||||
end
|
||||
object SessionReopenAutoIdleEdit: TUpDownEdit
|
||||
Left = 183
|
||||
Top = 97
|
||||
Top = 120
|
||||
Width = 97
|
||||
Height = 23
|
||||
Alignment = taRightJustify
|
||||
@@ -2161,7 +2161,17 @@ object PreferencesDialog: TPreferencesDialog
|
||||
MinValue = 1.000000000000000000
|
||||
Value = 5.000000000000000000
|
||||
MaxLength = 3
|
||||
TabOrder = 4
|
||||
end
|
||||
object SessionReopenAutoInactiveCheck: TCheckBox
|
||||
Left = 11
|
||||
Top = 97
|
||||
Width = 425
|
||||
Height = 17
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = 'Automatically reconnect session, if it breaks &while inactive'
|
||||
TabOrder = 3
|
||||
OnClick = ControlChange
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -359,6 +359,7 @@ __published:
|
||||
TComboEdit *SearchEdit;
|
||||
TTabSheet *SearchSheet;
|
||||
TGroupBox *SearchGroup;
|
||||
TCheckBox *SessionReopenAutoInactiveCheck;
|
||||
void __fastcall FormShow(TObject *Sender);
|
||||
void __fastcall ControlChange(TObject *Sender);
|
||||
void __fastcall EditorFontButtonClick(TObject *Sender);
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
#define CLOSE_WORKSPACE 1372
|
||||
#define TOO_MANY_SESSIONS 1373
|
||||
#define EDIT_CHANGED_EXTERNALLY 1374
|
||||
#define ALWAYS_RECONNECT 1375
|
||||
|
||||
#define WIN_INFORMATION_STRINGS 1400
|
||||
#define COMPARE_NO_DIFFERENCES 1402
|
||||
@@ -316,6 +317,9 @@
|
||||
#define PATTERNS_HINT_K 7000
|
||||
#define PATTERNS_HINT_BACKSLASH 7001
|
||||
#define USAGE_COPYID 7002
|
||||
#define SESSION_RECONNECTING 7003
|
||||
#define SESSION_RECONNECTED 7004
|
||||
#define SESSION_NEED_INTERACTION 7005
|
||||
|
||||
#define WIN_FORMS_STRINGS 1600
|
||||
#define COPY_FILE 1605
|
||||
|
||||
@@ -168,6 +168,7 @@ BEGIN
|
||||
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?"
|
||||
ALWAYS_RECONNECT, "&Always reconnect automatically"
|
||||
|
||||
WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
|
||||
COMPARE_NO_DIFFERENCES, "No differences found."
|
||||
@@ -321,6 +322,9 @@ BEGIN
|
||||
PATTERNS_HINT_K, "!K expands to current session private key path"
|
||||
PATTERNS_HINT_BACKSLASH, "!\\ expands to current local path"
|
||||
USAGE_COPYID, "Installs your public key in a server's authorized_keys."
|
||||
SESSION_RECONNECTING, "Trying to reconnect..."
|
||||
SESSION_RECONNECTED, "Reconnected."
|
||||
SESSION_NEED_INTERACTION, "Session needs interaction to reconnect."
|
||||
|
||||
WIN_FORMS_STRINGS, "WIN_FORMS_STRINGS"
|
||||
COPY_FILE, "%s file '%s' to %s:"
|
||||
|
||||
@@ -320,7 +320,7 @@ void __fastcall TEditorManager::Check()
|
||||
|
||||
if (Index >= 0)
|
||||
{
|
||||
UploadComplete(Index);
|
||||
UploadComplete(Index, false);
|
||||
}
|
||||
}
|
||||
while ((Index >= 0) && (FUploadCompleteEvents.size() > 0));
|
||||
@@ -405,7 +405,7 @@ void __fastcall TEditorManager::AddFile(TFileData & FileData, TEditedFileData *
|
||||
Data.release(); // ownership passed
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TEditorManager::UploadComplete(int Index)
|
||||
void TEditorManager::UploadComplete(int Index, bool Retry)
|
||||
{
|
||||
TFileData * FileData = &FFiles[Index];
|
||||
|
||||
@@ -427,7 +427,9 @@ void __fastcall TEditorManager::UploadComplete(int Index)
|
||||
FileData->Reupload = false;
|
||||
CheckFileChange(Index, true);
|
||||
}
|
||||
else if ((FileData->Token != NULL) && (FOnFileUploadComplete != NULL))
|
||||
// so far used only to signal to the internal editor that saving was complete,
|
||||
// so we do signal once an actual upload is done only
|
||||
else if ((FileData->Token != NULL) && (FOnFileUploadComplete != NULL) && !Retry)
|
||||
{
|
||||
FOnFileUploadComplete(FileData->Token);
|
||||
}
|
||||
@@ -583,11 +585,11 @@ void __fastcall TEditorManager::CheckFileChange(int Index, bool Force)
|
||||
DebugAssert(OnFileChange != NULL);
|
||||
bool Retry = false;
|
||||
AppLogFmt(L"Uploading opened/edited file \"%s\".", (FileData->FileName));
|
||||
OnFileChange(FileData->FileName, FileData->Data, FileData->UploadCompleteEvent, Retry);
|
||||
OnFileChange(FileData->FileName, FileData->Timestamp, FileData->Data, FileData->UploadCompleteEvent, Retry);
|
||||
if (Retry)
|
||||
{
|
||||
AppLogFmt(L"Will retry uploading opened/edited file \"%s\".", (FileData->FileName));
|
||||
UploadComplete(Index);
|
||||
UploadComplete(Index, true);
|
||||
FileData->Timestamp = PrevTimestamp;
|
||||
}
|
||||
}
|
||||
@@ -597,7 +599,7 @@ void __fastcall TEditorManager::CheckFileChange(int Index, bool Force)
|
||||
// upload failed (was not even started)
|
||||
if (FileData->UploadCompleteEvent != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
UploadComplete(Index);
|
||||
UploadComplete(Index, false);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct TEditedFileData
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
typedef void __fastcall (__closure * TEditedFileChangedEvent)
|
||||
(const UnicodeString & FileName, TEditedFileData * Data, HANDLE CompleteEvent, bool & Retry);
|
||||
(const UnicodeString & FileName, const TDateTime & Timestamp, TEditedFileData * Data, HANDLE CompleteEvent, bool & Retry);
|
||||
typedef void __fastcall (__closure * TEditedFileReloadEvent)
|
||||
(const UnicodeString & FileName, TEditedFileData * Data);
|
||||
typedef void __fastcall (__closure * TEditedFileEarlyClosedEvent)
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
TCriticalSection * FSection;
|
||||
|
||||
void __fastcall AddFile(TFileData & FileData, TEditedFileData * Data);
|
||||
void __fastcall UploadComplete(int Index);
|
||||
void UploadComplete(int Index, bool Retry);
|
||||
bool __fastcall CloseFile(int Index, bool IgnoreErrors, bool Delete);
|
||||
void __fastcall CloseProcess(int Index);
|
||||
bool __fastcall EarlyClose(int Index);
|
||||
|
||||
@@ -21,6 +21,7 @@ const int ccCopyResults = ccUser << 2;
|
||||
const int ccRemoteFiles = ccUser << 3;
|
||||
const int ccShowResultsInMsgBox = ccUser << 4;
|
||||
const int ccSet = 0x80000000;
|
||||
const int SessionReopenAutoIdleDefault = 9000;
|
||||
//---------------------------------------------------------------------------
|
||||
static const unsigned int AdditionalLanguageMask = 0xFFFFFF00;
|
||||
static const UnicodeString AdditionalLanguagePrefix(L"XX");
|
||||
@@ -578,7 +579,9 @@ void __fastcall TGUIConfiguration::Default()
|
||||
FCopyParamCurrent = L"";
|
||||
FKeepUpToDateChangeDelay = 500;
|
||||
FChecksumAlg = L"sha1";
|
||||
FSessionReopenAutoIdle = 9000;
|
||||
FSessionReopenAutoIdle = SessionReopenAutoIdleDefault;
|
||||
FSessionReopenAutoIdleOn = true;
|
||||
FSessionReopenAutoInactive = false;
|
||||
|
||||
FNewDirectoryProperties.Default();
|
||||
FNewDirectoryProperties.Rights = TRights::rfDefault | TRights::rfExec;
|
||||
@@ -655,6 +658,8 @@ void __fastcall TGUIConfiguration::UpdateStaticUsage()
|
||||
KEY(Integer, KeepUpToDateChangeDelay); \
|
||||
KEY(String, ChecksumAlg); \
|
||||
KEY(Integer, SessionReopenAutoIdle); \
|
||||
KEY(Bool, SessionReopenAutoIdleOn); \
|
||||
KEY(Bool, SessionReopenAutoInactive); \
|
||||
); \
|
||||
//---------------------------------------------------------------------------
|
||||
bool __fastcall TGUIConfiguration::DoSaveCopyParam(THierarchicalStorage * Storage, const TCopyParamType * CopyParam, const TCopyParamType * Defaults)
|
||||
|
||||
@@ -15,6 +15,7 @@ extern const int ccCopyResults;
|
||||
extern const int ccSet;
|
||||
extern const int ccRemoteFiles;
|
||||
extern const int ccShowResultsInMsgBox;
|
||||
extern const int SessionReopenAutoIdleDefault;
|
||||
//---------------------------------------------------------------------------
|
||||
const int soRecurse = 0x01;
|
||||
const int soSynchronize = 0x02;
|
||||
@@ -187,6 +188,8 @@ private:
|
||||
int FKeepUpToDateChangeDelay;
|
||||
UnicodeString FChecksumAlg;
|
||||
int FSessionReopenAutoIdle;
|
||||
bool FSessionReopenAutoIdleOn;
|
||||
bool FSessionReopenAutoInactive;
|
||||
LCID FAppliedLocale;
|
||||
// Corresponds to FAppliedLocale
|
||||
UnicodeString FLocaleModuleName;
|
||||
@@ -288,6 +291,8 @@ public:
|
||||
__property int KeepUpToDateChangeDelay = { read = FKeepUpToDateChangeDelay, write = FKeepUpToDateChangeDelay };
|
||||
__property UnicodeString ChecksumAlg = { read = FChecksumAlg, write = FChecksumAlg };
|
||||
__property int SessionReopenAutoIdle = { read = FSessionReopenAutoIdle, write = FSessionReopenAutoIdle };
|
||||
__property bool SessionReopenAutoIdleOn = { read = FSessionReopenAutoIdleOn, write = FSessionReopenAutoIdleOn };
|
||||
__property bool SessionReopenAutoInactive = { read = FSessionReopenAutoInactive, write = FSessionReopenAutoInactive };
|
||||
__property bool CanApplyLocaleImmediately = { read = GetCanApplyLocaleImmediately };
|
||||
__property LCID AppliedLocale = { read = FAppliedLocale };
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <HelpWin.h>
|
||||
#include <System.IOUtils.hpp>
|
||||
#include <StrUtils.hpp>
|
||||
#include <DateUtils.hpp>
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma package(smart_init)
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -158,6 +159,7 @@ __fastcall TTerminalManager::TTerminalManager() :
|
||||
FKeepAuthenticateForm = false;
|
||||
FUpdating = 0;
|
||||
FOpeningTerminal = NULL;
|
||||
FReconnectingInactiveTerminal = NULL;
|
||||
|
||||
FApplicationsEvents.reset(new TApplicationEvents(Application));
|
||||
FApplicationsEvents->OnException = ApplicationException;
|
||||
@@ -178,7 +180,6 @@ __fastcall TTerminalManager::TTerminalManager() :
|
||||
|
||||
FSessionList = new TStringList();
|
||||
FQueues = new TList();
|
||||
FTerminationMessages = new TStringList();
|
||||
std::unique_ptr<TSessionData> DummyData(new TSessionData(L""));
|
||||
FLocalTerminal = CreateTerminal(DummyData.get());
|
||||
SetupTerminal(FLocalTerminal);
|
||||
@@ -200,7 +201,6 @@ __fastcall TTerminalManager::~TTerminalManager()
|
||||
|
||||
delete FLocalTerminal;
|
||||
delete FQueues;
|
||||
delete FTerminationMessages;
|
||||
delete FSessionList;
|
||||
CloseAutheticateForm();
|
||||
delete FQueueSection;
|
||||
@@ -272,7 +272,6 @@ TManagedTerminal * __fastcall TTerminalManager::DoNewSession(TSessionData * Data
|
||||
try
|
||||
{
|
||||
FQueues->Add(NewQueue(Session));
|
||||
FTerminationMessages->Add(L"");
|
||||
|
||||
SetupTerminal(Session);
|
||||
}
|
||||
@@ -377,49 +376,85 @@ void __fastcall TTerminalManager::FreeActiveTerminal()
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
TTerminalThread * TTerminalManager::CreateTerminalThread(TTerminal * Terminal)
|
||||
{
|
||||
TTerminalThread * TerminalThread = new TTerminalThread(Terminal);
|
||||
TerminalThread->OnIdle = MakeMethod<TNotifyEvent>(NULL, TerminalThreadIdle);
|
||||
return TerminalThread;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalManager::ReconnectingTerminal(TManagedTerminal * ManagedTerminal)
|
||||
{
|
||||
// particularly when we are reconnecting RemoteDirectory of managed terminal
|
||||
// hold the last used remote directory as opposite to session data, which holds
|
||||
// the default remote directory.
|
||||
// make sure the last used directory is used, but the default is preserved too
|
||||
ManagedTerminal->OrigRemoteDirectory = ManagedTerminal->SessionData->RemoteDirectory;
|
||||
ManagedTerminal->SessionData->RemoteDirectory = ManagedTerminal->StateData->RemoteDirectory;
|
||||
|
||||
if ((double)ManagedTerminal->ReopenStart == 0)
|
||||
{
|
||||
ManagedTerminal->ReopenStart = Now();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalManager::ReconnectedTerminal(TManagedTerminal * ManagedTerminal)
|
||||
{
|
||||
ManagedTerminal->SessionData->RemoteDirectory = ManagedTerminal->OrigRemoteDirectory;
|
||||
if (ManagedTerminal->Active)
|
||||
{
|
||||
ManagedTerminal->ReopenStart = 0;
|
||||
ManagedTerminal->Permanent = true;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool Reopen, bool AdHoc)
|
||||
{
|
||||
TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminal);
|
||||
// it must be managed terminal, unless it is secondary terminal (of managed terminal)
|
||||
DebugAssert((ManagedTerminal != NULL) || (dynamic_cast<TSecondaryTerminal *>(Terminal) != NULL));
|
||||
|
||||
// particularly when we are reconnecting RemoteDirectory of managed terminal
|
||||
// hold the last used remote directory as opposite to session data, which holds
|
||||
// the default remote directory.
|
||||
// make sure the last used directory is used, but the default is preserved too
|
||||
UnicodeString OrigRemoteDirectory = Terminal->SessionData->RemoteDirectory;
|
||||
try
|
||||
{
|
||||
TValueRestorer<TTerminal *> OpeningTerminalRestorer(FOpeningTerminal, Terminal);
|
||||
TTerminalThread * TerminalThread = new TTerminalThread(Terminal);
|
||||
TerminalThread->AllowAbandon = (Terminal == ActiveTerminal);
|
||||
TTerminalThread * TerminalThread;
|
||||
bool ReconnectingInactiveTerminal = (Terminal == FReconnectingInactiveTerminal);
|
||||
if (ReconnectingInactiveTerminal)
|
||||
{
|
||||
DebugAssert(Terminal == ActiveTerminal);
|
||||
TerminalThread = ManagedTerminal->TerminalThread;
|
||||
FReconnectingInactiveTerminal = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
TerminalThread = CreateTerminalThread(Terminal);
|
||||
}
|
||||
try
|
||||
{
|
||||
if (ManagedTerminal != NULL)
|
||||
TerminalThread->AllowAbandon = (Terminal == ActiveTerminal);
|
||||
if (ReconnectingInactiveTerminal && DebugAlwaysTrue(Reopen))
|
||||
{
|
||||
Terminal->SessionData->RemoteDirectory = ManagedTerminal->StateData->RemoteDirectory;
|
||||
|
||||
if ((double)ManagedTerminal->ReopenStart == 0)
|
||||
{
|
||||
ManagedTerminal->ReopenStart = Now();
|
||||
}
|
||||
|
||||
ManagedTerminal->Disconnected = false;
|
||||
ManagedTerminal->DisconnectedTemporarily = false;
|
||||
DebugAssert(ManagedTerminal->TerminalThread == NULL);
|
||||
ManagedTerminal->TerminalThread = TerminalThread;
|
||||
}
|
||||
|
||||
TNotifyEvent OnIdle;
|
||||
((TMethod*)&OnIdle)->Code = TerminalThreadIdle;
|
||||
TerminalThread->OnIdle = OnIdle;
|
||||
if (Reopen)
|
||||
{
|
||||
TerminalThread->TerminalReopen();
|
||||
TerminalThread->ContinueTerminalReopenInteractive();
|
||||
}
|
||||
else
|
||||
{
|
||||
TerminalThread->TerminalOpen();
|
||||
if (ManagedTerminal != NULL)
|
||||
{
|
||||
ReconnectingTerminal(ManagedTerminal);
|
||||
ManagedTerminal->Disconnected = false;
|
||||
ManagedTerminal->DisconnectedTemporarily = false;
|
||||
DebugAssert(ManagedTerminal->TerminalThread == NULL);
|
||||
ManagedTerminal->TerminalThread = TerminalThread;
|
||||
}
|
||||
|
||||
if (Reopen)
|
||||
{
|
||||
TerminalThread->TerminalReopen();
|
||||
}
|
||||
else
|
||||
{
|
||||
TerminalThread->TerminalOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
__finally
|
||||
@@ -464,11 +499,9 @@ void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool R
|
||||
}
|
||||
__finally
|
||||
{
|
||||
Terminal->SessionData->RemoteDirectory = OrigRemoteDirectory;
|
||||
if (Terminal->Active && (ManagedTerminal != NULL))
|
||||
if (ManagedTerminal != NULL)
|
||||
{
|
||||
ManagedTerminal->ReopenStart = 0;
|
||||
ManagedTerminal->Permanent = true;
|
||||
ReconnectedTerminal(ManagedTerminal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +540,10 @@ void __fastcall TTerminalManager::TerminalThreadIdle(void * /*Data*/, TObject *
|
||||
//---------------------------------------------------------------------------
|
||||
bool __fastcall TTerminalManager::ConnectActiveTerminalImpl(bool Reopen)
|
||||
{
|
||||
ActiveTerminal->CollectUsage();
|
||||
if (ActiveTerminal != FReconnectingInactiveTerminal)
|
||||
{
|
||||
ActiveTerminal->CollectUsage();
|
||||
}
|
||||
|
||||
TTerminalPendingAction Action;
|
||||
bool Result;
|
||||
@@ -657,12 +693,11 @@ void __fastcall TTerminalManager::ReconnectActiveTerminal()
|
||||
{
|
||||
DebugAssert(ActiveTerminal);
|
||||
|
||||
if (ScpExplorer)
|
||||
if ((ScpExplorer != NULL) &&
|
||||
DebugAlwaysTrue(ActiveTerminal != FReconnectingInactiveTerminal) &&
|
||||
(ScpExplorer->Terminal == ActiveTerminal))
|
||||
{
|
||||
if (ScpExplorer->Terminal == ActiveTerminal)
|
||||
{
|
||||
ScpExplorer->UpdateSession(ActiveTerminal);
|
||||
}
|
||||
ScpExplorer->UpdateSession(ActiveTerminal);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -701,9 +736,19 @@ void __fastcall TTerminalManager::FreeAll()
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalManager::FreeTerminal(TTerminal * Terminal)
|
||||
{
|
||||
TTerminal * ATerminal = Terminal;
|
||||
bool IsActiveSession = ((ActiveSession != NULL) && (Terminal == ActiveSession));
|
||||
try
|
||||
{
|
||||
if (!IsActiveSession)
|
||||
{
|
||||
// For active session, done by DoSetActiveSession below.
|
||||
// For inactive session, needs to be done here before potentially abandoning the session.
|
||||
SaveTerminal(Terminal);
|
||||
}
|
||||
|
||||
TManagedTerminal * ManagedSession = DebugNotNull(dynamic_cast<TManagedTerminal *>(Terminal));
|
||||
|
||||
// we want the Login dialog to open on auto-workspace name,
|
||||
// as set in TCustomScpExplorerForm::FormClose
|
||||
if ((!FDestroying || !WinConfiguration->AutoSaveWorkspace) && !ManagedSession->LocalBrowser)
|
||||
@@ -719,22 +764,46 @@ void __fastcall TTerminalManager::FreeTerminal(TTerminal * Terminal)
|
||||
ScpExplorer->TerminalRemoved(Terminal);
|
||||
}
|
||||
|
||||
if (Terminal->Active)
|
||||
if (ManagedSession == FReconnectingInactiveTerminal)
|
||||
{
|
||||
DebugAssert(!IsActiveSession);
|
||||
AppLog(L"Cancelling background reconnect before closing session");
|
||||
TTerminalThread * TerminalThread = DebugNotNull(ManagedSession->TerminalThread);
|
||||
DebugAssert(TerminalThread->AllowAbandon);
|
||||
TerminalThread->Cancel();
|
||||
while (!TerminalThread->Abandoned())
|
||||
{
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
if (!TerminalThread->Release())
|
||||
{
|
||||
AppLog(L"Abandoned background reconnect");
|
||||
TerminalThread->Terminate();
|
||||
Terminal = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppLog(L"Background reconnect stopped");
|
||||
ManagedSession->TerminalThread = NULL;
|
||||
}
|
||||
FReconnectingInactiveTerminal = NULL;
|
||||
}
|
||||
else if (Terminal->Active)
|
||||
{
|
||||
Terminal->Close();
|
||||
}
|
||||
}
|
||||
__finally
|
||||
{
|
||||
int Index = IndexOf(Terminal);
|
||||
Extract(Terminal);
|
||||
int Index = IndexOf(ATerminal);
|
||||
Extract(ATerminal);
|
||||
|
||||
TTerminalQueue * Queue;
|
||||
Queue = reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index]);
|
||||
FQueues->Delete(Index);
|
||||
FTerminationMessages->Delete(Index);
|
||||
|
||||
if ((ActiveSession != NULL) && (Terminal == ActiveSession))
|
||||
if (IsActiveSession)
|
||||
{
|
||||
TManagedTerminal * NewActiveTerminal;
|
||||
bool LastTerminalClosed = false;
|
||||
@@ -767,15 +836,11 @@ void __fastcall TTerminalManager::FreeTerminal(TTerminal * Terminal)
|
||||
}
|
||||
DoSetActiveSession(NewActiveTerminal, false, LastTerminalClosed);
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveTerminal(Terminal);
|
||||
}
|
||||
|
||||
// only now all references to/from queue (particularly events to explorer)
|
||||
// are cleared
|
||||
delete Queue;
|
||||
delete Terminal;
|
||||
delete Terminal; // noop if abandoned
|
||||
|
||||
DoSessionListChanged();
|
||||
}
|
||||
@@ -880,30 +945,30 @@ void __fastcall TTerminalManager::DoSetActiveSession(TManagedTerminal * value, b
|
||||
|
||||
if (ActiveSession != NULL)
|
||||
{
|
||||
int Index = ActiveSessionIndex;
|
||||
if (!ActiveSession->Active &&
|
||||
!FTerminationMessages->Strings[Index].IsEmpty() &&
|
||||
DebugAlwaysTrue(!ActiveSession->LocalBrowser))
|
||||
if ((FReconnectingInactiveTerminal == ActiveSession) &&
|
||||
DebugAlwaysTrue(ScpExplorer != NULL) &&
|
||||
DebugAlwaysTrue(FReconnectingInactiveTerminal->TerminalThread != NULL))
|
||||
{
|
||||
UnicodeString Message = FTerminationMessages->Strings[Index];
|
||||
FTerminationMessages->Strings[Index] = L"";
|
||||
if (AutoReconnect)
|
||||
DebugAssert(FTerminalPendingAction == tpNull); // the only assumed and tested scenario
|
||||
ReconnectActiveTerminal();
|
||||
}
|
||||
else if (!ActiveSession->Active &&
|
||||
!ActiveSession->InactiveTerminationMessage.IsEmpty() &&
|
||||
DebugAlwaysTrue(!ActiveSession->LocalBrowser))
|
||||
{
|
||||
UnicodeString Message = ActiveSession->InactiveTerminationMessage;
|
||||
ActiveSession->InactiveTerminationMessage = EmptyStr;
|
||||
if (AutoReconnect || GUIConfiguration->SessionReopenAutoInactive)
|
||||
{
|
||||
ReconnectActiveTerminal();
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception * E = new ESshFatal(NULL, Message);
|
||||
try
|
||||
{
|
||||
// finally show pending terminal message,
|
||||
// this gives user also possibility to reconnect
|
||||
ActiveTerminal->ShowExtendedException(E);
|
||||
}
|
||||
__finally
|
||||
{
|
||||
delete E;
|
||||
}
|
||||
// finally show pending terminal message,
|
||||
// this gives user also possibility to reconnect
|
||||
std::unique_ptr<ESshFatal> E(new ESshFatal(NULL, Message));
|
||||
E->InactiveTerminationMessage = true;
|
||||
ActiveTerminal->ShowExtendedException(E.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,6 +992,7 @@ void __fastcall TTerminalManager::DoSetActiveSession(TManagedTerminal * value, b
|
||||
if ((ActiveSession != NULL) &&
|
||||
!ActiveSession->Active && !ActiveSession->Disconnected && !ActiveSession->LocalBrowser)
|
||||
{
|
||||
DebugAssert(ActiveSession->InactiveTerminationMessage.IsEmpty());
|
||||
ConnectActiveTerminal();
|
||||
}
|
||||
}
|
||||
@@ -1226,6 +1292,7 @@ void __fastcall TTerminalManager::TerminalQueryUser(TObject * Sender,
|
||||
const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers,
|
||||
const TQueryParams * Params, unsigned int & Answer, TQueryType Type, void * /*Arg*/)
|
||||
{
|
||||
TTerminal * Terminal = DebugNotNull(dynamic_cast<TTerminal *>(Sender));
|
||||
UnicodeString HelpKeyword;
|
||||
TMessageParams MessageParams(Params);
|
||||
UnicodeString AQuery = Query;
|
||||
@@ -1248,7 +1315,7 @@ void __fastcall TTerminalManager::TerminalQueryUser(TObject * Sender,
|
||||
if (ScpExplorer)
|
||||
{
|
||||
Answer = ScpExplorer->MoreMessageDialog(AQuery, MoreMessages, Type, Answers,
|
||||
HelpKeyword, &MessageParams, dynamic_cast<TTerminal *>(Sender));
|
||||
HelpKeyword, &MessageParams, Terminal);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1642,6 +1709,11 @@ UnicodeString __fastcall TTerminalManager::GetSessionTitle(TManagedTerminal * Se
|
||||
return Result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
UnicodeString TTerminalManager::FormatTerminalNoteMessage(TManagedTerminal * Terminal, const UnicodeString & Message)
|
||||
{
|
||||
return FORMAT(L"%s: %s", (GetSessionTitle(Terminal, true), Message));
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
UnicodeString __fastcall TTerminalManager::GetActiveSessionAppTitle()
|
||||
{
|
||||
UnicodeString Result;
|
||||
@@ -1782,6 +1854,90 @@ void __fastcall TTerminalManager::NewSession(
|
||||
while (Retry);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalManager::StartInactiveTerminalReconnect(TManagedTerminal * Terminal)
|
||||
{
|
||||
ReconnectingTerminal(Terminal);
|
||||
Terminal->NextInactiveReconnect = TDateTime(); // just in case
|
||||
FReconnectingInactiveTerminal = Terminal;
|
||||
|
||||
DebugAssert(Terminal->TerminalThread == NULL);
|
||||
TTerminalThread * TerminalThread = CreateTerminalThread(Terminal);
|
||||
Terminal->TerminalThread = TerminalThread;
|
||||
TerminalThread->AllowAbandon = true;
|
||||
TerminalThread->StartTerminalReopenNonInteractive();
|
||||
FReconnectingNote = FormatTerminalNoteMessage(Terminal, LoadStr(SESSION_RECONNECTING));
|
||||
ScpExplorer->PostNote(FReconnectingNote);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void TTerminalManager::CheckInactiveTerminalReconnect()
|
||||
{
|
||||
TManagedTerminal * Terminal = DebugNotNull(FReconnectingInactiveTerminal);
|
||||
TTerminalThread * TerminalThread = DebugNotNull(Terminal->TerminalThread);
|
||||
TTerminalReopenResult ReopenResult = TerminalThread->IsTerminalReopenComplete();
|
||||
UnicodeString SessionName = Terminal->SessionData->SessionName;
|
||||
if (ReopenResult == trrPending)
|
||||
{
|
||||
AppLog(FORMAT(L"Inactive session %s is still being reconnected", (SessionName)));
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
DebugCheck(TerminalThread->Release());
|
||||
}
|
||||
__finally
|
||||
{
|
||||
ReconnectedTerminal(Terminal);
|
||||
Terminal->TerminalThread = NULL;
|
||||
FReconnectingInactiveTerminal = NULL;
|
||||
}
|
||||
|
||||
if (ReopenResult == trrSucceeded)
|
||||
{
|
||||
AppLog(FORMAT(L"Inactive session %s was reconnected", (SessionName)));
|
||||
ScpExplorer->CancelNote(FReconnectingNote);
|
||||
ScpExplorer->InactiveTerminalNotify(Terminal, LoadStr(SESSION_RECONNECTED), qtInformation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ReopenResult == trrFailed)
|
||||
{
|
||||
// in case it was disabled meanwhile
|
||||
if (GUIConfiguration->SessionReopenAutoInactive)
|
||||
{
|
||||
int Interval = 2 * GUIConfiguration->SessionReopenAutoIdle;
|
||||
AppLog(FORMAT(L"Inactive session %s failed to reconnect, will try again in %d ms", (SessionName, Interval)));
|
||||
// Might want to consider SessionReopenTimeout
|
||||
Terminal->NextInactiveReconnect = IncMilliSecond(Now(), Interval);
|
||||
}
|
||||
else
|
||||
{
|
||||
Terminal->NextInactiveReconnect = TDateTime();
|
||||
}
|
||||
}
|
||||
else if (ReopenResult == trrNeedsInteraction)
|
||||
{
|
||||
AppLog(FORMAT(L"Inactive session %s needs interaction", (SessionName)));
|
||||
ScpExplorer->CancelNote(FReconnectingNote);
|
||||
ScpExplorer->InactiveTerminalNotify(Terminal, LoadStr(SESSION_NEED_INTERACTION), qtInformation);
|
||||
Terminal->NextInactiveReconnect = TDateTime();
|
||||
}
|
||||
try
|
||||
{
|
||||
if (Terminal->Active)
|
||||
{
|
||||
Terminal->Close();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
FReconnectingNote = EmptyStr;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void __fastcall TTerminalManager::Idle(bool SkipCurrentTerminal)
|
||||
{
|
||||
|
||||
@@ -1809,7 +1965,11 @@ void __fastcall TTerminalManager::Idle(bool SkipCurrentTerminal)
|
||||
TManagedTerminal * Terminal = Sessions[Index];
|
||||
try
|
||||
{
|
||||
if (!SkipCurrentTerminal || (Terminal != ActiveTerminal))
|
||||
if (Terminal == FReconnectingInactiveTerminal)
|
||||
{
|
||||
CheckInactiveTerminalReconnect();
|
||||
}
|
||||
else if (!SkipCurrentTerminal || (Terminal != ActiveTerminal))
|
||||
{
|
||||
// make sure Idle is called on the thread that runs the terminal
|
||||
if (Terminal->TerminalThread != NULL)
|
||||
@@ -1822,6 +1982,14 @@ void __fastcall TTerminalManager::Idle(bool SkipCurrentTerminal)
|
||||
{
|
||||
Terminal->Idle();
|
||||
}
|
||||
else if ((Terminal != ActiveTerminal) &&
|
||||
GUIConfiguration->SessionReopenAutoInactive &&
|
||||
(FReconnectingInactiveTerminal == NULL) &&
|
||||
(Terminal->NextInactiveReconnect != TDateTime()) &&
|
||||
(Terminal->NextInactiveReconnect < Now()))
|
||||
{
|
||||
StartInactiveTerminalReconnect(Terminal);
|
||||
}
|
||||
}
|
||||
|
||||
if (Terminal->Active)
|
||||
@@ -1846,17 +2014,17 @@ void __fastcall TTerminalManager::Idle(bool SkipCurrentTerminal)
|
||||
{
|
||||
// we may not have inactive terminal, unless there is a explorer,
|
||||
// also Idle is called from explorer anyway
|
||||
DebugAssert(ScpExplorer != NULL);
|
||||
if (ScpExplorer != NULL)
|
||||
if (DebugAlwaysTrue(ScpExplorer != NULL))
|
||||
{
|
||||
ScpExplorer->InactiveTerminalException(Terminal, &E);
|
||||
ScpExplorer->InactiveTerminalNotify(Terminal, EmptyStr, qtError, &E);
|
||||
}
|
||||
|
||||
if (!Terminal->Active)
|
||||
{
|
||||
// if session is lost, save the error message and rethrow it
|
||||
// once the terminal gets activated
|
||||
FTerminationMessages->Strings[Index] = E.Message;
|
||||
Terminal->InactiveTerminationMessage = E.Message;
|
||||
Terminal->NextInactiveReconnect = IncMilliSecond(Now(), GUIConfiguration->SessionReopenAutoIdle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1951,9 +2119,24 @@ void __fastcall TTerminalManager::SaveWorkspace(TList * DataList)
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool TTerminalManager::IsActiveTerminal(TTerminal * Terminal)
|
||||
{
|
||||
return
|
||||
(Terminal != NULL) &&
|
||||
Terminal->Active &&
|
||||
!IsReconnectingTerminal(Terminal);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool TTerminalManager::IsReconnectingTerminal(TTerminal * Terminal)
|
||||
{
|
||||
return
|
||||
(Terminal != NULL) &&
|
||||
((Terminal == FOpeningTerminal) || (Terminal == FReconnectingInactiveTerminal));
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool __fastcall TTerminalManager::IsActiveTerminalForSite(TTerminal * Terminal, TSessionData * Data)
|
||||
{
|
||||
bool Result = Terminal->Active;
|
||||
bool Result = IsActiveTerminal(Terminal);
|
||||
if (Result)
|
||||
{
|
||||
std::unique_ptr<TSessionData> TerminalData(Terminal->SessionData->Clone());
|
||||
|
||||
@@ -64,6 +64,10 @@ public:
|
||||
std::unique_ptr<TCriticalSection> ThumbnailsSection;
|
||||
bool ThumbnailsEnabled;
|
||||
TThumbnailDownloadQueueItem * ThumbnailDownloadQueueItem;
|
||||
UnicodeString InactiveTerminationMessage;
|
||||
TDateTime NextInactiveReconnect;
|
||||
TDateTime LastInactiveSave;
|
||||
UnicodeString OrigRemoteDirectory;
|
||||
|
||||
TRemoteThumbnailsMap Thumbnails;
|
||||
TRemoteThumbnailsQueue ThumbnailsQueue;
|
||||
@@ -103,12 +107,15 @@ public:
|
||||
void NewLocalSession(const UnicodeString & LocalDirectory = UnicodeString(), const UnicodeString & OtherLocalDirectory = UnicodeString());
|
||||
void __fastcall Idle(bool SkipCurrentTerminal);
|
||||
UnicodeString __fastcall GetSessionTitle(TManagedTerminal * Terminal, bool Unique);
|
||||
UnicodeString FormatTerminalNoteMessage(TManagedTerminal * Terminal, const UnicodeString & Message);
|
||||
UnicodeString __fastcall GetActiveSessionAppTitle();
|
||||
UnicodeString __fastcall GetAppProgressTitle();
|
||||
UnicodeString __fastcall FormatFormCaptionWithSession(TCustomForm * Form, const UnicodeString & Caption);
|
||||
void __fastcall HandleException(Exception * E);
|
||||
void __fastcall SaveWorkspace(TList * DataList);
|
||||
void __fastcall QueueStatusUpdated();
|
||||
bool IsActiveTerminal(TTerminal * Terminal);
|
||||
bool IsReconnectingTerminal(TTerminal * Terminal);
|
||||
bool __fastcall IsActiveTerminalForSite(TTerminal * Terminal, TSessionData * Data);
|
||||
TManagedTerminal * __fastcall FindActiveTerminalForSite(TSessionData * Data);
|
||||
TTerminalQueue * __fastcall FindQueueForTerminal(TTerminal * Terminal);
|
||||
@@ -129,6 +136,7 @@ public:
|
||||
__property TTerminal * LocalTerminal = { read = FLocalTerminal };
|
||||
__property TManagedTerminal * Sessions[int Index] = { read = GetSession };
|
||||
__property bool Updating = { read = IsUpdating };
|
||||
__property TManagedTerminal * ReconnectingInactiveTerminal = { read = FReconnectingInactiveTerminal };
|
||||
|
||||
protected:
|
||||
virtual TTerminal * __fastcall CreateTerminal(TSessionData * Data);
|
||||
@@ -146,7 +154,6 @@ private:
|
||||
TTerminalPendingAction FTerminalPendingAction;
|
||||
TStrings * FSessionList;
|
||||
TList * FQueues;
|
||||
TStrings * FTerminationMessages;
|
||||
UnicodeString FProgressTitle;
|
||||
UnicodeString FForegroundProgressTitle;
|
||||
TDateTime FDirectoryReadingStart;
|
||||
@@ -166,6 +173,8 @@ private:
|
||||
int FUpdating;
|
||||
int FMaxSessions;
|
||||
TTerminal * FOpeningTerminal;
|
||||
TManagedTerminal * FReconnectingInactiveTerminal;
|
||||
UnicodeString FReconnectingNote;
|
||||
|
||||
bool __fastcall ConnectActiveTerminalImpl(bool Reopen);
|
||||
bool __fastcall ConnectActiveTerminal();
|
||||
@@ -239,6 +248,11 @@ private:
|
||||
bool SupportedSession(TSessionData * Data);
|
||||
void __fastcall TerminalFatalExceptionTimer(unsigned int & Result);
|
||||
void NeedThumbnailDownloadQueueItem(TManagedTerminal * ATerminal);
|
||||
void StartInactiveTerminalReconnect(TManagedTerminal * Terminal);
|
||||
void CheckInactiveTerminalReconnect();
|
||||
TTerminalThread * CreateTerminalThread(TTerminal * Terminal);
|
||||
void ReconnectingTerminal(TManagedTerminal * ManagedTerminal);
|
||||
void ReconnectedTerminal(TManagedTerminal * ManagedTerminal);
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
class TThumbnailDownloadQueueItem : public TTransferQueueItem
|
||||
|
||||
@@ -218,8 +218,9 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
|
||||
ESshTerminate * Terminate = dynamic_cast<ESshTerminate*>(E);
|
||||
bool CloseOnCompletion = (Terminate != NULL);
|
||||
|
||||
EFatal * FatalException = dynamic_cast<EFatal *>(E);
|
||||
bool ForActiveTerminal =
|
||||
E->InheritsFrom(__classid(EFatal)) && (Terminal != NULL) &&
|
||||
(FatalException != NULL) && (Terminal != NULL) &&
|
||||
(Manager != NULL) && (Manager->ActiveTerminal == Terminal);
|
||||
|
||||
unsigned int Result;
|
||||
@@ -281,6 +282,12 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
|
||||
Result =
|
||||
ExceptionMessageDialog(E, qtInformation, L"", Answers | qaOK, HELP_NONE, &Params);
|
||||
}
|
||||
|
||||
if (Result == qaNeverAskAgain)
|
||||
{
|
||||
Result = qaYes;
|
||||
WinConfiguration->ConfirmExitOnCompletion = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -302,13 +309,27 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
|
||||
((double)Manager->ActiveTerminal->ReopenStart == 0) ||
|
||||
(int(double(Now() - Manager->ActiveTerminal->ReopenStart) * MSecsPerDay) < Configuration->SessionReopenTimeout)))
|
||||
{
|
||||
Params.Timeout = GUIConfiguration->SessionReopenAutoIdle;
|
||||
Params.Timeout = GUIConfiguration->SessionReopenAutoIdleOn ? GUIConfiguration->SessionReopenAutoIdle : 0;
|
||||
Params.TimeoutAnswer = qaRetry;
|
||||
Params.TimeoutResponse = Params.TimeoutAnswer;
|
||||
HookedDialog = Manager->HookFatalExceptionMessageDialog(Params);
|
||||
}
|
||||
|
||||
bool InactiveTerminationMessage = (FatalException != NULL) && FatalException->InactiveTerminationMessage;
|
||||
if (InactiveTerminationMessage)
|
||||
{
|
||||
Params.Params |= mpNeverAskAgainCheck;
|
||||
Params.NeverAskAgainTitle = LoadStr(ALWAYS_RECONNECT);
|
||||
Params.NeverAskAgainAnswer = qaRetry;
|
||||
}
|
||||
|
||||
Result = FatalExceptionMessageDialog(E, qtError, EmptyStr, qaOK, EmptyStr, &Params);
|
||||
|
||||
if ((Result == qaNeverAskAgain) && DebugAlwaysTrue(InactiveTerminationMessage))
|
||||
{
|
||||
GUIConfiguration->SessionReopenAutoInactive = true;
|
||||
Result = qaRetry;
|
||||
}
|
||||
}
|
||||
__finally
|
||||
{
|
||||
@@ -329,13 +350,6 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
|
||||
}
|
||||
}
|
||||
|
||||
if (Result == qaNeverAskAgain)
|
||||
{
|
||||
DebugAssert(CloseOnCompletion);
|
||||
Result = qaYes;
|
||||
WinConfiguration->ConfirmExitOnCompletion = false;
|
||||
}
|
||||
|
||||
if (Result == qaYes)
|
||||
{
|
||||
DebugAssert(CloseOnCompletion);
|
||||
|
||||
Reference in New Issue
Block a user