Using TListWindow (continued)

Responding to Events

Responding to events in the list is no more difficult than in other controls. The tricky (well less elegant anyway) part is determining which item to act on. The example here responds to a double click event by changing to a new directory (if that is what the user clicked).

This code is for Borland C++ 5.02 only. If you are using Borland C++ 5.01, click here.

DEFINE_RESPONSE_TABLE1(TLWWindow, TWindow)
  // ...
  EV_NM_DBLCLK(kListId, ListDoubleClick),
  // ...
END_RESPONSE_TABLE;

void TLWWindow::ListDoubleClick()
{
  // find the selected item using the GetSelIndexes
  // function (not in the help file)
  //
  int selItem;
  if (List->GetSelIndexes(&selItem, 1) == 1)
  {
    TListWindItem nameItem(*List, selItem, 0);
    TListWindItem typeItem(*List, selItem, 2);

    // if the item is a folder, change to it and refresh
    //
    if (!strcmp(typeItem.GetText(), "Directory"))
    {
      SetCurrentDirectory(nameItem.GetText());
      AddListItems();
    }
  }
}

Sorting

To sort the items in a list window, you need to override the TLwComparator class. In doing this, it is often a good idea to add some data members to your derived class so that it can access the items in the list window. The example here has a class TMyComparator, derived from TLwComparator, to hold the required data members. From TMyComparator are derived to more classes to handle string and integer comparisons.

//
// Our list window sorting base class
//
class TMyComparator : public TLwComparator
{
  public:
    TMyComparator(TListWindow* list, int col, bool up)
      : List(list), Col(col), Up(up) {}
  protected:
    TListWindow* List; // the list window to sort
    int Col;           // which column to sort on
    bool Up;           // ascending or descending
};


//
// Our comparator class for sorting strings
//
class TMyStringComparator : public TMyComparator
{
  public:
    TMyStringComparator(TListWindow* l, int c, bool u)
      : TMyComparator(l, c, u) {}
    virtual int Compare(uint32, uint32, uint32) const;
};

int TMyStringComparator::Compare(uint32 item1, uint32 item2, uint32) const
{
  TListWindItem lwItem1(*List, item1, Col);
  TListWindItem lwItem2(*List, item2, Col);
  int val = strcmp(lwItem1.GetText(), lwItem2.GetText());
  return (Up ? val : -val);
}


//
// Our comparator class for sorting integers
//
class TMyIntComparator : public TMyComparator
{
  public:
    TMyIntComparator(TListWindow* l, int c, bool u)
      : TMyComparator(l, c, u) {}
    virtual int Compare(uint32, uint32, uint32) const;
};

int TMyIntComparator::Compare(uint32 item1, uint32 item2, uint32) const
{
  TListWindItem lwItem1(*List, item1, Col);
  TListWindItem lwItem2(*List, item2, Col);
  int val = atoi(lwItem1.GetText()) - atoi(lwItem2.GetText());
  return (Up ? val : -val);
}

To invoke the sort, we respond to a column click event. Since the sorting function is passed the 32 bit item data rather than the index of the item, in this example we set the data for each item to be the index (a sort of cheat?) to make things easier.

DEFINE_RESPONSE_TABLE1(TLWWindow, TWindow)
  // ...
  EV_LVN_COLUMNCLICK(kListId, ListColumnClick),
  // ...
END_RESPONSE_TABLE;

void TLWWindow::ListColumnClick(TLwNotify& notify)
{
  // check if this is the same column as we sorted last
  // time, and if so change the direction of the sort
  //
  SortUp = (notify.iSubItem == Column ? !SortUp : true);
  Column = notify.iSubItem;

  // set all itemdatas for the first column to be their index
  //
  for (int i = 0, imax = List->GetItemCount(); i < imax; i++)
  {
    TListWindItem item(*List, i, 0);
    item.SetItemData(i);
    List->SetItem(item);
  }
    
  // sort the items in the list (use int comparator for the
  // size column, string comparator for all others
  //
  if (Column == 1)
    List->SortItems(TMyIntComparator(List, Column, SortUp));
  else
    List->SortItems(TMyStringComparator(List, Column, SortUp));
}

Showing Different Views

A nice feature of the list window is the ability to display data in a range of different formats: large icons, small icons, list and report. To allow the user to customise this in your application, add the appropriate menu, and then add in the following code (note the modifications to the constructor):

DEFINE_RESPONSE_TABLE1(TLWWindow, TWindow)
  // ...
  EV_COMMAND_AND_ID(CM_VIEWLARGEICONS, CmViewChange),
  EV_COMMAND_AND_ID(CM_VIEWSMALLICONS, CmViewChange),
  EV_COMMAND_AND_ID(CM_VIEWLIST, CmViewChange),
  EV_COMMAND_AND_ID(CM_VIEWDETAILS, CmViewChange),
  EV_COMMAND_ENABLE(CM_VIEWLARGEICONS, CeViewChange),
  EV_COMMAND_ENABLE(CM_VIEWSMALLICONS, CeViewChange),
  EV_COMMAND_ENABLE(CM_VIEWLIST, CeViewChange),
  EV_COMMAND_ENABLE(CM_VIEWDETAILS, CeViewChange),
  // ...
END_RESPONSE_TABLE;

TLWWindow::TLWWindow(TWindow* parent,
                     const char far* title,
                     TModule* module)
:
  TWindow(parent, title, module),
  ViewBy(LVS_REPORT),
  // ...
{
  // create the list window (the size is unimportant - see EvSize)
  //
  List = new TListWindow(this, kListId, 0000, module);
  List->Attr.Style |= ViewBy;

  // ...
}

//
// change how the items in the list are viewed
//
void TLWWindow::CmViewChange(WPARAM id)
{
  int newViewBy;
  switch (id)
  {
    case CM_VIEWLARGEICONS: newViewBy = LVS_ICON; break;
    case CM_VIEWSMALLICONS: newViewBy = LVS_SMALLICON; break;
    case CM_VIEWLIST:       newViewBy = LVS_LIST; break;
    case CM_VIEWDETAILS:    newViewBy = LVS_REPORT; break;
  }
  List->ModifyStyle(ViewBy, newViewBy);
  ViewBy = newViewBy;
}

//
// determine if the menu item should be checked to reflect
// how the items in the list are being viewed
//
void TLWWindow::CeViewChange(TCommandEnabler &tce)
{
  bool check;
  switch (tce.GetId())
  {
    case CM_VIEWLARGEICONS: check = (ViewBy == LVS_ICON); break;
    case CM_VIEWSMALLICONS: check = (ViewBy == LVS_SMALLICON); break;
    case CM_VIEWLIST:       check = (ViewBy == LVS_LIST); break;
    case CM_VIEWDETAILS:    check = (ViewBy == LVS_REPORT); break;
  }
  tce.SetCheck(check);
}

Conclusion

That's it! We now have a fully working (although simple) directory browser that makes good use of the features of TListWindow. Enjoy!

Previous Page
Start Page