Files
haiku-beta6/src/kits/interface/OutlineListView.cpp
T
Axel Dörfler 7c74b12b2a * Implemented BOutlineListView::FullListSortItems() and SortItemsUnder() - dunno
what Be's implementation did wrong but instead of taking almost 30 seconds to
  sort the MIME type database (roughly 1100 entries, and yes, that's why the
  original FileTypes is that slow when it has to show the internal types), this
  one needs only 4 ms for the same task (that's an amply 7500x speedup).
* Implemented some more missing functions - it's by no means complete yet, though.
* Rewrote OutlineListView.h, ListItem.h, and StringItem.h.
* Fixed some minor bugs, but there are probably a lot more.
* Major cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16349 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-02-11 15:23:23 +00:00

939 lines
17 KiB
C++

/*
* Copyright 2001-2006, Haiku Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Marc Flerackers ([email protected])
* Axel Dörfler, [email protected]
*/
//! BOutlineListView represents a "nestable" list view.
#include <OutlineListView.h>
#include <stdio.h>
#include <stdlib.h>
/*!
\brief A simple recursive quick sort implementations for BListItem arrays.
We need to implement it ourselves, since the BOutlineListView sort methods
use a different comparison function than the standard functions.
*/
static void
quick_sort_item_array(BListItem** items, int32 first, int32 last,
int (*compareFunc)(const BListItem* a, const BListItem* b))
{
if (last - 1 <= first)
return;
BListItem* pivot = items[first + (rand() % (last - first))];
// choose an arbitrary pivot element
int32 left = first;
int32 right = last;
do {
while (compareFunc(items[left], pivot) < 0) {
left++;
}
while (compareFunc(items[right], pivot) > 0) {
right--;
}
if (left <= right) {
// swap entries
BListItem* temp = items[left];
items[left] = items[right];
items[right] = temp;
left++;
right--;
}
} while (left <= right);
// At this point, the elements in the left half are all smaller
// than the elements in the right half
if (first < right) {
// sort left half
quick_sort_item_array(items, first, right, compareFunc);
}
if (left < last) {
// sort right half
quick_sort_item_array(items, left, last, compareFunc);
}
}
// #pragma mark -
BOutlineListView::BOutlineListView(BRect frame, const char* name,
list_view_type type, uint32 resizeMode, uint32 flags)
: BListView(frame, name, type, resizeMode, flags)
{
}
BOutlineListView::BOutlineListView(BMessage* archive)
: BListView(archive)
{
}
BOutlineListView::~BOutlineListView()
{
fFullList.MakeEmpty();
}
BArchivable *
BOutlineListView::Instantiate(BMessage* archive)
{
if (validate_instantiation(archive, "BOutlineListView"))
return new BOutlineListView(archive);
return NULL;
}
status_t
BOutlineListView::Archive(BMessage* archive, bool deep) const
{
return BListView::Archive(archive, deep);
}
void
BOutlineListView::MouseDown(BPoint point)
{
MakeFocus();
int32 index = IndexOf(point);
if (index != -1) {
BListItem *item = ItemAt(index);
if (item->fHasSubitems
&& LatchRect(ItemFrame(index), item->fLevel).Contains(point)) {
if (item->IsExpanded())
Collapse(item);
else
Expand(item);
} else
BListView::MouseDown(point);
}
}
void
BOutlineListView::KeyDown(const char* bytes, int32 numBytes)
{
if (numBytes == 1) {
switch (bytes[0]) {
case B_RIGHT_ARROW:
{
BListItem *item = ItemAt(CurrentSelection());
if (item && item->fHasSubitems)
Expand(item);
return;
}
case B_LEFT_ARROW:
{
BListItem *item = ItemAt(CurrentSelection());
if (item && item->fHasSubitems)
Collapse(item);
return;
}
}
}
BListView::KeyDown(bytes, numBytes);
}
void
BOutlineListView::FrameMoved(BPoint newPosition)
{
BListView::FrameMoved(newPosition);
}
void
BOutlineListView::FrameResized(float newWidth, float newHeight)
{
BListView::FrameResized(newWidth, newHeight);
}
void
BOutlineListView::MouseUp(BPoint where)
{
BListView::MouseUp(where);
}
bool
BOutlineListView::AddUnder(BListItem* item, BListItem* superitem)
{
fFullList.AddItem(item, FullListIndexOf(superitem) + 1);
item->fLevel = superitem->OutlineLevel() + 1;
superitem->fHasSubitems = true;
if (superitem->IsItemVisible() && superitem->IsExpanded()) {
item->SetItemVisible(true);
int32 index = BListView::IndexOf(superitem);
BListView::AddItem(item, index + 1);
Invalidate(LatchRect(ItemFrame(index), superitem->OutlineLevel()));
} else
item->SetItemVisible(false);
return true;
}
bool
BOutlineListView::AddItem(BListItem* item)
{
if (!fFullList.AddItem(item))
return false;
return BListView::AddItem(item);
}
bool
BOutlineListView::AddItem(BListItem* item, int32 fullListIndex)
{
if (fullListIndex < 0)
fullListIndex = 0;
else if (fullListIndex > CountItems())
fullListIndex = CountItems();
fFullList.AddItem(item, fullListIndex);
if (item->fLevel > 0) {
BListItem *super = SuperitemForIndex(fullListIndex, item->fLevel);
if (!super->IsItemVisible() || !super->IsExpanded())
return true;
}
int32 list_index = FindPreviousVisibleIndex(fullListIndex);
return BListView::AddItem(item, IndexOf(FullListItemAt(list_index)) + 1);
}
bool
BOutlineListView::AddList(BList* newItems)
{
printf("BOutlineListView::AddList Not implemented\n");
return false;
}
bool
BOutlineListView::AddList(BList* newItems, int32 fullListIndex)
{
printf("BOutlineListView::AddList Not implemented\n");
return false;
}
bool
BOutlineListView::RemoveItem(BListItem* item)
{
if (!FullListHasItem(item))
return false;
fFullList.RemoveItem(item);
if (item->fVisible)
BListView::RemoveItem(item);
// TODO: remove children
return true;
}
BListItem*
BOutlineListView::RemoveItem(int32 fullListIndex)
{
BListItem* item = FullListItemAt(fullListIndex);
if (item == NULL)
return NULL;
fFullList.RemoveItem(fullListIndex);
if (item->fVisible)
BListView::RemoveItem(item);
// TODO: remove children
return item;
}
bool
BOutlineListView::RemoveItems(int32 fullListIndex, int32 count)
{
printf("BOutlineListView::RemoveItems Not implemented\n");
return false;
}
BListItem *
BOutlineListView::FullListItemAt(int32 fullListIndex) const
{
return (BListItem*)fFullList.ItemAt(fullListIndex);
}
int32
BOutlineListView::FullListIndexOf(BPoint point) const
{
return BListView::IndexOf(point);
}
int32
BOutlineListView::FullListIndexOf(BListItem* item) const
{
return fFullList.IndexOf(item);
}
BListItem *
BOutlineListView::FullListFirstItem() const
{
return (BListItem*)fFullList.FirstItem();
}
BListItem *
BOutlineListView::FullListLastItem() const
{
return (BListItem*)fFullList.LastItem();
}
bool
BOutlineListView::FullListHasItem(BListItem *item) const
{
return fFullList.HasItem(item);
}
int32
BOutlineListView::FullListCountItems() const
{
return fFullList.CountItems();
}
int32
BOutlineListView::FullListCurrentSelection(int32 index) const
{
int32 i = BListView::CurrentSelection(index);
BListItem *item = BListView::ItemAt(i);
if (item)
return fFullList.IndexOf(item);
return -1;
}
void
BOutlineListView::MakeEmpty()
{
fFullList.MakeEmpty();
BListView::MakeEmpty();
}
bool
BOutlineListView::FullListIsEmpty() const
{
return fFullList.IsEmpty();
}
void
BOutlineListView::FullListDoForEach(bool(*func)(BListItem* item))
{
fFullList.DoForEach(reinterpret_cast<bool (*)(void*)>(func));
}
void
BOutlineListView::FullListDoForEach(bool (*func)(BListItem* item, void* arg),
void* arg)
{
fList.DoForEach(reinterpret_cast<bool (*)(void*, void*)>(func), arg);
}
BListItem *
BOutlineListView::Superitem(const BListItem* item)
{
int32 index = FullListIndexOf((BListItem*)item);
if (index == -1)
return NULL;
return SuperitemForIndex(index, item->OutlineLevel());
}
void
BOutlineListView::Expand(BListItem* item)
{
if (item->IsExpanded() || !FullListHasItem(item))
return;
item->fExpanded = true;
uint32 level = item->fLevel;
uint32 fullIndex = FullListIndexOf(item);
uint32 index = IndexOf(item) + 1;
uint32 count = FullListCountItems() - fullIndex - 1;
BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
BFont font;
GetFont(&font);
while (count-- > 0) {
item = items[0];
if (item->fLevel <= level)
break;
if (!item->IsItemVisible()) {
fList.AddItem(item, index++);
item->Update(this, &font);
item->SetItemVisible(true);
}
if (item->HasSubitems() && !item->IsExpanded()) {
// Skip hidden children
uint32 subLevel = item->fLevel;
items++;
while (--count > 0 && items[0]->fLevel > subLevel)
items++;
} else
items++;
}
_FixupScrollBar();
Invalidate();
}
void
BOutlineListView::Collapse(BListItem* item)
{
if (!item->IsExpanded() || !FullListHasItem(item))
return;
item->fExpanded = false;
uint32 level = item->fLevel;
uint32 fullIndex = FullListIndexOf(item);
uint32 count = FullListCountItems() - fullIndex - 1;
BListItem** items = (BListItem**)fFullList.Items() + fullIndex + 1;
while (count-- > 0) {
item = items[0];
if (item->fLevel <= level)
break;
if (item->IsItemVisible()) {
fList.RemoveItem(item);
item->SetItemVisible(false);
}
items++;
}
_FixupScrollBar();
Invalidate();
}
bool
BOutlineListView::IsExpanded(int32 fullListIndex)
{
BListItem *item = FullListItemAt(fullListIndex);
if (!item)
return false;
return item->IsExpanded();
}
BHandler *
BOutlineListView::ResolveSpecifier(BMessage* msg, int32 index,
BMessage* specifier, int32 what, const char* property)
{
return BListView::ResolveSpecifier(msg, index, specifier, what, property);
}
status_t
BOutlineListView::GetSupportedSuites(BMessage* data)
{
return BListView::GetSupportedSuites(data);
}
status_t
BOutlineListView::Perform(perform_code d, void* arg)
{
return BListView::Perform(d, arg);
}
void
BOutlineListView::ResizeToPreferred()
{
BListView::ResizeToPreferred();
}
void
BOutlineListView::GetPreferredSize(float* _width, float* _height)
{
BListView::GetPreferredSize(_width, _height);
}
void
BOutlineListView::MakeFocus(bool state)
{
BListView::MakeFocus(state);
}
void
BOutlineListView::AllAttached()
{
BListView::AllAttached();
}
void
BOutlineListView::AllDetached()
{
BListView::AllDetached();
}
void
BOutlineListView::DetachedFromWindow()
{
BListView::DetachedFromWindow();
}
void
BOutlineListView::FullListSortItems(int (*compareFunc)(const BListItem* a,
const BListItem* b))
{
SortItemsUnder(NULL, false, compareFunc);
}
void
BOutlineListView::SortItemsUnder(BListItem* underItem, bool oneLevelOnly,
int (*compareFunc)(const BListItem* a, const BListItem* b))
{
// This method is quite complicated: basically, it creates a real tree
// from the items of the full list, sorts them as needed, and then
// populates the entries back into the full and display lists
int32 firstIndex = FullListIndexOf(underItem) + 1;
int32 lastIndex = firstIndex;
BList* tree = _BuildTree(underItem, lastIndex);
_SortTree(tree, oneLevelOnly, compareFunc);
// Populate to the full list
_PopulateTree(tree, fFullList, firstIndex, false);
// Populate to BListView's list
firstIndex = fList.IndexOf(underItem) + 1;
_PopulateTree(tree, fList, firstIndex, true);
_DestructTree(tree);
}
void
BOutlineListView::_PopulateTree(BList* tree, BList& target,
int32& firstIndex, bool onlyVisible)
{
BListItem** items = (BListItem**)target.Items();
int32 count = tree->CountItems();
for (int32 index = 0; index < count; index++) {
BListItem* item = (BListItem*)tree->ItemAtFast(index);
items[firstIndex++] = item;
if (item->HasSubitems() && (!onlyVisible || item->IsExpanded()))
_PopulateTree(item->fTemporaryList, target, firstIndex, onlyVisible);
}
}
void
BOutlineListView::_SortTree(BList* tree, bool oneLevelOnly,
int (*compareFunc)(const BListItem* a, const BListItem* b))
{
// home-brewn quick sort for our compareFunc
quick_sort_item_array((BListItem**)tree->Items(), 0, tree->CountItems() - 1,
compareFunc);
if (oneLevelOnly)
return;
for (int32 index = tree->CountItems(); index-- > 0;) {
BListItem* item = (BListItem*)tree->ItemAt(index);
if (item->HasSubitems())
_SortTree(item->fTemporaryList, false, compareFunc);
}
}
void
BOutlineListView::_DestructTree(BList* tree)
{
for (int32 index = tree->CountItems(); index-- > 0;) {
BListItem* item = (BListItem*)tree->ItemAt(index);
if (item->HasSubitems())
_DestructTree(item->fTemporaryList);
}
delete tree;
}
BList*
BOutlineListView::_BuildTree(BListItem* underItem, int32& fullIndex)
{
int32 fullCount = FullListCountItems();
uint32 level = underItem != NULL ? underItem->OutlineLevel() + 1 : 0;
BList* list = new BList;
if (underItem != NULL)
underItem->fTemporaryList = list;
while (fullIndex < fullCount) {
BListItem *item = FullListItemAt(fullIndex);
// If we jump out of the subtree, break out
if (item->fLevel < level)
break;
if (item->fLevel == level) {
// If the level matches, put them into the list
list->AddItem(item);
}
fullIndex++;
if (item->HasSubitems()) {
// we're going deeper
_BuildTree(item, fullIndex);
}
}
return list;
}
int32
BOutlineListView::CountItemsUnder(BListItem* underItem,
bool oneLevelOnly) const
{
int32 i = IndexOf(underItem);
if (i == -1)
return 0;
int32 count = 0;
while (i < FullListCountItems()) {
BListItem *item = FullListItemAt(i);
// If we jump out of the subtree, return count
if (item->fLevel < underItem->OutlineLevel())
return count;
// If the level matches, increase count
if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1)
count++;
i++;
}
return count;
}
BListItem *
BOutlineListView::EachItemUnder(BListItem *underItem, bool oneLevelOnly,
BListItem *(*eachFunc)(BListItem* item, void* arg), void* arg)
{
int32 i = IndexOf(underItem);
if (i == -1)
return NULL;
while (i < FullListCountItems()) {
BListItem *item = FullListItemAt(i);
// If we jump out of the subtree, return NULL
if (item->fLevel < underItem->OutlineLevel())
return NULL;
// If the level matches, check the index
if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) {
item = eachFunc(item, arg);
if (item != NULL)
return item;
}
i++;
}
return NULL;
}
BListItem *
BOutlineListView::ItemUnderAt(BListItem* underItem,
bool oneLevelOnly, int32 index) const
{
int32 i = IndexOf(underItem);
if (i == -1)
return NULL;
while (i < FullListCountItems()) {
BListItem *item = FullListItemAt(i);
// If we jump out of the subtree, return NULL
if (item->fLevel < underItem->OutlineLevel())
return NULL;
// If the level matches, check the index
if (!oneLevelOnly || item->fLevel == underItem->OutlineLevel() + 1) {
if (index == 0)
return item;
index--;
}
i++;
}
return NULL;
}
bool
BOutlineListView::DoMiscellaneous(MiscCode code, MiscData* data)
{
return BListView::DoMiscellaneous(code, data);
}
void
BOutlineListView::MessageReceived(BMessage* msg)
{
BListView::MessageReceived(msg);
}
void BOutlineListView::_ReservedOutlineListView1() {}
void BOutlineListView::_ReservedOutlineListView2() {}
void BOutlineListView::_ReservedOutlineListView3() {}
void BOutlineListView::_ReservedOutlineListView4() {}
int32
BOutlineListView::FullListIndex(int32 index) const
{
BListItem *item = ItemAt(index);
if (item == NULL)
return -1;
return FullListIndexOf(item);
}
int32
BOutlineListView::ListViewIndex(int32 index) const
{
BListItem *item = ItemAt(index);
if (item == NULL)
return -1;
return BListView::IndexOf(item);
}
void
BOutlineListView::ExpandOrCollapse(BListItem *underItem, bool expand)
{
}
BRect
BOutlineListView::LatchRect(BRect itemRect, int32 level) const
{
return BRect(itemRect.left, itemRect.top, itemRect.left
+ (level * 10.0f + 15.0f), itemRect.bottom);
}
void
BOutlineListView::DrawLatch(BRect itemRect, int32 level, bool collapsed,
bool highlighted, bool misTracked)
{
float left = level * 10.0f;
if (collapsed) {
SetHighColor(192, 192, 192);
FillTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
SetHighColor(0, 0, 0);
StrokeTriangle(itemRect.LeftTop() + BPoint(left + 4.0f, 2.0f),
itemRect.LeftTop() + BPoint(left + 4.0f, 10.0f),
itemRect.LeftTop() + BPoint(left + 8.0f, 6.0f));
} else {
SetHighColor(192, 192, 192);
FillTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
SetHighColor(0, 0, 0);
StrokeTriangle(itemRect.LeftTop() + BPoint(left + 2.0f, 4.0f),
itemRect.LeftTop() + BPoint(left + 10.0f, 4.0f),
itemRect.LeftTop() + BPoint(left + 6.0f, 8.0f));
}
}
void
BOutlineListView::DrawItem(BListItem* item, BRect itemRect, bool complete)
{
if (item->fHasSubitems)
DrawLatch(itemRect, item->fLevel, !item->IsExpanded(), false, false);
itemRect.left += (item->fLevel) * 10.0f + 15.0f;
item->DrawItem(this, itemRect, complete);
}
BListItem *
BOutlineListView::RemoveCommon(int32 fullListIndex)
{
return NULL;
}
BListItem *
BOutlineListView::RemoveOne(int32 fullListIndex)
{
return NULL;
}
void
BOutlineListView::TrackInLatchItem(void *)
{
}
void
BOutlineListView::TrackOutLatchItem(void *)
{
}
bool
BOutlineListView::OutlineSwapItems(int32 a, int32 b)
{
return false;
}
bool
BOutlineListView::OutlineMoveItem(int32 from, int32 to)
{
return false;
}
bool
BOutlineListView::OutlineReplaceItem(int32 index, BListItem *item)
{
return false;
}
void
BOutlineListView::CommonMoveItems(int32 from, int32 count, int32 to)
{
}
BListItem *
BOutlineListView::SuperitemForIndex(int32 fullListIndex, int32 level)
{
BListItem *item;
fullListIndex--;
while (fullListIndex >= 0) {
if ((item = FullListItemAt(fullListIndex))->OutlineLevel() <
(uint32)level)
return item;
fullListIndex--;
}
return NULL;
}
int32
BOutlineListView::FindPreviousVisibleIndex(int32 fullListIndex)
{
fullListIndex--;
while (fullListIndex >= 0) {
if (FullListItemAt(fullListIndex)->fVisible)
return fullListIndex;
fullListIndex--;
}
return -1;
}