//-------------------------------------------------------------------
// OWL Extensions (OWLEXT) Class Library
// Copyright(c) 1996 by Manic Software.
// All rights reserved.
//
// Original code by:
//----------------------------------------------------------------
// Copyright � Antony Corbett 1995. All Rights Reserved
// Author:  Antony Corbett
//        Compuserve  100277,330
//        Tel +44 (1926) 856131,  Fax +44 (1926) 856727
//
// Description:
//    TExpandableComboBox implementation.
//
//    TComboBox derivatives that provide disabling of selected
//    list items, horizontal scrolling and dynamic switching
//    between single & multiple selection.
//
//
//    History:
//      15 Dec 1995      Initial release
//
//      21 Dec 1995       Added support for Tab Stops
//                  Added MakeSingleSel()
//                  Removed oldCaret_
//                  Added colored items support
//
//       22 Dec 1995      Modified SetSel()
//                  Added SetSelIndex()
//
//      29 Dec 1995      Fixed bug in TExpandableComboBox::SetSel()
//                    (Thanks to Greg Bullock)
//
//       10 Jun 1997       Updated for Inphorm
//                         Converted Class to ComboBox Derivative
//
//-------------------------------------------------------------------
#include <owlext\pch.h>
#pragma hdrstop
 
#include <owl/shddel.h>
 
#include <owlext/comboexp.h>
 
#include <algorithm>
 
using namespace owl;
using namespace std;
 
