1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/packages/my/HistoryComboBox.cpp
Martin Prikryl a2119a7eac Types and casts safe in respect to pointer size for forward 64-bit compatibility
Also GetWindowLongPtr and GWLP_USERDATA

https://winscp.net/tracker/618

Source commit: c85fdf15e50ce8a6f35b1c4d2a0125a0b9c4f1f0
2026-01-22 14:51:13 +01:00

243 lines
7.2 KiB
C++

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "HistoryComboBox.h"
#include "PasTools.hpp"
#include "Common.h"
#include <algorithm>
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
void SaveToHistory(TStrings * Strings, const UnicodeString & T, TObject * Data, int MaxHistorySize)
{
if (!T.IsEmpty())
{
while (Strings->IndexOf(T) >= 0)
{
Strings->Delete(Strings->IndexOf(T));
}
Strings->InsertObject(0, T, Data);
}
while (Strings->Count > MaxHistorySize)
{
Strings->Delete(Strings->Count - 1);
}
}
//---------------------------------------------------------------------------
// TUIStateAwareComboBox
//---------------------------------------------------------------------------
__fastcall TUIStateAwareComboBox::TUIStateAwareComboBox(TComponent * AOwner) :
TComboBox(AOwner)
{
}
//---------------------------------------------------------------------------
void __fastcall TUIStateAwareComboBox::ComboWndProc(TMessage & Message, HWND ComboWnd, TWindowProcPtr ComboProc)
{
TComboBox::ComboWndProc(Message, ComboWnd, ComboProc);
if (Message.Msg == WM_SYSKEYDOWN)
{
UpdateUIState(reinterpret_cast<TWMKey &>(Message).CharCode);
}
}
//---------------------------------------------------------------------------
void __fastcall TUIStateAwareComboBox::CreateWnd()
{
TComboBox::CreateWnd();
if (DarkMode)
{
SetDarkModeTheme(this, L"CFD");
}
}
//---------------------------------------------------------------------------
void TUIStateAwareComboBox::SetDarkMode(bool Value)
{
if (DarkMode != Value)
{
FDarkMode = Value;
RecreateWnd();
}
}
//---------------------------------------------------------------------------
// THistoryComboBox
//---------------------------------------------------------------------------
THistoryComboHistoryEvent THistoryComboBox::OnLoadHistory = nullptr;
THistoryComboHistoryEvent THistoryComboBox::OnSaveHistory = nullptr;
//---------------------------------------------------------------------------
__fastcall THistoryComboBox::THistoryComboBox(TComponent * AOwner) :
TUIStateAwareComboBox(AOwner)
{
FSaveOn = THistorySaveOn(DefaultHistorySaveOn);
FMaxHistorySize = DefaultMaxHistorySize;
FOnGetData = nullptr;
FOnSetData = nullptr;
DropDownCount = DefaultHistoryDropDownCount;
AutoComplete = DefaultHistoryAutoComplete;
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::KeyDown(unsigned short & Key, TShiftState Shift)
{
if (((Key == VK_DOWN) || (Key == VK_UP)) &&
!Shift.Contains(ssAlt) && SaveOn.Contains(soDropDown))
{
if (Items->IndexOf(Text) < 0)
{
AddToHistory();
}
}
if (DroppedDown && (Key == VK_DELETE) && Shift.Contains(ssCtrl) && !SaveOn.Empty())
{
Items->Clear();
Key = 0;
}
TUIStateAwareComboBox::KeyDown(Key, Shift);
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::SetMaxHistorySize(int AMaxHistorySize)
{
FMaxHistorySize = AMaxHistorySize;
while (Items->Count > FMaxHistorySize)
{
Items->Delete(Items->Count - 1);
}
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::DoExit()
{
TUIStateAwareComboBox::DoExit();
if (SaveOn.Contains(soExit))
{
AddToHistory();
}
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::DropDown()
{
TUIStateAwareComboBox::DropDown();
if (SaveOn.Contains(soDropDown))
{
AddToHistory();
}
int ItemWidth = GetMaxItemWidth() + ScaleByPixelsPerInch(8, this);
int VScrollWidth = GetSystemMetricsForControl(this, SM_CXVSCROLL);
if (Items->Count > DropDownCount)
{
ItemWidth += VScrollWidth;
}
// If dropdown fits from control's left to desktop's right edge, it's dropped to the right.
// Otherwise it drops to the left.
// The following limits the width do that it fits either to the right or left on the current monitor only.
TRect MonitorRect = GetParentForm(this)->Monitor->WorkareaRect;
TPoint ScreenPosition = ClientToScreen(TPoint());
// VScrollWidth is used just as a convenient size of margin between monitor edge and scrollber
int RightSpace = MonitorRect.Right - ScreenPosition.X - VScrollWidth;
int LeftSpace = ScreenPosition.X + Width - MonitorRect.Left - VScrollWidth;
int DropSpace = RightSpace;
// Drop to the left only if there's considerably more space on left than right
if (LeftSpace > RightSpace * 11 / 10)
{
DropSpace = LeftSpace;
}
ItemWidth = std::min(ItemWidth, DropSpace);
Perform(CB_SETDROPPEDWIDTH, static_cast<WPARAM>(ItemWidth), static_cast<LPARAM>(0));
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::Change()
{
TUIStateAwareComboBox::Change();
if (OnSetData != nullptr)
{
// note that ItemIndex is not reliable
int Index = Items->IndexOf( Text );
if (Index >= 0)
{
OnSetData(this, Items->Objects[Index]);
}
}
}
//---------------------------------------------------------------------------
void THistoryComboBox::AddToHistory()
{
if (!Text.IsEmpty())
{
TObject * Data = nullptr;
if (OnGetData != nullptr)
{
OnGetData(this, Data);
}
::SaveToHistory(Items, Text, Data, MaxHistorySize);
ItemIndex = 0;
}
}
//---------------------------------------------------------------------------
void THistoryComboBox::SaveToHistory()
{
AddToHistory();
if (OnSaveHistory != nullptr)
{
OnSaveHistory(this);
}
}
//---------------------------------------------------------------------------
int THistoryComboBox::GetMaxItemWidth()
{
int Result = 0;
HDC DC = GetDC(0);
try
{
HGDIOBJ SaveFont = SelectObject(DC, Font->Handle);
for (int Index = 0; Index < Items->Count; Index++)
{
TSize Size;
GetTextExtentPoint32(DC, Items->Strings[Index].c_str(), Items->Strings[Index].Length(), &Size);
if (Size.cx > Result)
{
Result = Size.cx;
}
}
SelectObject(DC, SaveFont);
}
__finally
{
ReleaseDC(0, DC);
}
return Result;
}
//---------------------------------------------------------------------------
void THistoryComboBox::LoadHistory()
{
if (!SaveOn.Empty() && !HistoryKey.IsEmpty() && HandleAllocated())
{
if (OnLoadHistory != nullptr)
{
OnLoadHistory(this);
}
else
{
Items->Clear();
}
}
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::SetHistoryKey(UnicodeString value)
{
if (HistoryKey != value)
{
FHistoryKey = value;
LoadHistory();
}
}
//---------------------------------------------------------------------------
void __fastcall THistoryComboBox::CreateWnd()
{
TUIStateAwareComboBox::CreateWnd();
LoadHistory();
}