//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
//
//   Implementation of class TListBox and TlistBoxData.
//----------------------------------------------------------------------------
#pragma hdrignore SECTION
#include <owl/owlpch.h>
#include <owl/listbox.h>
#include <stdlib.h>
#include <cstring.h>

#if !defined(SECTION) || SECTION == 1

#define MULTIPLESEL    (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)

//
// TListBoxData constructor
//
TListBoxData::TListBoxData()
:
  Strings(10, 0, 10),
  ItemDatas(10, 0, 10),
  SelIndices(1, 0, 10)
{
}

//
// TListBoxData destructor
//
TListBoxData::~TListBoxData()
{
}

//
// adds "str" to "Strings"
//
// if "isSelected" is true, marks it as selected
//
void
TListBoxData::AddString(const char* str, bool isSelected)
{
  Strings.Add(str);
  if (isSelected)
    Select(Strings.GetItemsInContainer()-1);
}

//
// adds str and associated item data to strings and datas
//
void
TListBoxData::AddStringItem(const char* str, uint32 itemData, bool isSelected)
{
  ItemDatas.Add(itemData);
  AddString(str, isSelected);
}

//
// selects an item at a given index.
//
void
TListBoxData::Select(int index)
{
  if (index != LB_ERR)
    SelIndices.Add(index);
}

//
// adds "str" to selection lists if it is in Strings
//
void
TListBoxData::SelectString(const char far* str)
{
  for (int i = 0; i < Strings.GetItemsInContainer(); i++)
    if (strcmp(Strings[i].c_str(), str) == 0) {
      Select(i);
      break;
    }
}

//
// returns the length of the string at the passed selection index
// excluding the terminating 0
//
int
TListBoxData::GetSelStringLength(int index) const
{
  if (index >= 0 && index < GetSelCount())
    return Strings[SelIndices[index]].length();
  return -1;
}

//
// copies the string at the passed selection index into buffer
//
// bufferSize includes the terminating 0
//
void
TListBoxData::GetSelString(char far* buffer, int bufferSize, int index) const
{
  if (bufferSize > 0) {
    if (index < 0 || index >= GetSelCount())
      *buffer = 0;

    else {
      strncpy(buffer, Strings[SelIndices[index]].c_str(), bufferSize-1);
      buffer[bufferSize - 1] = 0;
    }
  }
}

//
// copies the string at the passed index in SelStrings into str
//
void
TListBoxData::GetSelString(string& str, int index) const
{
  if (index >= 0 && index < GetSelCount())
    str = Strings[SelIndices[index]];
  else
    str = "";
}

//----------------------------------------------------------------------------

TStringArray::TStringArray(int upper, int lower, int delta)
:
  Data(upper, lower, delta)
{
}

int TStringArray::LowerBound() const
{
  return Data.LowerBound();
}

int TStringArray::UpperBound() const
{
  return Data.UpperBound();
}

unsigned TStringArray::ArraySize() const
{
  return Data.ArraySize();
}

int TStringArray::IsFull() const
{
  return Data.IsFull();
}

int TStringArray::IsEmpty() const
{
  return Data.IsEmpty();
}

unsigned TStringArray::GetItemsInContainer() const
{
  return Data.GetItemsInContainer();
}

int TStringArray::Add(const string& t)
{
  return Data.Add(t);
}

int TStringArray::Detach(const string& t)
{
  return Data.Detach(t);
}

int TStringArray::Detach(int loc)
{
  return Data.Detach(loc);
}

int TStringArray::Destroy(const string& t)
{
  return Detach(t);
}

int TStringArray::Destroy(int loc)
{
  return Detach(loc);
}

int TStringArray::HasMember(const string& t) const
{
  return Data.HasMember(t);
}

int TStringArray::Find(const string& t) const
{
  return Data.Find(t);
}

string& TStringArray::operator [](int loc)
{
  return Data[loc];
}

string& TStringArray::operator [](int loc) const
{
  return Data[loc];
}

