1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Issue 2360 – Optionally do not show error message when connection is lost while idle

https://winscp.net/tracker/2360

Source commit: f4e51fcf1f07202811e4c5f9d2c5f3e777f71e33
This commit is contained in:
Martin Prikryl
2026-05-28 13:00:34 +00:00
parent bebb10377f
commit 4057c84bfa
18 changed files with 376 additions and 118 deletions

View File

@@ -665,6 +665,10 @@ void __fastcall TCustomScpExplorerForm::SessionChanged(bool Replaced)
RemoteDirView->Terminal = NULL;
}
// By now, the timeout should already be canceled in general,
// as the session change must have been triggered by mouse click or key press, but let's be safe
RestartToolbarDialogTimeout(this, -1, ReconnectItem);
InitStatusBar();
UpdateTransferList();
// Update panels Enable state before refreshing the labels
@@ -5171,6 +5175,17 @@ void __fastcall TCustomScpExplorerForm::UpdateStatusPanelText(TTBXStatusPanel *
Panel->Caption = L"";
}
//---------------------------------------------------------------------------
void TCustomScpExplorerForm::CheckInitiateReconnectTimeout()
{
// When showing the disconnect message, we are typically "Busy" (as the code is triggered while "showing exception"),
// so we triggers the reconnect timeout only here
if (!NonVisualDataModule->Busy && (ManagedSession != NULL) && ManagedSession->InitiateReconnectTimeout)
{
ManagedSession->InitiateReconnectTimeout = false;
RestartToolbarDialogTimeout(this, GUIConfiguration->SessionReopenAutoIdle, ReconnectItem);
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::Idle()
{
@@ -5226,6 +5241,8 @@ void __fastcall TCustomScpExplorerForm::Idle()
MenuToolbar->Idle();
}
CheckInitiateReconnectTimeout();
if (FShowing || StandaloneOperation)
{
if (FQueueStatusInvalidated)
@@ -8077,6 +8094,8 @@ void __fastcall TCustomScpExplorerForm::ShowExtendedException(
NonVisualDataModule->EndBusy();
FTrayIcon->CancelBalloon();
}
// update the reconnect button as soon as we are not busy
CheckInitiateReconnectTimeout();
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::SessionReady()
@@ -9549,8 +9568,22 @@ void __fastcall TCustomScpExplorerForm::UpdateControls()
NonVisualDataModule->ReconnectSessionAction->Update();
ReconnectToolbar->Visible = NonVisualDataModule->ReconnectSessionAction->Visible;
// ReconnectSessionAction is hidden when disabled, so enabling it actualy resizes the toolbar
CenterReconnectToolbar();
bool HasDisconnectMessage = ReconnectToolbar->Visible && (ManagedSession != NULL) && !ManagedSession->DisconnectMessage.IsEmpty();
auto DisconnectMessage = HasDisconnectMessage ? UnformatMessage(ManagedSession->DisconnectMessage) : EmptyStr;
if (ReconnectLabel->Caption != DisconnectMessage)
{
ReconnectLabel->Caption = DisconnectMessage;
if (DisconnectMessage.IsEmpty())
{
ReconnectPanel->Hide();
}
else
{
// place before showing
PlaceReconnectControls(true);
ReconnectPanel->Show();
}
}
// The only place, where we use IsOpenedTerminal and not IsAvailableTerminal
// (i.e. not checking for reconnecting status)
@@ -9566,6 +9599,7 @@ void __fastcall TCustomScpExplorerForm::UpdateControls()
RemoteDriveView->Enabled = RemoteDirView->Enabled;
RemoteDriveView->Color = RemoteDirView->Color;
RemoteDriveView->Font->Color = RemoteDirView->Font->Color;
ReconnectPanel->Color = RemoteDirView->Color;
QueueView3->Enabled = HasTerminal && Terminal->IsCapable[fcBackgroundTransfers];
QueueView3->Color = QueueView3->Enabled ? GetWindowColor() : DisabledPanelColor();
@@ -10205,10 +10239,63 @@ void __fastcall TCustomScpExplorerForm::CMShowingChanged(TMessage & Message)
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::CenterReconnectToolbar()
void TCustomScpExplorerForm::PlaceReconnectControls(bool PanelShowing)
{
ReconnectToolbar->Left = (ReconnectToolbar->Parent->ClientWidth - ReconnectToolbar->Width) / 2;
ReconnectToolbar->Top = (ReconnectToolbar->Parent->ClientHeight - ReconnectToolbar->Height) / 2;
DebugAssert(RemoteDirView->Parent == ReconnectToolbar->Parent);
int Padding = ScaleByTextHeight(this, 8);
int AvailableHeight = ReconnectToolbar->Parent->ClientHeight;
int AvailableWidth = ReconnectToolbar->Parent->ClientWidth;
int ListTop = 0;
HWND HeaderHandle = ListView_GetHeader(RemoteDirView->Handle);
if (HeaderHandle != 0)
{
TRect R;
GetWindowRect(HeaderHandle, &R);
ListTop += R.Height();
AvailableHeight -= R.Height();
}
int WndStyle = GetWindowLong(RemoteDirView->Handle, GWL_STYLE);
if ((WndStyle & WS_VSCROLL) != 0)
{
AvailableWidth -= GetSystemMetricsForControl(RemoteDirView, SM_CXVSCROLL);
}
if ((WndStyle & WS_HSCROLL) != 0)
{
AvailableHeight -= GetSystemMetricsForControl(RemoteDirView, SM_CYHSCROLL);
}
int Top;
if (!ReconnectPanel->Visible && !PanelShowing)
{
Top = ListTop + (AvailableHeight - ReconnectToolbar->Height) / 2;
}
else
{
std::unique_ptr<TCanvas> Canvas(CreateControlCanvas(ReconnectLabel));
int Flags = DT_EXPANDTABS | DT_NOPREFIX | ReconnectLabel->DrawTextBiDiModeFlagsReadingOnly();
int ReconnectPanelWidth = AvailableWidth - (2 * Padding);
int ReconnectPanelHeight = CalculateLabelSize(Canvas.get(), ReconnectPanelWidth, ReconnectLabel->Caption, Flags).Height;
ReconnectPanelHeight = std::min(ReconnectPanelHeight, ReconnectToolbar->Parent->ClientHeight - Padding);
int ControlsHeight = ReconnectToolbar->Height + ReconnectPanelHeight;
int IntraPadding = (2 * Padding);
int TotalHeight = ControlsHeight + IntraPadding;
if (TotalHeight > AvailableHeight - (2 * Padding))
{
IntraPadding = Padding;
TotalHeight = ControlsHeight + IntraPadding;
}
int PanelTop = std::max(ListTop + ((AvailableHeight - TotalHeight) / 2), Padding / 2);
ReconnectPanel->SetBounds(Padding, PanelTop, AvailableWidth - (2 * Padding), ReconnectPanelHeight);
Top = ReconnectPanel->Top + ReconnectPanel->Height + IntraPadding;
Top = std::min(Top, ReconnectToolbar->Parent->ClientHeight - ReconnectToolbar->Height - (Padding / 2));
}
ReconnectToolbar->SetBounds(
(AvailableWidth - ReconnectToolbar->Width) / 2, Top, ReconnectToolbar->Width, ReconnectToolbar->Height);
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::FormConstrainedResize(
@@ -11994,7 +12081,7 @@ void __fastcall TCustomScpExplorerForm::RemoteStatusBarMouseDown(TObject *, TMou
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::RemoteDirViewResize(TObject *)
{
CenterReconnectToolbar();
PlaceReconnectControls();
}
//---------------------------------------------------------------------------
void TCustomScpExplorerForm::DoExploreFile(TCustomDirView * DirView, const UnicodeString & FileName)
@@ -12506,3 +12593,9 @@ void TCustomScpExplorerForm::ChangeDirViewStyle(TOperationSide Side, TDirViewSty
UpdateControls();
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::ReconnectToolbarResize(TObject *)
{
PlaceReconnectControls();
}
//---------------------------------------------------------------------------

View File

@@ -126,6 +126,27 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
OnStartReading = RemoteDirViewStartReading
OnThumbnailNeeded = RemoteDirViewThumbnailNeeded
end
object ReconnectPanel: TPanel
Left = 48
Top = 54
Width = 321
Height = 52
BevelOuter = bvNone
Color = clWindow
ParentBackground = False
TabOrder = 2
object ReconnectLabel: TLabel
Left = 0
Top = 0
Width = 84
Height = 15
Align = alClient
Alignment = taCenter
Caption = 'ReconnectLabel'
ShowAccelChar = False
WordWrap = True
end
end
object ReconnectToolbar: TTBXToolbar
Left = 136
Top = 112
@@ -137,7 +158,8 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
ParentShowHint = False
ShowHint = True
TabOrder = 1
object TBXItem254: TTBXItem
OnResize = ReconnectToolbarResize
object ReconnectItem: TTBXItem
Action = NonVisualDataModule.ReconnectSessionAction
end
end

View File

@@ -109,10 +109,12 @@ __published:
TTBXItem *TBXItem173;
TApplicationEvents *ApplicationEvents;
TTBXToolbar *ReconnectToolbar;
TTBXItem *TBXItem254;
TTBXItem *ReconnectItem;
TSplitter *QueueFileListSplitter;
TListView *QueueFileList;
TTBXDock *MessageDock;
TPanel *ReconnectPanel;
TLabel *ReconnectLabel;
void __fastcall ApplicationMinimize(TObject * Sender);
void __fastcall ApplicationRestore(TObject * Sender);
void __fastcall RemoteDirViewContextPopup(TObject *Sender,
@@ -230,6 +232,7 @@ __published:
void __fastcall RemoteDirViewStartLoading(TObject *Sender);
void __fastcall RemoteDirViewStartReading(TObject *Sender);
void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
void __fastcall ReconnectToolbarResize(TObject *Sender);
private:
TManagedTerminal * FManagedSession;
@@ -382,6 +385,7 @@ private:
void __fastcall StoreTransitionCloseClick(TObject * Sender);
void __fastcall StoreTransitionLinkClick(TObject * Sender);
void InitializeRemoteThumbnailMask();
void CheckInitiateReconnectTimeout();
protected:
TOperationSide FCurrentSide;
@@ -742,7 +746,7 @@ protected:
TListItem * __fastcall SearchFile(const UnicodeString & Text, bool SkipCurrent, bool Reverse);
void __fastcall CMDialogKey(TWMKeyDown & Message);
DYNAMIC void __fastcall Deactivate();
void __fastcall CenterReconnectToolbar();
void PlaceReconnectControls(bool PanelShowing = false);
void DoOpenFolderOrWorkspace(const UnicodeString & Name, bool ConnectFirstTerminal, bool CheckMaxSessions);
void __fastcall ThemeChanged();
int __fastcall GetStaticQueuePanelComponentsHeight();

View File

@@ -1143,26 +1143,23 @@ TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
Message->Font->Color = LabelColor;
}
TRect TextRect;
SetRect(&TextRect, 0, 0, MaxTextWidth, 0);
DrawText(Message->Canvas->Handle, LabelMsg.c_str(), LabelMsg.Length() + 1, &TextRect,
DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX |
Result->DrawTextBiDiModeFlagsReadingOnly());
int TextFlags = DT_EXPANDTABS | DT_NOPREFIX | Message->DrawTextBiDiModeFlagsReadingOnly();
TSize TextSize = CalculateLabelSize(Message->Canvas, MaxTextWidth, LabelMsg, TextFlags);
int MaxWidth = Monitor->Width - HorzMargin * 2 - IconWidth - 30;
// 5% buffer for potential WM_DPICHANGED, as after re-scaling the text can otherwise narrowly not fit in.
// Though note that the buffer is lost on the first re-scale due to the AutoSize
TextRect.right = MulDiv(TextRect.right, 105, 100);
TextSize.Width = MulDiv(TextSize.Width, 105, 100);
// this will truncate the text, we should implement something smarter eventually
TextRect.right = Min(TextRect.right, MaxWidth);
TextSize.Width = Min(TextSize.Width, MaxWidth);
IconTextWidth = Max(IconTextWidth, IconWidth + TextRect.Right);
IconTextWidth = Max(IconTextWidth, IconWidth + TextSize.Width);
if (IconTextHeight > 0)
{
IconTextHeight += VertMargin;
}
Message->SetBounds(ALeft, VertMargin + IconTextHeight, TextRect.Right, TextRect.Bottom);
IconTextHeight += TextRect.Bottom;
Message->SetBounds(ALeft, VertMargin + IconTextHeight, TextSize.Width, TextSize.Height);
IconTextHeight += TextSize.Height;
}
}

View File

@@ -518,6 +518,7 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
BOOLPROP(ExternalSessionInExistingInstance);
BOOLPROP(ShowLoginWhenNoSession);
BOOLPROP(KeepOpenWhenNoSession);
BOOLPROP(SessionSilentDisconnect);
BOOLPROP(SessionTabCaptionTruncation);
BOOLPROP(ShowTips);
@@ -925,6 +926,7 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
BOOLPROP(ExternalSessionInExistingInstance);
BOOLPROP(ShowLoginWhenNoSession);
BOOLPROP(KeepOpenWhenNoSession);
BOOLPROP(SessionSilentDisconnect);
BOOLPROP(SessionTabCaptionTruncation);
BOOLPROP(ShowTips);

View File

@@ -2593,13 +2593,13 @@ object PreferencesDialog: TPreferencesDialog
Left = 8
Top = 251
Width = 445
Height = 164
Height = 187
Anchors = [akLeft, akTop, akRight]
Caption = 'Miscellaneous'
TabOrder = 2
DesignSize = (
445
164)
187)
object MinimizeToTrayCheck: TCheckBox
Left = 11
Top = 22
@@ -2632,12 +2632,12 @@ object PreferencesDialog: TPreferencesDialog
end
object ShowTipsCheck: TCheckBox
Left = 11
Top = 137
Top = 160
Width = 425
Height = 17
Anchors = [akLeft, akTop, akRight]
Caption = '&Display tips on startup'
TabOrder = 5
TabOrder = 6
OnClick = ControlChange
end
object ShowLoginWhenNoSessionCheck: TCheckBox
@@ -2654,11 +2654,21 @@ object PreferencesDialog: TPreferencesDialog
end
object SessionTabCaptionTruncationCheck: TCheckBox
Left = 11
Top = 114
Top = 137
Width = 425
Height = 17
Anchors = [akLeft, akTop, akRight]
Caption = '&Truncate tab titles when they do not fit to window'
TabOrder = 5
OnClick = ControlChange
end
object SessionSilentDisconnectCheck: TCheckBox
Left = 11
Top = 114
Width = 425
Height = 17
Anchors = [akLeft, akTop, akRight]
Caption = 'Do not popup error message when idle session is disconnected'
TabOrder = 4
OnClick = ControlChange
end

View File

@@ -367,6 +367,7 @@ __published:
TCheckBox *WorkspaceConnectAllCheck;
TLabel *Label13;
TComboBox *AuthAgentCombo;
TCheckBox *SessionSilentDisconnectCheck;
void __fastcall FormShow(TObject *Sender);
void __fastcall ControlChange(TObject *Sender);
void __fastcall EditorFontButtonClick(TObject *Sender);

View File

@@ -165,6 +165,7 @@
#define TOO_MANY_SESSIONS 1373
#define EDIT_CHANGED_EXTERNALLY 1374
#define ALWAYS_RECONNECT 1375
#define NEVER_POPUP_DISCONNECT 1376
#define WIN_INFORMATION_STRINGS 1400
#define COMPARE_NO_DIFFERENCES 1402

View File

@@ -169,6 +169,7 @@ BEGIN
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"
NEVER_POPUP_DISCONNECT, "&Never popup disconnect messages"
WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
COMPARE_NO_DIFFERENCES, "No differences found."

View File

@@ -574,6 +574,7 @@ void __fastcall TGUIConfiguration::Default()
FSessionReopenAutoIdle = SessionReopenAutoIdleDefault;
FSessionReopenAutoIdleOn = true;
FSessionReopenAutoInactive = false;
FSessionSilentDisconnect = false;
FNewDirectoryProperties.Default();
FNewDirectoryProperties.Rights = TRights::rfDefault | TRights::rfExec;
@@ -652,6 +653,7 @@ void __fastcall TGUIConfiguration::UpdateStaticUsage()
KEY(Integer, SessionReopenAutoIdle); \
KEY(Bool, SessionReopenAutoIdleOn); \
KEY(Bool, SessionReopenAutoInactive); \
KEY(Bool, SessionSilentDisconnect); \
); \
//---------------------------------------------------------------------------
bool __fastcall TGUIConfiguration::DoSaveCopyParam(THierarchicalStorage * Storage, const TCopyParamType * CopyParam, const TCopyParamType * Defaults)

View File

@@ -190,6 +190,7 @@ private:
int FSessionReopenAutoIdle;
bool FSessionReopenAutoIdleOn;
bool FSessionReopenAutoInactive;
bool FSessionSilentDisconnect;
LCID FAppliedLocale;
// Corresponds to FAppliedLocale
UnicodeString FLocaleModuleName;
@@ -291,6 +292,7 @@ public:
__property int SessionReopenAutoIdle = { read = FSessionReopenAutoIdle, write = FSessionReopenAutoIdle };
__property bool SessionReopenAutoIdleOn = { read = FSessionReopenAutoIdleOn, write = FSessionReopenAutoIdleOn };
__property bool SessionReopenAutoInactive = { read = FSessionReopenAutoInactive, write = FSessionReopenAutoInactive };
__property bool SessionSilentDisconnect = { read = FSessionSilentDisconnect, write = FSessionSilentDisconnect };
__property bool CanApplyLocaleImmediately = { read = GetCanApplyLocaleImmediately };
__property LCID AppliedLocale = { read = FAppliedLocale };
};

View File

@@ -16,7 +16,8 @@ __fastcall TManagedTerminal::TManagedTerminal(TSessionData * SessionData,
TConfiguration * Configuration) :
TTerminal(SessionData, Configuration),
LocalBrowser(false), LocalExplorerState(NULL), RemoteExplorerState(NULL), OtherLocalExplorerState(NULL),
ReopenStart(), DirectoryLoaded(Now()), TerminalThread(NULL), Disconnected(false), DisconnectedTemporarily(false),
ReopenStart(), DirectoryLoaded(Now()), TerminalThread(NULL),
Disconnected(false), DisconnectedTemporarily(false), InitiateReconnectTimeout(false),
ThumbnailsSection(new TCriticalSection()), ThumbnailsEnabled(false), ThumbnailDownloadQueueItem(NULL),
ThumbnailVisibleResult(-1)
{
@@ -430,6 +431,8 @@ void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool R
ReconnectingTerminal(ManagedTerminal);
ManagedTerminal->Disconnected = false;
ManagedTerminal->DisconnectedTemporarily = false;
ManagedTerminal->DisconnectMessage = EmptyStr;
ManagedTerminal->InitiateReconnectTimeout = false;
DebugAssert(ManagedTerminal->TerminalThread == NULL);
ManagedTerminal->TerminalThread = TerminalThread;
}
@@ -579,17 +582,17 @@ bool __fastcall TTerminalManager::ConnectActiveTerminalImpl(bool Reopen)
if (Action == tpFree)
{
DisconnectActiveTerminalIfPermanentFreeOtherwise();
DisconnectActiveTerminalIfPermanentFreeOtherwise(EmptyStr);
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TTerminalManager::DisconnectActiveTerminalIfPermanentFreeOtherwise()
void __fastcall TTerminalManager::DisconnectActiveTerminalIfPermanentFreeOtherwise(const UnicodeString & Message)
{
if (ActiveTerminal->Permanent)
{
DisconnectActiveTerminal();
DisconnectActiveTerminal(Message);
}
else
{
@@ -649,7 +652,7 @@ bool __fastcall TTerminalManager::ConnectActiveTerminal()
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TTerminalManager::DisconnectActiveTerminal()
void __fastcall TTerminalManager::DisconnectActiveTerminal(const UnicodeString & Message)
{
DebugAssert(ActiveTerminal);
if (ActiveTerminal->Active)
@@ -671,6 +674,8 @@ void __fastcall TTerminalManager::DisconnectActiveTerminal()
delete OldQueue;
ActiveTerminal->Disconnected = true;
ActiveTerminal->DisconnectMessage = Message;
ActiveTerminal->InitiateReconnectTimeout = !Message.IsEmpty() && GUIConfiguration->SessionReopenAutoIdleOn;
ActiveTerminal->DisableThumbnails();
if (ScpExplorer != NULL)
{
@@ -753,6 +758,8 @@ void TTerminalManager::FreeTerminalCleanup(TTerminal * ATerminal, bool IsActiveS
{
NewActiveTerminal->Disconnected = true;
NewActiveTerminal->DisconnectedTemporarily = true;
NewActiveTerminal->DisconnectMessage = EmptyStr;
NewActiveTerminal->InitiateReconnectTimeout = false;
}
}
else

View File

@@ -58,6 +58,8 @@ public:
// should not be reconnected when their tab is activated.
bool Disconnected;
bool DisconnectedTemporarily;
UnicodeString DisconnectMessage;
bool InitiateReconnectTimeout;
// Sessions that should not close when they fail to connect
// (i.e. those that were ever connected or were opened as a part of a workspace)
bool Permanent;
@@ -92,8 +94,8 @@ public:
TManagedTerminal * __fastcall NewSessions(TList * DataList);
virtual void __fastcall FreeTerminal(TTerminal * Terminal);
void __fastcall Move(TTerminal * Source, TTerminal * Target);
void __fastcall DisconnectActiveTerminalIfPermanentFreeOtherwise();
void __fastcall DisconnectActiveTerminal();
void __fastcall DisconnectActiveTerminalIfPermanentFreeOtherwise(const UnicodeString & Message);
void __fastcall DisconnectActiveTerminal(const UnicodeString & Message = UnicodeString());
void __fastcall ReconnectActiveTerminal();
void __fastcall FreeActiveTerminal();
void __fastcall CycleTerminals(bool Forward);

View File

@@ -196,6 +196,7 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
// swallow
}
UnicodeString DisconnectError;
TTerminalManager * Manager = TTerminalManager::Instance(false);
if (!DoNotDisplay)
{
@@ -284,42 +285,66 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
{
if (ForActiveTerminal)
{
bool HookedDialog = false;
try
bool InactiveTerminationMessage = (FatalException != NULL) && FatalException->InactiveTerminationMessage;
if (!InactiveTerminationMessage && GUIConfiguration->SessionSilentDisconnect)
{
TMessageParams Params;
if (DebugAlwaysTrue(Manager->ActiveTerminal != NULL) &&
((Configuration->SessionReopenTimeout == 0) ||
(Manager->ActiveTerminal->ReopenStart == TDateTime()) ||
(int(double(Now() - Manager->ActiveTerminal->ReopenStart) * MSecsPerDay) < Configuration->SessionReopenTimeout)))
{
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;
}
Result = qaOK;
DisconnectError = E->Message;
}
__finally
else
{
if (HookedDialog)
bool HookedDialog = false;
try
{
Manager->UnhookFatalExceptionMessageDialog();
TMessageParams Params;
if (DebugAlwaysTrue(Manager->ActiveTerminal != NULL) &&
((Configuration->SessionReopenTimeout == 0) ||
(Manager->ActiveTerminal->ReopenStart == TDateTime()) ||
(int(double(Now() - Manager->ActiveTerminal->ReopenStart) * MSecsPerDay) < Configuration->SessionReopenTimeout)))
{
Params.Timeout = GUIConfiguration->SessionReopenAutoIdleOn ? GUIConfiguration->SessionReopenAutoIdle : 0;
Params.TimeoutAnswer = qaRetry;
Params.TimeoutResponse = Params.TimeoutAnswer;
HookedDialog = Manager->HookFatalExceptionMessageDialog(Params);
}
bool PermanentTerminal = DebugAlwaysTrue(Manager->ActiveTerminal != NULL) && Manager->ActiveTerminal->Permanent;
if (InactiveTerminationMessage)
{
Params.Params |= mpNeverAskAgainCheck;
Params.NeverAskAgainTitle = LoadStr(ALWAYS_RECONNECT);
Params.NeverAskAgainAnswer = qaRetry;
}
// For now - maybe it is worth doing even for non-permanent sessions
else if (PermanentTerminal)
{
Params.Params |= mpNeverAskAgainCheck;
Params.NeverAskAgainTitle = LoadStr(NEVER_POPUP_DISCONNECT);
Params.NeverAskAgainAnswer = qaOK;
}
Result = FatalExceptionMessageDialog(E, qtError, EmptyStr, qaOK, EmptyStr, &Params);
if (Result == qaNeverAskAgain)
{
Result = Params.NeverAskAgainAnswer;
if (InactiveTerminationMessage)
{
GUIConfiguration->SessionReopenAutoInactive = true;
}
else if (DebugAlwaysTrue(PermanentTerminal))
{
GUIConfiguration->SessionSilentDisconnect = true;
}
}
}
__finally
{
if (HookedDialog)
{
Manager->UnhookFatalExceptionMessageDialog();
}
}
}
}
@@ -370,7 +395,7 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
{
if (ForActiveTerminal)
{
Manager->DisconnectActiveTerminalIfPermanentFreeOtherwise();
Manager->DisconnectActiveTerminalIfPermanentFreeOtherwise(DisconnectError);
}
}
}

View File

@@ -2145,12 +2145,8 @@ static void __fastcall FocusableLabelCanvas(TStaticText * StaticText,
}
else
{
TRect TextRect;
SetRect(&TextRect, 0, 0, StaticText->Width, 0);
DrawText(Canvas->Handle, StaticText->Caption.c_str(), -1, &TextRect,
DT_CALCRECT | DT_WORDBREAK |
StaticText->DrawTextBiDiModeFlagsReadingOnly());
TextSize = TextRect.GetSize();
int Flags = StaticText->DrawTextBiDiModeFlagsReadingOnly();
TextSize = CalculateLabelSize(Canvas, StaticText->Width, StaticText->Caption, Flags);
}
R.Bottom = R.Top + TextSize.cy;
@@ -3132,18 +3128,23 @@ void AutoSizeButton(TButton * Button)
}
}
//---------------------------------------------------------------------------
TSize CalculateLabelSize(TCanvas * Canvas, int Width, const UnicodeString & Caption, int Flags)
{
TRect TextRect(0, 0, Width, 0);
Flags |= DT_CALCRECT | DT_WORDBREAK;
DrawText(Canvas->Handle, Caption.c_str(), -1, &TextRect, Flags);
return TextRect.GetSize();
}
//---------------------------------------------------------------------------
template<class T>
bool DoAutoSizeLabel(T * Label, TCanvas * Canvas)
{
TRect TextRect;
SetRect(&TextRect, 0, 0, Label->Width, 0);
DrawText(Canvas->Handle, Label->Caption.c_str(), Label->Caption.Length() + 1, &TextRect,
DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX |
Label->DrawTextBiDiModeFlagsReadingOnly());
bool Result = (TextRect.Height() > Label->Height);
int Flags = DT_EXPANDTABS | DT_NOPREFIX | Label->DrawTextBiDiModeFlagsReadingOnly();
int Height = CalculateLabelSize(Canvas, Label->Width, Label->Caption, Flags).Height;
bool Result = (Height > Label->Height);
if (Result)
{
Label->Height = TextRect.Height();
Label->Height = Height;
Label->AutoSize = false;
}
return Result;

View File

@@ -106,6 +106,7 @@ void AutoSizeButton(TButton * Button);
namespace Tb2item { class TTBCustomItem; }
void GiveTBItemPriority(Tb2item::TTBCustomItem * Item);
void DeleteChildren(TWinControl * Control);
TSize CalculateLabelSize(TCanvas * Canvas, int Width, const UnicodeString & Caption, int Flags);
void AutoSizeLabel(TLabel * Label);
void AutoSizeLabel(TStaticText * Label);
void SetAccessibleName(TWinControl * Control, const UnicodeString & Name);

View File

@@ -306,35 +306,35 @@ void __fastcall TMessageTimer::DoTimer(TObject * /*Sender*/)
class TMessageTimeout : public TTimer
{
public:
__fastcall TMessageTimeout(TComponent * AOwner, unsigned int Timeout, TButton * Button, unsigned int Answer);
__fastcall TMessageTimeout(TForm * AOwner, int Timeout);
void Restart(int Timeout);
protected:
unsigned int FOrigTimeout;
unsigned int FTimeout;
TButton * FButton;
UnicodeString FOrigCaption;
TForm * FOwner;
int FOrigTimeout;
int FTimeout;
TPoint FOrigCursorPos;
std::unique_ptr<TApplicationEvents> FApplicationEvents;
unsigned int FAnswer;
void __fastcall DoTimer(TObject * Sender);
void __fastcall UpdateButton();
virtual void UpdateButton() = 0;
virtual void Timeouted() = 0;
void __fastcall ApplicationMessage(TMsg & Msg, bool & Handled);
void __fastcall MouseMove();
void __fastcall Cancel();
UnicodeString FormatTimeoutCaption(const UnicodeString & OrigCaption);
};
//---------------------------------------------------------------------------
__fastcall TMessageTimeout::TMessageTimeout(TComponent * AOwner,
unsigned int Timeout, TButton * Button, unsigned int Answer) :
TTimer(AOwner), FOrigTimeout(Timeout), FTimeout(Timeout), FButton(Button), FAnswer(Answer)
__fastcall TMessageTimeout::TMessageTimeout(TForm * AOwner, int Timeout) :
TTimer(AOwner), FOwner(AOwner), FOrigTimeout(Timeout), FTimeout(Timeout)
{
Enabled = (FTimeout >= 0);
OnTimer = DoTimer;
Interval = MSecsPerSec;
FOrigCaption = FButton->Caption;
FOrigCursorPos = Mouse->CursorPos;
FApplicationEvents.reset(new TApplicationEvents(Application));
FApplicationEvents->OnMessage = ApplicationMessage;
UpdateButton();
}
//---------------------------------------------------------------------------
void __fastcall TMessageTimeout::ApplicationMessage(TMsg & Msg, bool & DebugUsedArg(Handled))
@@ -356,15 +356,15 @@ void __fastcall TMessageTimeout::MouseMove()
int Delta = std::max(std::abs(FOrigCursorPos.X - CursorPos.X), std::abs(FOrigCursorPos.Y - CursorPos.Y));
int Threshold = 8;
if (DebugAlwaysTrue(FButton != NULL))
if (DebugAlwaysTrue(FOwner != NULL))
{
Threshold = ScaleByTextHeight(FButton, Threshold);
Threshold = ScaleByTextHeight(FOwner, Threshold);
}
if (Delta > Threshold)
{
FOrigCursorPos = CursorPos;
const unsigned int SuspendTime = 30 * MSecsPerSec;
const int SuspendTime = 30 * MSecsPerSec;
FTimeout = std::max(FOrigTimeout, SuspendTime);
UpdateButton();
}
@@ -376,34 +376,18 @@ void __fastcall TMessageTimeout::Cancel()
UpdateButton();
}
//---------------------------------------------------------------------------
void __fastcall TMessageTimeout::UpdateButton()
UnicodeString TMessageTimeout::FormatTimeoutCaption(const UnicodeString & OrigCaption)
{
DebugAssert(FButton != NULL);
FButton->Caption =
!Enabled ? FOrigCaption : FMTLOAD(TIMEOUT_BUTTON, (FOrigCaption, int(FTimeout / MSecsPerSec)));
return FMTLOAD(TIMEOUT_BUTTON, (OrigCaption, int(FTimeout / MSecsPerSec)));
}
//---------------------------------------------------------------------------
void __fastcall TMessageTimeout::DoTimer(TObject * /*Sender*/)
{
if (FTimeout <= Interval)
if (FTimeout <= static_cast<int>(Interval))
{
DebugAssert(FButton != NULL);
// Needed particularly for "keep up to date" dialog, which does not close on the button click
Enabled = false;
TModalResult PrevModalResult = FButton->ModalResult;
if (FAnswer != 0)
{
FButton->ModalResult = FAnswer;
}
try
{
FButton->Click();
}
__finally
{
FButton->ModalResult = PrevModalResult;
}
UpdateButton();
Timeouted();
}
else
{
@@ -412,11 +396,113 @@ void __fastcall TMessageTimeout::DoTimer(TObject * /*Sender*/)
}
}
//---------------------------------------------------------------------------
void InitiateDialogTimeout(TForm * Dialog, unsigned int Timeout, TButton * Button, unsigned int Answer)
void TMessageTimeout::Restart(int Timeout)
{
TMessageTimeout * MessageTimeout = new TMessageTimeout(Application, Timeout, Button, Answer);
MessageTimeout->Name = L"MessageTimeout";
Dialog->InsertComponent(MessageTimeout);
FTimeout = Timeout;
Enabled = (FTimeout >= 0);
UpdateButton();
}
//---------------------------------------------------------------------------
class TButtonMessageTimeout : public TMessageTimeout
{
public:
__fastcall TButtonMessageTimeout(TForm * AOwner, int Timeout, TButton * Button, unsigned int Answer);
protected:
virtual void UpdateButton();
virtual void Timeouted();
private:
TButton * FButton;
UnicodeString FOrigCaption;
unsigned int FAnswer;
};
//---------------------------------------------------------------------------
__fastcall TButtonMessageTimeout::TButtonMessageTimeout(
TForm * AOwner, int Timeout, TButton * Button, unsigned int Answer) :
TMessageTimeout(AOwner, Timeout),
FButton(Button), FAnswer(Answer)
{
FOrigCaption = FButton->Caption;
UpdateButton();
}
//---------------------------------------------------------------------------
void TButtonMessageTimeout::UpdateButton()
{
DebugAssert(FButton != NULL);
FButton->Caption = !Enabled ? FOrigCaption : FormatTimeoutCaption(FOrigCaption);
}
//---------------------------------------------------------------------------
void TButtonMessageTimeout::Timeouted()
{
DebugAssert(FButton != NULL);
// Needed particularly for "keep up to date" dialog, which does not close on the button click
Enabled = false;
TModalResult PrevModalResult = FButton->ModalResult;
if (FAnswer != 0)
{
FButton->ModalResult = FAnswer;
}
try
{
FButton->Click();
}
__finally
{
FButton->ModalResult = PrevModalResult;
}
}
//---------------------------------------------------------------------------
void InitiateDialogTimeout(TForm * Dialog, int Timeout, TButton * Button, unsigned int Answer)
{
InsertComponentInstance(Dialog, new TButtonMessageTimeout(Dialog, Timeout, Button, Answer));
}
//---------------------------------------------------------------------------
class TToolbarMessageTimeout : public TMessageTimeout
{
public:
__fastcall TToolbarMessageTimeout(TForm * AOwner, int Timeout, TTBCustomItem * Item);
protected:
virtual void UpdateButton();
virtual void Timeouted();
private:
TTBCustomItem * FItem;
};
//---------------------------------------------------------------------------
__fastcall TToolbarMessageTimeout::TToolbarMessageTimeout(TForm * AOwner, int Timeout, TTBCustomItem * Item) :
TMessageTimeout(AOwner, Timeout),
FItem(Item)
{
DebugAssert(Item->Action != nullptr);
UpdateButton();
}
//---------------------------------------------------------------------------
void TToolbarMessageTimeout::UpdateButton()
{
UnicodeString OrigCaption = DebugNotNull(dynamic_cast<TCustomAction *>(FItem->Action))->Caption;
FItem->Caption = !Enabled ? OrigCaption : FormatTimeoutCaption(OrigCaption);
}
//---------------------------------------------------------------------------
void TToolbarMessageTimeout::Timeouted()
{
FItem->Click();
}
//---------------------------------------------------------------------------
void RestartToolbarDialogTimeout(TForm * Dialog, int Timeout, TTBCustomItem * Item)
{
auto ToolbarMessageTimeout = FindComponentInstance<TToolbarMessageTimeout>(Dialog);
if (ToolbarMessageTimeout == nullptr)
{
ToolbarMessageTimeout = new TToolbarMessageTimeout(Dialog, Timeout, Item);
InsertComponentInstance(Dialog, ToolbarMessageTimeout);
}
else
{
ToolbarMessageTimeout->Restart(Timeout);
}
}
//---------------------------------------------------------------------
class TPublicControl : public TControl

View File

@@ -125,7 +125,8 @@ Tbx::TTBXSeparatorItem * __fastcall AddMenuSeparator(Tb2item::TTBCustomItem * Me
void __fastcall AddMenuLabel(Tb2item::TTBCustomItem * Menu, const UnicodeString & Label);
void __fastcall ClickToolbarItem(Tb2item::TTBCustomItem * Item, bool PositionCursor);
void InitiateDialogTimeout(TForm * Dialog, unsigned int Timeout, TButton * Button, unsigned int Answer = 0);
void InitiateDialogTimeout(TForm * Dialog, int Timeout, TButton * Button, unsigned int Answer = 0);
void RestartToolbarDialogTimeout(TForm * Dialog, int Timeout, TTBCustomItem * Item);
// windows\WinHelp.cpp
void __fastcall InitializeWinHelp();