//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1991, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of TComboBox & TComboBoxData.
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/combobox.h>
using namespace std;
namespace owl {
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlControl);
//
/// Constructs a TComboBoxData object, initializes Strings and ItemDatas to empty
/// arrays, and initializes Selection and SelIndex to 0.
//
TComboBoxData::TComboBoxData()
{
Strings = new TStringArray;
ItemDatas = new TLParamArray;
SelIndex = 0;
}
//
/// Destructor for TComboBoxData. Deletes Strings, ItemDatas, and Selection.
//
TComboBoxData::~TComboBoxData()
{
delete Strings;
delete ItemDatas;
}
///30.05.2007 - Submitted by Frank Rast:
///TComboBoxData needs a copy constructor because the
///default copy constructor does not deep copy the
///protected data of this class. For the same reason a
///assignment operator is needed.
TComboBoxData::TComboBoxData(const TComboBoxData& tCBD)
{
Strings = new TStringArray;
ItemDatas = new TLParamArray;
SelIndex = 0;
*this = tCBD;
}
TComboBoxData &TComboBoxData::operator=(const TComboBoxData& tCBD)
{
*Strings = *tCBD.Strings;
*ItemDatas = *tCBD.ItemDatas;
Selection = tCBD.Selection;
SelIndex = tCBD.SelIndex;
return *this;
}
//
/// Flushes the Strings and ItemDatas members. Resets the index and selected string
/// values.
//
void TComboBoxData::Clear()
{
Strings->Flush();
ItemDatas->Flush();
ResetSelections();
}
//
/// Adds the specified string to the array of Strings. If IsSelected is true,
/// AddString deletes Selection and copies str into Selection.
//
void
TComboBoxData::AddString(LPCTSTR str, bool isSelected)
{
Strings->Add(str); // add to end
if (isSelected)
Select(Strings->Size()-1);
}
//
/// Adds a given string and uint32 item to the "Strings" and "ItemDatas"
/// array and copies the string into "Selection" if "isSelected" is true
//
void
TComboBoxData::AddStringItem(LPCTSTR str, LPARAM itemData, bool isSelected)
{
ItemDatas->Add(itemData);
AddString(str, isSelected);
}
//
/// Selects an item at a given index.
//
void
TComboBoxData::Select(int index)
{
if (index != CB_ERR) {
SelIndex = index;
if (index < (int)Strings->Size())
Selection = (*Strings)[index];
}
}
//
/// Selects "str", marking the matching String entry (if any) as selected
//
void
TComboBoxData::SelectString(LPCTSTR str)
{
int numStrings = Strings->Size();
SelIndex = CB_ERR;
for (int i = 0; i < numStrings; i++)
if (_tcscmp((*Strings)[i].c_str(), str) == 0) {
SelIndex = i;
break;
}
if (Selection != str)
Selection = str;
}
//
/// Returns the length of the selection string excluding the terminating 0
//
int
TComboBoxData::GetSelStringLength() const
{
return static_cast<int>(Selection.length());
}
//
/// Copies the selected string into Buffer. BufferSize includes the terminating 0
//
void
TComboBoxData::GetSelString(tchar * buffer, int bufferSize) const
{
if (bufferSize > 0) {
_tcsncpy(buffer, Selection.c_str(), bufferSize-1);
buffer[bufferSize - 1] = 0;
}
}
//----------------------------------------------------------------------------
//
// Constructors for a TComboBox object
//
/// Constructs a combo box object with the specified parent window (parent),
/// control ID (Id), position (x, y) relative to the origin of the parent window's
/// client area, width (w), height (h), style (style), and text length (textLimit).
///
/// Invokes the TListBox constructor with similar parameters. Then sets Attr.Style
/// as follows:
/// \code
/// Attr.Style = WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | CBS_SORT |
/// CBS_AUTOHSCROLL | WS_VSCROLL | style;
/// \endcode
/// One of the following combo box style constants must be among the styles set in
/// style: CBS_SIMPLE, CBS_DROPDOWN, CBS_DROPDOWNLIST, CBS_OWNERDRAWFIXED, or
/// CBS_OWNERDRAWVARIABLE.
///
/// By default, an MS-Windows combobox associated with the TComboBox will have
/// a vertical scrollbar and will maintain its entries in alphabetical order
//
TComboBox::TComboBox(TWindow* parent,
int id,
int x, int y, int w, int h,
uint32 style,
uint textLimit,
TModule* module)
:
TListBox(parent, id, x, y, w, h, module),
TextLimit(textLimit)
{
Attr.Style = WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP |
CBS_SORT | CBS_AUTOHSCROLL | WS_VSCROLL | style;
TRACEX(OwlControl, OWL_CDLEVEL, _T("TComboBox constructed @") << (void*)this);
}
//
/// Constructs a default combo box with the given parent window control ID
/// text length.
//
TComboBox::TComboBox(TWindow* parent,
int resourceId,
uint textLimit,
TModule* module)
:
TListBox(parent, resourceId, module),
TextLimit(textLimit)
{
TRACEX(OwlControl, OWL_CDLEVEL, _T("TComboBox constructed from resource @") << (void*)this);
}
//
/// Constructs a combo box object to encapsulate (alias) an existing control.
//
TComboBox::TComboBox(THandle hWnd, TModule* module)
:
TListBox(hWnd, module),
TextLimit(-1) // TODO: Retrieve the current text limit from the control (how?).
{
TRACEX(OwlControl, OWL_CDLEVEL, _T("TComboBox constructed as an alias @") << (void*)this);
}
//
//
//
TComboBox::~TComboBox()
{
TRACEX(OwlControl, OWL_CDLEVEL, _T("TComboBox destructed @") << (void*)this);
}
//
/// Returns a COMBOBOXINFO struct containing information about the combobox.
/// Wrapper for Windows API function GetComboBoxInfo.
/// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775939.aspx
//
auto TComboBox::GetInfo() const -> COMBOBOXINFO
{
PRECONDITION(GetHandle());
auto i = COMBOBOXINFO{sizeof(COMBOBOXINFO)};
const auto ok = ::GetComboBoxInfo(GetHandle(), &i); CHECK(ok);
if (!ok) throw TXOwl{_T("::GetComboBoxInfo failed")};
return i;
}
//
/// Selects the first string in the associated list box that begins with the
/// supplied str. If there is no match, SetText sets the contents of the associated
/// edit control to the supplied string and selects it.
//
void
TComboBox::SetText(LPCTSTR str)
{
// If str is 0, then use empty str
//
if (!str)
str = _T("");
// If not in listbox, then set the edit/static portion
//
if (SetSelStringExact(str, -1) < 0)//DLN was SetSelString, which would use partial matches
{
SetWindowText(str);
SetEditSel(0, static_cast<int>(::_tcslen(str)));
}
}
//
/// Sets the text length limit member and associated control
//
void
TComboBox::SetTextLimit(uint textlimit)
{
TextLimit = textlimit;
if (GetHandle() && TextLimit != 0)
SendMessage(CB_LIMITTEXT, TextLimit-1);
}
//
/// Returns, in the supplied reference parameters, the starting and
/// ending positions of the text selected in the associated edit control
//
/// Returns CB_ERR is the combo box has no edit control
//
int
TComboBox::GetEditSel(int& startPos, int& endPos)
{
TResult retValue = SendMessage(CB_GETEDITSEL);
startPos = LoUint16(retValue);
endPos = HiUint16(retValue);
return (int)retValue;
}
//
/// Functional style overload
//
std::pair<int, int>
TComboBox::GetEditSel()
{
std::pair<int, int> r(0,0);
GetEditSel(r.first, r.second);
return r;
}
//
/// Shows or hides the drop down or drop down list combo box depending on the value
/// of show. If show is true, shows the list; if show is false, hides the list.
//
void
TComboBox::ShowList(bool show)
{
if ((GetWindowLong(GWL_STYLE) & CBS_DROPDOWN) == CBS_DROPDOWN)
SendMessage(CB_SHOWDROPDOWN, show);
}
//
/// For combo boxes, gets the screen coordinates of the dropped down list box.
//
void
TComboBox::GetDroppedControlRect(TRect& rect) const
{
PRECONDITION(GetHandle());
CONST_CAST(TComboBox*,this)->
SendMessage(CB_GETDROPPEDCONTROLRECT, 0, TParam2((TRect *)&rect));
// BUG suggested by Luigi Bianchi
int vertRes = TDesktopDC().GetDeviceCaps(VERTRES);
if(rect.Bottom() >= vertRes)
rect.Offset(0, -rect.Height());
}
//
/// Transfers the items and selection of the combo box to or from a transfer buffer
/// if tdSetData or tdGetData, respectively, is passed as the direction. buffer is
/// expected to point to a TComboBoxData structure.
///
/// Transfer returns the size of the TComboBoxData structure. To retrieve the size
/// without transferring data, your application must pass tdSizeData as the
/// direction.
//
uint
TComboBox::Transfer(void* buffer, TTransferDirection direction)
{
if (!buffer && direction != tdSizeData) return 0;
TComboBoxData& data = *static_cast<TComboBoxData*>(buffer);
if (direction == tdGetData)
{
// Get the strings and associated item data from the combo box.
//
data.Clear();
const int n = GetCount();
for (int i = 0; i != n; ++i)
data.AddStringItem(GetString(i), GetItemData(i), false);
// Get the selected string from the list by index, or
// if the combobox has no selection, get the string from the edit part.
//
int selIndex = GetSelIndex();
if (selIndex >= 0)
data.Select(selIndex);
else
data.SelectString(GetText());
}
else if (direction == tdSetData)
{
// Fill the combo box with strings and associated item data, and restore the selection, if any.
//
ClearList();
TStringArray& s = data.GetStrings();
TLParamArray& d = data.GetItemDatas();
const int n = s.GetItemsInContainer();
const int m = d.GetItemsInContainer();
for (int i = 0; i != n; ++i)
{
int index = AddString(s[i]);
if (i < m)
SetItemData(index, d[i]); // Set the data of the item for the inserted string.
// Note that inserted position may be different from 'i' when the combobox is sorted.
// So to ensure that we restore selection correctly, we select the indicated item early.
// The selection will automatically move with the item, if later strings are inserted above.
//
if (i == data.GetSelIndex())
SetSelIndex(index);
}
// If there is no selection, set the content of the edit part manually.
//
int selIndex = data.GetSelIndex();
if (selIndex < 0)
SetText(data.GetSelection());
}
return sizeof(TComboBoxData);
}
//
/// Returns the name of TComboBox's registration class, "ComboBox."
//
auto TComboBox::GetWindowClassName() -> TWindowClassName
{
return TWindowClassName{_T("COMBOBOX")};
}
//
/// Limits the amount of text that the user can enter in the combo box's
/// edit control to the value of TextLimit minus 1
//
/// Creates plain TWindow aliases for the children in the combo box so that
/// TWindow can handle kill focus messages for focus support.
//
void
TComboBox::SetupWindow()
{
TRACEX(OwlControl, 1, _T("TComboBox::SetupWindow() @ ") << (void*)this);
TListBox::SetupWindow();
SetTextLimit(TextLimit);
THandle hWnd = ::GetWindow(GetHandle(), GW_CHILD);
while (hWnd) {
if (!GetWindowPtr(hWnd))
(new TWindow(hWnd))->SetParent(this);
hWnd = ::GetWindow(hWnd, GW_HWNDNEXT);
}
}
//
/// Cleanup aliases created in SetupWindow
//
void
TComboBox::CleanupWindow()
{
HWND hWnd = ::GetWindow(GetHandle(), GW_CHILD);
while (hWnd) {
TWindow* wnd = GetWindowPtr(hWnd);
delete wnd;
hWnd = ::GetWindow(hWnd, GW_HWNDNEXT);
}
// call base class
//
TListBox::CleanupWindow();
TRACEX(OwlControl, 1, _T("TComboBox::CleanupWindow() @ ") << (void*)this);
}
IMPLEMENT_STREAMABLE1(TComboBox, TListBox);
#if OWL_PERSISTENT_STREAMS
//
/// Reads an instance of TComboBox from the supplied ipstream
//
void*
TComboBox::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
ReadBaseObject((TListBox*)GetObject(), is);
is >> GetObject()->TextLimit;
return GetObject();
}
//
/// Writes the TComboBox to the supplied opstream
//
void
TComboBox::Streamer::Write(opstream& os) const
{
WriteBaseObject((TListBox*)GetObject(), os);
os << GetObject()->TextLimit;
}
#endif
} // OWL namespace
↑ V522 There might be dereferencing of a potential null pointer 'static_cast< TComboBoxData * > (buffer)'.