void TStringArray::ForEach(IterFunc iter, void* args)
{
  Data.ForEach(iter, args);
}

string* TStringArray::FirstThat(CondFunc cond, void* args) const
{
  return Data.FirstThat(cond, args);
}

string* TStringArray::LastThat(CondFunc cond, void* args) const
{
  return Data.LastThat(cond, args);
}

void TStringArray::Flush()
{
  Data.Flush();
}

//----------------------------------------------------------------------------

TIntArray::TIntArray(int upper, int lower, int delta)
:
  Data(upper, lower, delta)
{
}

int TIntArray::LowerBound() const
{
  return Data.LowerBound();
}

int TIntArray::UpperBound() const
{
  return Data.UpperBound();
}

unsigned TIntArray::ArraySize() const
{
  return Data.ArraySize();
}

int TIntArray::IsFull() const
{
  return Data.IsFull();
}

int TIntArray::IsEmpty() const
{
  return Data.IsEmpty();
}

unsigned TIntArray::GetItemsInContainer() const
{
  return Data.GetItemsInContainer();
}

int TIntArray::Add(const TInt& t)
{
  return Data.Add(t);
}

int TIntArray::Detach(const TInt& t)
{
  return Data.Detach(t);
}

int TIntArray::Detach(int loc)
{
  return Data.Detach(loc);
}

int TIntArray::Destroy(const TInt& t)
{
  return Detach(t);
}

int TIntArray::Destroy(int loc)
{
  return Detach(loc);
}

int TIntArray::HasMember(const TInt& t) const
{
  return Data.HasMember(t);
}

int TIntArray::Find(const TInt& t) const
{
  return Data.Find(t);
}

TInt& TIntArray::operator [](int loc)
{
  return Data[loc];
}

TInt& TIntArray::operator [](int loc) const
{
  return Data[loc];
}

void TIntArray::ForEach(IterFunc iter, void* args)
{
  Data.ForEach(iter, args);
}

TInt* TIntArray::FirstThat(CondFunc cond, void* args) const
{
  return Data.FirstThat(cond, args);
}

TInt* TIntArray::LastThat(CondFunc cond, void* args) const
{
  return Data.LastThat(cond, args);
}

void TIntArray::Flush()
{
  Data.Flush();
}

//----------------------------------------------------------------------------

TDwordArray::TDwordArray(int upper, int lower, int delta)
:
  Data(upper, lower, delta)
{
}

int TDwordArray::LowerBound() const
{
  return Data.LowerBound();
}

int TDwordArray::UpperBound() const
{
  return Data.UpperBound();
}

unsigned TDwordArray::ArraySize() const
{
  return Data.ArraySize();
}

int TDwordArray::IsFull() const
{
  return Data.IsFull();
}

int TDwordArray::IsEmpty() const
{
  return Data.IsEmpty();
}

unsigned TDwordArray::GetItemsInContainer() const
{
  return Data.GetItemsInContainer();
}

int TDwordArray::Add(const uint32& t)
{
  return Data.Add(t);
}

int TDwordArray::Detach(const uint32& t)
{
  return Data.Detach(t);
}

int TDwordArray::Detach(int loc)
{
  return Data.Detach(loc);
}

int TDwordArray::Destroy(const uint32& t)
{
  return Detach(t);
}

int TDwordArray::Destroy(int loc)
{
  return Detach(loc);
}

int TDwordArray::HasMember(const uint32& t) const
{
  return Data.HasMember(t);
}

int TDwordArray::Find(const uint32& t) const
{
  return Data.Find(t);
}

uint32& TDwordArray::operator [](int loc)
{
  return Data[loc];
}

uint32& TDwordArray::operator [](int loc) const
{
  return Data[loc];
}

void TDwordArray::ForEach(IterFunc iter, void* args)
{
  Data.ForEach(iter, args);
}

uint32* TDwordArray::FirstThat(CondFunc cond, void* args) const
{
  return Data.FirstThat(cond, args);
}