namespace OwlExt {
 
//***************************************
//    TExpandableComboBox
//***************************************
 
DEFINE_RESPONSE_TABLE1(TExpandableComboBox, TComboBox)
 
// owl routes this message from parent back to control...
EV_WM_MEASUREITEM,
 
// The CBN_SELCHANGE notification is sent for a
//  multiple-selection list box whenever the user
// presses an arrow key, even if the selection
// does not change....
EV_NOTIFY_AT_CHILD(CBN_SELCHANGE, CmSelChange),
EV_NOTIFY_AT_CHILD(CBN_KILLFOCUS, CmKillFocus),
// EV_NOTIFY_AT_CHILD(CBN_SELENDOK, CmSelEndOk),
 
END_RESPONSE_TABLE;
 
 
//----------------------------------------------------------------
// Description:
//    c'tor. Constructs a TExpandableComboBox control to be associated
//    with a TDialog.
//
// Arguments:
//    useHScroll = flag indicating if we should use a
//    horizontal scroll bar.
//
//----------------------------------------------------------------
TExpandableComboBox::TExpandableComboBox(TWindow* parent, int resourceId,
                     eExpAlign fTCBAlign,
                     int nUnitWidth,
                     TModule* module  // = 0
                     ) :
TComboBox(parent, resourceId, 0, module),
textExtents_(*new SortedIntegers),  // store extents of strings
pseudoSingleSel_(FALSE)
{
  tabs_ = new int[1];
  numTabs_ = 1;
  hwndList = 0;
  fTCBAlign_ = fTCBAlign;
  nUnitWidth_ = nUnitWidth;
  nItemHeight = 0;
  bFirstRedraw = true;
 
  hasHScroll_ = true;
  greatestExtent = 0;
  nPasses = 0;
  bShowCompleteLine_ = false;
}
 
//----------------------------------------------------------------
// Description:
//    d'tor. Deletes the integer array used to hold text extents.
//
//----------------------------------------------------------------
TExpandableComboBox::~TExpandableComboBox()
{
  Destroy();
  delete &textExtents_;
  delete[] tabs_;
}
 
// Setupwindow -- Used to to set the position of the default tab
void
TExpandableComboBox::SetupWindow()
{
  TComboBox::SetupWindow();
 
  TRect cbRect = GetWindowRect();
  int  nDlgUnits = LOWORD (GetDialogBaseUnits());
  int nThumb = GetSystemMetrics (SM_CXHTHUMB);
 
  int nDlgTabUnits = (((cbRect.Width()-nThumb) * nDlgUnits) / (4));
  tabs_[0] = nDlgTabUnits / 2;
}
 
 
 
//****************************************************
//
// operations on individual list box items:
//
//    int AddString(const char *str);
//    int AddDisabledString(const char *str);
//    int InsertString(const char *str, int index);
//    int DeleteString(int index);
//    void Enable(int index, BOOL enable = TRUE);
//    int SetItemData(int index, uint32 data);
//    int SetSel(int index, BOOL select);
//    int SetSelIndex(int index);
//    void SetTextColor(int index, TColor color);
//    void ReSetTextColor(int index);
//
//****************************************************
 
 
//----------------------------------------------------------------
// Description:
//    Adds a string to the list.
//
// Arguments:
//    str = string to add
//
// Returns:
//    zero-based index of added string.
//----------------------------------------------------------------
int
TExpandableComboBox::AddString(LPCTSTR str)
{
  // add the string...
  int index = TComboBox::AddString(str);
 
  if(index >= 0){
    // store the extent of string in sorted container...
    StoreStrExtent(str);
    // now the item data...
    TComboBox::SetItemData(index, reinterpret_cast<LPARAM>(new TCBItemData()));
  }
  return index;
}
 
 
//----------------------------------------------------------------
// Description:
//    Adds a string to the list and disables it.
//
// Arguments:
//    str = string to add
//
// Returns:
//    zero-based index of added string.
//----------------------------------------------------------------
int
TExpandableComboBox::AddDisabledString(LPCTSTR str)
{
  int index = AddString(str);
  if(index >= 0)
    Enable(index, FALSE);
  return index;
}
 
 
//----------------------------------------------------------------
// Description:
//    Inserts a string at the given position in the list.
//
// Arguments:
//    str = string to insert
//    index = zero-based index giving insertion position.
//    If index == -1 the string is appended to end of list.
//
// Returns:
//    zero-based index of inserted string.
//----------------------------------------------------------------
int
TExpandableComboBox::InsertString(LPCTSTR str, int index)
{
  int actualIndex = TComboBox::InsertString(str, index);
  if(actualIndex >= 0){
    // store the extent of string in sorted container...
    StoreStrExtent(str);
    // now the item data...
    TComboBox::SetItemData(actualIndex, reinterpret_cast<LPARAM>(new TCBItemData()));
  }
  return actualIndex;
}
 
 
//----------------------------------------------------------------
// Description:
//    Deletes a string in the list.
//
// Arguments:
//    index = zero-based index of string to remove.
//
// Returns:
//    Number of remaining items or -ve if an error.
//----------------------------------------------------------------
int
TExpandableComboBox::DeleteString(int index)
{
  // first remove any item data...
  TCBItemData* id = GetItemDataPtr(index);
  CHECK(id);
  delete id;
 
  // find extent of string to be deleted...
  LPTSTR tempStr = new _TCHAR[256];
  GetString(tempStr, index);
 
  int length = FindTextExtent(tempStr);
  delete[] tempStr;
 
  textExtents_.DestroyItem(length);
 
  UpdateHorizontalExtent();
 
  return (TComboBox::DeleteString(index));
}
 
 
//----------------------------------------------------------------
// Description:
//    Changes the enable state of an item in the list.
//
// Arguments:
//    index = zero-based index of item to enable/disable.
//      If index == -1 the all items are enabled/disabled.
//
//    enable = TRUE/FALSE flag indicating if item
//    should be enabled or disabled. Defaults to TRUE
//
//----------------------------------------------------------------
void
TExpandableComboBox::Enable(int index, bool enable/* = true*/)
{
  if(index == -1){
    for(int n=0; n<GetCount(); n++){
      if(GetEnabled(n) != enable){
        // change in status required...
        GetItemDataPtr(n)->enabled_ = enable;
      }
    }
    Invalidate();
  }
  else{
    if(GetEnabled(index) != enable){
      // change in status required...
      GetItemDataPtr(index)->enabled_ = enable;
 
      // now update the display...
      TRect rc;
      int rv = GetItemRect(index, rc);
      if(rv != CB_ERR)
        InvalidateRect(rc);
    }
  }
}
 
//----------------------------------------------------------------
// Description:
//    Sets list item data.
//    NB - we've replaced the standard behaviour to allow us
//    to store the item's enable status.
//
// Arguments:
//    index = zero-based index of item.
//    data = custom data to associate with the item.
//
// Returns:
//    CB_ERR if an error.
//----------------------------------------------------------------
int
TExpandableComboBox::SetItemData(int index, LPARAM data)
{
  // the data is actually a ptr to a TCBItemData struct.
  GetItemDataPtr(index)->data_ = data;
  return !CB_ERR;
}
 
 
//----------------------------------------------------------------
// Description:
//    Selects an item at the position specified in index.
//
// Arguments:
//    index = zero-based index of item to select.
//    select = TRUE/FALSE
//
// Returns:
//    CB_ERR if an error.
//    If the item is disabled, we do not select and no error
//    is returned.
//----------------------------------------------------------------
int
TExpandableComboBox::SetSel(int index, bool select)
{
  if(!(select && !GetEnabled(index)))
    return (TComboBox::SetSel(index, select));
  return !CB_ERR;
}
 
 
 
 
//----------------------------------------------------------------
// Description:
//    For single-selection list boxes. Forces the selection
//    of the item at the zero-based index
//
// Arguments:
//    index = zero-based index of item to select.
//      If index is -1, the list box is cleared
//      of any selection.
//
// Returns:
//    A negative number if an error occurs.
//----------------------------------------------------------------
int
TExpandableComboBox::SetSelIndex(int index)
{
  return (TComboBox::SetSelIndex(index));
}
 
 
//----------------------------------------------------------------
// Description:
//    Sets the color of an item or all items in the list
//
// Arguments:
//    index = zero-based index of item to color.
//      If index == -1 then all items are set to the
//      specified colour
//    color = color of item text
//
//----------------------------------------------------------------
void
TExpandableComboBox::SetTextColor(int index, TColor color)
{
  if(index == -1){
    for(int n=0; n<GetCount(); n++){
      TCBItemData *id = GetItemDataPtr(n);
      id->textColor_ = color;
      id->useSysColor_ = FALSE;
    }
    Invalidate();
  }
  else{
    TCBItemData *id = GetItemDataPtr(index);
    id->textColor_ = color;
    id->useSysColor_ = FALSE;
 
    TRect rc;
    if(GetItemRect(index, rc) != CB_ERR)
      InvalidateRect(rc);
  }
}
 
 
//----------------------------------------------------------------
// Description:
//    Sets the color of an item or all items to the
//    default system color.
//
// Arguments:
//    index = zero-based index of item to reset color for.
//      If index == -1 then all items are reset.
//
//----------------------------------------------------------------
void
TExpandableComboBox::ResetTextColor(int index)
{
  if(index == -1){
    for(int n=0; n<GetCount(); n++){
      TCBItemData *id = GetItemDataPtr(n);
      id->textColor_ = TColor();
      id->useSysColor_ = TRUE;
    }
    Invalidate();
  }
  else{
    TCBItemData *id = GetItemDataPtr(index);
    id->textColor_ = TColor();
    id->useSysColor_ = TRUE;
 
    TRect rc;
    if(GetItemRect(index, rc) != CB_ERR)
      InvalidateRect(rc);
  }
}
 
 
//****************************************************
//
// operations on listbox:
//
//      void ClearList();
//      BOOL SetTabStops(int numTabs, int * tabs, bool bShowCompleteLine);
//      BOOL MakeSingleSel(BOOL single = TRUE);
//
//****************************************************
 
//----------------------------------------------------------------
// Description:
//    Clears the list of all items. We override to make sure
//    all the TCBItemData objects are deleted and the hscrolling
//    cleaned up.
//
//----------------------------------------------------------------
void
TExpandableComboBox::ClearList()
{
  for(int n=0; n<GetCount(); n++)
    delete GetItemDataPtr(n);
 
  textExtents_.Flush();
  UpdateHorizontalExtent();
 
  TComboBox::ClearList();
}
 
 
//----------------------------------------------------------------
// Description:
//    Sets tab stops.
//
// Arguments:
//   numTabs = number of tab stops
//    tabs = array of integers representing the tab positions
//
// Returns:
//    non-zero if all tab stops could be set
//----------------------------------------------------------------
bool
TExpandableComboBox::SetTabStops(int numTabs, int * tabs, bool bShowCompleteLine)
{
  bool rv = !0;
 
  bShowCompleteLine_ = bShowCompleteLine;
  numTabs_ = numTabs;
  delete[] tabs_;
  tabs_ = new int[numTabs];
 
  // check they're in size order...
  for(int n=0, lastTab = -1; n<numTabs; n++){
    if(tabs[n] <= lastTab){
      // out of sequence!
      rv = 0;
      numTabs_ = n;  // modify num tabs
      break;
    }
    lastTab = tabs[n];
    tabs_[n] = tabs[n];
  }
 
  return rv;
}
 
//----------------------------------------------------------------
// Description:
//    Gets the data stored for the specified item
//
// Arguments:
//    index = zero-based index of item to get data for
//
// Returns:
//    data value
//----------------------------------------------------------------
LPARAM
TExpandableComboBox::GetItemData(int index) const
{
  // the actual data is a ptr to a TCBItemData struct
  // which contains a member 'data'
  return (GetItemDataPtr(index)->data_);
}
 
 
//----------------------------------------------------------------
// Description:
//    Returns the selection state of the listbox item at
//    given index.
//
// Arguments:
//    index = zero-based index of item to test.
//
// Returns:
//    TRUE/FALSE
//----------------------------------------------------------------
bool
TExpandableComboBox::GetSel(int index) const
{
  // if the item _is_ selected but is disabled,
  // we say it isn't selected.
  return (TComboBox::GetSel(index)?  GetEnabled(index) : FALSE);
}
 
 
 
//----------------------------------------------------------------
// Description:
//    Gets the enable status of an item.
//
// Arguments:
//    index = zero-based index of item to get status for.
//
// Returns:
//    TRUE/FALSE
//----------------------------------------------------------------
bool
TExpandableComboBox::GetEnabled(int index) const
{
  if (index < 0)
    return true;
  else
    return (GetItemDataPtr(index)->enabled_);
}
 
 
 
//----------------------------------------------------------------
// Description:
//    Gets a ptr to the TCBItemData object associated with
//    the given item. We retain a ptr to the TCBItemData object
//    in the 32-bit item data.
//
// Arguments:
//    zero-based index of item to get TCBItemData object for.
//
// Returns:
//    TCBItemData ptr
//----------------------------------------------------------------
TCBItemData*
TExpandableComboBox::GetItemDataPtr(int index) const
{
  if (index < 0)
    index = 0;
  return ((TCBItemData *)(TComboBox::GetItemData(index)));
}
 
 
 
//****************************************************
//
// query:
//
//    int GetSelCount() const;
//    int GetSelIndex() const;
//
//****************************************************
 
 
//----------------------------------------------------------------
// Description:
//    Returns the number of selected items in the list.
//
//----------------------------------------------------------------
int
TExpandableComboBox::GetSelCount() const
{
  return (GetSelIndex() < 0 ? 0 : 1);  // We've overridden GetSelIndex
}
 
//----------------------------------------------------------------
// Description:
//    For single-sel listboxes. Returns the index of the
//    current selection.
//
// Returns:
//    zero-based index of current selection or -ve number if
//    no selection.
//----------------------------------------------------------------
int
TExpandableComboBox::GetSelIndex() const
{
  int index = TComboBox::GetSelIndex();
  return (GetEnabled(index)? index : CB_ERR);
}
 
 
 
//****************************************************
// protected methods...
//****************************************************
 
//----------------------------------------------------------------
// Description:
//    Cleans up interface elements
//
//----------------------------------------------------------------
void
TExpandableComboBox::CleanupWindow()
{
  ClearList();
  TComboBox::CleanupWindow();
}
 
 
//----------------------------------------------------------------
// Description:
//    Finds the extent of a given listbox string. Used in
//    calculating horizontal scrolling.
//
// Arguments:
//    str = string to find extent of
//
// Returns:
//    extent of string in logical units
//----------------------------------------------------------------
int
TExpandableComboBox::FindTextExtent(LPCTSTR str)
{
  PRECONDITION(str);
  TSize extent;
 
  TClientDC dc(GetHandle());
  HFONT hfont = GetWindowFont();
 
  if(hfont)  // non system font...
    dc.SelectObject(hfont);
 
  extent = dc.GetTabbedTextExtent(str, lstrlen(str), numTabs_, tabs_);
 
  if(hfont)
    dc.RestoreFont();
 
  return extent.cx;
}
 
 
//----------------------------------------------------------------
// Description:
//    Updates the extent of horizontal scrolling
//
//----------------------------------------------------------------
void
TExpandableComboBox::UpdateHorizontalExtent()
{
  //int greatestExtent;
 
  // find the extent of the largest string and
  // set the horizontal extent accordingly...
  int lastElement = textExtents_.GetItemsInContainer() - 1;
  if(lastElement < 0){
    // no elements in listbox.
    greatestExtent = 0;
  }
  else
    greatestExtent = textExtents_[lastElement];
 
  // add a bit of space so that last char is visible
  // when we scroll to right...
  TClientDC dc(GetHandle());
  greatestExtent += dc.GetTextExtentPoint32(_T("X")).cx;
 
  // if longest str fits completely then scroll
  // listbox to the left, so Windows will hide
  // the scrollbar...
  TRect rect;
  GetClientRect(rect);
 
  int listWidth = rect.right - rect.left;
  if(listWidth >= greatestExtent)
    HandleMessage(WM_HSCROLL, SB_TOP, 0);
 
  SetHorizontalExtent(greatestExtent);
}
 
 
 
//----------------------------------------------------------------
// Description:
//    Stores the extent of the given string in our integer array.
//    Used by horizontal scrolling mechanism.
//
// Arguments:
//    str = string to store extent of.
//
//----------------------------------------------------------------
void
TExpandableComboBox::StoreStrExtent(LPCTSTR str)
{
  int length = FindTextExtent(str);
  textExtents_.Add(length);
  UpdateHorizontalExtent();
}
 
 
 
//----------------------------------------------------------------
// Description:
//    Responds to notification to draw entire control (actually
//    just a single item in the list)
//
//----------------------------------------------------------------
void
TExpandableComboBox::ODADrawEntire(DRAWITEMSTRUCT& drawInfo)
{
  // get text to display...
  int len = TComboBox::GetStringLen(drawInfo.itemID);
  LPTSTR buffer;
 
  if (len < 0){
    buffer = new _TCHAR[5];
    _tcscpy (buffer, _T(""));
  }
  else{
    buffer = new _TCHAR[len+1];
  }
 
  if (GetHandle() == drawInfo.hwndItem){
 
    //TRect cbRect = GetWindowRect();
    //int nThumb = GetSystemMetrics (SM_CXHTHUMB);
    //drawInfo.rcItem.right = drawInfo.rcItem.left + cbRect.Width() - nThumb -2 ;
    if (bShowCompleteLine_)
      GetString (buffer, drawInfo.itemID);
    else
      GetStringX (buffer, drawInfo.itemID);
  }
  else
    TComboBox::GetString(buffer, drawInfo.itemID);
 
  DrawListItem(drawInfo, buffer);
 
  delete[] buffer;
}
 
 
//----------------------------------------------------------------
// Description:
//    Responds to notification that focus has shifted to or from
//    an item.
//
//----------------------------------------------------------------
void
TExpandableComboBox::ODAFocus(DRAWITEMSTRUCT &drawInfo)
{
  TDC DC(drawInfo.hDC);
  TRect rc(drawInfo.rcItem);
  DC.DrawFocusRect(rc);
}
 
 
//----------------------------------------------------------------
// Description:
//    Responds to notification that selection state of item
//    has changed
//
//----------------------------------------------------------------
void
TExpandableComboBox::ODASelect(DRAWITEMSTRUCT &drawInfo)
{
  ODADrawEntire(drawInfo);
}
 
 
//----------------------------------------------------------------
// Description:
//    Handles WM_MEASUREITEM msg. owl sends this message from
//    parent back to the control.
//
//----------------------------------------------------------------
void
TExpandableComboBox::EvMeasureItem(uint /*ctrlId*/, MEASUREITEMSTRUCT &measure)
{
  TWindowDC dc(GetHandle());
 
  // get the height of the font...
  HFONT font = GetWindowFont();
 
  if(font == NULL) // system font is being used...
    dc.SelectStockObject(SYSTEM_FONT);
  else
    dc.SelectObject(font);
 
  TEXTMETRIC tm;
  dc.GetTextMetrics(tm);
 
  // use tmHeight to give a larger spacing,
  // but use tmAscent to duplicate Windows behaviour...
  measure.itemHeight = tm.tmHeight;
  nItemHeight = tm.tmHeight;
 
  dc.RestoreFont();
}
 
 
 
//----------------------------------------------------------------
// Description:
//    Query the background color to use for given item.
//
// Returns:
//    Color to use.
//----------------------------------------------------------------
TColor
TExpandableComboBox::QueryBkColor(DRAWITEMSTRUCT &drawInfo) const
{
  if((drawInfo.itemState & ODS_SELECTED) &&  GetEnabled(drawInfo.itemID)){
    // item is selected and
    // not disabled and
    // it's not a pseduo single-sel list with
    // this item unfocused...
 
    return TColor::SysHighlight;
  }
  else
    return TColor::SysWindow;
}
 
 
 
 
//----------------------------------------------------------------
// Description:
//    Query the text color to use for given item.
//
// Returns:
//    Text color.
//----------------------------------------------------------------
TColor
TExpandableComboBox::QueryTextColor(DRAWITEMSTRUCT &drawInfo) const
{
  TColor color;
 
  if(!GetEnabled(drawInfo.itemID)){  // item disabled
    // note we ignore the case where the device doesn't
    // support solid gray...
    TColor gray = TColor::SysGrayText;
    TDC dc(drawInfo.hDC);
    if(dc.GetBkColor() == TColor(gray)){
      // listbox background is same color as graytext
      // so choose the other gray...
      gray = (dc.GetBkColor() == TColor::LtGray? TColor::Gray : TColor::LtGray);
    }
    color = gray;
  }
  else{
    TCBItemData *id = GetItemDataPtr(drawInfo.itemID);
    if (!id || reinterpret_cast<INT_PTR>(id) == -1){
      color = (drawInfo.itemState & ODS_SELECTED?
        TColor::SysHighlightText : TColor::SysWindowText);
    }
    else{
      color = (drawInfo.itemState & ODS_SELECTED?
        TColor::SysHighlightText :
      (id->useSysColor_? TColor::SysWindowText : id->textColor_));
    }
  }
  return color;
}
 
 
 
//----------------------------------------------------------------
// Description:
//    Draws an item in the list.
//
// Arguments:
//    str = text to draw.
//
//----------------------------------------------------------------
void
TExpandableComboBox::DrawListItem(DRAWITEMSTRUCT &drawInfo, LPCTSTR str)
{
  TDC dc(drawInfo.hDC);
  TRect rc(drawInfo.rcItem);
 
  // first erase the item...
  TBrush brush(QueryBkColor(drawInfo));
  dc.FillRect(rc, brush);
 
  // make some space between left border of listbox and
  // first character of item. (This seems to match the space
  // allowed for in a standard listbox).
  rc.left += (2 * ::GetSystemMetrics(SM_CXBORDER));
  DrawText(drawInfo, rc, str);
}
 
 
//----------------------------------------------------------------
// Description:
//    Draws an item's text as part of the DrawListItem operation.
//
// Arguments:
//    rc = bounding rect
//    str = string to draw
//
//----------------------------------------------------------------
void
TExpandableComboBox::DrawText(DRAWITEMSTRUCT &drawInfo, const TRect& rc,
                LPCTSTR str)
{
  PRECONDITION(str);
  TDC DC(drawInfo.hDC);
  DC.SetBkMode(TRANSPARENT);
 
  DC.SetTextColor(QueryTextColor(drawInfo));
 
  // need to take care of tabs...
  DC.TabbedTextOut(rc.TopLeft(), str, -1, numTabs_, tabs_, 0);
}
 
 
 
void
TExpandableComboBox::CmKillFocus()
{
  static bool forParent = false; // to prevent infinite loop
 
  if(forParent)
    DefaultProcessing();
  else{
    // clean up the focus rect...
    TRect rc;
    if(GetItemRect(GetCaretIndex(), rc) != CB_ERR)
      InvalidateRect(rc);
 
    forParent = true;
 
    // notify parent...
    Parent->SendMessage(WM_COMMAND, Attr.Id, MAKELPARAM(GetHandle(), LBN_KILLFOCUS));
    forParent = false;
  }
}
 
 
//----------------------------------------------------------------
// Description:
//    Responds to LBN_SELCHANGE
//
//----------------------------------------------------------------
void
TExpandableComboBox::CmSelChange()
{
  static bool forParent = false; // to prevent infinite loop
 
  if(forParent)
    DefaultProcessing();
  else{
    forParent = true;
    // notify parent...
 
    if(Attr.Style & LBS_NOTIFY)
      Parent->SendMessage(WM_COMMAND, Attr.Id, MAKELPARAM(GetHandle(), LBN_SELCHANGE));
    forParent = false;
  }
}
 
//----------------------------------------------------------------
// Description:
//    Responds to LBN_SELENDOK
//
//----------------------------------------------------------------
void
TExpandableComboBox::CmSelEndOk()
{
  static bool forParent = false; // to prevent infinite loop
 
  if(forParent)
    DefaultProcessing();
  else{
    forParent = true;
 
    // notify parent...
    if(Attr.Style & LBS_NOTIFY)
      Parent->SendMessage(WM_COMMAND, Attr.Id,MAKELPARAM(GetHandle(), CBN_SELENDOK));
    forParent = false;
  }
}
 
 
///////////////////////
// New Code
LRESULT
TExpandableComboBox::DefWindowProc(uint msg, WPARAM wParam, LPARAM lParam)
{
  if (msg == WM_CTLCOLORLISTBOX) {
    if (hwndList == 0) {
      bFirstRedraw = true;
      hwndList = reinterpret_cast<HWND>(lParam);
      ::GetWindowRect(hwndList, &RectList);
      ::SendMessage (hwndList, WM_HSCROLL, SB_TOP, 0);
    }
    ResizeToFit();
  }
  return TComboBox::DefWindowProc(msg, wParam, lParam);
}
 
 
 
void
TExpandableComboBox::ResizeToFit()
{
  static bool bResizing = false;
 
  nPasses++;
 
  if (bResizing)
    return;
  bResizing = true;
 
  if (hwndList == 0){
    bResizing = false;
    MessageBeep(0);
    return;
  }
 
  TRect cbRect = GetWindowRect();
  int  nThumb = GetSystemMetrics (SM_CXHTHUMB);
  int  cbWidth = cbRect.Width();
 
  TRect ParentRect(Parent->GetWindowRect());
 
  // Sel
  int nlbPixels = 0;
  if (nUnitWidth_ > 0)
    nlbPixels = UnitsToPixels (nUnitWidth_);
  else if (nUnitWidth_ == 0){
    nlbPixels = cbRect.Width();
  }
  else if (nUnitWidth_ == -1){
    nlbPixels = greatestExtent;
    nlbPixels = std::max(cbWidth, nlbPixels);
  }
  else if (nUnitWidth_ < 0){
    nlbPixels = UnitsToPixels (nUnitWidth_);
    nlbPixels = std::min(greatestExtent, nlbPixels);
    nlbPixels = std::max(cbWidth, nlbPixels);
  }
 
  int nItems = GetCount();
  if (nItems <= 0)
    nItems = 1;
  int ht = nItems * nItemHeight;
  //
 
  TRect PopRect;
  ::GetWindowRect (hwndList, &PopRect);
 
  // Need to Adjust size for Left/Right/Top/Bottom Margins
 
 
  // Adjust for Direct Placement
  TRect NewRectSize;
  NewRectSize.left = cbRect.left;
  NewRectSize.top = PopRect.top; // cbRect.bottom+2;
  NewRectSize.right = NewRectSize.left + nlbPixels;
 
  //
  switch (fTCBAlign_){
    case eExpAlignDefault:
    case eExpAlignLeft:
      NewRectSize.right = std::min(NewRectSize.right, ParentRect.right - nThumb);
      break;
    case eExpAlignRight:     {
      int nShift = NewRectSize.Width() - cbRect.Width();
      OffsetRect(&NewRectSize, -nShift, 0);
      NewRectSize.left = std::max (NewRectSize.left, ParentRect.left + nThumb);
                 }
                 break;
    case eExpAlignCenter:
      break;
    default:
      break;
  }
 
  // Set the Horizontal Extents based on the new size of the box
  int nExtent;
 
  long wWord = ::GetWindowLong (hwndList, GWL_STYLE);
  if (greatestExtent > NewRectSize.Width()){
    // Add Scroll bar, if needed
    ht += GetSystemMetrics (SM_CYHSCROLL)+1;
    wWord |= WS_HSCROLL | LBS_USETABSTOPS;
    ::SetWindowLong (hwndList, GWL_STYLE, wWord);
    nExtent = greatestExtent;
  }
  else{
    ht += 2;
    wWord &= ~WS_HSCROLL;
    wWord |= LBS_USETABSTOPS;
    ::SetWindowLong (hwndList, GWL_STYLE, wWord);
    nExtent = 0;
  }
  NewRectSize.bottom = NewRectSize.top + ht;
 
  // Now Adjust the bottom size, so it doesn't go over the Dialog Box
  if (NewRectSize.bottom > ParentRect.bottom - nThumb){
    wWord |= WS_VSCROLL;
    NewRectSize.bottom = std::min(NewRectSize.bottom, ParentRect.bottom - nThumb);
  }
  else
    wWord &= ~WS_VSCROLL;
 
  ::SetWindowLong (hwndList, GWL_STYLE, wWord);
 
  // Never Size Less Than the Original Combo Box
  RectList = NewRectSize;
 
  ::SendMessage (hwndList, LB_SETHORIZONTALEXTENT, nExtent, TRUE);
  SetHorizontalExtent(nExtent);
  ::MoveWindow (hwndList, RectList.left, RectList.top, RectList.Width(), RectList.Height(), TRUE);
 
  if (bFirstRedraw){
    ::InvalidateRect (hwndList, NULL, FALSE);
    bFirstRedraw = false;
  }
  bResizing = false;
}
 
 
 
int
TExpandableComboBox::UnitsToPixels(int nUnits)
{
  int nDlgUnits = LOWORD (GetDialogBaseUnits());
  int nPixels = (nUnits * nDlgUnits) / (4);
  return nPixels;
}
 
 
int
TExpandableComboBox::PixelsToUnits(int nUnits)
{
  int nDlgUnits = LOWORD (GetDialogBaseUnits());
  int nPixels = (nUnits * (4)) / nDlgUnits;
  return nPixels;
}
 
 
 
int
TExpandableComboBox::GetStringX (LPTSTR str, int nIndex) const
{
  if (nIndex < 0)
    _tcscpy (str, _T(""));
  else{
    int nLen = GetStringLen(nIndex);
    _TCHAR* szTemp = new _TCHAR [nLen+2];
 
    GetString(szTemp, nIndex);
    _TCHAR* pTab = _tcschr(szTemp, _T('\t'));
    if (pTab)
      *pTab = NULL;
    _tcscpy (str, szTemp);
    delete []szTemp;
  }
  return static_cast<int>(_tcslen(str));
}
 
 
} // OwlExt namespace
//==============================================================================

V1004 The 'str' pointer was used unsafely after it was verified against nullptr. Check lines: 647, 656.

V1004 The 'str' pointer was used unsafely after it was verified against nullptr. Check lines: 929, 936.

V519 The 'forParent' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 976, 981.

V519 The 'forParent' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 998, 1003.

V519 The 'forParent' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 954, 958.

V547 Expression 'nUnitWidth_ < 0' is always true.