Virtual listview this might help


2008-01-16 06:23:52 PM
cppbuilder109
Hi,
Recently I had to change all listviews in my app to make them virtual.
I finally developped a "feeder" which does what has to be done for
virtual list view to work properly and also handles different kind of
sorts and some custom drawing related to sorts.
As I had to face some problem that seem common, ans as I get lot of
helps from guys of this NG, I wanted to give back this "feeder" which
might help.
Note: I'm not a C++ guru but I try to make things clean
Here is a piece of code that show how to use the "feeder"
lvList is a TListView declared elsewhere
// create a feeder
ClBaseVirtualListViewFeed<YourDataClass>VLVProxy;
// connect it to LV
VLVProxy.connectTo(lvList);
// provide callback for columns' content and columns' sort
VLVProxy.SmColumnDefinition.push_back(col1_def);
VLVProxy.SmColumnSort.push_back(col1_sort);
VLVProxy.SmColumnDefinition.push_back(col2_def);
VLVProxy.SmColumnSort.push_back(col2_sort);
...
Columns are not created automatically to make it work with dynamically
created LV (in code) and LV created graphically with BCB.
LV is suppose to be in report mode.
following ClBaseVirtualListViewFeed.h
#ifndef __ClBaseVirtualListViewFeed_H__
#define __ClBaseVirtualListViewFeed_H__
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
// BEGIN OF USER LICENSE TERMS.
// You must not alter or remove the user license terms.
// You are not allowed to bring any kind of restriction to this user
licence.
// Copyright Eric Britz (I keep ownership and all rights).
// Provided "as is" without express or implied warranty nor support.
// Free of use for any kind of application (use it if you want don't ask).
//
// If you are fair, I suppose you will share any improvement or
correction you might make.
// If you are not fair, well, do not worry there are a lot of people
like you in the world
// you may still find friends.
// END OF USER LICENSE TERMS
//------------------------------------------------------------------------------
template <class _data_class_>class ClBaseVirtualListViewFeed;
//------------------------------------------------------------------------------
// see use in void
ClBaseVirtualListViewFeed::sort(std::vector<std::string>& regexps)
template <class _data_class_>
struct tRegexpSortFunctor
{
static std::vector<std::string>* regexps;
static
std::vector<ClBaseVirtualListViewFeed<_data_class_>::tSmColumnDefinitionMth>
* coldefs;
static int __stdcall order(
ClBaseVirtualListViewFeed<_data_class_>::tUserDataRef o1
, ClBaseVirtualListViewFeed<_data_class_>::tUserDataRef o2
)
{
if (!o1 || !o2)
{
return memcmp((void*)o1,(void*)o2,sizeof(ClNodeObject*)) ;
}
std::vector<std::string>str1;
std::vector<std::string>str2;
// get columns content for each object
for
(std::vector<ClBaseVirtualListViewFeed<_data_class_>::tSmColumnDefinitionMth>::iterator
col = coldefs->begin()
; col != coldefs->end()
; ++col)
{
std::string t1 = (*col)(o1);
std::string t2 = (*col)(o2);
str1.push_back( t1 );
str2.push_back( t2 );
}
// apply filter
int r1 = 0;
int r2 = 0;
for ( std::vector<std::string>::iterator reg = regexps->begin(),
st1 = str1.begin(), st2 = str2.begin()
; (reg != regexps->end()) && (st1 != str1.end()) && (st2 != str2.end())
; ++reg, ++st1, ++st2)
{
if ( !reg->size() ) continue;
if ( ClRegexp::contains(
st1->c_str()
, reg->c_str()
, true //ignoreCase
)
)
++ r1;
if ( ClRegexp::contains(
st2->c_str()
, reg->c_str()
, true //ignoreCase
)
)
++ r2;
}
return r2 - r1; // les items qui matchent en début de liste
}
}
;
//------------------------------------------------------------------------------
template <class _data_class_>
class ClBaseVirtualListViewFeed
{
// --
//
// -- Attribut
//
// --
protected:
TListView * FListView;
std::vector<_data_class_>& FData;
void __fastcall (__closure * FOldColumnClick)(
System::TObject* Sender
, TListColumn* c
);
void __fastcall (__closure * FOldCustomDrawSubItem)(
TCustomListView* Sender
, TListItem* Item
, int SubItem
, TCustomDrawState State
, bool &DefaultDraw
);
void __fastcall (__closure * FOldCustomDrawItem)(
TCustomListView* Sender
, TListItem* Item
, TCustomDrawState State
, bool &DefaultDraw
);
private:
int FCurrentSort;
std::vector<std::string>FColumnsFilters;
public:
// Typedefs for external use
typedef _data_class_ & tUserDataRef;
// Image
static TImageList * ImageList;
typedef int (*tSmImageIndexMth)(
tUserDataRef
) ;
static tSmImageIndexMth SmImageIndex;
// Columns definition
typedef std::string (*tSmColumnDefinitionMth)(
tUserDataRef
) ;
std::vector<tSmColumnDefinitionMth>SmColumnDefinition;
// Columns sort
typedef int (__stdcall * tSmColumnSortMth)(
tUserDataRef o1
, tUserDataRef o2
) ;
std::vector<tSmColumnSortMth>SmColumnSort;
tSmColumnSortMth SmDefaultSort;
// Hint
static tSmColumnDefinitionMth SmInfoTypeDefinition;
// Sort sequence
struct tSortSequence
{
std::vector<ClBaseVirtualListViewFeed<_data_class_>::tSmColumnSortMth>
criterias;
bool operator()(
ClBaseVirtualListViewFeed<_data_class_>::tUserDataRef o1
, ClBaseVirtualListViewFeed<_data_class_>::tUserDataRef o2
)
{
for
(std::vector<ClBaseVirtualListViewFeed<_data_class_>::tSmColumnSortMth>::iterator
mth = criterias.begin()
; mth != criterias.end()
; ++ mth)
{
int res = (*mth)(o1,o2) ;
if ( res < 0 ) return true;
if ( res>0 ) return false;
// continue if ==
}
return memcmp((void*)o1,(void*)o2,sizeof(_data_class_)) < 0; //
pour etre sur d'avoir un < strict
}
} ;
// --
//
// -- Constructor / destructors
//
// --
public:
// -----------------------------------------------------
// Constructeur
ClBaseVirtualListViewFeed(std::vector<_data_class_>& data)
: FData(data)
, FListView(0)
, FCurrentSort(-1)
, FOldColumnClick(0)
, FOldCustomDrawItem(0)
, FOldCustomDrawSubItem(0)
, SmDefaultSort(0)
{};
// -----------------------------------------------------
// Destructeur
~ClBaseVirtualListViewFeed(void)
{
if ( FListView )
{
FListView->OnData = 0;
FListView->OnInfoTip = 0;
FListView->OnColumnClick = 0;
}
};
// --
//
// -- Methods
//
// --
private:
// -----------------------------------------------------
static int noImage(tUserDataRef)
{ return -1; };
public:
// -----------------------------------------------------
// Listview event to handle for virtual list view
// -----------------------------------------------------
void __fastcall lvData(
TObject *Sender
,TListItem *Item
)
{
// must sets the items characteristics according to the data
(caption, subitems, imageindex, ...)
const int idx = Item->Index;
// retrieve associated object
tUserDataRef data = *(FData.begin() + idx);
// reset item informations
{
Item->Caption = " ---- ";
Item->SubItems->Clear();
}
// set image index
Item->ImageIndex = (
SmImageIndex
? SmImageIndex(data)
: -1
);
// set caption and sub items
int nbCol = SmColumnDefinition.size();
if ( nbCol>0 )
{
// caption
std::vector<tSmColumnDefinitionMth>::iterator curs
= SmColumnDefinition.begin();
Item->Caption = (*curs)(data).c_str();
// sub items
Item->SubItems->Clear();
for ( ++curs
; curs != SmColumnDefinition.end()
; ++curs
)
{
Item->SubItems->AddObject(
(*curs)(data).c_str()
, 0
);
}
}
};
void __fastcall lvInfoTip(
System::TObject* Sender
, TListItem* Item
, AnsiString &InfoTip
)
{
// must provide individual item's hint
const int idx = Item->Index;
// retrieve associated object
tUserDataRef data = *(FData.begin() + idx);
InfoTip = (
SmInfoTypeDefinition
? SmInfoTypeDefinition(data).c_str()
: ""
);
};
// -----------------------------------------------------
// Listview event to handle column sort
// -----------------------------------------------------
void __fastcall lvColumnClick(
System::TObject* Sender
, TListColumn* c
)
{
if ( FOldColumnClick )
FOldColumnClick (
Sender
, c
);
if ( FListView->ComponentState.Contains(csDestroying) )
return ;
TCursor prev_cursor = Screen->Cursor;
Screen->Cursor = crHourGlass;
try {
// must sort local data and invalidate listview to force redrawing
if ( c->Index < (int)SmColumnSort.size() )
{
// remember column index for later sort
FCurrentSort = c->Index;
// sort data
tSortSequence sort_sequence ;
sort_sequence.criterias.clear();
if ( SmDefaultSort )
sort_sequence.criterias.push_back(SmDefaultSort);
std::vector<tSmColumnSortMth>::iterator colsort =
SmColumnSort.begin()+FCurrentSort;
for ( std::vector<tSmColumnSortMth>::iterator colsort =
SmColumnSort.begin()+FCurrentSort
; colsort != SmColumnSort.end()
; ++ colsort)
{
sort_sequence.criterias.push_back(*colsort);
}
for ( std::vector<tSmColumnSortMth>::iterator colsort =
SmColumnSort.begin(), end = SmColumnSort.begin()+FCurrentSort
; colsort != end
; ++ colsort)
{
sort_sequence.criterias.push_back(*colsort);
}
std::sort(
FData.begin()
, FData.end()
, sort_sequence
);
// invalidate list view to force redraw
updateList();
}
} __finally { Screen->Cursor = prev_cursor; }
};
// -----------------------------------------------------
// Listview event to customize items' drawing
// -----------------------------------------------------
void __fastcall lvCustomDrawSubItem(
TCustomListView* Sender
, TListItem* Item
, int SubItem
, TCustomDrawState State
, bool &DefaultDraw
)
{
if ( FOldCustomDrawSubItem )
FOldCustomDrawSubItem (
Sender
, Item
, SubItem
, State
, DefaultDraw
);
Sender->Canvas->Brush->Style = bsClear;
const int col_idx = SubItem;
// if currently sorting from column val
if ( col_idx == FCurrentSort )
{
Sender->Canvas->Brush->Style = bsSolid;
Sender->Canvas->Brush->Color = (TColor)RGB(254,255,183);
}
else
{
Sender->Canvas->Brush->Style = bsClear;
}
// if currently sorting from list af regexps
if ( (FCurrentSort == - 2) && (FColumnsFilters[col_idx].size()>
0) ) //regexp filter
{
std::string val = SmColumnDefinition[col_idx](*(FData.begin() +
Item->Index));
if ( ClRegexp::contains(
val.c_str()
, FColumnsFilters[col_idx].c_str()
, true //ignoreCase
)
)
{
Sender->Canvas->Brush->Style = bsSolid;
Sender->Canvas->Brush->Color = (TColor)RGB(254,255,183);
}
else
{
Sender->Canvas->Brush->Style = bsClear;
}
}
};
void __fastcall lvCustomDrawItem(
TCustomListView* Sender
, TListItem* Item
, TCustomDrawState State
, bool &DefaultDraw
)
{
if ( FOldCustomDrawItem )
FOldCustomDrawItem (
Sender
, Item
, State
, DefaultDraw
);
Sender->Canvas->Brush->Style = bsClear;
// if currently sorting from 1st column val
if ( FCurrentSort == 0 )
{
Sender->Canvas->Brush->Style = bsSolid;
Sender->Canvas->Brush->Color = (TColor)RGB(254,255,183);
}
else
{
Sender->Canvas->Brush->Style = bsClear;
}
// if currently sorting from list of regexps
if ( (FCurrentSort == - 2) && (FColumnsFilters[0].size()>0) )
//regexp filter
{
std::string val = SmColumnDefinition[0](*(FData.begin() +
Item->Index));
if ( ClRegexp::contains(
val.c_str()
, FColumnsFilters[0].c_str()
, true //ignoreCase
)
)
{
Sender->Canvas->Brush->Style = bsSolid;
Sender->Canvas->Brush->Color = (TColor)RGB(254,255,183);
}
else
{
Sender->Canvas->Brush->Style = bsClear;
}
}
};
public:
void connectTo(TListView * lv)
{
if ( FListView ) return;
FListView = lv;
FListView->Items->Clear();
FListView->SmallImages = ImageList;
FListView->OwnerData = true;
FListView->OnData = &lvData;
FListView->OnInfoTip = &lvInfoTip;
FOldColumnClick = FListView->OnColumnClick;
FListView->OnColumnClick = &lvColumnClick;
FOldCustomDrawSubItem = FListView->OnCustomDrawSubItem ;
FListView->OnCustomDrawSubItem = &lvCustomDrawSubItem;
FOldCustomDrawItem = FListView->OnCustomDrawItem ;
FListView->OnCustomDrawItem = &lvCustomDrawItem;
};
void updateList(void) const
{
if ( !FListView ) return;
FListView->Items->Count = FData.size();
FListView->Invalidate();
};
void sort(std::vector<std::string>& regexps)
{
FColumnsFilters.clear();
FColumnsFilters = regexps;
// sort data
tRegexpSortFunctor<_data_class_>::regexps = &FColumnsFilters;
tRegexpSortFunctor<_data_class_>::coldefs = &SmColumnDefinition;
tSortSequence sort_sequence ;
sort_sequence.criterias.clear();
if ( SmDefaultSort )
sort_sequence.criterias.push_back(SmDefaultSort);
sort_sequence.criterias.push_back(tRegexpSortFunctor<_data_class_>::order);
FCurrentSort = -2;
std::sort(
FData.begin()
, FData.end()
, sort_sequence
);
// invalidate list view to force redraw
updateList();
};
};
//------------------------------------------------------------------------------
#ifndef ClBaseVirtualListViewFeed_statics
#define ClBaseVirtualListViewFeed_statics
template <class _data_class_>
TImageList *
ClBaseVirtualListViewFeed<_data_class_>::ImageList = 0;
template <class _data_class_>
ClBaseVirtualListViewFeed<_data_class_>::tSmImageIndexMth
ClBaseVirtualListViewFeed<_data_class_>::SmImageIndex
= 0;
template <class _data_class_>
std::vector<std::string>*
tRegexpSortFunctor<_data_class_>::regexps
= 0;
template <class _data_class_>
std::vector<ClBaseVirtualListViewFeed<_data_class_>::tSmColumnDefinitionMth>
*
tRegexpSortFunctor<_data_class_>::coldefs
= 0;
template <class _data_class_>
ClBaseVirtualListViewFeed<_data_class_>::tSmColumnDefinitionMth
ClBaseVirtualListViewFeed<_data_class_>::SmInfoTypeDefinition = 0;
#endif
#endif