uint32* TDwordArray::LastThat(CondFunc cond, void* args) const
{
  return Data.LastThat(cond, args);
}

void TDwordArray::Flush()
{
  Data.Flush();
}

//----------------------------------------------------------------------------

//
// constructor for TListBox
//
// initializes its data fields using parameters passed and default values
//
// by default, an MS-Windows listbox associated with the TListBox will:
//   - be visible upon creation
//   - have a border and a vertical scrollbar
//   - maintain entries in alphabetical order
//   - notify its parent when a selection is made
//
TListBox::TListBox(TWindow*   parent,
                   int        id,
                   int x, int y, int w, int h,
                   TModule*   module)
:
  TControl(parent, id, 0, x, y, w, h, module)
{
  Attr.Style |= LBS_STANDARD;
}

TListBox::TListBox(TWindow*   parent,
                   int        resourceId,
                   TModule*   module)
:
  TControl(parent, resourceId, module)
{
}

char far*
TListBox::GetClassName()
{
  return "LISTBOX";
}

//
// transfers state information for a TListBox
//
// transfers the items and selection of the list to or from a transfer
// buffer if tdSetData or tdGetData, respectively, is passed as the
// direction
//
// buffer should point to a TListBoxData which points to the data to be
// transferred
//
// Transfer returns the size of TListBoxData
//
// to retrieve the size without transferring data, pass tdSizeData as the
// direction
//
uint
TListBox::Transfer(void* buffer, TTransferDirection direction)
{
  long           style = GetWindowLong(GWL_STYLE);
  TListBoxData*  listBoxData = (TListBoxData*)buffer;

  if (direction == tdGetData) {
    //
    // first, clear out Strings array and fill with contents of list box
    //
    listBoxData->Clear();

    //
    // pre-calculate max string length so that one big buffer can be used
    //
    int  count = GetCount();
    int  maxStrLen = 0;
    for (int i = 0; i < count; i++) {
      int  strLen = GetStringLen(i);
      if (strLen > maxStrLen)
        maxStrLen = strLen;
    }
    //
    // Get each string and item data in the listbox & add to listboxdata
    //
    char*  tmpStr = new char[maxStrLen+1];
    for (i = 0; i < GetCount(); i++) {
      GetString(tmpStr, i);
      listBoxData->AddStringItem(tmpStr, GetItemData(i), false);
    }
    delete tmpStr;

    //
    // update transfer data with new selected item(s)
    //
    listBoxData->ResetSelections();

    if (!(style & MULTIPLESEL)) {
      //
      // single selection
      //
      listBoxData->Select(GetSelIndex());
    }
    else {
      //
      // multiple selection
      //
      int  selCount = GetSelCount();
      if (selCount > 0) {
        int*  selections = new int[selCount];

        GetSelIndexes(selections, selCount);

        // Select each item by index
        //
        for (int selIndex = 0; selIndex < selCount; selIndex++)
          listBoxData->Select(selections[selIndex]);

        delete selections;
      }
    }
  }
  else if (direction == tdSetData) {
    ClearList();

    //
    // add each string, item data and selections in listBoxData to list box
    //
    const int noSelection = -1;
    int  selCount = listBoxData->GetSelCount();  // for multi selection
    int  selIndex = noSelection;                 // for single selection
    for (int i = 0; i < listBoxData->GetStrings().GetItemsInContainer(); i++) {
      // index may be different from i when the listbox is sorted.
      //
      int index = AddString(listBoxData->GetStrings()[i].c_str());
      SetItemData(index, listBoxData->GetItemDatas()[i]);
      if (style & MULTIPLESEL) {
        for (int j = 0; j < selCount; j++)
          if (listBoxData->GetSelIndices()[j] == i) {
            SetSel(index, true);
            break;
          }
      }
      else {
        if (listBoxData->GetSelIndices()[0] == i)
          selIndex = index;
        else
          // inserted something before item and the item to select has been
          // pushed further down in the list.
          //
          if (selIndex != noSelection && index <= selIndex)
            selIndex++;
      }
    }
    if (selIndex != noSelection && !(style & MULTIPLESEL))
      SetSelIndex(selIndex);
  }

  return sizeof(TListBoxData);
}

