Switch the list to a BColumnListView-based one.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39476 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Philippe Houdoin
2010-11-18 12:17:51 +00:00
parent 8faf0ba7cb
commit 7015777f33
4 changed files with 313 additions and 197 deletions
@@ -9,32 +9,190 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ListView.h>
#include <Bitmap.h> #include <Bitmap.h>
#include <ColumnTypes.h>
#include <FindDirectory.h> #include <FindDirectory.h>
#include <MimeType.h>
#include <MessageRunner.h>
#include <NodeInfo.h> #include <NodeInfo.h>
#include <Path.h> #include <Path.h>
#include <Roster.h> #include <Roster.h>
#include <MimeType.h>
#include <MessageRunner.h>
#include <String.h> #include <String.h>
#include "TeamsListView.h" #include "TeamsListView.h"
TeamListItem::TeamListItem(team_info& info) // #pragma mark - BitmapStringField
BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string)
: :
BStringItem("", false), Inherited(string),
fIcon(NULL) fBitmap(bitmap)
{
}
BBitmapStringField::~BBitmapStringField()
{
delete fBitmap;
}
void
BBitmapStringField::SetBitmap(BBitmap* bitmap)
{
delete fBitmap;
fBitmap = bitmap;
// TODO: cause a redraw?
}
// #pragma mark - TeamsColumn
float TeamsColumn::sTextMargin = 0.0;
TeamsColumn::TeamsColumn(const char* title, float width, float minWidth,
float maxWidth, uint32 truncateMode, alignment align)
:
Inherited(title, width, minWidth, maxWidth, align),
fTruncateMode(truncateMode)
{
SetWantsEvents(true);
}
void
TeamsColumn::DrawField(BField* field, BRect rect, BView* parent)
{
BBitmapStringField* bitmapField
= dynamic_cast<BBitmapStringField*>(field);
BStringField* stringField = dynamic_cast<BStringField*>(field);
if (bitmapField) {
const BBitmap* bitmap = bitmapField->Bitmap();
// figure out the placement
float x = 0.0;
BRect r = bitmap ? bitmap->Bounds() : BRect(0, 0, 15, 15);
float y = rect.top + ((rect.Height() - r.Height()) / 2);
float width = 0.0;
switch (Alignment()) {
default:
case B_ALIGN_LEFT:
case B_ALIGN_CENTER:
x = rect.left + sTextMargin;
width = rect.right - (x + r.Width()) - (2 * sTextMargin);
r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom);
break;
case B_ALIGN_RIGHT:
x = rect.right - sTextMargin - r.Width();
width = (x - rect.left - (2 * sTextMargin));
r.Set(rect.left, rect.top, rect.left + width, rect.bottom);
break;
}
if (width != bitmapField->Width()) {
BString truncatedString(bitmapField->String());
parent->TruncateString(&truncatedString, fTruncateMode, width + 2);
bitmapField->SetClippedString(truncatedString.String());
bitmapField->SetWidth(width);
}
// draw the bitmap
if (bitmap) {
parent->SetDrawingMode(B_OP_ALPHA);
parent->DrawBitmap(bitmap, BPoint(x, y));
parent->SetDrawingMode(B_OP_OVER);
}
// draw the string
DrawString(bitmapField->ClippedString(), parent, r);
} else if (stringField) {
float width = rect.Width() - (2 * sTextMargin);
if (width != stringField->Width()) {
BString truncatedString(stringField->String());
parent->TruncateString(&truncatedString, fTruncateMode, width + 2);
stringField->SetClippedString(truncatedString.String());
stringField->SetWidth(width);
}
DrawString(stringField->ClippedString(), parent, rect);
}
}
float
TeamsColumn::GetPreferredWidth(BField *_field, BView* parent) const
{
BBitmapStringField* bitmapField
= dynamic_cast<BBitmapStringField*>(_field);
BStringField* stringField = dynamic_cast<BStringField*>(_field);
float parentWidth = Inherited::GetPreferredWidth(_field, parent);
float width = 0.0;
if (bitmapField) {
const BBitmap* bitmap = bitmapField->Bitmap();
BFont font;
parent->GetFont(&font);
width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin;
if (bitmap)
width += bitmap->Bounds().Width();
else
width += 16;
} else if (stringField) {
BFont font;
parent->GetFont(&font);
width = font.StringWidth(stringField->String()) + 2 * sTextMargin;
}
return max_c(width, parentWidth);
}
bool
TeamsColumn::AcceptsField(const BField* field) const
{
return dynamic_cast<const BStringField*>(field) != NULL;
}
void
TeamsColumn::InitTextMargin(BView* parent)
{
BFont font;
parent->GetFont(&font);
sTextMargin = ceilf(font.Size() * 0.8);
}
// #pragma mark - TeamRow
enum {
kNameColumn,
kIDColumn,
kThreadCountColumn,
};
TeamRow::TeamRow(team_info& info)
: BRow(20.0)
{ {
_SetTo(info); _SetTo(info);
} }
TeamListItem::TeamListItem(team_id team) TeamRow::TeamRow(team_id team)
: : BRow(20.0)
BStringItem("", false),
fIcon(NULL)
{ {
team_info info; team_info info;
get_team_info(team, &info); get_team_info(team, &info);
@@ -42,89 +200,8 @@ TeamListItem::TeamListItem(team_id team)
} }
TeamListItem::~TeamListItem()
{
delete fIcon;
}
void
TeamListItem::DrawItem(BView* owner, BRect frame, bool complete)
{
BRect rect = frame;
if (fIcon) {
rgb_color highColor = owner->HighColor();
rgb_color lowColor = owner->LowColor();
if (IsSelected() || complete) {
// Draw the background...
if (IsSelected())
owner->SetLowColor(tint_color(lowColor, B_DARKEN_2_TINT));
owner->FillRect(rect, B_SOLID_LOW);
}
BPoint point(rect.left + 2.0f,
rect.top + (rect.Height() - B_MINI_ICON) / 2.0f);
// Draw icon
owner->SetDrawingMode(B_OP_ALPHA);
owner->DrawBitmap(fIcon, point);
owner->SetDrawingMode(B_OP_COPY);
owner->MovePenTo(rect.left + B_MINI_ICON + 8.0f, frame.top + fBaselineOffset);
if (!IsEnabled())
owner->SetHighColor(tint_color(owner->HighColor(), B_LIGHTEN_2_TINT));
else
owner->SetHighColor(0, 0, 0);
owner->DrawString(Text());
owner->SetHighColor(highColor);
owner->SetLowColor(lowColor);
} else
// No icon, fallback on plain StringItem...
BStringItem::DrawItem(owner, rect, complete);
}
void
TeamListItem::Update(BView* owner, const BFont* font)
{
BStringItem::Update(owner, font);
if (Height() < B_MINI_ICON + 4.0f)
SetHeight(B_MINI_ICON + 4.0f);
font_height fontHeight;
font->GetHeight(&fontHeight);
fBaselineOffset = fontHeight.ascent +
+ (Height() - ceilf(fontHeight.ascent + fontHeight.descent)) / 2.0f;
}
/* static */
int
TeamListItem::Compare(const void* a, const void* b)
{
const BListItem*itemA = *static_cast<const BListItem* const *>(a);
const BListItem*itemB = *static_cast<const BListItem* const *>(b);
const TeamListItem*teamItemA = dynamic_cast<const TeamListItem*>(itemA);
const TeamListItem*teamItemB = dynamic_cast<const TeamListItem*>(itemB);
if (teamItemA != NULL && teamItemB != NULL) {
return teamItemA->fTeamInfo.team - teamItemB->fTeamInfo.team;
}
return 0;
}
status_t status_t
TeamListItem::_SetTo(team_info& info) TeamRow::_SetTo(team_info& info)
{ {
BPath systemPath; BPath systemPath;
team_info teamInfo = fTeamInfo = info; team_info teamInfo = fTeamInfo = info;
@@ -156,39 +233,61 @@ TeamListItem::_SetTo(team_info& info)
entry.GetRef(&appInfo.ref); entry.GetRef(&appInfo.ref);
} }
SetText(teamInfo.args); BBitmap* icon = new BBitmap(BRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1), B_RGBA32);
fIcon = new BBitmap(BRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1), B_RGBA32); status = BNodeInfo::GetTrackerIcon(&appInfo.ref, icon, B_MINI_ICON);
status = BNodeInfo::GetTrackerIcon(&appInfo.ref, fIcon, B_MINI_ICON);
if (status != B_OK) { if (status != B_OK) {
BMimeType genericAppType(B_APP_MIME_TYPE); BMimeType genericAppType(B_APP_MIME_TYPE);
status = genericAppType.GetIcon(fIcon, B_MINI_ICON); status = genericAppType.GetIcon(icon, B_MINI_ICON);
} }
if (status != B_OK) { if (status != B_OK) {
delete fIcon; delete icon;
fIcon = NULL; icon = NULL;
} }
BString tmp;
tmp << teamInfo.team;
SetField(new BBitmapStringField(icon, teamInfo.args), kNameColumn);
SetField(new BStringField(tmp), kIDColumn);
tmp = "";
tmp << teamInfo.thread_count;
SetField(new BStringField(tmp), kThreadCountColumn);
return status; return status;
} }
// #pragma mark -
// #pragma mark - TeamsListView
TeamsListView::TeamsListView(BRect frame, const char* name) TeamsListView::TeamsListView(BRect frame, const char* name)
: :
BListView(frame, name, B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL), Inherited(frame, name, B_FOLLOW_ALL, 0, B_NO_BORDER, true),
fUpdateRunner(NULL) fUpdateRunner(NULL)
{ {
AddColumn(new TeamsColumn("Name", 400, 100, 600,
B_TRUNCATE_BEGINNING), kNameColumn);
AddColumn(new TeamsColumn("ID", 80, 40, 100,
B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT), kIDColumn);
/*
AddColumn(new TeamsColumn("Thread count", 100, 50, 500,
B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT), kThreadCountColumn);
*/
SetSortingEnabled(false);
team_info tmi; team_info tmi;
get_team_info(B_CURRENT_TEAM, &tmi); get_team_info(B_CURRENT_TEAM, &tmi);
fThisTeam = tmi.team; fThisTeam = tmi.team;
/*
#ifdef __HAIKU__ #ifdef __HAIKU__
SetFlags(Flags() | B_SUBPIXEL_PRECISE); SetFlags(Flags() | B_SUBPIXEL_PRECISE);
#endif #endif
*/
} }
@@ -201,7 +300,8 @@ TeamsListView::~TeamsListView()
void void
TeamsListView::AttachedToWindow() TeamsListView::AttachedToWindow()
{ {
BListView::AttachedToWindow(); Inherited::AttachedToWindow();
TeamsColumn::InitTextMargin(ScrollView());
_InitList(); _InitList();
@@ -215,18 +315,14 @@ TeamsListView::AttachedToWindow()
void void
TeamsListView::DetachedFromWindow() TeamsListView::DetachedFromWindow()
{ {
BListView::DetachedFromWindow(); Inherited::DetachedFromWindow();
be_roster->StopWatching(this); be_roster->StopWatching(this);
delete fUpdateRunner; delete fUpdateRunner;
fUpdateRunner = NULL; fUpdateRunner = NULL;
// free all items, they will be retrieved again in AttachedToWindow() Clear(); // MakeEmpty();
for (int32 i = CountItems(); i-- > 0;) {
delete ItemAt(i);
}
MakeEmpty();
} }
@@ -244,12 +340,12 @@ TeamsListView::MessageReceived(BMessage* message)
if (message->FindInt32("be:team", &team) != B_OK) if (message->FindInt32("be:team", &team) != B_OK)
break; break;
TeamListItem* item = new(std::nothrow) TeamListItem(team); TeamRow* row = new(std::nothrow) TeamRow(team);
if (item != NULL) { if (row != NULL) {
if (!AddItem(item)) AddRow(row);
delete item; /*else
else
SortItems(&TeamListItem::Compare); SortItems(&TeamListItem::Compare);
*/
} }
break; break;
} }
@@ -260,30 +356,30 @@ TeamsListView::MessageReceived(BMessage* message)
if (message->FindInt32("be:team", &team) != B_OK) if (message->FindInt32("be:team", &team) != B_OK)
break; break;
TeamListItem* item = FindItem(team); TeamRow* row = FindTeamRow(team);
if (item != NULL) { if (row != NULL) {
RemoveItem(item); RemoveRow(row);
delete item; delete row;
} }
break; break;
} }
default: default:
BListView::MessageReceived(message); Inherited::MessageReceived(message);
} }
} }
TeamListItem* TeamRow*
TeamsListView::FindItem(team_id teamId) TeamsListView::FindTeamRow(team_id teamId)
{ {
for (int32 i = CountItems(); i-- > 0;) { for (int32 i = CountRows(); i-- > 0;) {
TeamListItem* item = dynamic_cast<TeamListItem*>(ItemAt(i)); TeamRow* row = dynamic_cast<TeamRow*>(RowAt(i));
if (item == NULL) if (row == NULL)
continue; continue;
if (item->TeamID() == teamId) if (row->TeamID() == teamId)
return item; return row;
} }
return NULL; return NULL;
@@ -297,8 +393,8 @@ TeamsListView::_InitList()
team_info tmi; team_info tmi;
while (get_next_team_info(&tmi_cookie, &tmi) == B_OK) { while (get_next_team_info(&tmi_cookie, &tmi) == B_OK) {
TeamListItem* item = new(std::nothrow) TeamListItem(tmi); TeamRow* row = new(std::nothrow) TeamRow(tmi);
if (item == NULL) { if (row == NULL) {
// Memory issue. Bail out. // Memory issue. Bail out.
break; break;
} }
@@ -306,15 +402,11 @@ TeamsListView::_InitList()
if (tmi.team == B_SYSTEM_TEAM || if (tmi.team == B_SYSTEM_TEAM ||
tmi.team == fThisTeam) { tmi.team == fThisTeam) {
// We don't support debugging kernel and... ourself! // We don't support debugging kernel and... ourself!
item->SetEnabled(false); row->SetEnabled(false);
} }
if (!AddItem(item)) AddRow(row);
delete item;
} }
// SortItems(&TeamListItem::Compare);
} }
@@ -323,44 +415,37 @@ TeamsListView::_UpdateList()
{ {
int32 tmi_cookie = 0; int32 tmi_cookie = 0;
team_info tmi; team_info tmi;
TeamListItem* item; TeamRow* row;
int32 index = 0; int32 index = 0;
// NOTA: assuming get_next_team_info() returns teams ordered by team ID... // NOTA: assuming get_next_team_info() returns teams ordered by team ID...
while (get_next_team_info(&tmi_cookie, &tmi) == B_OK) { while (get_next_team_info(&tmi_cookie, &tmi) == B_OK) {
item = (TeamListItem*) ItemAt(index); row = dynamic_cast<TeamRow*>(RowAt(index));
while (item && tmi.team > item->TeamID()) { while (row && tmi.team > row->TeamID()) {
RemoveItem(item); RemoveRow(row);
delete item; delete row;
item = (TeamListItem*) ItemAt(index); row = dynamic_cast<TeamRow*>(RowAt(index));
} }
if (item == NULL || tmi.team != item->TeamID()) { if (row == NULL || tmi.team != row->TeamID()) {
// Team not found in previously known teams list: insert a new item // Team not found in previously known teams list: insert a new row
TeamListItem* newItem = new(std::nothrow) TeamListItem(tmi); TeamRow* newRow = new(std::nothrow) TeamRow(tmi);
if (newItem != NULL) { if (newRow != NULL) {
bool added; if (row == NULL) {
// No row found with bigger team id: append at list end
if (item == NULL) { AddRow(newRow);
// No item found with bigger team id: append at list end
added = AddItem(newItem);
} else } else
added = AddItem(newItem, index); AddRow(newRow, index);
if (!added) {
// AddItem() failed! Memory Issue?
delete newItem;
}
} }
} }
index++; // Move list sync head. index++; // Move list sync head.
} }
// Remove tail list items, if we don't walk list thru the end // Remove tail list rows, if we don't walk list thru the end
while ((item = (TeamListItem*) ItemAt(index)) != NULL) { while ((row = dynamic_cast<TeamRow*>(RowAt(index))) != NULL) {
RemoveItem(item); RemoveRow(row);
delete item; delete row;
item = (TeamListItem*) ItemAt(++index); row = dynamic_cast<TeamRow*>(RowAt(++index));
} }
} }
@@ -1,50 +1,93 @@
/* /*
* Copyright 2009-2010, Philippe Houdoin, [email protected]. All rights reserved. * Copyright 2009-2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT license.
*
* Authors:
* Philippe Houdoin
*/ */
#ifndef TEAMS_LIST_ITEM_H #ifndef TEAMS_LIST_ITEM_H
#define TEAMS_LIST_ITEM_H #define TEAMS_LIST_ITEM_H
#include <ListView.h> #include <ColumnListView.h>
#include <StringItem.h> #include <ColumnTypes.h>
#include <OS.h> #include <OS.h>
class BBitmap; class BBitmap;
class BMessageRunner; class BMessageRunner;
class TeamListItem : public BStringItem { // A field type displaying both a bitmap and a string so that the
// tree display looks nicer (both text and bitmap are indented)
class BBitmapStringField : public BStringField {
typedef BStringField Inherited;
public: public:
TeamListItem(team_info & teamInfo); BBitmapStringField(BBitmap* bitmap,
TeamListItem(team_id teamId); const char* string);
virtual ~TeamListItem(); virtual ~BBitmapStringField();
void SetBitmap(BBitmap* bitmap);
const BBitmap* Bitmap() const
{ return fBitmap; }
private:
BBitmap* fBitmap;
};
// BColumn for TeamsListView which knows how to render
// a BBitmapStringField
class TeamsColumn : public BTitledColumn {
typedef BTitledColumn Inherited;
public:
TeamsColumn(const char* title,
float width, float minWidth,
float maxWidth, uint32 truncateMode,
alignment align = B_ALIGN_LEFT);
virtual void DrawField(BField* field, BRect rect,
BView* parent);
virtual float GetPreferredWidth(BField* field, BView* parent) const;
virtual bool AcceptsField(const BField* field) const;
static void InitTextMargin(BView* parent);
private:
uint32 fTruncateMode;
static float sTextMargin;
};
class TeamRow : public BRow {
typedef BRow Inherited;
public:
TeamRow(team_info & teamInfo);
TeamRow(team_id teamId);
public: public:
virtual void DrawItem(BView *owner, BRect itemRect, team_id TeamID() const { return fTeamInfo.team; }
bool drawEverything = false);
virtual void Update(BView *owner, const BFont *font);
team_id TeamID() { return fTeamInfo.team; }; virtual void SetEnabled(bool enabled) { fEnabled = enabled; }
bool IsEnabled() const { return fEnabled; }
static int Compare(const void* a, const void* b);
private: private:
status_t _SetTo(team_info & info); status_t _SetTo(team_info & info);
private: private:
bool fEnabled;
team_info fTeamInfo; team_info fTeamInfo;
BBitmap * fIcon;
float fBaselineOffset;
}; };
class TeamsListView : public BListView { class TeamsListView : public BColumnListView {
typedef BColumnListView Inherited;
public: public:
TeamsListView(BRect frame, const char* name); TeamsListView(BRect frame, const char* name);
virtual ~TeamsListView(); virtual ~TeamsListView();
TeamListItem* FindItem(team_id teamId); TeamRow* FindTeamRow(team_id teamId);
protected: protected:
virtual void AttachedToWindow(); virtual void AttachedToWindow();
@@ -60,12 +60,10 @@ TeamsWindow::MessageReceived(BMessage* message)
switch (message->what) { switch (message->what) {
case kMsgDebugThisTeam: case kMsgDebugThisTeam:
{ {
TeamListItem* item = dynamic_cast<TeamListItem*>(fTeamsListView->ItemAt( TeamRow* row = dynamic_cast<TeamRow*>(fTeamsListView->CurrentSelection());
fTeamsListView->CurrentSelection())); if (row != NULL) {
if (item != NULL) {
BMessage message(MSG_DEBUG_THIS_TEAM); BMessage message(MSG_DEBUG_THIS_TEAM);
message.AddInt32("team", item->TeamID()); message.AddInt32("team", row->TeamID());
be_app_messenger.SendMessage(&message); be_app_messenger.SendMessage(&message);
} }
break; break;
@@ -109,21 +107,11 @@ TeamsWindow::_Init()
// Add a teams list view // Add a teams list view
frame = Bounds(); frame = Bounds();
frame.right -= B_V_SCROLL_BAR_WIDTH; frame.InsetBy(-1, -1);
fTeamsListView = new TeamsListView(frame, "TeamsList"); fTeamsListView = new TeamsListView(frame, "TeamsList");
fTeamsListView->SetInvocationMessage(new BMessage(kMsgDebugThisTeam)); fTeamsListView->SetInvocationMessage(new BMessage(kMsgDebugThisTeam));
BScrollView * teamsScroller = new BScrollView("TeamsListScroller", AddChild(fTeamsListView);
fTeamsListView, B_FOLLOW_ALL_SIDES, 0, false, true, B_NO_BORDER);
AddChild(teamsScroller);
// small visual tweak
if (BScrollBar* scrollBar = teamsScroller->ScrollBar(B_VERTICAL)) {
scrollBar->MoveBy(0, -1);
scrollBar->ResizeBy(0, -(B_H_SCROLL_BAR_HEIGHT - 2));
}
} }
@@ -12,7 +12,7 @@ class BListView;
class BFile; class BFile;
class BMessage; class BMessage;
class SettingsManager; class SettingsManager;
class TeamsListView;
class TeamsWindow : public BWindow { class TeamsWindow : public BWindow {
public: public:
@@ -32,7 +32,7 @@ private:
status_t _SaveSettings(); status_t _SaveSettings();
private: private:
BListView* fTeamsListView; TeamsListView* fTeamsListView;
SettingsManager* fSettingsManager; SettingsManager* fSettingsManager;
}; };