//
// returns the index of the first string in the associated listbox
// which is the same as the passed string
//
// searches for a match beginning at the passed indexStart
//
// if a match is not found after the last string has been compared,
// the search continues from the beginning of the list until a match
// is found or until the list has been completely traversed
//
// searches from beginning of list when -1 is passed as the index
//
// returns the index of the selected string; a negative value is returned
// if an error occurs
//
// for single or multiple-selection list boxes
//
int
TListBox::FindExactString(const char far* findStr, int indexStart) const
{
  bool  found = false;
  int   firstMatch = indexStart = FindString(findStr, indexStart);
  do {
    if (indexStart > -1) {
      char*  tmpStr = new char[GetStringLen(indexStart) + 1];

      GetString(tmpStr, indexStart);

      if (strcmp(tmpStr, findStr) == 0)
        found = true;

      else
        indexStart = FindString(findStr, indexStart);

      delete tmpStr;
    }
  } while (!found && indexStart != firstMatch);

  return found ? indexStart : -1;
}

//
// for use with single-selection list boxes (and combo boxes)
//
// retrieves the text of the string which is selected in the associated
// listbox
//
// returns the number of characters copied; -1 is returned if no string
// is selected or this is a multiple-selection list box
//
// since the Windows function is not passed a size parameter, we have to
// allocate a string to hold the largest string (gotten from a query), and
// copy a part of it
//
int
TListBox::GetSelString(char far* str, int maxChars) const
{
  int  index = GetSelIndex();

  if (index > -1) {
    int  length = GetStringLen(index);

    if (maxChars >= length)
      return GetString(str, index);

    else {
      char*  tmpStr = new char[length + 1];

      if (tmpStr) {
        GetString(tmpStr, index);
        strncpy(str, tmpStr, maxChars);
        delete tmpStr;
        return maxChars;
      }
    }
  }
  return -1;
}

//
// returns the number of selected items in the list box. For
// multiple-selection list boxes only
//
int
TListBox::GetSelCount() const
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return GetSelIndex() < 0 ? 0 : 1;

  //
  // multiple-selection list box
  //
  return (int)CONST_CAST(TListBox*,this)->HandleMessage(LB_GETSELCOUNT);
}

//
// retrieves the text of the strings which are selected in the
// associated listbox
//
// returns the number of items put into Strings.  -1 is returned if this is
// not a multiple-selection list box
//
// since the Windows function is not passed a size parameter, we have to
// allocate a string to hold the largest string (gotten from a query), and
// copy a part of it
//
// only for use with multiple-selection list boxes
//
int
TListBox::GetSelStrings(char far** strs, int maxCount, int maxChars) const
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return -1;

  int i = GetSelCount();

  if (i < maxCount)
    maxCount = i;

  if (maxCount > 0) {
    int*  selections = new int[maxCount];

    GetSelIndexes(selections, maxCount);

    for (int selIndex = 0; selIndex < maxCount; selIndex++) {
      int  tmpStrLen = GetStringLen(selections[selIndex]);

      if (maxChars >= tmpStrLen)
        GetString(strs[selIndex], selections[selIndex]);

      else {
        char*  tmpStr = new char[tmpStrLen+1];

        if (tmpStr) {
          GetString(tmpStr, selections[selIndex]);
          strncpy(strs[selIndex], tmpStr, maxChars);
          delete tmpStr;
        }
      }
    }
    delete selections;
  }
  return maxCount;
}

//
// selects the first string in the associated listbox following the
// passed index which begins with the passed string
//
// searches for a match beginning at the passed Index. if a match is not
// found after the last string has been compared, the search continues
// from the beginning of the list until a match is found or until the list
// has been completely traversed
//
// searches from beginning of list when -1 is passed as the index
//
// returns the index of the selected string. a negative value is returned
// if an error occurs
//
// only for single-selection list boxes
//
int
TListBox::SetSelString(const char far* findStr, int indexStart)
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return (int)HandleMessage(LB_SELECTSTRING, indexStart, (LPARAM)findStr);
  return -1;
}

//
// selects the strings in the associated list box which begin with
// the passed prefixes
//
// for each string the search begins at the beginning of the list
// and continues until a match is found or until the list has been
// completely traversed
//
// if ShouldSet is true, the matched strings are selected and highlighted;
// otherwise the highlight is removed from the matched strings and they
// are no longer selected
//
// returns the number of strings successfully selected or deselected
//
// if NumSelections is less than zero, all strings are selected or deselected
// and a negative value is returned on failure
//
// only for multiple-selection list boxes (-1 is returned if this is not
// a multiple-selection list box)
//
int
TListBox::SetSelStrings(const char far** strs, int numSelections, bool shouldSet)
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return -1;

  if (numSelections < 0)
    return SetSel(-1, shouldSet);

  int  successes = 0;
  for (int i = 0; i < numSelections; i++) {
    int  selIndex;
    if ((selIndex = FindString(strs[i], -1)) > -1)
      if (SetSel(selIndex, shouldSet) > -1)
        successes++;
  }
  return successes;
}

//
// returns the index of the selected string in the associated listbox
//
// a negative value is returned if no string is selected or this
// is a multiple-selection list box
//
// only for single-selection list boxes
//
int
TListBox::GetSelIndex() const
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return (int)CONST_CAST(TListBox*,this)->HandleMessage(LB_GETCURSEL);
  return -1;
}

//
// fills indexes with the indexes of up to maxCount selected strings
//
// returns number of items put in the array(-1 for single-selection
// list boxes)
//
int
TListBox::GetSelIndexes(int* indexes, int maxCount) const
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return -1;
  return (int)CONST_CAST(TListBox*,this)->HandleMessage(LB_GETSELITEMS,
                                                        maxCount,
                                                        (LPARAM)indexes);
}

// selects the string at passed index in the associated listbox and
// forces the string into view
//
// clears selection when -1 is passed as the index. a negative value is
// returned if an error occurs
//
// only for single-selection list boxes
//
int
TListBox::SetSelIndex(int index)
{
  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return (int)HandleMessage(LB_SETCURSEL, index);
  return -1;
}

//
// selects/deselects the strings in the associated list box at the
// passed indexes
//
// if ShouldSet is true, the indexed strings are selected and highlighted;
// otherwise the highlight is removed and they are no longer selected
//
// returns the number of strings successfully selected or deselected(-1
// if not a multiple-selection list box)
//
// if NumSelections is less than zero, all strings are selected or deselected
// and a negative value is returned on failure
//
// only for multiple-selection list boxes
//
int
TListBox::SetSelIndexes(int* indexes, int numSelections, bool shouldSet)
{
  int  successes = 0;

  if (!(GetWindowLong(GWL_STYLE) & MULTIPLESEL))
    return -1;  // including if it's a combobox

  if (numSelections < 0)
    return (int)HandleMessage(LB_SETSEL, shouldSet, -1);

  else {
    for (int i = 0; i < numSelections; i++)
      if ((int)HandleMessage(LB_SETSEL, shouldSet, indexes[i]) > -1)
        successes++;
  }
  return successes;
}

#endif
#if !defined(SECTION) || SECTION == 2

IMPLEMENT_STREAMABLE1(TListBox, TControl);

//
// reads an instance of TListBox from the supplied ipstream
//
void*
TListBox::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
  ReadBaseObject((TControl*)GetObject(), is);
  return GetObject();
}

//
// writes the TListBox to the supplied opstream
//
void
TListBox::Streamer::Write(opstream& os) const
{
  WriteBaseObject((TControl*)GetObject(), os);
}

#endif

